Получить кадр из видео с помощью libvlc smem и преобразовать его в opencv Mat. (C++)
[ОБНОВЛЕНО С ЧАСТИЧНЫМ ОТВЕТОМ]
Вот мой код:
void cbVideoPrerender(void *p_video_data, uint8_t **pp_pixel_buffer, int size) {
// Locking
imageMutex.lock();
videoBuffer = (uint8_t *)malloc(size);
*pp_pixel_buffer = videoBuffer;
}
void cbVideoPostrender(void *p_video_data, uint8_t *p_pixel_buffer
, int width, int height, int pixel_pitch, int size, int64_t pts) {
// Unlocking
imageMutex.unlock();
Mat img = Mat(Size(width,height), CV_8UC3, p_pixel_buffer);
//cvtColor(img,img,CV_RGB2BGR);
}
int main(int argc, char ** argv)
{
libvlc_instance_t * inst;
char smem_options[1000];
sprintf(smem_options
, "#transcode{vcodec=RV24}:smem{"
"video-prerender-callback=%lld,"
"video-postrender-callback=%lld,"
"video-data=%lld,"
"no-time-sync},"
, (long long int)(intptr_t)(void*)&cbVideoPrerender
, (long long int)(intptr_t)(void*)&cbVideoPostrender //This would normally be useful data, 100 is just test data
, (long long int)200 //Test data
);
const char * const vlc_args[] = {
"-I", "dummy", // Don't use any interface
"--ignore-config", // Don't use VLC's config
"--extraintf=logger", // Log anything
"--verbose=1", // Be verbose
"--sout", smem_options // Stream to memory
};
// We launch VLC
inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
...
return 0;
}
ВОПРОС ОБНОВЛЕНО
Я проверил, что две мои функции обратного вызова кажутся правильно выполненными.
_Какой тип данных выводит RV32? Подходит ли он для CV_8U3C (здесь нужен беззнаковый 8-битный 3-канальный канал?
_ Мне нужно добавить шаг в мой класс Mat? (Шаг - Количество байтов, занимаемых каждой строкой матрицы)
UPDATED2
Я изменил RV32 на RV24, что имеет больше смысла. Я добавляю cvtColor, потому что матрице Mat требуется пиксель BGR, а не RGB, но изображение по-прежнему отображается неправильно.
_Есть ли vcodec, который выдаст мне формат YUV в качестве выходных данных, чтобы я мог проверить данные пикселей перед тем, как пытаться вывести opencv:: Mat img?
[РЕДАКТИРОВАТЬ ВЫХОД IMG] (Изменяя тип vlc на четырехканальный CV_8UC4 (не знаю почему), мы можем почти видеть кадр, но в действительно низком качестве, почему это так?
[РЕШЕНИЕ]
Я обнаружил, что изображения в начале моего видео были низкого качества, поэтому мой Mat imshow() показал мне такую уродливую вещь, что приведенный выше код должен работать сейчас (по-видимому, нет необходимости в cvtColor)
1 ответ
Во-первых, быстрое предупреждение: начиная с VLC2.2 (текущая версия git, которая скоро выйдет), параметром size является size_t. Нет никакого API для smem (пока? Надеюсь, это изменится), что отстой, так что это молча сломало бы ваше приложение.
Затем краткий комментарий о параметре "data": он должен содержать то, что вам нужно для обработки. Это указатель на структуру, экземпляр класса, вы называете это. Я сильно сомневаюсь, что передача long long будет работать на 32-битной машине, так как вы будете форсировать 64-битные данные во что-то, что может содержать только 32. Что вам нужно сделать, это объявить структуру и сохранить в ней то, что вам нужно. Вот хороший пример:
struct MyParamStruct
{
YourMutexType imageMutex; // Here mutex is not a global variable anymore
int otherParam; // You can use this to store the value 200 that you were passing before
};
//...
// Init the struct somewhere
MyParamStruct* param = new MyStructParam;
param->otherParam = 200;
//...
sprintf(smem_options
, "#transcode{vcodec=h264}:smem{"
"video-prerender-callback=%lld,"
"video-postrender-callback=%lld,"
"video-data=%lld,"
"no-time-sync},"
, (long long int)(intptr_t)(void*)&cbVideoPrerender
, (long long int)(intptr_t)(void*)&cbVideoPostrender //This would normally be useful data, 100 is just test data
, (long long int)(intptr_t)(void*)param
);
Насчет использования мьютекса, это выглядит хорошо для меня. На самом деле кажется, что у вас нет проблем с параллелизмом, поскольку вы синхронно выделяете новый буфер для каждого кадра. Если бы вы использовали предварительно выделенный буфер каждый раз, вам нужно было бы рассмотреть возможность блокировки при выходе из функции пост-рендеринга.
На самом деле я даже не уверен, что именно является пустым указателем p_video_data.
Это зависит от вашего формата изображения. Для H264 это будет зависеть от формата пикселя, который будет выводиться декодером. Поскольку вы запрашиваете выходной сигнал H264, вполне вероятно, что вы получите плоский пиксельный формат, хотя точный тип будет зависеть от вашего профиля H264.
Если вы ожидаете получить в результате rawdata (что, по-видимому, имеет место, поскольку CV_8UC3, похоже, ссылается на необработанное 3-канальное изображение, после быстрого взгляда на Google), я бы порекомендовал вам переключиться на RV32:#transcode{vcodec=RV32}
То, что вам нужно передать модулю транскодирования, это ваш вывод fourcc, VLC позаботится о вводе за вас:)
Обновить
Я понятия не имею, принимает ли класс Mat ваш указатель, но вы также можете проверить это.
Обновление 2
Чтобы ответить на ваш следующий вопрос о том, что такое RV32:
/* 24 bits RGB */
#define VLC_CODEC_RGB24 VLC_FOURCC('R','V','2','4')
/* 24 bits RGB padded to 32 bits */
#define VLC_CODEC_RGB32 VLC_FOURCC('R','V','3','2')
/* 32 bits RGBA */
#define VLC_CODEC_RGBA VLC_FOURCC('R','G','B','A')
Если вы ожидаете только 3 байта, то вам, вероятно, стоит попробовать RV24! Я, вероятно, должен был предположить, что с самого начала, так как 8CU3 определенно предлагает только 3 байта...