From a61b19b524cd2b66a7c43e67edd7cc780bf46cbb Mon Sep 17 00:00:00 2001
From: Alexander Alekhin <alexander.alekhin@itseez.com>
Date: Wed, 2 Mar 2016 17:54:17 +0300
Subject: [PATCH] backport ffmpeg fixes

Signed-off-by: Bernd Kuhls <bernd.kuhls@t-online.de>
Downloaded from upstream commit:
https://github.com/Itseez/opencv/commit/a61b19b524cd2b66a7c43e67edd7cc780bf46cbb
---
 modules/highgui/src/cap_ffmpeg_impl.hpp | 364 +++++++++++++++++++++++++++-----
 1 file changed, 314 insertions(+), 50 deletions(-)

diff --git a/modules/highgui/src/cap_ffmpeg_impl.hpp b/modules/highgui/src/cap_ffmpeg_impl.hpp
index 1b79870..6df542a 100644
--- a/modules/highgui/src/cap_ffmpeg_impl.hpp
+++ b/modules/highgui/src/cap_ffmpeg_impl.hpp
@@ -118,11 +118,6 @@ extern "C" {
 #define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
 #endif
 
-/* PIX_FMT_RGBA32 macro changed in newer ffmpeg versions */
-#ifndef PIX_FMT_RGBA32
-#define PIX_FMT_RGBA32 PIX_FMT_RGB32
-#endif
-
 #define CALC_FFMPEG_VERSION(a,b,c) ( a<<16 | b<<8 | c )
 
 #if defined WIN32 || defined _WIN32
@@ -132,6 +127,11 @@ extern "C" {
     #include <stdio.h>
     #include <sys/types.h>
     #include <sys/sysctl.h>
+    #include <sys/time.h>
+#if defined __APPLE__
+    #include <mach/clock.h>
+    #include <mach/mach.h>
+#endif
 #endif
 
 #ifndef MIN
@@ -156,6 +156,155 @@ extern "C" {
 #  define CV_CODEC(name) name
 #endif
 
+#if LIBAVUTIL_BUILD < (LIBAVUTIL_VERSION_MICRO >= 100 \
+    ? CALC_FFMPEG_VERSION(51, 74, 100) : CALC_FFMPEG_VERSION(51, 42, 0))
+#define AVPixelFormat PixelFormat
+#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24
+#define AV_PIX_FMT_RGB24 PIX_FMT_RGB24
+#define AV_PIX_FMT_GRAY8 PIX_FMT_GRAY8
+#define AV_PIX_FMT_YUV422P PIX_FMT_YUV422P
+#define AV_PIX_FMT_YUV420P PIX_FMT_YUV420P
+#define AV_PIX_FMT_YUV444P PIX_FMT_YUV444P
+#define AV_PIX_FMT_YUVJ420P PIX_FMT_YUVJ420P
+#define AV_PIX_FMT_GRAY16LE PIX_FMT_GRAY16LE
+#define AV_PIX_FMT_GRAY16BE PIX_FMT_GRAY16BE
+#endif
+
+#if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
+    ? CALC_FFMPEG_VERSION(52, 38, 100) : CALC_FFMPEG_VERSION(52, 13, 0))
+#define USE_AV_FRAME_GET_BUFFER 1
+#else
+#define USE_AV_FRAME_GET_BUFFER 0
+#ifndef AV_NUM_DATA_POINTERS // required for 0.7.x/0.8.x ffmpeg releases
+#define AV_NUM_DATA_POINTERS 4
+#endif
+#endif
+
+
+#ifndef USE_AV_INTERRUPT_CALLBACK
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 21, 0)
+#define USE_AV_INTERRUPT_CALLBACK 1
+#else
+#define USE_AV_INTERRUPT_CALLBACK 0
+#endif
+#endif
+
+#if USE_AV_INTERRUPT_CALLBACK
+#define LIBAVFORMAT_INTERRUPT_TIMEOUT_MS 30000
+
+#ifdef WIN32
+// http://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows
+
+static
+inline LARGE_INTEGER get_filetime_offset()
+{
+    SYSTEMTIME s;
+    FILETIME f;
+    LARGE_INTEGER t;
+
+    s.wYear = 1970;
+    s.wMonth = 1;
+    s.wDay = 1;
+    s.wHour = 0;
+    s.wMinute = 0;
+    s.wSecond = 0;
+    s.wMilliseconds = 0;
+    SystemTimeToFileTime(&s, &f);
+    t.QuadPart = f.dwHighDateTime;
+    t.QuadPart <<= 32;
+    t.QuadPart |= f.dwLowDateTime;
+    return t;
+}
+
+static
+inline void get_monotonic_time(timespec *tv)
+{
+    LARGE_INTEGER           t;
+    FILETIME				f;
+    double                  microseconds;
+    static LARGE_INTEGER    offset;
+    static double           frequencyToMicroseconds;
+    static int              initialized = 0;
+    static BOOL             usePerformanceCounter = 0;
+
+    if (!initialized)
+    {
+        LARGE_INTEGER performanceFrequency;
+        initialized = 1;
+        usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
+        if (usePerformanceCounter)
+        {
+            QueryPerformanceCounter(&offset);
+            frequencyToMicroseconds = (double)performanceFrequency.QuadPart / 1000000.;
+        }
+        else
+        {
+            offset = get_filetime_offset();
+            frequencyToMicroseconds = 10.;
+        }
+    }
+
+    if (usePerformanceCounter)
+    {
+        QueryPerformanceCounter(&t);
+    } else {
+        GetSystemTimeAsFileTime(&f);
+        t.QuadPart = f.dwHighDateTime;
+        t.QuadPart <<= 32;
+        t.QuadPart |= f.dwLowDateTime;
+    }
+
+    t.QuadPart -= offset.QuadPart;
+    microseconds = (double)t.QuadPart / frequencyToMicroseconds;
+    t.QuadPart = microseconds;
+    tv->tv_sec = t.QuadPart / 1000000;
+    tv->tv_nsec = (t.QuadPart % 1000000) * 1000;
+}
+#else
+static
+inline void get_monotonic_time(timespec *time)
+{
+#if defined(__APPLE__) && defined(__MACH__)
+    clock_serv_t cclock;
+    mach_timespec_t mts;
+    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+    clock_get_time(cclock, &mts);
+    mach_port_deallocate(mach_task_self(), cclock);
+    time->tv_sec = mts.tv_sec;
+    time->tv_nsec = mts.tv_nsec;
+#else
+    clock_gettime(CLOCK_MONOTONIC, time);
+#endif
+}
+#endif
+
+static
+inline timespec get_monotonic_time_diff(timespec start, timespec end)
+{
+    timespec temp;
+    if (end.tv_nsec - start.tv_nsec < 0)
+    {
+        temp.tv_sec = end.tv_sec - start.tv_sec - 1;
+        temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec;
+    }
+    else
+    {
+        temp.tv_sec = end.tv_sec - start.tv_sec;
+        temp.tv_nsec = end.tv_nsec - start.tv_nsec;
+    }
+    return temp;
+}
+
+static
+inline double get_monotonic_time_diff_ms(timespec time1, timespec time2)
+{
+    timespec delta = get_monotonic_time_diff(time1, time2);
+    double milliseconds = delta.tv_sec * 1000 + (double)delta.tv_nsec / 1000000.0;
+
+    return milliseconds;
+}
+#endif // USE_AV_INTERRUPT_CALLBACK
+
 static int get_number_of_cpus(void)
 {
 #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(52, 111, 0)
@@ -205,12 +354,36 @@ struct Image_FFMPEG
 };
 
 
+#if USE_AV_INTERRUPT_CALLBACK
+struct AVInterruptCallbackMetadata
+{
+    timespec value;
+    unsigned int timeout_after_ms;
+    int timeout;
+};
+
+static
 inline void _opencv_ffmpeg_free(void** ptr)
 {
     if(*ptr) free(*ptr);
     *ptr = 0;
 }
 
+static
+inline int _opencv_ffmpeg_interrupt_callback(void *ptr)
+{
+    AVInterruptCallbackMetadata* metadata = (AVInterruptCallbackMetadata*)ptr;
+    assert(metadata);
+
+    timespec now;
+    get_monotonic_time(&now);
+
+    metadata->timeout = get_monotonic_time_diff_ms(metadata->value, now) > metadata->timeout_after_ms;
+
+    return metadata->timeout ? -1 : 0;
+}
+#endif
+
 
 struct CvCapture_FFMPEG
 {
@@ -264,6 +437,10 @@ struct CvCapture_FFMPEG
 #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
     AVDictionary *dict;
 #endif
+
+#if USE_AV_INTERRUPT_CALLBACK
+    AVInterruptCallbackMetadata interrupt_metadata;
+#endif
 };
 
 void CvCapture_FFMPEG::init()
@@ -301,8 +478,10 @@ void CvCapture_FFMPEG::close()
 
     if( picture )
     {
-        // FFmpeg and Libav added avcodec_free_frame in different versions.
 #if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
+    ? CALC_FFMPEG_VERSION(55, 45, 101) : CALC_FFMPEG_VERSION(55, 28, 1))
+        av_frame_free(&picture);
+#elif LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
     ? CALC_FFMPEG_VERSION(54, 59, 100) : CALC_FFMPEG_VERSION(54, 28, 0))
         avcodec_free_frame(&picture);
 #else
@@ -333,11 +512,15 @@ void CvCapture_FFMPEG::close()
         ic = NULL;
     }
 
+#if USE_AV_FRAME_GET_BUFFER
+    av_frame_unref(&rgb_picture);
+#else
     if( rgb_picture.data[0] )
     {
         free( rgb_picture.data[0] );
         rgb_picture.data[0] = 0;
     }
+#endif
 
     // free last packet if exist
     if (packet.data) {
@@ -556,6 +739,16 @@ bool CvCapture_FFMPEG::open( const char* _filename )
 
     close();
 
+#if USE_AV_INTERRUPT_CALLBACK
+    /* interrupt callback */
+    interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_TIMEOUT_MS;
+    get_monotonic_time(&interrupt_metadata.value);
+
+    ic = avformat_alloc_context();
+    ic->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback;
+    ic->interrupt_callback.opaque = &interrupt_metadata;
+#endif
+
 #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
     av_dict_set(&dict, "rtsp_transport", "tcp", 0);
     int err = avformat_open_input(&ic, _filename, NULL, &dict);
@@ -619,19 +812,18 @@ bool CvCapture_FFMPEG::open( const char* _filename )
 
             video_stream = i;
             video_st = ic->streams[i];
+#if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
+    ? CALC_FFMPEG_VERSION(55, 45, 101) : CALC_FFMPEG_VERSION(55, 28, 1))
+            picture = av_frame_alloc();
+#else
             picture = avcodec_alloc_frame();
-
-            rgb_picture.data[0] = (uint8_t*)malloc(
-                    avpicture_get_size( PIX_FMT_BGR24,
-                                        enc->width, enc->height ));
-            avpicture_fill( (AVPicture*)&rgb_picture, rgb_picture.data[0],
-                            PIX_FMT_BGR24, enc->width, enc->height );
+#endif
 
             frame.width = enc->width;
             frame.height = enc->height;
             frame.cn = 3;
-            frame.step = rgb_picture.linesize[0];
-            frame.data = rgb_picture.data[0];
+            frame.step = 0;
+            frame.data = NULL;
             break;
         }
     }
@@ -668,6 +860,16 @@ bool CvCapture_FFMPEG::grabFrame()
     // get the next frame
     while (!valid)
     {
+        av_free_packet (&packet);
+
+#if USE_AV_INTERRUPT_CALLBACK
+        if (interrupt_metadata.timeout)
+        {
+            valid = false;
+            break;
+        }
+#endif
+
         int ret = av_read_frame(ic, &packet);
         if (ret == AVERROR(EAGAIN)) continue;
 
@@ -703,6 +905,11 @@ bool CvCapture_FFMPEG::grabFrame()
                 picture_pts = packet.pts != AV_NOPTS_VALUE_ && packet.pts != 0 ? packet.pts : packet.dts;
             frame_number++;
             valid = true;
+
+#if USE_AV_INTERRUPT_CALLBACK
+            // update interrupt value
+            get_monotonic_time(&interrupt_metadata.value);
+#endif
         }
         else
         {
@@ -727,38 +934,59 @@ bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int*
     if( !video_st || !picture->data[0] )
         return false;
 
-    avpicture_fill((AVPicture*)&rgb_picture, rgb_picture.data[0], PIX_FMT_RGB24,
-                   video_st->codec->width, video_st->codec->height);
-
     if( img_convert_ctx == NULL ||
         frame.width != video_st->codec->width ||
-        frame.height != video_st->codec->height )
+        frame.height != video_st->codec->height ||
+        frame.data == NULL )
     {
-        if( img_convert_ctx )
-            sws_freeContext(img_convert_ctx);
-
-        frame.width = video_st->codec->width;
-        frame.height = video_st->codec->height;
+        // Some sws_scale optimizations have some assumptions about alignment of data/step/width/height
+        // Also we use coded_width/height to workaround problem with legacy ffmpeg versions (like n0.8)
+        int buffer_width = video_st->codec->coded_width, buffer_height = video_st->codec->coded_height;
 
         img_convert_ctx = sws_getCachedContext(
-                NULL,
-                video_st->codec->width, video_st->codec->height,
+                img_convert_ctx,
+                buffer_width, buffer_height,
                 video_st->codec->pix_fmt,
-                video_st->codec->width, video_st->codec->height,
-                PIX_FMT_BGR24,
+                buffer_width, buffer_height,
+                AV_PIX_FMT_BGR24,
                 SWS_BICUBIC,
                 NULL, NULL, NULL
                 );
 
         if (img_convert_ctx == NULL)
             return false;//CV_Error(0, "Cannot initialize the conversion context!");
+
+#if USE_AV_FRAME_GET_BUFFER
+        av_frame_unref(&rgb_picture);
+        rgb_picture.format = AV_PIX_FMT_BGR24;
+        rgb_picture.width = buffer_width;
+        rgb_picture.height = buffer_height;
+        if (0 != av_frame_get_buffer(&rgb_picture, 32))
+        {
+            CV_WARN("OutOfMemory");
+            return false;
+        }
+#else
+        int aligns[AV_NUM_DATA_POINTERS];
+        avcodec_align_dimensions2(video_st->codec, &buffer_width, &buffer_height, aligns);
+        rgb_picture.data[0] = (uint8_t*)realloc(rgb_picture.data[0],
+                avpicture_get_size( AV_PIX_FMT_BGR24,
+                                    buffer_width, buffer_height ));
+        avpicture_fill( (AVPicture*)&rgb_picture, rgb_picture.data[0],
+                        AV_PIX_FMT_BGR24, buffer_width, buffer_height );
+#endif
+        frame.width = video_st->codec->width;
+        frame.height = video_st->codec->height;
+        frame.cn = 3;
+        frame.data = rgb_picture.data[0];
+        frame.step = rgb_picture.linesize[0];
     }
 
     sws_scale(
             img_convert_ctx,
             picture->data,
             picture->linesize,
-            0, video_st->codec->height,
+            0, video_st->codec->coded_height,
             rgb_picture.data,
             rgb_picture.linesize
             );
@@ -1099,10 +1327,20 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo
     uint8_t * picture_buf;
     int size;
 
+#if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
+    ? CALC_FFMPEG_VERSION(55, 45, 101) : CALC_FFMPEG_VERSION(55, 28, 1))
+    picture = av_frame_alloc();
+#else
     picture = avcodec_alloc_frame();
+#endif
     if (!picture)
         return NULL;
-    size = avpicture_get_size( (PixelFormat) pix_fmt, width, height);
+
+    picture->format = pix_fmt;
+    picture->width = width;
+    picture->height = height;
+
+    size = avpicture_get_size( (AVPixelFormat) pix_fmt, width, height);
     if(alloc){
         picture_buf = (uint8_t *) malloc(size);
         if (!picture_buf)
@@ -1111,7 +1349,7 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo
             return NULL;
         }
         avpicture_fill((AVPicture *)picture, picture_buf,
-                       (PixelFormat) pix_fmt, width, height);
+                       (AVPixelFormat) pix_fmt, width, height);
     }
     else {
     }
@@ -1211,7 +1449,7 @@ static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc,
 #endif
 
     c->gop_size = 12; /* emit one intra frame every twelve frames at most */
-    c->pix_fmt = (PixelFormat) pixel_format;
+    c->pix_fmt = (AVPixelFormat) pixel_format;
 
     if (c->codec_id == CV_CODEC(CODEC_ID_MPEG2VIDEO)) {
         c->max_b_frames = 2;
@@ -1372,12 +1610,12 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
 #endif
 
     // check parameters
-    if (input_pix_fmt == PIX_FMT_BGR24) {
+    if (input_pix_fmt == AV_PIX_FMT_BGR24) {
         if (cn != 3) {
             return false;
         }
     }
-    else if (input_pix_fmt == PIX_FMT_GRAY8) {
+    else if (input_pix_fmt == AV_PIX_FMT_GRAY8) {
         if (cn != 1) {
             return false;
         }
@@ -1390,13 +1628,13 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
         assert( input_picture );
         // let input_picture point to the raw data buffer of 'image'
         avpicture_fill((AVPicture *)input_picture, (uint8_t *) data,
-                       (PixelFormat)input_pix_fmt, width, height);
+                       (AVPixelFormat)input_pix_fmt, width, height);
 
         if( !img_convert_ctx )
         {
             img_convert_ctx = sws_getContext(width,
                                              height,
-                                             (PixelFormat)input_pix_fmt,
+                                             (AVPixelFormat)input_pix_fmt,
                                              c->width,
                                              c->height,
                                              c->pix_fmt,
@@ -1414,7 +1652,7 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
     }
     else{
         avpicture_fill((AVPicture *)picture, (uint8_t *) data,
-                       (PixelFormat)input_pix_fmt, width, height);
+                       (AVPixelFormat)input_pix_fmt, width, height);
     }
 
     picture->pts = frame_idx;
@@ -1547,10 +1785,10 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
 
     /* determine optimal pixel format */
     if (is_color) {
-        input_pix_fmt = PIX_FMT_BGR24;
+        input_pix_fmt = AV_PIX_FMT_BGR24;
     }
     else {
-        input_pix_fmt = PIX_FMT_GRAY8;
+        input_pix_fmt = AV_PIX_FMT_GRAY8;
     }
 
     /* Lookup codec_id for given fourcc */
@@ -1587,21 +1825,21 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
         break;
 #endif
     case CV_CODEC(CODEC_ID_HUFFYUV):
-        codec_pix_fmt = PIX_FMT_YUV422P;
+        codec_pix_fmt = AV_PIX_FMT_YUV422P;
         break;
     case CV_CODEC(CODEC_ID_MJPEG):
     case CV_CODEC(CODEC_ID_LJPEG):
-        codec_pix_fmt = PIX_FMT_YUVJ420P;
+        codec_pix_fmt = AV_PIX_FMT_YUVJ420P;
         bitrate_scale = 3;
         break;
     case CV_CODEC(CODEC_ID_RAWVIDEO):
-        codec_pix_fmt = input_pix_fmt == PIX_FMT_GRAY8 ||
-                        input_pix_fmt == PIX_FMT_GRAY16LE ||
-                        input_pix_fmt == PIX_FMT_GRAY16BE ? input_pix_fmt : PIX_FMT_YUV420P;
+        codec_pix_fmt = input_pix_fmt == AV_PIX_FMT_GRAY8 ||
+                        input_pix_fmt == AV_PIX_FMT_GRAY16LE ||
+                        input_pix_fmt == AV_PIX_FMT_GRAY16BE ? input_pix_fmt : AV_PIX_FMT_YUV420P;
         break;
     default:
         // good for lossy formats, MPEG, etc.
-        codec_pix_fmt = PIX_FMT_YUV420P;
+        codec_pix_fmt = AV_PIX_FMT_YUV420P;
         break;
     }
 
@@ -1826,7 +2064,7 @@ struct OutputMediaStream_FFMPEG
     void write(unsigned char* data, int size, int keyFrame);
 
     // add a video output stream to the container
-    static AVStream* addVideoStream(AVFormatContext *oc, CV_CODEC_ID codec_id, int w, int h, int bitrate, double fps, PixelFormat pixel_format);
+    static AVStream* addVideoStream(AVFormatContext *oc, CV_CODEC_ID codec_id, int w, int h, int bitrate, double fps, AVPixelFormat pixel_format);
 
     AVOutputFormat* fmt_;
     AVFormatContext* oc_;
@@ -1873,7 +2111,7 @@ void OutputMediaStream_FFMPEG::close()
     }
 }
 
-AVStream* OutputMediaStream_FFMPEG::addVideoStream(AVFormatContext *oc, CV_CODEC_ID codec_id, int w, int h, int bitrate, double fps, PixelFormat pixel_format)
+AVStream* OutputMediaStream_FFMPEG::addVideoStream(AVFormatContext *oc, CV_CODEC_ID codec_id, int w, int h, int bitrate, double fps, AVPixelFormat pixel_format)
 {
     #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 10, 0)
         AVStream* st = avformat_new_stream(oc, 0);
@@ -2011,7 +2249,7 @@ bool OutputMediaStream_FFMPEG::open(const char* fileName, int width, int height,
     oc_->max_delay = (int)(0.7 * AV_TIME_BASE); // This reduces buffer underrun warnings with MPEG
 
     // set a few optimal pixel formats for lossless codecs of interest..
-    PixelFormat codec_pix_fmt = PIX_FMT_YUV420P;
+    AVPixelFormat codec_pix_fmt = AV_PIX_FMT_YUV420P;
     int bitrate_scale = 64;
 
     // TODO -- safe to ignore output audio stream?
@@ -2150,6 +2388,10 @@ struct InputMediaStream_FFMPEG
     AVFormatContext* ctx_;
     int video_stream_id_;
     AVPacket pkt_;
+
+#if USE_AV_INTERRUPT_CALLBACK
+    AVInterruptCallbackMetadata interrupt_metadata;
+#endif
 };
 
 bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma_format, int* width, int* height)
@@ -2160,6 +2402,16 @@ bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma
     video_stream_id_ = -1;
     memset(&pkt_, 0, sizeof(AVPacket));
 
+#if USE_AV_INTERRUPT_CALLBACK
+    /* interrupt callback */
+    interrupt_metadata.timeout_after_ms = LIBAVFORMAT_INTERRUPT_TIMEOUT_MS;
+    get_monotonic_time(&interrupt_metadata.value);
+
+    ctx_ = avformat_alloc_context();
+    ctx_->interrupt_callback.callback = _opencv_ffmpeg_interrupt_callback;
+    ctx_->interrupt_callback.opaque = &interrupt_metadata;
+#endif
+
     #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 13, 0)
         avformat_network_init();
     #endif
@@ -2220,15 +2472,15 @@ bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma
 
             switch (enc->pix_fmt)
             {
-            case PIX_FMT_YUV420P:
+            case AV_PIX_FMT_YUV420P:
                 *chroma_format = ::VideoChromaFormat_YUV420;
                 break;
 
-            case PIX_FMT_YUV422P:
+            case AV_PIX_FMT_YUV422P:
                 *chroma_format = ::VideoChromaFormat_YUV422;
                 break;
 
-            case PIX_FMT_YUV444P:
+            case AV_PIX_FMT_YUV444P:
                 *chroma_format = ::VideoChromaFormat_YUV444;
                 break;
 
@@ -2276,11 +2528,23 @@ bool InputMediaStream_FFMPEG::read(unsigned char** data, int* size, int* endOfFi
     // get the next frame
     for (;;)
     {
+#if USE_AV_INTERRUPT_CALLBACK
+        if(interrupt_metadata.timeout)
+        {
+            break;
+        }
+#endif
+
         int ret = av_read_frame(ctx_, &pkt_);
 
         if (ret == AVERROR(EAGAIN))
             continue;
 
+#if USE_AV_INTERRUPT_CALLBACK
+        // update interrupt value
+        get_monotonic_time(&interrupt_metadata.value);
+#endif
+
         if (ret < 0)
         {
             if (ret == (int)AVERROR_EOF)
