[Из песочницы] OpenCV: установка таймаута на ожидание кадра в классе VideoCapture
Кому интересно — прошу под кат.
Исследования
При помощи Гугла было выяснено, что данная проблема корнями упирается в ffmpeg — там есть callback’и, которые дают информацию о разрыве соединения. Таймаут в 30 секунд установили в пулл-реквесте #6053. Проблема добавилась в следующем виде: на текущий момент cmake-сборщик скачивает файл opencv_ffmpeg.dll вместо его сборки на месте, причем инструкция по сборке с ffmpeg исчезла. Код с константами таймаута (который, по крайней мере, в Windows никаким образом не компилируется) находится в файле modules/videoio/src/cap_ffmpeg_impl.hpp:
#define LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS 30000
#define LIBAVFORMAT_INTERRUPT_READ_TIMEOUT_MS 30000
Задача, таким образом, сформировалась следующим образом:
- Заставить данный файл собираться под Windows;
- Изменить в нем константы;
- Убедиться в отсутствии проблем при работе.
Недолго думая, расскажу о том, каким образом она была решена. Для начала нужно скачать последнюю девелоперскую версию ffmpeg с заголовочными файлами, dll, и lib, и положить все это по нужным местам в source/3rdparty — ради удобства, на самом деле можно сложить куда угодно. Далее, нужно внести следующие изменения в исходные файлы OpenCV:
modules/videoio/src/cap_ffmpeg.cpp:
Закомментировать в инклудах строки, касающиеся потенциального включения cap_ffmpeg_api.hpp
//#if defined HAVE_FFMPEG && !defined WIN32
#include "cap_ffmpeg_impl.hpp"
//#else
//#include "cap_ffmpeg_api.hpp"
//#endif
В функции icvInitFFMPEG ()
...
#ifdef WIN32...
//все закомментировать, включая директивы препроцессора
#elif defined HAVE_FFMPEG
//оставить только то, что есть внутри
#endif
...
Что здесь произошло — мы отказались от использования подгрузки функциональности ffmpeg из opencv_ffmpeg.dll при помощи LoadLibrary, и переключились на внутреннюю реализацию, которая находится внутри файла cap_ffmpeg_impl.hpp.
modules/videoio/src/cap_ffmpeg_impl.hpp:
Находим вышеуказанные константы, меняем их на желаемые. В моем случае —
#define LIBAVFORMAT_INTERRUPT_OPEN_TIMEOUT_MS 2000
#define LIBAVFORMAT_INTERRUPT_READ_TIMEOUT_MS 1000
Данный файл в общем-то не создан для сборки под Windows, поэтому, кое-какая функциональность, требуемая для его компиляции, в ней отсутствует — речь идет о snprintf. Код был стянут откуда-то с StackOverflow. Вставить в любое удобное место.
#if defined(_MSC_VER) && _MSC_VER < 1900
#define snprintf c99_snprintf
#define vsnprintf c99_vsnprintf
__inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap)
{
int count = -1;
if (size != 0)
count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap);
if (count == -1)
count = _vscprintf(format, ap);
return count;
}
__inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...)
{
int count;
va_list ap;
va_start(ap, format);
count = c99_vsnprintf(outBuf, size, format, ap);
va_end(ap);
return count;
}
#endif
После внесения изменений, нужно заставить все это добро компилироваться. Для начала, нужно сгенерировать *.sln для OpenCV при помощи CMake. Попытка сборки opencv_videoio на текущий момент не сработает. Поправимо. Открываем Visual Studio-проект для модуля videoio: build/modules/videoio/opencv_videoio.sln. В include directories добавляем заголовочные файлы ffmpeg, его *.lib добавляем к линковке.
Как только opencv_videoio будет собираться, эту dll можно спокойно подкладывать вместо «стандартной». Помним, что теперь для работы также будут нужны все dll из поставки ffmpeg — без них приложение работать не будет.
Важный момент: теперь, для того, чтобы это работало, в качестве бэкэнда VideoCapture необходимо указывать cv: CAP_FFMPEG.
Результат на текущий момент — полет нормальный, багов за месяц не заметил. Однако, учитывая все вышенаписанное, они более, чем возможны, поэтому использовать только на свой страх и риск. Если есть иные способы добиться желаемого, как уже говорил, буду очень рад послушать.
Спасибо за внимание.
Комментарии (2)
14 декабря 2016 в 19:12
+1↑
↓
А причём тут реверс инженерия, если Вы с исходными кодами работали?14 декабря 2016 в 20:45
0↑
↓
Действительно, лишнее. Подумал и убрал. Спасибо за замечание:)