From 5440fd6cb43ea65a056c46b691fcdab1a425e92d Mon Sep 17 00:00:00 2001
From: Maksim Shabunin <maksim.shabunin@gmail.com>
Date: Sat, 19 Mar 2022 20:06:50 +0300
Subject: [PATCH] videoio: initial FFmpeg 5.0 support

---
 modules/videoio/src/cap_ffmpeg_impl.hpp | 442 ++++++++++++++++--------
 1 file changed, 304 insertions(+), 138 deletions(-)

diff --git a/modules/videoio/src/cap_ffmpeg_impl.hpp b/modules/videoio/src/cap_ffmpeg_impl.hpp
index 43c555309b9d..91a0f710aa63 100644
--- a/modules/videoio/src/cap_ffmpeg_impl.hpp
+++ b/modules/videoio/src/cap_ffmpeg_impl.hpp
@@ -41,6 +41,8 @@
 //M*/
 
 #include "cap_ffmpeg_legacy_api.hpp"
+#include "opencv2/core/utils/logger.hpp"
+#include "cap_interface.hpp"
 
 using namespace cv;
 
@@ -49,6 +51,7 @@ using namespace cv;
 #endif
 #include <algorithm>
 #include <limits>
+#include <string.h>
 
 #ifndef __OPENCV_BUILD
 #define CV_FOURCC(c1, c2, c3, c4) (((c1) & 255) + (((c2) & 255) << 8) + (((c3) & 255) << 16) + (((c4) & 255) << 24))
@@ -79,6 +82,7 @@ extern "C" {
 
 #include <libavutil/mathematics.h>
 #include <libavutil/opt.h>
+#include <libavutil/display.h>
 
 #if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
     ? CALC_FFMPEG_VERSION(51, 63, 100) : CALC_FFMPEG_VERSION(54, 6, 0))
@@ -88,6 +92,62 @@ extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libswscale/swscale.h>
 
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L602-L605
+#if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(58, 9, 100)
+#  define CV_FFMPEG_REGISTER
+#endif
+
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L654-L657
+#if LIBAVCODEC_BUILD < CALC_FFMPEG_VERSION(58, 9, 100)
+#  define CV_FFMPEG_LOCKMGR
+#endif
+
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L390-L392
+#if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(58, 87, 100)
+#include <libavcodec/bsf.h>
+#endif
+
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L208-L210
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(59, 0, 100)
+#  define CV_FFMPEG_FMT_CONST const
+#else
+#  define CV_FFMPEG_FMT_CONST
+#endif
+
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L623-L624
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 7, 100)
+#  define CV_FFMPEG_URL
+#endif
+
+// AVStream.codec deprecated in favor of AVStream.codecpar
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L1039-L1040
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(59, 16, 100)
+//#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(57, 33, 100)
+#  define CV_FFMPEG_CODECPAR
+#  define CV_FFMPEG_CODEC_FIELD codecpar
+#else
+#  define CV_FFMPEG_CODEC_FIELD codec
+#endif
+
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(59, 16, 100)
+#  define CV_FFMPEG_PTS_FIELD pts
+#else
+#  define CV_FFMPEG_PTS_FIELD pkt_pts
+#endif
+
+// https://github.com/FFmpeg/FFmpeg/blob/b6af56c034759b81985f8ea094e41cbd5f7fecfb/doc/APIchanges#L1757-L1758
+#if LIBAVUTIL_BUILD < CALC_FFMPEG_VERSION(52, 63, 100)
+inline static AVRational av_make_q(int num, int den)
+{
+    AVRational res;
+    res.num = num;
+    res.den = den;
+    return res;
+}
+#endif
+
+
+
 #ifdef __cplusplus
 }
 #endif
@@ -471,6 +531,15 @@ static AVRational _opencv_ffmpeg_get_sample_aspect_ratio(AVStream *stream)
 #endif
 }
 
+inline static std::string _opencv_ffmpeg_get_error_string(int error_code)
+{
+    char buf[255] = {0};
+    const int err = av_strerror(error_code, buf, 254);
+    if (err == 0)
+        return std::string(buf);
+    else
+        return std::string("Unknown error");
+}
 
 struct CvCapture_FFMPEG
 {
@@ -502,6 +571,7 @@ struct CvCapture_FFMPEG
 
     AVFormatContext * ic;
     AVCodec         * avcodec;
+    AVCodecContext  * context;
     int               video_stream;
     AVStream        * video_st;
     AVFrame         * picture;
@@ -565,6 +635,7 @@ void CvCapture_FFMPEG::init()
     img_convert_ctx = 0;
 
     avcodec = 0;
+    context = 0;
     frame_number = 0;
     eps_zero = 0.000025;
 
@@ -617,10 +688,19 @@ void CvCapture_FFMPEG::close()
 
     if( video_st )
     {
-        avcodec_close( video_st->codec );
+#ifdef CV_FFMPEG_CODECPAR
+        avcodec_close( context );
+#endif
         video_st = NULL;
     }
 
+    if (context)
+    {
+#ifdef CV_FFMPEG_CODECPAR
+        avcodec_free_context(&context);
+#endif
+    }
+
     if( ic )
     {
         avformat_close_input(&ic);
@@ -798,8 +878,10 @@ class AutoLock
 };
 #endif
 
+
 static ImplMutex _mutex;
 
+#ifdef CV_FFMPEG_LOCKMGR
 static int LockCallBack(void **mutex, AVLockOp op)
 {
     ImplMutex* localMutex = reinterpret_cast<ImplMutex*>(*mutex);
@@ -830,7 +912,7 @@ static int LockCallBack(void **mutex, AVLockOp op)
     }
     return 0;
 }
-
+#endif
 
 static void ffmpeg_log_callback(void *ptr, int level, const char *fmt, va_list vargs)
 {
@@ -881,19 +963,59 @@ class InternalFFMpegRegister
     {
         avformat_network_init();
 
+#ifdef CV_FFMPEG_REGISTER
         /* register all codecs, demux and protocols */
         av_register_all();
+#endif
 
+#ifdef CV_FFMPEG_LOCKMGR
         /* register a callback function for synchronization */
         av_lockmgr_register(&LockCallBack);
+#endif
     }
     ~InternalFFMpegRegister()
     {
+#ifdef CV_FFMPEG_LOCKMGR
         av_lockmgr_register(NULL);
+#endif
         av_log_set_callback(NULL);
     }
 };
 
+inline void fill_codec_context(AVCodecContext * enc, AVDictionary * dict)
+{
+//#ifdef FF_API_THREAD_INIT
+//  avcodec_thread_init(enc, get_number_of_cpus());
+//#else
+    enc->thread_count = get_number_of_cpus();
+//#endif
+
+    AVDictionaryEntry* avdiscard_entry = av_dict_get(dict, "avdiscard", NULL, 0);
+
+    if (avdiscard_entry)
+    {
+        if(strcmp(avdiscard_entry->value, "all") == 0)
+            enc->skip_frame = AVDISCARD_ALL;
+        else if (strcmp(avdiscard_entry->value, "bidir") == 0)
+            enc->skip_frame = AVDISCARD_BIDIR;
+        else if (strcmp(avdiscard_entry->value, "default") == 0)
+            enc->skip_frame = AVDISCARD_DEFAULT;
+        else if (strcmp(avdiscard_entry->value, "none") == 0)
+            enc->skip_frame = AVDISCARD_NONE;
+        // NONINTRA flag was introduced with version bump at revision:
+        // https://github.com/FFmpeg/FFmpeg/commit/b152152df3b778d0a86dcda5d4f5d065b4175a7b
+        // This key is supported only for FFMPEG version
+#if LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(55, 67, 100)
+        else if (strcmp(avdiscard_entry->value, "nonintra") == 0)
+            enc->skip_frame = AVDISCARD_NONINTRA;
+#endif
+        else if (strcmp(avdiscard_entry->value, "nonkey") == 0)
+            enc->skip_frame = AVDISCARD_NONKEY;
+        else if (strcmp(avdiscard_entry->value, "nonref") == 0)
+            enc->skip_frame = AVDISCARD_NONREF;
+    }
+}
+
 bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters& params)
 {
     InternalFFMpegRegister::init();
@@ -997,7 +1119,7 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
 #else
     av_dict_set(&dict, "rtsp_transport", "tcp", 0);
 #endif
-    AVInputFormat* input_format = NULL;
+    CV_FFMPEG_FMT_CONST AVInputFormat* input_format = NULL;
     AVDictionaryEntry* entry = av_dict_get(dict, "input_format", NULL, 0);
     if (entry != 0)
     {
@@ -1015,60 +1137,44 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
     err = avformat_find_stream_info(ic, NULL);
     if (err < 0)
     {
-        CV_WARN("Could not find codec parameters");
+        CV_LOG_WARNING(NULL, "Unable to read codec parameters from stream (" << _opencv_ffmpeg_get_error_string(err) << ")");
         goto exit_func;
     }
     for(i = 0; i < ic->nb_streams; i++)
     {
-        AVCodecContext* enc = ic->streams[i]->codec;
-
-//#ifdef FF_API_THREAD_INIT
-//        avcodec_thread_init(enc, get_number_of_cpus());
-//#else
-        enc->thread_count = get_number_of_cpus();
-//#endif
-
-        AVDictionaryEntry* avdiscard_entry = av_dict_get(dict, "avdiscard", NULL, 0);
-
-        if (avdiscard_entry) {
-            if(strcmp(avdiscard_entry->value, "all") == 0)
-                enc->skip_frame = AVDISCARD_ALL;
-            else if (strcmp(avdiscard_entry->value, "bidir") == 0)
-                enc->skip_frame = AVDISCARD_BIDIR;
-            else if (strcmp(avdiscard_entry->value, "default") == 0)
-                enc->skip_frame = AVDISCARD_DEFAULT;
-            else if (strcmp(avdiscard_entry->value, "none") == 0)
-                enc->skip_frame = AVDISCARD_NONE;
-            // NONINTRA flag was introduced with version bump at revision:
-            // https://github.com/FFmpeg/FFmpeg/commit/b152152df3b778d0a86dcda5d4f5d065b4175a7b
-            // This key is supported only for FFMPEG version
-#if LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(55, 67, 100)
-            else if (strcmp(avdiscard_entry->value, "nonintra") == 0)
-                enc->skip_frame = AVDISCARD_NONINTRA;
+#ifndef CV_FFMPEG_CODECPAR
+        context = ic->streams[i]->codec;
+        AVCodecID codec_id = context->codec_id;
+        AVMediaType codec_type = context->codec_type;
+#else
+        AVCodecParameters* par = ic->streams[i]->codecpar;
+        AVCodecID codec_id = par->codec_id;
+        AVMediaType codec_type = par->codec_type;
 #endif
-            else if (strcmp(avdiscard_entry->value, "nonkey") == 0)
-                enc->skip_frame = AVDISCARD_NONKEY;
-            else if (strcmp(avdiscard_entry->value, "nonref") == 0)
-                enc->skip_frame = AVDISCARD_NONREF;
-        }
 
-        if( AVMEDIA_TYPE_VIDEO == enc->codec_type && video_stream < 0)
+        if( AVMEDIA_TYPE_VIDEO == codec_type && video_stream < 0)
         {
-            CV_LOG_DEBUG(NULL, "FFMPEG: stream[" << i << "] is video stream with codecID=" << (int)enc->codec_id
-                    << " width=" << enc->width
-                    << " height=" << enc->height
+            // backup encoder' width/height
+#ifndef CV_FFMPEG_CODECPAR
+            int enc_width = context->width;
+            int enc_height = context->height;
+#else
+            int enc_width = par->width;
+            int enc_height = par->height;
+#endif
+
+            CV_LOG_DEBUG(NULL, "FFMPEG: stream[" << i << "] is video stream with codecID=" << (int)codec_id
+                    << " width=" << enc_width
+                    << " height=" << enc_height
             );
 
-            // backup encoder' width/height
-            int enc_width = enc->width;
-            int enc_height = enc->height;
 
 #if !USE_AV_HW_CODECS
             va_type = VIDEO_ACCELERATION_NONE;
 #endif
 
             // find and open decoder, try HW acceleration types specified in 'hw_acceleration' list (in order)
-            AVCodec *codec = NULL;
+            const AVCodec *codec = NULL;
             err = -1;
 #if USE_AV_HW_CODECS
             HWAccelIterator accel_iter(va_type, false/*isEncoder*/, dict);
@@ -1080,21 +1186,27 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
 #if USE_AV_HW_CODECS
                 accel_iter.parse_next();
                 AVHWDeviceType hw_type = accel_iter.hw_type();
-                enc->get_format = avcodec_default_get_format;
-                if (enc->hw_device_ctx) {
-                    av_buffer_unref(&enc->hw_device_ctx);
-                }
                 if (hw_type != AV_HWDEVICE_TYPE_NONE)
                 {
                     CV_LOG_DEBUG(NULL, "FFMPEG: trying to configure H/W acceleration: '" << accel_iter.hw_type_device_string() << "'");
                     AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
-                    codec = hw_find_codec(enc->codec_id, hw_type, av_codec_is_decoder, accel_iter.disabled_codecs().c_str(), &hw_pix_fmt);
-                    if (codec) {
+                    codec = hw_find_codec(codec_id, hw_type, av_codec_is_decoder, accel_iter.disabled_codecs().c_str(), &hw_pix_fmt);
+                    if (codec)
+                    {
+#ifdef CV_FFMPEG_CODECPAR
+                        context = avcodec_alloc_context3(codec);
+#endif
+                        CV_Assert(context);
+                        context->get_format = avcodec_default_get_format;
+                        if (context->hw_device_ctx) {
+                            av_buffer_unref(&context->hw_device_ctx);
+                        }
                         if (hw_pix_fmt != AV_PIX_FMT_NONE)
-                            enc->get_format = hw_get_format_callback; // set callback to select HW pixel format, not SW format
-                        enc->hw_device_ctx = hw_create_device(hw_type, hw_device, accel_iter.device_subname(), use_opencl != 0);
-                        if (!enc->hw_device_ctx)
+                            context->get_format = hw_get_format_callback; // set callback to select HW pixel format, not SW format
+                        context->hw_device_ctx = hw_create_device(hw_type, hw_device, accel_iter.device_subname(), use_opencl != 0);
+                        if (!context->hw_device_ctx)
                         {
+                            context->get_format = avcodec_default_get_format;
                             CV_LOG_DEBUG(NULL, "FFMPEG: ... can't create H/W device: '" << accel_iter.hw_type_device_string() << "'");
                             codec = NULL;
                         }
@@ -1106,10 +1218,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
                     AVDictionaryEntry* video_codec_param = av_dict_get(dict, "video_codec", NULL, 0);
                     if (video_codec_param == NULL)
                     {
-                        codec = avcodec_find_decoder(enc->codec_id);
+                        codec = avcodec_find_decoder(codec_id);
                         if (!codec)
                         {
-                            CV_LOG_ERROR(NULL, "Could not find decoder for codec_id=" << (int)enc->codec_id);
+                            CV_LOG_ERROR(NULL, "Could not find decoder for codec_id=" << (int)codec_id);
                         }
                     }
                     else
@@ -1121,10 +1233,26 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
                             CV_LOG_ERROR(NULL, "Could not find decoder '" << video_codec_param->value << "'");
                         }
                     }
+                    if (codec)
+                    {
+#ifdef CV_FFMPEG_CODECPAR
+                        context = avcodec_alloc_context3(codec);
+#endif
+                        CV_Assert(context);
+                    }
                 }
                 if (!codec)
+                {
+#ifdef CV_FFMPEG_CODECPAR
+                    avcodec_free_context(&context);
+#endif
                     continue;
-                err = avcodec_open2(enc, codec, NULL);
+                }
+                fill_codec_context(context, dict);
+#ifdef CV_FFMPEG_CODECPAR
+                avcodec_parameters_to_context(context, par);
+#endif
+                err = avcodec_open2(context, codec, NULL);
                 if (err >= 0) {
 #if USE_AV_HW_CODECS
                     va_type = hw_type_to_va_type(hw_type);
@@ -1146,10 +1274,10 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
             }
 
             // checking width/height (since decoder can sometimes alter it, eg. vp6f)
-            if (enc_width && (enc->width != enc_width))
-                enc->width = enc_width;
-            if (enc_height && (enc->height != enc_height))
-                enc->height = enc_height;
+            if (enc_width && (context->width != enc_width))
+                context->width = enc_width;
+            if (enc_height && (context->height != enc_height))
+                context->height = enc_height;
 
             video_stream = i;
             video_st = ic->streams[i];
@@ -1160,8 +1288,8 @@ bool CvCapture_FFMPEG::open(const char* _filename, const VideoCaptureParameters&
             picture = avcodec_alloc_frame();
 #endif
 
-            frame.width = enc->width;
-            frame.height = enc->height;
+            frame.width = context->width;
+            frame.height = context->height;
             frame.cn = 3;
             frame.step = 0;
             frame.data = NULL;
@@ -1306,7 +1434,7 @@ bool CvCapture_FFMPEG::grabFrame()
     int count_errs = 0;
     const int max_number_of_attempts = 1 << 9;
 
-    if( !ic || !video_st )  return false;
+    if( !ic || !video_st || !context )  return false;
 
     if( ic->streams[video_stream]->nb_frames > 0 &&
         frame_number > ic->streams[video_stream]->nb_frames )
@@ -1322,7 +1450,7 @@ bool CvCapture_FFMPEG::grabFrame()
 
 #if USE_AV_SEND_FRAME_API
     // check if we can receive frame from previously decoded packet
-    valid = avcodec_receive_frame(video_st->codec, picture) >= 0;
+    valid = avcodec_receive_frame(context, picture) >= 0;
 #endif
 
     // get the next frame
@@ -1372,19 +1500,19 @@ bool CvCapture_FFMPEG::grabFrame()
 
         // Decode video frame
 #if USE_AV_SEND_FRAME_API
-        if (avcodec_send_packet(video_st->codec, &packet) < 0) {
+        if (avcodec_send_packet(context, &packet) < 0) {
             break;
         }
-        ret = avcodec_receive_frame(video_st->codec, picture);
+        ret = avcodec_receive_frame(context, picture);
 #else
         int got_picture = 0;
-        avcodec_decode_video2(video_st->codec, picture, &got_picture, &packet);
+        avcodec_decode_video2(context, picture, &got_picture, &packet);
         ret = got_picture ? 0 : -1;
 #endif
         if (ret >= 0) {
             //picture_pts = picture->best_effort_timestamp;
             if( picture_pts == AV_NOPTS_VALUE_ )
-                picture_pts = picture->pkt_pts != AV_NOPTS_VALUE_ && picture->pkt_pts != 0 ? picture->pkt_pts : picture->pkt_dts;
+                picture_pts = picture->CV_FFMPEG_PTS_FIELD != AV_NOPTS_VALUE_ && picture->CV_FFMPEG_PTS_FIELD != 0 ? picture->CV_FFMPEG_PTS_FIELD : picture->pkt_dts;
 
             valid = true;
         } else if (ret == AVERROR(EAGAIN)) {
@@ -1415,7 +1543,7 @@ bool CvCapture_FFMPEG::grabFrame()
 
 bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step, int* width, int* height, int* cn)
 {
-    if (!video_st)
+    if (!video_st || !context)
         return false;
 
     if (rawMode || flag == extraDataIdx)
@@ -1428,8 +1556,8 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
             ret = p.data != NULL;
         }
         else if (flag == extraDataIdx) {
-            *data = ic->streams[video_stream]->codec->extradata;
-            *step = ic->streams[video_stream]->codec->extradata_size;
+            *data = ic->streams[video_stream]->CV_FFMPEG_CODEC_FIELD->extradata;
+            *step = ic->streams[video_stream]->CV_FFMPEG_CODEC_FIELD->extradata_size;
         }
         *width = *step;
         *height = 1;
@@ -1454,13 +1582,13 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
         return false;
 
     if( img_convert_ctx == NULL ||
-        frame.width != video_st->codec->width ||
-        frame.height != video_st->codec->height ||
+        frame.width != video_st->CV_FFMPEG_CODEC_FIELD->width ||
+        frame.height != video_st->CV_FFMPEG_CODEC_FIELD->height ||
         frame.data == NULL )
     {
         // 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;
+        int buffer_width = context->coded_width, buffer_height = context->coded_height;
 
         img_convert_ctx = sws_getCachedContext(
                 img_convert_ctx,
@@ -1494,8 +1622,8 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
         _opencv_ffmpeg_av_image_fill_arrays(&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.width = video_st->CV_FFMPEG_CODEC_FIELD->width;
+        frame.height = video_st->CV_FFMPEG_CODEC_FIELD->height;
         frame.cn = 3;
         frame.data = rgb_picture.data[0];
         frame.step = rgb_picture.linesize[0];
@@ -1505,7 +1633,7 @@ bool CvCapture_FFMPEG::retrieveFrame(int flag, unsigned char** data, int* step,
             img_convert_ctx,
             sw_picture->data,
             sw_picture->linesize,
-            0, video_st->codec->coded_height,
+            0, context->coded_height,
             rgb_picture.data,
             rgb_picture.linesize
             );
@@ -1529,12 +1657,12 @@ bool CvCapture_FFMPEG::retrieveHWFrame(cv::OutputArray output)
 {
 #if USE_AV_HW_CODECS
     // check that we have HW frame in GPU memory
-    if (!picture || !picture->hw_frames_ctx) {
+    if (!picture || !picture->hw_frames_ctx || !context) {
         return false;
     }
 
     // GPU color conversion NV12->BGRA, from GPU media buffer to GPU OpenCL buffer
-    return hw_copy_frame_to_umat(video_st->codec->hw_device_ctx, picture, output);
+    return hw_copy_frame_to_umat(context->hw_device_ctx, picture, output);
 #else
     CV_UNUSED(output);
     return false;
@@ -1543,7 +1671,7 @@ bool CvCapture_FFMPEG::retrieveHWFrame(cv::OutputArray output)
 
 double CvCapture_FFMPEG::getProperty( int property_id ) const
 {
-    if( !video_st ) return 0;
+    if( !video_st || !context ) return 0;
 
     double codec_tag = 0;
     CV_CODEC_ID codec_id = AV_CODEC_ID_NONE;
@@ -1570,8 +1698,8 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
     case CAP_PROP_FPS:
         return get_fps();
     case CAP_PROP_FOURCC:
-        codec_id = video_st->codec->codec_id;
-        codec_tag = (double) video_st->codec->codec_tag;
+        codec_id = video_st->CV_FFMPEG_CODEC_FIELD->codec_id;
+        codec_tag = (double) video_st->CV_FFMPEG_CODEC_FIELD->codec_tag;
 
         if(codec_tag || codec_id == AV_CODEC_ID_NONE)
         {
@@ -1591,7 +1719,11 @@ double CvCapture_FFMPEG::getProperty( int property_id ) const
         return _opencv_ffmpeg_get_sample_aspect_ratio(ic->streams[video_stream]).den;
     case CAP_PROP_CODEC_PIXEL_FORMAT:
     {
+#ifdef CV_FFMPEG_CODECPAR
+        AVPixelFormat pix_fmt = (AVPixelFormat)video_st->codecpar->format;
+#else
         AVPixelFormat pix_fmt = video_st->codec->pix_fmt;
+#endif
         unsigned int fourcc_tag = avcodec_pix_fmt_to_codec_tag(pix_fmt);
         return (fourcc_tag == 0) ? (double)-1 : (double)fourcc_tag;
     }
@@ -1671,7 +1803,7 @@ double CvCapture_FFMPEG::get_fps() const
 
     if (fps < eps_zero)
     {
-        fps = 1.0 / r2d(ic->streams[video_stream]->codec->time_base);
+        fps = 1.0 / r2d(ic->streams[video_stream]->time_base);
     }
 #endif
     return fps;
@@ -1703,7 +1835,16 @@ double CvCapture_FFMPEG::dts_to_sec(int64_t dts) const
 void CvCapture_FFMPEG::get_rotation_angle()
 {
     rotation_angle = 0;
-#if LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(57, 68, 100)
+    const uint8_t *data = 0;
+    data = av_stream_get_side_data(video_st, AV_PKT_DATA_DISPLAYMATRIX, NULL);
+    if (data)
+    {
+        rotation_angle = cvRound(av_display_rotation_get((const int32_t*)data));
+        if (rotation_angle < 0)
+            rotation_angle += 360;
+    }
+#elif LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
     AVDictionaryEntry *rotate_tag = av_dict_get(video_st->metadata, "rotate", NULL, 0);
     if (rotate_tag != NULL)
         rotation_angle = atoi(rotate_tag->value);
@@ -1712,6 +1853,7 @@ void CvCapture_FFMPEG::get_rotation_angle()
 
 void CvCapture_FFMPEG::seek(int64_t _frame_number)
 {
+    CV_Assert(context);
     _frame_number = std::min(_frame_number, get_total_frames());
     int delta = 16;
 
@@ -1728,7 +1870,7 @@ void CvCapture_FFMPEG::seek(int64_t _frame_number)
         double  time_base  = r2d(ic->streams[video_stream]->time_base);
         time_stamp += (int64_t)(sec / time_base + 0.5);
         if (get_total_frames() > 1) av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_BACKWARD);
-        avcodec_flush_buffers(ic->streams[video_stream]->codec);
+        avcodec_flush_buffers(context);
         if( _frame_number > 0 )
         {
             grabFrame();
@@ -1833,7 +1975,7 @@ struct CvVideoWriter_FFMPEG
 
     void init();
 
-    AVOutputFormat  * fmt;
+    CV_FFMPEG_FMT_CONST AVOutputFormat  * fmt;
     AVFormatContext * oc;
     uint8_t         * outbuf;
     uint32_t          outbuf_size;
@@ -1842,6 +1984,7 @@ struct CvVideoWriter_FFMPEG
     AVFrame         * input_picture;
     uint8_t         * picbuf;
     AVStream        * video_st;
+    AVCodecContext  * context;
     AVPixelFormat     input_pix_fmt;
     unsigned char   * aligned_input;
     size_t            aligned_input_size;
@@ -1906,6 +2049,7 @@ void CvVideoWriter_FFMPEG::init()
     input_picture = 0;
     picbuf = 0;
     video_st = 0;
+    context = 0;
     input_pix_fmt = AV_PIX_FMT_NONE;
     aligned_input = NULL;
     aligned_input_size = 0;
@@ -1957,23 +2101,32 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo
 }
 
 /* configure video stream */
-static bool icv_configure_video_stream_FFMPEG(AVFormatContext *oc,
+static AVCodecContext * icv_configure_video_stream_FFMPEG(AVFormatContext *oc,
                                                    AVStream *st,
                                                    const AVCodec* codec,
                                                    int w, int h, int bitrate,
-                                                   double fps, AVPixelFormat pixel_format)
+                                                   double fps, AVPixelFormat pixel_format, int fourcc)
 {
+#ifdef CV_FFMPEG_CODECPAR
+    AVCodecContext *c = avcodec_alloc_context3(codec);
+#else
     AVCodecContext *c = st->codec;
+#endif
+    CV_Assert(c);
+
     int frame_rate, frame_rate_base;
 
     c->codec_id = codec->id;
     c->codec_type = AVMEDIA_TYPE_VIDEO;
+    c->codec_tag = fourcc;
 
+#ifndef CV_FFMPEG_CODECPAR
     // Set per-codec defaults
     CV_CODEC_ID c_id = c->codec_id;
     avcodec_get_context_defaults3(c, codec);
     // avcodec_get_context_defaults3 erases codec_id for some reason
     c->codec_id = c_id;
+#endif
 
     /* put sample parameters */
     int64_t lbit_rate = (int64_t)bitrate;
@@ -2016,7 +2169,12 @@ static bool icv_configure_video_stream_FFMPEG(AVFormatContext *oc,
             }
         }
         if (best == NULL)
-            return false;
+        {
+#ifdef CV_FFMPEG_CODECPAR
+            avcodec_free_context(&c);
+#endif
+            return NULL;
+        }
         c->time_base.den= best->num;
         c->time_base.num= best->den;
     }
@@ -2059,26 +2217,20 @@ static bool icv_configure_video_stream_FFMPEG(AVFormatContext *oc,
 #endif
     }
 
-#if defined(_MSC_VER)
-    AVRational avg_frame_rate = {frame_rate, frame_rate_base};
-    st->avg_frame_rate = avg_frame_rate;
-#else
-    st->avg_frame_rate = (AVRational){frame_rate, frame_rate_base};
-#endif
+    st->avg_frame_rate = av_make_q(frame_rate, frame_rate_base);
 #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 20, 0)
     st->time_base = c->time_base;
 #endif
 
-    return true;
+    return c;
 }
 
 static const int OPENCV_NO_FRAMES_WRITTEN_CODE = 1000;
 
-static int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st,
+static int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st, AVCodecContext * c,
                                       uint8_t *, uint32_t,
                                       AVFrame * picture, int frame_idx)
 {
-    AVCodecContext* c = video_st->codec;
     int ret = OPENCV_NO_FRAMES_WRITTEN_CODE;
 
 #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(57, 0, 0)
@@ -2176,9 +2328,6 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
     width = frame_width;
     height = frame_height;
 
-    // typecast from opaque data type to implemented struct
-    AVCodecContext* c = video_st->codec;
-
     // FFmpeg contains SIMD optimizations which can sometimes read data past
     // the supplied input buffer.
     // Related info: https://trac.ffmpeg.org/ticket/6763
@@ -2215,10 +2364,10 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
         step = aligned_step;
     }
 
-    AVPixelFormat sw_pix_fmt = c->pix_fmt;
+    AVPixelFormat sw_pix_fmt = context->pix_fmt;
 #if USE_AV_HW_CODECS
-    if (c->hw_frames_ctx)
-        sw_pix_fmt = ((AVHWFramesContext*)c->hw_frames_ctx->data)->sw_format;
+    if (context->hw_frames_ctx)
+        sw_pix_fmt = ((AVHWFramesContext*)context->hw_frames_ctx->data)->sw_format;
 #endif
     if ( sw_pix_fmt != input_pix_fmt ) {
         CV_Assert( input_picture );
@@ -2232,8 +2381,8 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
             img_convert_ctx = sws_getContext(width,
                                              height,
                                              (AVPixelFormat)input_pix_fmt,
-                                             c->width,
-                                             c->height,
+                                             context->width,
+                                             context->height,
                                              sw_pix_fmt,
                                              SWS_BICUBIC,
                                              NULL, NULL, NULL);
@@ -2255,14 +2404,14 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
 
     bool ret;
 #if USE_AV_HW_CODECS
-    if (video_st->codec->hw_device_ctx) {
+    if (context->hw_device_ctx) {
         // copy data to HW frame
         AVFrame* hw_frame = av_frame_alloc();
         if (!hw_frame) {
             CV_LOG_ERROR(NULL, "Error allocating AVFrame (av_frame_alloc)");
             return false;
         }
-        if (av_hwframe_get_buffer(video_st->codec->hw_frames_ctx, hw_frame, 0) < 0) {
+        if (av_hwframe_get_buffer(context->hw_frames_ctx, hw_frame, 0) < 0) {
             CV_LOG_ERROR(NULL, "Error obtaining HW frame (av_hwframe_get_buffer)");
             av_frame_free(&hw_frame);
             return false;
@@ -2273,14 +2422,14 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
             return false;
         }
         hw_frame->pts = frame_idx;
-        int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, outbuf, outbuf_size, hw_frame, frame_idx);
+        int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, hw_frame, frame_idx);
         ret = ret_write >= 0 ? true : false;
         av_frame_free(&hw_frame);
     } else
 #endif
     {
         picture->pts = frame_idx;
-        int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, outbuf, outbuf_size, picture, frame_idx);
+        int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, picture, frame_idx);
         ret = ret_write >= 0 ? true : false;
     }
 
@@ -2291,7 +2440,7 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
 
 bool CvVideoWriter_FFMPEG::writeHWFrame(cv::InputArray input) {
 #if USE_AV_HW_CODECS
-    if (!video_st->codec->hw_frames_ctx)
+    if (!video_st || !context || !context->hw_frames_ctx || !context->hw_device_ctx)
         return false;
 
     // Get hardware frame from frame pool
@@ -2299,20 +2448,20 @@ bool CvVideoWriter_FFMPEG::writeHWFrame(cv::InputArray input) {
     if (!hw_frame) {
         return false;
     }
-    if (av_hwframe_get_buffer(video_st->codec->hw_frames_ctx, hw_frame, 0) < 0) {
+    if (av_hwframe_get_buffer(context->hw_frames_ctx, hw_frame, 0) < 0) {
         av_frame_free(&hw_frame);
         return false;
     }
 
     // GPU to GPU copy
-    if (!hw_copy_umat_to_frame(video_st->codec->hw_device_ctx, input, hw_frame)) {
+    if (!hw_copy_umat_to_frame(context->hw_device_ctx, input, hw_frame)) {
         av_frame_free(&hw_frame);
         return false;
     }
 
     // encode
     hw_frame->pts = frame_idx;
-    icv_av_write_frame_FFMPEG( oc, video_st, outbuf, outbuf_size, hw_frame, frame_idx);
+    icv_av_write_frame_FFMPEG( oc, video_st, context, outbuf, outbuf_size, hw_frame, frame_idx);
     frame_idx++;
 
     av_frame_free(&hw_frame);
@@ -2365,7 +2514,7 @@ void CvVideoWriter_FFMPEG::close()
         {
             for(;;)
             {
-                int ret = icv_av_write_frame_FFMPEG( oc, video_st, outbuf, outbuf_size, NULL, frame_idx);
+                int ret = icv_av_write_frame_FFMPEG( oc, video_st, context, outbuf, outbuf_size, NULL, frame_idx);
                 if( ret == OPENCV_NO_FRAMES_WRITTEN_CODE || ret < 0 )
                     break;
             }
@@ -2380,7 +2529,7 @@ void CvVideoWriter_FFMPEG::close()
     }
 
     // free pictures
-    if( video_st->codec->pix_fmt != input_pix_fmt)
+    if( context->pix_fmt != input_pix_fmt)
     {
         if(picture->data[0])
             free(picture->data[0]);
@@ -2392,7 +2541,7 @@ void CvVideoWriter_FFMPEG::close()
         av_free(input_picture);
 
     /* close codec */
-    avcodec_close(video_st->codec);
+    avcodec_close(context);
 
     av_free(outbuf);
 
@@ -2599,8 +2748,15 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
 
     /* set file name */
     oc->oformat = fmt;
+#ifndef CV_FFMPEG_URL
     snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
-
+#else
+    size_t name_len = strlen(filename);
+    oc->url = (char*)av_malloc(name_len + 1);
+    CV_Assert(oc->url);
+    memcpy((void*)oc->url, filename, name_len + 1);
+    oc->url[name_len] = '\0';
+#endif
     /* set some options */
     oc->max_delay = (int)(0.7*AV_TIME_BASE);  /* This reduces buffer underrun warnings with MPEG */
 
@@ -2715,7 +2871,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
     double bitrate = std::min(bitrate_scale*fps*width*height, (double)INT_MAX/2);
 
     if (codec_id == AV_CODEC_ID_NONE) {
-        codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_VIDEO);
+        codec_id = av_guess_codec(oc->oformat, NULL, filename, NULL, AVMEDIA_TYPE_VIDEO);
     }
 
     // Add video stream to output file
@@ -2733,11 +2889,9 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
     }
 #endif
 
-    AVCodecContext *c = video_st->codec;
-
     // find and open encoder, try HW acceleration types specified in 'hw_acceleration' list (in order)
     int err = -1;
-    AVCodec* codec = NULL;
+    const AVCodec* codec = NULL;
 #if USE_AV_HW_CODECS
     AVBufferRef* hw_device_ctx = NULL;
     HWAccelIterator accel_iter(va_type, true/*isEncoder*/, dict);
@@ -2780,9 +2934,17 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
         AVPixelFormat format = codec_pix_fmt;
 #endif
 
-        if (!icv_configure_video_stream_FFMPEG(oc, video_st, codec,
-                                               width, height, (int) (bitrate + 0.5),
-                                               fps, format)) {
+#ifdef CV_FFMPEG_CODECPAR
+        if (context)
+        {
+            avcodec_free_context(&context);
+        }
+#endif
+        context = icv_configure_video_stream_FFMPEG(oc, video_st, codec,
+                                              width, height, (int) (bitrate + 0.5),
+                                              fps, format, fourcc);
+        if (!context)
+        {
             continue;
         }
 
@@ -2794,27 +2956,25 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
 #endif
 #endif
 
-        c->codec_tag = fourcc;
-
 #if USE_AV_HW_CODECS
         if (hw_device_ctx) {
-            c->hw_device_ctx = av_buffer_ref(hw_device_ctx);
+            context->hw_device_ctx = av_buffer_ref(hw_device_ctx);
             if (hw_format != AV_PIX_FMT_NONE) {
-                c->hw_frames_ctx = hw_create_frames(NULL, hw_device_ctx, width, height, hw_format);
-                if (!c->hw_frames_ctx)
+                context->hw_frames_ctx = hw_create_frames(NULL, hw_device_ctx, width, height, hw_format);
+                if (!context->hw_frames_ctx)
                     continue;
             }
         }
 #endif
 
-        int64_t lbit_rate = (int64_t) c->bit_rate;
+        int64_t lbit_rate = (int64_t) context->bit_rate;
         lbit_rate += (int64_t)(bitrate / 2);
         lbit_rate = std::min(lbit_rate, (int64_t) INT_MAX);
-        c->bit_rate_tolerance = (int) lbit_rate;
-        c->bit_rate = (int) lbit_rate;
+        context->bit_rate_tolerance = (int) lbit_rate;
+        context->bit_rate = (int) lbit_rate;
 
         /* open the codec */
-        err = avcodec_open2(c, codec, NULL);
+        err = avcodec_open2(context, codec, NULL);
         if (err >= 0) {
 #if USE_AV_HW_CODECS
             va_type = hw_type_to_va_type(hw_type);
@@ -2823,7 +2983,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
 #endif
             break;
         } else {
-            CV_LOG_ERROR(NULL, "Could not open codec " << codec->name << ", error: " << icvFFMPEGErrStr(err));
+            CV_LOG_ERROR(NULL, "Could not open codec " << codec->name << ", error: " << icvFFMPEGErrStr(err) << " (" << err << ")");
         }
 #if USE_AV_HW_CODECS
     }  // while (accel_iter.good())
@@ -2844,6 +3004,12 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
         return false;
     }
 
+#ifdef CV_FFMPEG_CODECPAR
+    // Copy all to codecpar...
+    // !!! https://stackoverflow.com/questions/15897849/c-ffmpeg-not-writing-avcc-box-information
+    avcodec_parameters_from_context(video_st->codecpar, context);
+#endif
+
     outbuf = NULL;
 
 
@@ -2858,16 +3024,16 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
     }
 
     bool need_color_convert;
-    AVPixelFormat sw_pix_fmt = c->pix_fmt;
+    AVPixelFormat sw_pix_fmt = context->pix_fmt;
 #if USE_AV_HW_CODECS
-    if (c->hw_frames_ctx)
-        sw_pix_fmt = ((AVHWFramesContext*)c->hw_frames_ctx->data)->sw_format;
+    if (context->hw_frames_ctx)
+        sw_pix_fmt = ((AVHWFramesContext*)context->hw_frames_ctx->data)->sw_format;
 #endif
 
     need_color_convert = (sw_pix_fmt != input_pix_fmt);
 
     /* allocate the encoded raw picture */
-    picture = icv_alloc_picture_FFMPEG(sw_pix_fmt, c->width, c->height, need_color_convert);
+    picture = icv_alloc_picture_FFMPEG(sw_pix_fmt, context->width, context->height, need_color_convert);
     if (!picture) {
         return false;
     }
@@ -2877,7 +3043,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
    to the required output format */
     input_picture = NULL;
     if ( need_color_convert ) {
-        input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, c->width, c->height, false);
+        input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, context->width, context->height, false);
         if (!input_picture) {
             return false;
         }
