2011-07-05 00:18:12 +08:00
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
2012-04-30 22:33:52 +08:00
// License Agreement
2011-07-05 00:18:12 +08:00
// For Open Source Computer Vision Library
//
2012-04-30 22:33:52 +08:00
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
2011-07-05 00:18:12 +08:00
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
2012-04-30 22:33:52 +08:00
// * The name of the copyright holders may not be used to endorse or promote products
2011-07-05 00:18:12 +08:00
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
2019-05-31 21:18:00 +08:00
# include "cap_ffmpeg_legacy_api.hpp"
2022-03-20 01:06:50 +08:00
# include "opencv2/core/utils/logger.hpp"
# include "cap_interface.hpp"
2019-05-31 21:18:00 +08:00
using namespace cv ;
2017-07-25 18:23:44 +08:00
# if !(defined(_WIN32) || defined(WINCE))
2013-06-13 16:19:25 +08:00
# include <pthread.h>
# endif
2012-03-15 15:22:31 +08:00
# include <algorithm>
2012-04-30 22:33:52 +08:00
# include <limits>
2022-03-20 01:06:50 +08:00
# include <string.h>
2011-07-05 00:18:12 +08:00
2018-11-03 10:17:17 +08:00
# ifndef __OPENCV_BUILD
# define CV_FOURCC(c1, c2, c3, c4) (((c1) & 255) + (((c2) & 255) << 8) + (((c3) & 255) << 16) + (((c4) & 255) << 24))
# endif
2013-03-14 18:50:03 +08:00
# define CALC_FFMPEG_VERSION(a,b,c) ( a<<16 | b<<8 | c )
2011-07-05 00:18:12 +08:00
# if defined _MSC_VER && _MSC_VER >= 1200
2017-12-22 07:42:21 +08:00
# pragma warning( disable: 4244 4510 4610 )
2011-07-05 00:18:12 +08:00
# endif
2013-02-12 23:58:59 +08:00
# ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
# endif
2021-03-01 23:51:04 +08:00
# ifdef _MSC_VER
# pragma warning(disable: 4996) // was declared deprecated
# endif
2013-02-12 23:58:59 +08:00
2018-09-18 06:13:01 +08:00
# ifndef CV_UNUSED // Required for standalone compilation mode (OpenCV defines this in base.hpp)
# define CV_UNUSED(name) (void)name
# endif
2011-07-05 00:18:12 +08:00
# ifdef __cplusplus
extern " C " {
# endif
2012-03-23 18:52:36 +08:00
# include "ffmpeg_codecs.hpp"
2012-04-30 22:33:52 +08:00
# include <libavutil/mathematics.h>
2020-08-25 02:39:05 +08:00
# include <libavutil/opt.h>
2022-10-24 23:20:54 +08:00
// https://github.com/FFmpeg/FFmpeg/blame/d79c240196f43b93bd204363f1facc270029f113/doc/APIchanges#L1689-L1695
# if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 52 , 85 , 100 ) : CALC_FFMPEG_VERSION ( 53 , 15 , 0 ) )
2022-03-20 01:06:50 +08:00
# include <libavutil/display.h>
2022-10-24 23:20:54 +08:00
# endif
2013-03-14 18:50:03 +08:00
2016-03-22 20:26:55 +08:00
# if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 51 , 63 , 100 ) : CALC_FFMPEG_VERSION ( 54 , 6 , 0 ) )
# include <libavutil/imgutils.h>
# endif
2016-11-28 04:31:04 +08:00
# include <libavcodec/avcodec.h>
# include <libswscale/swscale.h>
2022-11-12 06:28:02 +08:00
# ifdef HAVE_FFMPEG_LIBAVDEVICE
# include <libavdevice/avdevice.h>
# endif
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
// 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
2022-12-14 18:09:37 +08:00
# include <libavutil/pixdesc.h>
2022-03-20 01:06:50 +08:00
// 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
2011-07-05 00:18:12 +08:00
# ifdef __cplusplus
}
# endif
2021-11-12 16:50:17 +08:00
// GCC 4.x compilation bug. Details: https://github.com/opencv/opencv/issues/20292
# if (defined(__GNUC__) && __GNUC__ < 5) && !defined(__clang__)
# undef USE_AV_HW_CODECS
# define USE_AV_HW_CODECS 0
# endif
2021-03-01 23:51:04 +08:00
//#define USE_AV_HW_CODECS 0
# ifndef USE_AV_HW_CODECS
# if LIBAVUTIL_VERSION_MAJOR >= 56 // FFMPEG 4.0+
# define USE_AV_HW_CODECS 1
# include "cap_ffmpeg_hw.hpp"
# else
# define USE_AV_HW_CODECS 0
# endif
# endif
2011-07-05 00:18:12 +08:00
# if defined _MSC_VER && _MSC_VER >= 1200
2017-12-22 07:42:21 +08:00
# pragma warning( default: 4244 4510 4610 )
2011-07-05 00:18:12 +08:00
# endif
# ifdef NDEBUG
# define CV_WARN(message)
# else
# define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
# endif
2017-07-25 18:23:44 +08:00
# if defined _WIN32
2011-08-02 22:27:21 +08:00
# include <windows.h>
2016-02-03 08:00:43 +08:00
# if defined _MSC_VER && _MSC_VER < 1900
struct timespec
{
time_t tv_sec ;
long tv_nsec ;
} ;
# endif
2018-03-14 22:09:10 +08:00
# elif defined __linux__ || defined __APPLE__ || defined __HAIKU__
2011-08-02 22:27:21 +08:00
# include <unistd.h>
# include <stdio.h>
2012-04-30 22:33:52 +08:00
# include <sys/types.h>
2016-02-03 08:00:43 +08:00
# include <sys/time.h>
2015-01-13 08:52:56 +08:00
# if defined __APPLE__
2011-08-02 22:27:21 +08:00
# include <sys/sysctl.h>
2016-02-03 08:00:43 +08:00
# include <mach/clock.h>
# include <mach/mach.h>
2011-08-02 22:27:21 +08:00
# endif
2015-01-13 08:52:56 +08:00
# endif
2011-08-02 22:27:21 +08:00
2012-04-30 22:33:52 +08:00
# if defined(__APPLE__)
# define AV_NOPTS_VALUE_ ((int64_t)0x8000000000000000LL)
# else
# define AV_NOPTS_VALUE_ ((int64_t)AV_NOPTS_VALUE)
# endif
2012-09-19 20:48:19 +08:00
# ifndef AVERROR_EOF
# define AVERROR_EOF (-MKTAG( 'E','O','F',' '))
# endif
2013-04-04 15:27:43 +08:00
# if LIBAVCODEC_BUILD >= CALC_FFMPEG_VERSION(54,25,0)
# define CV_CODEC_ID AVCodecID
# define CV_CODEC(name) AV_##name
# else
# define CV_CODEC_ID CodecID
# define CV_CODEC(name) name
# endif
2017-11-19 02:08:41 +08:00
# ifndef PKT_FLAG_KEY
# define PKT_FLAG_KEY AV_PKT_FLAG_KEY
# endif
2016-01-12 22:51:26 +08:00
# 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
2016-02-03 08:00:43 +08:00
2016-02-06 20:20:36 +08:00
# ifndef USE_AV_INTERRUPT_CALLBACK
# define USE_AV_INTERRUPT_CALLBACK 1
# endif
2020-08-07 05:43:47 +08:00
# ifndef USE_AV_SEND_FRAME_API
// https://github.com/FFmpeg/FFmpeg/commit/7fc329e2dd6226dfecaa4a1d7adf353bf2773726
# if LIBAVCODEC_VERSION_MICRO >= 100 \
& & LIBAVCODEC_BUILD > = CALC_FFMPEG_VERSION ( 57 , 37 , 100 )
# define USE_AV_SEND_FRAME_API 1
# else
# define USE_AV_SEND_FRAME_API 0
# endif
# endif
2016-02-06 20:20:36 +08:00
# if USE_AV_INTERRUPT_CALLBACK
2021-08-14 04:12:05 +08:00
# define LIBAVFORMAT_INTERRUPT_OPEN_DEFAULT_TIMEOUT_MS 30000
# define LIBAVFORMAT_INTERRUPT_READ_DEFAULT_TIMEOUT_MS 30000
2016-02-03 08:00:43 +08:00
2017-07-25 18:23:44 +08:00
# ifdef _WIN32
2016-02-03 08:00:43 +08:00
// http://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
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 ;
}
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
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 ;
2021-03-01 23:51:04 +08:00
t . QuadPart = ( LONGLONG ) microseconds ;
2016-02-03 08:00:43 +08:00
tv - > tv_sec = t . QuadPart / 1000000 ;
tv - > tv_nsec = ( t . QuadPart % 1000000 ) * 1000 ;
}
# else
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
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
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
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 ;
}
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
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 ;
}
2016-02-06 20:20:36 +08:00
# endif // USE_AV_INTERRUPT_CALLBACK
2016-02-03 08:00:43 +08:00
2011-08-02 22:27:21 +08:00
2011-07-05 00:18:12 +08:00
struct Image_FFMPEG
{
unsigned char * data ;
int step ;
int width ;
int height ;
} ;
2016-02-06 20:20:36 +08:00
# if USE_AV_INTERRUPT_CALLBACK
2016-02-03 08:00:43 +08:00
struct AVInterruptCallbackMetadata
{
timespec value ;
unsigned int timeout_after_ms ;
int timeout ;
} ;
2018-09-30 01:43:01 +08:00
// https://github.com/opencv/opencv/pull/12693#issuecomment-426236731
static
2020-08-17 20:24:54 +08:00
inline const char * _opencv_avcodec_get_name ( CV_CODEC_ID id )
2018-09-30 01:43:01 +08:00
{
# if LIBAVCODEC_VERSION_MICRO >= 100 \
& & LIBAVCODEC_BUILD > = CALC_FFMPEG_VERSION ( 53 , 47 , 100 )
return avcodec_get_name ( id ) ;
# else
const AVCodecDescriptor * cd ;
AVCodec * codec ;
if ( id = = AV_CODEC_ID_NONE )
{
return " none " ;
}
cd = avcodec_descriptor_get ( id ) ;
if ( cd )
{
return cd - > name ;
}
codec = avcodec_find_decoder ( id ) ;
if ( codec )
{
return codec - > name ;
}
codec = avcodec_find_encoder ( id ) ;
if ( codec )
{
return codec - > name ;
}
return " unknown_codec " ;
# endif
}
2011-07-05 00:18:12 +08:00
2016-02-06 20:20:36 +08:00
static
2016-02-03 08:00:43 +08:00
inline int _opencv_ffmpeg_interrupt_callback ( void * ptr )
{
AVInterruptCallbackMetadata * metadata = ( AVInterruptCallbackMetadata * ) ptr ;
2021-11-28 02:34:52 +08:00
CV_Assert ( metadata ) ;
2016-02-03 08:00:43 +08:00
2016-04-04 18:08:52 +08:00
if ( metadata - > timeout_after_ms = = 0 )
{
return 0 ; // timeout is disabled
}
2016-02-03 08:00:43 +08:00
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 ;
}
2016-02-06 20:20:36 +08:00
# endif
2016-02-03 08:00:43 +08:00
2016-03-22 20:26:55 +08:00
static
inline void _opencv_ffmpeg_av_packet_unref ( AVPacket * pkt )
{
# if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 55 , 25 , 100 ) : CALC_FFMPEG_VERSION ( 55 , 16 , 0 ) )
av_packet_unref ( pkt ) ;
# else
av_free_packet ( pkt ) ;
# endif
} ;
static
inline void _opencv_ffmpeg_av_image_fill_arrays ( void * frame , uint8_t * ptr , enum AVPixelFormat pix_fmt , int width , int height )
{
# if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 51 , 63 , 100 ) : CALC_FFMPEG_VERSION ( 54 , 6 , 0 ) )
av_image_fill_arrays ( ( ( AVFrame * ) frame ) - > data , ( ( AVFrame * ) frame ) - > linesize , ptr , pix_fmt , width , height , 1 ) ;
# else
avpicture_fill ( ( AVPicture * ) frame , ptr , pix_fmt , width , height ) ;
# endif
} ;
static
inline int _opencv_ffmpeg_av_image_get_buffer_size ( enum AVPixelFormat pix_fmt , int width , int height )
{
# if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 51 , 63 , 100 ) : CALC_FFMPEG_VERSION ( 54 , 6 , 0 ) )
return av_image_get_buffer_size ( pix_fmt , width , height , 1 ) ;
# else
return avpicture_get_size ( pix_fmt , width , height ) ;
# endif
} ;
2018-02-21 23:42:40 +08:00
static AVRational _opencv_ffmpeg_get_sample_aspect_ratio ( AVStream * stream )
{
# if LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(54, 5, 100)
return av_guess_sample_aspect_ratio ( NULL , stream , NULL ) ;
# else
AVRational undef = { 0 , 1 } ;
// stream
AVRational ratio = stream ? stream - > sample_aspect_ratio : undef ;
av_reduce ( & ratio . num , & ratio . den , ratio . num , ratio . den , INT_MAX ) ;
if ( ratio . num > 0 & & ratio . den > 0 )
return ratio ;
// codec
ratio = stream & & stream - > codec ? stream - > codec - > sample_aspect_ratio : undef ;
av_reduce ( & ratio . num , & ratio . den , ratio . num , ratio . den , INT_MAX ) ;
if ( ratio . num > 0 & & ratio . den > 0 )
return ratio ;
return undef ;
# endif
}
2022-03-20 01:06:50 +08:00
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 " ) ;
}
2016-02-03 08:00:43 +08:00
2011-07-05 00:18:12 +08:00
struct CvCapture_FFMPEG
{
2021-01-28 14:00:38 +08:00
bool open ( const char * filename , const VideoCaptureParameters & params ) ;
2011-07-05 00:18:12 +08:00
void close ( ) ;
2014-12-11 01:17:35 +08:00
double getProperty ( int ) const ;
2011-07-05 00:18:12 +08:00
bool setProperty ( int , double ) ;
bool grabFrame ( ) ;
2022-12-01 06:29:43 +08:00
bool retrieveFrame ( int flag , unsigned char * * data , int * step , int * width , int * height , int * cn , int * depth ) ;
2021-05-15 00:48:50 +08:00
bool retrieveHWFrame ( cv : : OutputArray output ) ;
2020-06-02 23:34:52 +08:00
void rotateFrame ( cv : : Mat & mat ) const ;
2011-07-05 00:18:12 +08:00
void init ( ) ;
2012-04-30 22:33:52 +08:00
void seek ( int64_t frame_number ) ;
void seek ( double sec ) ;
2012-06-08 01:21:29 +08:00
bool slowSeek ( int framenumber ) ;
2012-04-30 22:33:52 +08:00
2014-12-11 01:17:35 +08:00
int64_t get_total_frames ( ) const ;
double get_duration_sec ( ) const ;
double get_fps ( ) const ;
2020-03-10 21:44:22 +08:00
int64_t get_bitrate ( ) const ;
2012-04-30 22:33:52 +08:00
2014-12-11 01:17:35 +08:00
double r2d ( AVRational r ) const ;
2012-04-30 22:33:52 +08:00
int64_t dts_to_frame_number ( int64_t dts ) ;
2019-06-14 13:47:03 +08:00
double dts_to_sec ( int64_t dts ) const ;
2020-06-02 23:34:52 +08:00
void get_rotation_angle ( ) ;
2012-04-30 22:33:52 +08:00
AVFormatContext * ic ;
AVCodec * avcodec ;
2022-03-20 01:06:50 +08:00
AVCodecContext * context ;
2012-04-30 22:33:52 +08:00
int video_stream ;
AVStream * video_st ;
2012-06-08 01:21:29 +08:00
AVFrame * picture ;
AVFrame rgb_picture ;
2012-04-30 22:33:52 +08:00
int64_t picture_pts ;
AVPacket packet ;
Image_FFMPEG frame ;
2011-07-05 00:18:12 +08:00
struct SwsContext * img_convert_ctx ;
2012-04-30 22:33:52 +08:00
int64_t frame_number , first_frame_number ;
2020-06-02 23:34:52 +08:00
bool rotation_auto ;
int rotation_angle ; // valid 0, 90, 180, 270
2012-04-30 22:33:52 +08:00
double eps_zero ;
2011-07-05 00:18:12 +08:00
/*
' filename ' contains the filename of the videosource ,
' filename = = NULL ' indicates that ffmpeg ' s seek support works
for the particular file .
' filename ! = NULL ' indicates that the slow fallback function is used for seeking ,
and so the filename is needed to reopen the file on backward seeking .
*/
char * filename ;
2014-10-06 15:56:30 +08:00
AVDictionary * dict ;
2016-02-06 20:20:36 +08:00
# if USE_AV_INTERRUPT_CALLBACK
2021-08-14 04:12:05 +08:00
int open_timeout ;
int read_timeout ;
2016-02-03 08:00:43 +08:00
AVInterruptCallbackMetadata interrupt_metadata ;
2016-02-06 20:20:36 +08:00
# endif
2019-11-18 22:07:06 +08:00
bool setRaw ( ) ;
bool processRawPacket ( ) ;
bool rawMode ;
bool rawModeInitialized ;
2022-12-01 06:29:43 +08:00
bool convertRGB ;
2019-11-18 22:07:06 +08:00
AVPacket packet_filtered ;
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
AVBSFContext * bsfc ;
# else
AVBitStreamFilterContext * bsfc ;
# endif
2021-03-01 23:51:04 +08:00
VideoAccelerationType va_type ;
int hw_device ;
2021-05-15 00:48:50 +08:00
int use_opencl ;
2021-11-24 05:18:55 +08:00
int extraDataIdx ;
2011-07-05 00:18:12 +08:00
} ;
void CvCapture_FFMPEG : : init ( )
{
2022-11-12 06:28:02 +08:00
# ifdef HAVE_FFMPEG_LIBAVDEVICE
//libavdevice is available, so let's register all input and output devices (e.g v4l2)
avdevice_register_all ( ) ;
# endif
2011-07-05 00:18:12 +08:00
ic = 0 ;
video_stream = - 1 ;
video_st = 0 ;
picture = 0 ;
2012-04-30 22:33:52 +08:00
picture_pts = AV_NOPTS_VALUE_ ;
first_frame_number = - 1 ;
2011-07-05 00:18:12 +08:00
memset ( & rgb_picture , 0 , sizeof ( rgb_picture ) ) ;
memset ( & frame , 0 , sizeof ( frame ) ) ;
filename = 0 ;
2012-04-30 22:33:52 +08:00
memset ( & packet , 0 , sizeof ( packet ) ) ;
av_init_packet ( & packet ) ;
2011-07-05 00:18:12 +08:00
img_convert_ctx = 0 ;
2012-04-30 22:33:52 +08:00
avcodec = 0 ;
2022-03-20 01:06:50 +08:00
context = 0 ;
2012-04-30 22:33:52 +08:00
frame_number = 0 ;
eps_zero = 0.000025 ;
2014-10-06 15:56:30 +08:00
2020-06-02 23:34:52 +08:00
rotation_angle = 0 ;
2020-06-08 22:31:18 +08:00
# if (LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 92, 100))
rotation_auto = true ;
# else
rotation_auto = false ;
# endif
2014-10-06 15:56:30 +08:00
dict = NULL ;
2019-11-18 22:07:06 +08:00
2021-08-14 04:12:05 +08:00
# if USE_AV_INTERRUPT_CALLBACK
open_timeout = LIBAVFORMAT_INTERRUPT_OPEN_DEFAULT_TIMEOUT_MS ;
read_timeout = LIBAVFORMAT_INTERRUPT_READ_DEFAULT_TIMEOUT_MS ;
# endif
2019-11-18 22:07:06 +08:00
rawMode = false ;
rawModeInitialized = false ;
2022-12-01 06:29:43 +08:00
convertRGB = true ;
2019-11-18 22:07:06 +08:00
memset ( & packet_filtered , 0 , sizeof ( packet_filtered ) ) ;
av_init_packet ( & packet_filtered ) ;
bsfc = NULL ;
2021-03-03 16:41:30 +08:00
va_type = cv : : VIDEO_ACCELERATION_NONE ; // TODO OpenCV 5.0: change to _ANY?
2021-03-01 23:51:04 +08:00
hw_device = - 1 ;
2021-05-15 00:48:50 +08:00
use_opencl = 0 ;
2021-11-24 05:18:55 +08:00
extraDataIdx = 1 ;
2011-07-05 00:18:12 +08:00
}
void CvCapture_FFMPEG : : close ( )
{
2012-04-30 22:33:52 +08:00
if ( img_convert_ctx )
{
sws_freeContext ( img_convert_ctx ) ;
img_convert_ctx = 0 ;
}
2012-06-08 01:21:29 +08:00
2011-07-05 00:18:12 +08:00
if ( picture )
2015-02-13 20:12:52 +08:00
{
# if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
2015-09-09 07:35:40 +08:00
? CALC_FFMPEG_VERSION ( 55 , 45 , 101 ) : CALC_FFMPEG_VERSION ( 55 , 28 , 1 ) )
av_frame_free ( & picture ) ;
# elif LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
2015-02-13 20:12:52 +08:00
? CALC_FFMPEG_VERSION ( 54 , 59 , 100 ) : CALC_FFMPEG_VERSION ( 54 , 28 , 0 ) )
avcodec_free_frame ( & picture ) ;
# else
2012-04-14 05:50:59 +08:00
av_free ( picture ) ;
2015-02-13 20:12:52 +08:00
# endif
}
2011-07-05 00:18:12 +08:00
if ( video_st )
{
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_CODECPAR
avcodec_close ( context ) ;
# endif
2011-07-05 00:18:12 +08:00
video_st = NULL ;
}
2022-03-20 01:06:50 +08:00
if ( context )
{
# ifdef CV_FFMPEG_CODECPAR
avcodec_free_context ( & context ) ;
# endif
}
2011-07-05 00:18:12 +08:00
if ( ic )
{
2012-06-08 01:21:29 +08:00
avformat_close_input ( & ic ) ;
2011-07-05 00:18:12 +08:00
ic = NULL ;
}
2016-01-12 22:51:26 +08:00
# if USE_AV_FRAME_GET_BUFFER
av_frame_unref ( & rgb_picture ) ;
# else
2011-07-05 00:18:12 +08:00
if ( rgb_picture . data [ 0 ] )
{
free ( rgb_picture . data [ 0 ] ) ;
rgb_picture . data [ 0 ] = 0 ;
}
2016-01-12 22:51:26 +08:00
# endif
2011-07-05 00:18:12 +08:00
// free last packet if exist
if ( packet . data ) {
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_packet_unref ( & packet ) ;
2012-04-30 22:33:52 +08:00
packet . data = NULL ;
2011-07-05 00:18:12 +08:00
}
2014-10-06 15:56:30 +08:00
if ( dict ! = NULL )
av_dict_free ( & dict ) ;
2019-11-18 22:07:06 +08:00
if ( packet_filtered . data )
{
_opencv_ffmpeg_av_packet_unref ( & packet_filtered ) ;
packet_filtered . data = NULL ;
}
if ( bsfc )
{
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
av_bsf_free ( & bsfc ) ;
# else
av_bitstream_filter_close ( bsfc ) ;
# endif
}
2011-07-05 00:18:12 +08:00
init ( ) ;
}
# ifndef AVSEEK_FLAG_FRAME
2012-04-14 05:50:59 +08:00
# define AVSEEK_FLAG_FRAME 0
2011-07-05 00:18:12 +08:00
# endif
2012-02-01 17:54:18 +08:00
# ifndef AVSEEK_FLAG_ANY
2012-04-14 05:50:59 +08:00
# define AVSEEK_FLAG_ANY 1
2012-02-01 05:02:05 +08:00
# endif
2012-04-30 22:33:52 +08:00
2019-05-31 21:18:00 +08:00
# if defined(__OPENCV_BUILD) || defined(BUILD_PLUGIN)
typedef cv : : Mutex ImplMutex ;
# else
2013-02-12 18:20:23 +08:00
class ImplMutex
2012-04-30 22:33:52 +08:00
{
2013-02-12 18:20:23 +08:00
public :
2013-02-18 19:35:42 +08:00
ImplMutex ( ) { init ( ) ; }
~ ImplMutex ( ) { destroy ( ) ; }
2013-02-12 18:20:23 +08:00
void init ( ) ;
void destroy ( ) ;
void lock ( ) ;
bool trylock ( ) ;
void unlock ( ) ;
struct Impl ;
protected :
Impl * impl ;
private :
ImplMutex ( const ImplMutex & ) ;
ImplMutex & operator = ( const ImplMutex & m ) ;
} ;
2017-07-25 18:23:44 +08:00
# if defined _WIN32 || defined WINCE
2013-02-12 18:20:23 +08:00
struct ImplMutex : : Impl
{
2013-08-12 21:30:40 +08:00
void init ( )
{
# if (_WIN32_WINNT >= 0x0600)
: : InitializeCriticalSectionEx ( & cs , 1000 , 0 ) ;
# else
: : InitializeCriticalSection ( & cs ) ;
# endif
refcount = 1 ;
}
2013-02-12 18:20:23 +08:00
void destroy ( ) { DeleteCriticalSection ( & cs ) ; }
void lock ( ) { EnterCriticalSection ( & cs ) ; }
bool trylock ( ) { return TryEnterCriticalSection ( & cs ) ! = 0 ; }
void unlock ( ) { LeaveCriticalSection ( & cs ) ; }
CRITICAL_SECTION cs ;
int refcount ;
} ;
# elif defined __APPLE__
# include <libkern/OSAtomic.h>
struct ImplMutex : : Impl
{
void init ( ) { sl = OS_SPINLOCK_INIT ; refcount = 1 ; }
void destroy ( ) { }
void lock ( ) { OSSpinLockLock ( & sl ) ; }
bool trylock ( ) { return OSSpinLockTry ( & sl ) ; }
void unlock ( ) { OSSpinLockUnlock ( & sl ) ; }
OSSpinLock sl ;
int refcount ;
} ;
2017-07-10 17:43:59 +08:00
# elif defined __linux__ && !defined __ANDROID__
2013-02-12 18:20:23 +08:00
struct ImplMutex : : Impl
{
void init ( ) { pthread_spin_init ( & sl , 0 ) ; refcount = 1 ; }
void destroy ( ) { pthread_spin_destroy ( & sl ) ; }
void lock ( ) { pthread_spin_lock ( & sl ) ; }
bool trylock ( ) { return pthread_spin_trylock ( & sl ) = = 0 ; }
void unlock ( ) { pthread_spin_unlock ( & sl ) ; }
pthread_spinlock_t sl ;
int refcount ;
} ;
# else
struct ImplMutex : : Impl
{
void init ( ) { pthread_mutex_init ( & sl , 0 ) ; refcount = 1 ; }
void destroy ( ) { pthread_mutex_destroy ( & sl ) ; }
void lock ( ) { pthread_mutex_lock ( & sl ) ; }
bool trylock ( ) { return pthread_mutex_trylock ( & sl ) = = 0 ; }
void unlock ( ) { pthread_mutex_unlock ( & sl ) ; }
pthread_mutex_t sl ;
int refcount ;
} ;
# endif
void ImplMutex : : init ( )
{
2017-06-29 21:40:22 +08:00
impl = new Impl ( ) ;
2013-02-18 19:35:42 +08:00
impl - > init ( ) ;
2013-02-12 18:20:23 +08:00
}
2013-02-18 19:35:42 +08:00
void ImplMutex : : destroy ( )
2013-02-12 18:20:23 +08:00
{
2013-02-18 19:35:42 +08:00
impl - > destroy ( ) ;
2017-06-29 21:40:22 +08:00
delete ( impl ) ;
2013-02-18 19:35:42 +08:00
impl = NULL ;
2013-02-12 18:20:23 +08:00
}
void ImplMutex : : lock ( ) { impl - > lock ( ) ; }
void ImplMutex : : unlock ( ) { impl - > unlock ( ) ; }
bool ImplMutex : : trylock ( ) { return impl - > trylock ( ) ; }
2019-05-31 21:18:00 +08:00
class AutoLock
{
public :
AutoLock ( ImplMutex & m ) : mutex ( & m ) { mutex - > lock ( ) ; }
~ AutoLock ( ) { mutex - > unlock ( ) ; }
protected :
ImplMutex * mutex ;
private :
AutoLock ( const AutoLock & ) ; // disabled
AutoLock & operator = ( const AutoLock & ) ; // disabled
} ;
# endif
2022-03-20 01:06:50 +08:00
2019-05-31 21:18:00 +08:00
static ImplMutex _mutex ;
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_LOCKMGR
2013-02-12 18:20:23 +08:00
static int LockCallBack ( void * * mutex , AVLockOp op )
{
ImplMutex * localMutex = reinterpret_cast < ImplMutex * > ( * mutex ) ;
switch ( op )
2012-11-19 20:44:23 +08:00
{
2013-02-12 18:20:23 +08:00
case AV_LOCK_CREATE :
2019-05-31 21:18:00 +08:00
localMutex = new ImplMutex ( ) ;
2018-09-07 23:43:47 +08:00
if ( ! localMutex )
return 1 ;
2013-02-12 18:20:23 +08:00
* mutex = localMutex ;
if ( ! * mutex )
return 1 ;
break ;
case AV_LOCK_OBTAIN :
localMutex - > lock ( ) ;
break ;
case AV_LOCK_RELEASE :
localMutex - > unlock ( ) ;
break ;
case AV_LOCK_DESTROY :
2019-05-31 21:18:00 +08:00
delete localMutex ;
2013-02-12 18:20:23 +08:00
localMutex = NULL ;
2014-09-27 00:44:25 +08:00
* mutex = NULL ;
2013-02-12 18:20:23 +08:00
break ;
}
return 0 ;
}
2022-03-20 01:06:50 +08:00
# endif
2017-06-27 21:32:32 +08:00
2018-05-29 20:40:52 +08:00
static void ffmpeg_log_callback ( void * ptr , int level , const char * fmt , va_list vargs )
{
static bool skip_header = false ;
static int prev_level = - 1 ;
2018-09-07 19:33:52 +08:00
CV_UNUSED ( ptr ) ;
2021-10-19 00:30:13 +08:00
if ( level > av_log_get_level ( ) ) return ;
2018-05-29 20:40:52 +08:00
if ( ! skip_header | | level ! = prev_level ) printf ( " [OPENCV:FFMPEG:%02d] " , level ) ;
vprintf ( fmt , vargs ) ;
size_t fmt_len = strlen ( fmt ) ;
skip_header = fmt_len > 0 & & fmt [ fmt_len - 1 ] ! = ' \n ' ;
prev_level = level ;
}
2017-06-27 21:32:32 +08:00
2013-02-12 18:20:23 +08:00
class InternalFFMpegRegister
{
public :
2019-01-14 18:33:38 +08:00
static void init ( )
2012-11-19 20:44:23 +08:00
{
2017-06-27 21:32:32 +08:00
AutoLock lock ( _mutex ) ;
2019-01-14 18:33:38 +08:00
static InternalFFMpegRegister instance ;
2021-03-03 05:46:30 +08:00
initLogger_ ( ) ; // update logger setup unconditionally (GStreamer's libav plugin may override these settings)
2019-01-14 18:33:38 +08:00
}
2021-03-03 04:41:05 +08:00
static void initLogger_ ( )
2019-01-14 18:33:38 +08:00
{
2018-05-29 20:40:52 +08:00
# ifndef NO_GETENV
2019-01-14 18:33:38 +08:00
char * debug_option = getenv ( " OPENCV_FFMPEG_DEBUG " ) ;
2021-10-19 00:30:13 +08:00
char * level_option = getenv ( " OPENCV_FFMPEG_LOGLEVEL " ) ;
int level = AV_LOG_VERBOSE ;
if ( level_option ! = NULL )
2019-01-14 18:33:38 +08:00
{
2021-10-19 00:30:13 +08:00
level = atoi ( level_option ) ;
}
if ( ( debug_option ! = NULL ) | | ( level_option ! = NULL ) )
{
av_log_set_level ( level ) ;
2019-01-14 18:33:38 +08:00
av_log_set_callback ( ffmpeg_log_callback ) ;
}
else
2018-05-29 20:40:52 +08:00
# endif
2019-01-14 18:33:38 +08:00
{
av_log_set_level ( AV_LOG_ERROR ) ;
2013-02-12 18:20:23 +08:00
}
2012-04-30 22:33:52 +08:00
}
2021-03-03 04:41:05 +08:00
public :
InternalFFMpegRegister ( )
{
avformat_network_init ( ) ;
2012-04-30 22:33:52 +08:00
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_REGISTER
2021-03-03 04:41:05 +08:00
/* register all codecs, demux and protocols */
av_register_all ( ) ;
2022-03-20 01:06:50 +08:00
# endif
2021-03-03 04:41:05 +08:00
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_LOCKMGR
2021-03-03 04:41:05 +08:00
/* register a callback function for synchronization */
av_lockmgr_register ( & LockCallBack ) ;
2022-03-20 01:06:50 +08:00
# endif
2012-04-30 22:33:52 +08:00
}
2013-02-12 18:20:23 +08:00
~ InternalFFMpegRegister ( )
{
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_LOCKMGR
2013-02-12 18:20:23 +08:00
av_lockmgr_register ( NULL ) ;
2022-03-20 01:06:50 +08:00
# endif
2021-03-03 04:41:05 +08:00
av_log_set_callback ( NULL ) ;
2012-04-30 22:33:52 +08:00
}
2013-02-12 18:20:23 +08:00
} ;
2022-03-20 01:06:50 +08:00
inline void fill_codec_context ( AVCodecContext * enc , AVDictionary * dict )
{
2022-12-22 12:36:05 +08:00
if ( ! enc - > thread_count )
{
int nCpus = cv : : getNumberOfCPUs ( ) ;
int requestedThreads = std : : min ( nCpus , 16 ) ; // [OPENCV:FFMPEG:24] Application has requested XX threads. Using a thread count greater than 16 is not recommended.
char * threads_option = getenv ( " OPENCV_FFMPEG_THREADS " ) ;
if ( threads_option ! = NULL )
{
requestedThreads = atoi ( threads_option ) ;
}
enc - > thread_count = requestedThreads ;
}
2022-03-20 01:06:50 +08:00
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 ;
}
}
2021-01-28 14:00:38 +08:00
bool CvCapture_FFMPEG : : open ( const char * _filename , const VideoCaptureParameters & params )
2011-07-05 00:18:12 +08:00
{
2019-01-14 18:33:38 +08:00
InternalFFMpegRegister : : init ( ) ;
2021-03-03 04:41:05 +08:00
2017-06-27 21:32:32 +08:00
AutoLock lock ( _mutex ) ;
2021-03-03 04:41:05 +08:00
2011-07-05 00:18:12 +08:00
unsigned i ;
bool valid = false ;
2022-09-12 14:12:28 +08:00
int nThreads = 0 ;
2011-07-05 00:18:12 +08:00
close ( ) ;
2012-06-08 01:21:29 +08:00
2021-01-28 14:00:38 +08:00
if ( ! params . empty ( ) )
{
2022-12-01 06:29:43 +08:00
convertRGB = params . get < bool > ( CAP_PROP_CONVERT_RGB , true ) ;
if ( ! convertRGB )
{
CV_LOG_WARNING ( NULL , " VIDEOIO/FFMPEG: BGR conversion turned OFF, decoded frame will be "
" returned in its original format. "
" Multiplanar formats are not supported by the backend. "
" Only GRAY8/GRAY16LE pixel formats have been tested. "
" Use at your own risk. " ) ;
}
2021-01-28 14:00:38 +08:00
if ( params . has ( CAP_PROP_FORMAT ) )
{
int value = params . get < int > ( CAP_PROP_FORMAT ) ;
if ( value = = - 1 )
{
CV_LOG_INFO ( NULL , " VIDEOIO/FFMPEG: enabled demuxer only mode: ' " < < ( _filename ? _filename : " <NULL> " ) < < " ' " ) ;
rawMode = true ;
}
else
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: CAP_PROP_FORMAT parameter value is invalid/unsupported: " < < value ) ;
return false ;
}
}
2021-03-01 23:51:04 +08:00
if ( params . has ( CAP_PROP_HW_ACCELERATION ) )
{
va_type = params . get < VideoAccelerationType > ( CAP_PROP_HW_ACCELERATION ) ;
# if !USE_AV_HW_CODECS
if ( va_type ! = VIDEO_ACCELERATION_NONE & & va_type ! = VIDEO_ACCELERATION_ANY )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: FFmpeg backend is build without acceleration support. Can't handle CAP_PROP_HW_ACCELERATION parameter. Bailout " ) ;
return false ;
}
# endif
}
if ( params . has ( CAP_PROP_HW_DEVICE ) )
{
hw_device = params . get < int > ( CAP_PROP_HW_DEVICE ) ;
if ( va_type = = VIDEO_ACCELERATION_NONE & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Invalid usage of CAP_PROP_HW_DEVICE without requested H/W acceleration. Bailout " ) ;
return false ;
}
if ( va_type = = VIDEO_ACCELERATION_ANY & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Invalid usage of CAP_PROP_HW_DEVICE with 'ANY' H/W acceleration. Bailout " ) ;
return false ;
}
}
2021-05-15 00:48:50 +08:00
if ( params . has ( CAP_PROP_HW_ACCELERATION_USE_OPENCL ) ) {
use_opencl = params . get < int > ( CAP_PROP_HW_ACCELERATION_USE_OPENCL ) ;
}
2021-08-14 04:12:05 +08:00
# if USE_AV_INTERRUPT_CALLBACK
if ( params . has ( CAP_PROP_OPEN_TIMEOUT_MSEC ) )
{
open_timeout = params . get < int > ( CAP_PROP_OPEN_TIMEOUT_MSEC ) ;
}
if ( params . has ( CAP_PROP_READ_TIMEOUT_MSEC ) )
{
read_timeout = params . get < int > ( CAP_PROP_READ_TIMEOUT_MSEC ) ;
}
# endif
2022-09-12 14:12:28 +08:00
if ( params . has ( CAP_PROP_N_THREADS ) )
{
nThreads = params . get < int > ( CAP_PROP_N_THREADS ) ;
}
2021-01-28 14:00:38 +08:00
if ( params . warnUnusedParameters ( ) )
{
2021-03-01 23:51:04 +08:00
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: unsupported parameters in .open(), see logger INFO channel for details. Bailout " ) ;
2021-01-28 14:00:38 +08:00
return false ;
}
}
2016-02-06 20:20:36 +08:00
# if USE_AV_INTERRUPT_CALLBACK
2016-02-03 08:00:43 +08:00
/* interrupt callback */
2021-08-14 04:12:05 +08:00
interrupt_metadata . timeout_after_ms = open_timeout ;
2016-02-03 08:00:43 +08:00
get_monotonic_time ( & interrupt_metadata . value ) ;
ic = avformat_alloc_context ( ) ;
ic - > interrupt_callback . callback = _opencv_ffmpeg_interrupt_callback ;
ic - > interrupt_callback . opaque = & interrupt_metadata ;
2016-02-06 20:20:36 +08:00
# endif
2016-02-03 08:00:43 +08:00
2017-09-19 18:29:43 +08:00
# ifndef NO_GETENV
char * options = getenv ( " OPENCV_FFMPEG_CAPTURE_OPTIONS " ) ;
if ( options = = NULL )
{
2022-02-11 00:45:34 +08:00
# if LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 48, 100)
av_dict_set ( & dict , " rtsp_flags " , " prefer_tcp " , 0 ) ;
# else
2017-09-19 18:29:43 +08:00
av_dict_set ( & dict , " rtsp_transport " , " tcp " , 0 ) ;
2022-02-11 00:45:34 +08:00
# endif
2017-09-19 18:29:43 +08:00
}
else
{
2022-10-27 06:27:17 +08:00
CV_LOG_DEBUG ( NULL , " VIDEOIO/FFMPEG: using capture options from environment: " < < options ) ;
2017-09-19 18:29:43 +08:00
# if LIBAVUTIL_BUILD >= (LIBAVUTIL_VERSION_MICRO >= 100 ? CALC_FFMPEG_VERSION(52, 17, 100) : CALC_FFMPEG_VERSION(52, 7, 0))
av_dict_parse_string ( & dict , options , " ; " , " | " , 0 ) ;
# else
av_dict_set ( & dict , " rtsp_transport " , " tcp " , 0 ) ;
# endif
}
# else
2014-10-06 15:56:30 +08:00
av_dict_set ( & dict , " rtsp_transport " , " tcp " , 0 ) ;
2017-09-19 18:29:43 +08:00
# endif
2022-03-20 01:06:50 +08:00
CV_FFMPEG_FMT_CONST AVInputFormat * input_format = NULL ;
2019-07-20 02:33:22 +08:00
AVDictionaryEntry * entry = av_dict_get ( dict , " input_format " , NULL , 0 ) ;
if ( entry ! = 0 )
{
input_format = av_find_input_format ( entry - > value ) ;
}
int err = avformat_open_input ( & ic , _filename , input_format , & dict ) ;
2012-06-08 01:21:29 +08:00
2013-02-12 18:20:23 +08:00
if ( err < 0 )
{
2012-04-30 22:33:52 +08:00
CV_WARN ( " Error opening file " ) ;
2015-07-22 17:11:58 +08:00
CV_WARN ( _filename ) ;
2012-04-30 22:33:52 +08:00
goto exit_func ;
2011-07-05 00:18:12 +08:00
}
2020-08-25 02:39:05 +08:00
err = avformat_find_stream_info ( ic , NULL ) ;
2013-02-12 18:20:23 +08:00
if ( err < 0 )
{
2022-03-20 01:06:50 +08:00
CV_LOG_WARNING ( NULL , " Unable to read codec parameters from stream ( " < < _opencv_ffmpeg_get_error_string ( err ) < < " ) " ) ;
2012-04-30 22:33:52 +08:00
goto exit_func ;
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
for ( i = 0 ; i < ic - > nb_streams ; i + + )
{
2022-03-20 01:06:50 +08:00
# 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 ;
2020-01-26 15:19:09 +08:00
# endif
2012-04-30 22:33:52 +08:00
2022-03-20 01:06:50 +08:00
if ( AVMEDIA_TYPE_VIDEO = = codec_type & & video_stream < 0 )
2013-02-12 18:20:23 +08:00
{
2022-03-20 01:06:50 +08:00
// 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
2021-03-01 23:51:04 +08:00
) ;
2013-02-20 21:19:55 +08:00
2021-03-01 23:51:04 +08:00
# 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)
2022-03-20 01:06:50 +08:00
const AVCodec * codec = NULL ;
2021-03-01 23:51:04 +08:00
err = - 1 ;
# if USE_AV_HW_CODECS
HWAccelIterator accel_iter ( va_type , false /*isEncoder*/ , dict ) ;
while ( accel_iter . good ( ) )
{
# else
do {
# endif
# if USE_AV_HW_CODECS
accel_iter . parse_next ( ) ;
AVHWDeviceType hw_type = accel_iter . hw_type ( ) ;
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 ;
2022-03-20 01:06:50 +08:00
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 ) ;
}
2021-03-01 23:51:04 +08:00
if ( hw_pix_fmt ! = AV_PIX_FMT_NONE )
2022-03-20 01:06:50 +08:00
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 )
2021-03-01 23:51:04 +08:00
{
2022-03-20 01:06:50 +08:00
context - > get_format = avcodec_default_get_format ;
2021-03-01 23:51:04 +08:00
CV_LOG_DEBUG ( NULL , " FFMPEG: ... can't create H/W device: ' " < < accel_iter . hw_type_device_string ( ) < < " ' " ) ;
codec = NULL ;
}
}
}
else if ( hw_type = = AV_HWDEVICE_TYPE_NONE )
# endif // USE_AV_HW_CODECS
{
AVDictionaryEntry * video_codec_param = av_dict_get ( dict , " video_codec " , NULL , 0 ) ;
if ( video_codec_param = = NULL )
{
2022-03-20 01:06:50 +08:00
codec = avcodec_find_decoder ( codec_id ) ;
2021-03-01 23:51:04 +08:00
if ( ! codec )
{
2022-03-20 01:06:50 +08:00
CV_LOG_ERROR ( NULL , " Could not find decoder for codec_id= " < < ( int ) codec_id ) ;
2021-03-01 23:51:04 +08:00
}
}
else
{
CV_LOG_DEBUG ( NULL , " FFMPEG: Using video_codec=' " < < video_codec_param - > value < < " ' " ) ;
codec = avcodec_find_decoder_by_name ( video_codec_param - > value ) ;
if ( ! codec )
{
CV_LOG_ERROR ( NULL , " Could not find decoder ' " < < video_codec_param - > value < < " ' " ) ;
}
}
2022-03-20 01:06:50 +08:00
if ( codec )
{
# ifdef CV_FFMPEG_CODECPAR
context = avcodec_alloc_context3 ( codec ) ;
# endif
CV_Assert ( context ) ;
}
2021-03-01 23:51:04 +08:00
}
if ( ! codec )
2022-03-20 01:06:50 +08:00
{
# ifdef CV_FFMPEG_CODECPAR
avcodec_free_context ( & context ) ;
# endif
2021-03-01 23:51:04 +08:00
continue ;
2022-03-20 01:06:50 +08:00
}
2022-09-12 14:12:28 +08:00
context - > thread_count = nThreads ;
2022-03-20 01:06:50 +08:00
fill_codec_context ( context , dict ) ;
# ifdef CV_FFMPEG_CODECPAR
avcodec_parameters_to_context ( context , par ) ;
# endif
err = avcodec_open2 ( context , codec , NULL ) ;
2021-03-01 23:51:04 +08:00
if ( err > = 0 ) {
# if USE_AV_HW_CODECS
va_type = hw_type_to_va_type ( hw_type ) ;
if ( hw_type ! = AV_HWDEVICE_TYPE_NONE & & hw_device < 0 )
hw_device = 0 ;
# endif
break ;
} else {
CV_LOG_ERROR ( NULL , " Could not open codec " < < codec - > name < < " , error: " < < err ) ;
}
# if USE_AV_HW_CODECS
} // while (accel_iter.good())
# else
} while ( 0 ) ;
# endif
if ( err < 0 ) {
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Failed to initialize VideoCapture " ) ;
2013-02-12 18:20:23 +08:00
goto exit_func ;
2021-03-01 23:51:04 +08:00
}
2012-04-30 22:33:52 +08:00
2013-02-20 21:19:55 +08:00
// checking width/height (since decoder can sometimes alter it, eg. vp6f)
2022-03-20 01:06:50 +08:00
if ( enc_width & & ( context - > width ! = enc_width ) )
context - > width = enc_width ;
if ( enc_height & & ( context - > height ! = enc_height ) )
context - > height = enc_height ;
2013-02-20 21:19:55 +08:00
2011-07-05 00:18:12 +08:00
video_stream = i ;
video_st = ic - > streams [ i ] ;
2015-09-09 07:35:40 +08:00
# if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 55 , 45 , 101 ) : CALC_FFMPEG_VERSION ( 55 , 28 , 1 ) )
picture = av_frame_alloc ( ) ;
# else
2011-07-05 00:18:12 +08:00
picture = avcodec_alloc_frame ( ) ;
2015-09-09 07:35:40 +08:00
# endif
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
frame . width = context - > width ;
frame . height = context - > height ;
2016-01-12 22:51:26 +08:00
frame . step = 0 ;
frame . data = NULL ;
2020-06-02 23:34:52 +08:00
get_rotation_angle ( ) ;
2011-07-05 00:18:12 +08:00
break ;
}
}
2021-03-01 23:51:04 +08:00
if ( video_stream > = 0 )
valid = true ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
exit_func :
2011-07-05 00:18:12 +08:00
2016-04-04 18:08:52 +08:00
# if USE_AV_INTERRUPT_CALLBACK
// deactivate interrupt callback
interrupt_metadata . timeout_after_ms = 0 ;
# endif
2011-07-05 00:18:12 +08:00
if ( ! valid )
close ( ) ;
return valid ;
}
2019-11-18 22:07:06 +08:00
bool CvCapture_FFMPEG : : setRaw ( )
{
if ( ! rawMode )
{
if ( frame_number ! = 0 )
{
CV_WARN ( " Incorrect usage: do not grab frames before .set(CAP_PROP_FORMAT, -1) " ) ;
}
// binary stream filter creation is moved into processRawPacket()
rawMode = true ;
}
return true ;
}
2023-04-25 13:25:06 +08:00
static inline bool h26xContainer ( const char * formatLongName ) {
return ! strcmp ( formatLongName , " QuickTime / MOV " ) | | ! strcmp ( formatLongName , " FLV (Flash Video) " ) | | ! strcmp ( formatLongName , " Matroska / WebM " ) ;
}
2019-11-18 22:07:06 +08:00
bool CvCapture_FFMPEG : : processRawPacket ( )
{
if ( packet . data = = NULL ) // EOF
return false ;
if ( ! rawModeInitialized )
{
rawModeInitialized = true ;
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
2020-08-17 20:24:54 +08:00
CV_CODEC_ID eVideoCodec = ic - > streams [ video_stream ] - > codecpar - > codec_id ;
2019-11-18 22:07:06 +08:00
# else
2020-08-25 02:39:05 +08:00
CV_CODEC_ID eVideoCodec = video_st - > codec - > codec_id ;
2019-11-18 22:07:06 +08:00
# endif
const char * filterName = NULL ;
2019-11-18 22:07:06 +08:00
if ( eVideoCodec = = CV_CODEC ( CODEC_ID_H264 )
# if LIBAVCODEC_VERSION_MICRO >= 100 \
& & LIBAVCODEC_BUILD > = CALC_FFMPEG_VERSION ( 57 , 24 , 102 ) // FFmpeg 3.0
| | eVideoCodec = = CV_CODEC ( CODEC_ID_H265 )
# elif LIBAVCODEC_VERSION_MICRO < 100 \
& & LIBAVCODEC_BUILD > = CALC_FFMPEG_VERSION ( 55 , 34 , 1 ) // libav v10+
| | eVideoCodec = = CV_CODEC ( CODEC_ID_HEVC )
# endif
)
2019-11-18 22:07:06 +08:00
{
2023-04-25 13:25:06 +08:00
if ( h26xContainer ( ic - > iformat - > long_name ) )
2019-11-18 22:07:06 +08:00
filterName = eVideoCodec = = CV_CODEC ( CODEC_ID_H264 ) ? " h264_mp4toannexb " : " hevc_mp4toannexb " ;
}
if ( filterName )
{
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
const AVBitStreamFilter * bsf = av_bsf_get_by_name ( filterName ) ;
if ( ! bsf )
{
CV_WARN ( cv : : format ( " Bitstream filter is not available: %s " , filterName ) . c_str ( ) ) ;
return false ;
}
int err = av_bsf_alloc ( bsf , & bsfc ) ;
if ( err < 0 )
{
CV_WARN ( " Error allocating context for bitstream buffer " ) ;
return false ;
}
avcodec_parameters_copy ( bsfc - > par_in , ic - > streams [ video_stream ] - > codecpar ) ;
err = av_bsf_init ( bsfc ) ;
if ( err < 0 )
{
CV_WARN ( " Error initializing bitstream buffer " ) ;
return false ;
}
# else
bsfc = av_bitstream_filter_init ( filterName ) ;
if ( ! bsfc )
{
CV_WARN ( cv : : format ( " Bitstream filter is not available: %s " , filterName ) . c_str ( ) ) ;
return false ;
}
# endif
}
}
if ( bsfc )
{
if ( packet_filtered . data )
{
2019-11-18 22:07:06 +08:00
_opencv_ffmpeg_av_packet_unref ( & packet_filtered ) ;
2019-11-18 22:07:06 +08:00
}
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(58, 20, 100)
int err = av_bsf_send_packet ( bsfc , & packet ) ;
if ( err < 0 )
{
CV_WARN ( " Packet submission for filtering failed " ) ;
return false ;
}
err = av_bsf_receive_packet ( bsfc , & packet_filtered ) ;
if ( err < 0 )
{
CV_WARN ( " Filtered packet retrieve failed " ) ;
return false ;
}
# else
AVCodecContext * ctx = ic - > streams [ video_stream ] - > codec ;
int err = av_bitstream_filter_filter ( bsfc , ctx , NULL , & packet_filtered . data ,
& packet_filtered . size , packet . data , packet . size , packet_filtered . flags & AV_PKT_FLAG_KEY ) ;
if ( err < 0 )
{
CV_WARN ( " Packet filtering failed " ) ;
return false ;
}
# endif
return packet_filtered . data ! = NULL ;
}
return packet . data ! = NULL ;
}
2011-07-05 00:18:12 +08:00
bool CvCapture_FFMPEG : : grabFrame ( )
{
bool valid = false ;
2012-04-30 22:33:52 +08:00
int count_errs = 0 ;
2014-09-16 00:09:44 +08:00
const int max_number_of_attempts = 1 < < 9 ;
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
if ( ! ic | | ! video_st | | ! context ) return false ;
2012-06-08 01:21:29 +08:00
2012-06-01 16:55:16 +08:00
if ( ic - > streams [ video_stream ] - > nb_frames > 0 & &
frame_number > ic - > streams [ video_stream ] - > nb_frames )
return false ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
picture_pts = AV_NOPTS_VALUE_ ;
2011-07-05 00:18:12 +08:00
2016-04-04 18:08:52 +08:00
# if USE_AV_INTERRUPT_CALLBACK
// activate interrupt callback
2022-09-12 14:12:28 +08:00
interrupt_metadata . timeout = 0 ;
2016-04-04 18:08:52 +08:00
get_monotonic_time ( & interrupt_metadata . value ) ;
2021-08-14 04:12:05 +08:00
interrupt_metadata . timeout_after_ms = read_timeout ;
2016-04-04 18:08:52 +08:00
# endif
2021-03-01 23:51:04 +08:00
# if USE_AV_SEND_FRAME_API
// check if we can receive frame from previously decoded packet
2022-03-20 01:06:50 +08:00
valid = avcodec_receive_frame ( context , picture ) > = 0 ;
2021-03-01 23:51:04 +08:00
# endif
2011-07-05 00:18:12 +08:00
// get the next frame
2012-04-30 22:33:52 +08:00
while ( ! valid )
{
2015-03-09 23:19:47 +08:00
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_packet_unref ( & packet ) ;
2016-02-03 08:00:43 +08:00
2016-02-06 20:20:36 +08:00
# if USE_AV_INTERRUPT_CALLBACK
2016-02-03 08:00:43 +08:00
if ( interrupt_metadata . timeout )
{
valid = false ;
break ;
}
2016-02-06 20:20:36 +08:00
# endif
2016-02-03 08:00:43 +08:00
2011-07-05 00:18:12 +08:00
int ret = av_read_frame ( ic , & packet ) ;
2012-04-30 22:33:52 +08:00
2020-05-30 03:52:26 +08:00
if ( ret = = AVERROR ( EAGAIN ) )
continue ;
if ( ret = = AVERROR_EOF )
{
if ( rawMode )
break ;
// flush cached frames from video decoder
packet . data = NULL ;
packet . size = 0 ;
packet . stream_index = video_stream ;
}
2012-04-30 22:33:52 +08:00
if ( packet . stream_index ! = video_stream )
{
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_packet_unref ( & packet ) ;
2012-04-30 22:33:52 +08:00
count_errs + + ;
if ( count_errs > max_number_of_attempts )
break ;
2012-04-14 05:50:59 +08:00
continue ;
}
2012-06-08 01:21:29 +08:00
2019-11-18 22:07:06 +08:00
if ( rawMode )
{
valid = processRawPacket ( ) ;
break ;
}
2012-04-30 22:33:52 +08:00
// Decode video frame
2021-03-01 23:51:04 +08:00
# if USE_AV_SEND_FRAME_API
2022-03-20 01:06:50 +08:00
if ( avcodec_send_packet ( context , & packet ) < 0 ) {
2021-03-01 23:51:04 +08:00
break ;
}
2022-03-20 01:06:50 +08:00
ret = avcodec_receive_frame ( context , picture ) ;
2021-03-01 23:51:04 +08:00
# else
int got_picture = 0 ;
2022-03-20 01:06:50 +08:00
avcodec_decode_video2 ( context , picture , & got_picture , & packet ) ;
2021-03-01 23:51:04 +08:00
ret = got_picture ? 0 : - 1 ;
# endif
if ( ret > = 0 ) {
2012-04-30 22:33:52 +08:00
valid = true ;
2021-03-01 23:51:04 +08:00
} else if ( ret = = AVERROR ( EAGAIN ) ) {
continue ;
2012-04-30 22:33:52 +08:00
}
else
{
count_errs + + ;
if ( count_errs > max_number_of_attempts )
break ;
2011-07-05 00:18:12 +08:00
}
}
2022-06-23 21:09:17 +08:00
if ( valid ) {
if ( picture_pts = = AV_NOPTS_VALUE_ )
picture_pts = picture - > CV_FFMPEG_PTS_FIELD ! = AV_NOPTS_VALUE_ & & picture - > CV_FFMPEG_PTS_FIELD ! = 0 ? picture - > CV_FFMPEG_PTS_FIELD : picture - > pkt_dts ;
2019-11-18 22:07:06 +08:00
frame_number + + ;
2022-06-23 21:09:17 +08:00
}
2019-11-18 22:07:06 +08:00
if ( ! rawMode & & valid & & first_frame_number < 0 )
2012-04-30 22:33:52 +08:00
first_frame_number = dts_to_frame_number ( picture_pts ) ;
2012-06-08 01:21:29 +08:00
2016-04-04 18:08:52 +08:00
# if USE_AV_INTERRUPT_CALLBACK
// deactivate interrupt callback
interrupt_metadata . timeout_after_ms = 0 ;
# endif
2019-11-18 22:07:06 +08:00
// return if we have a new frame or not
2011-07-05 00:18:12 +08:00
return valid ;
}
2022-12-01 06:29:43 +08:00
bool CvCapture_FFMPEG : : retrieveFrame ( int flag , unsigned char * * data , int * step , int * width , int * height , int * cn , int * depth )
2011-07-05 00:18:12 +08:00
{
2022-03-20 01:06:50 +08:00
if ( ! video_st | | ! context )
2019-11-18 22:07:06 +08:00
return false ;
2021-11-24 05:18:55 +08:00
if ( rawMode | | flag = = extraDataIdx )
2019-11-18 22:07:06 +08:00
{
2021-11-24 05:18:55 +08:00
bool ret = true ;
if ( flag = = 0 ) {
AVPacket & p = bsfc ? packet_filtered : packet ;
* data = p . data ;
* step = p . size ;
ret = p . data ! = NULL ;
}
else if ( flag = = extraDataIdx ) {
2022-03-20 01:06:50 +08:00
* data = ic - > streams [ video_stream ] - > CV_FFMPEG_CODEC_FIELD - > extradata ;
* step = ic - > streams [ video_stream ] - > CV_FFMPEG_CODEC_FIELD - > extradata_size ;
2021-11-24 05:18:55 +08:00
}
* width = * step ;
2019-11-18 22:07:06 +08:00
* height = 1 ;
* cn = 1 ;
2022-12-01 06:29:43 +08:00
* depth = CV_8U ;
2021-11-24 05:18:55 +08:00
return ret ;
2019-11-18 22:07:06 +08:00
}
2021-03-01 23:51:04 +08:00
AVFrame * sw_picture = picture ;
# if USE_AV_HW_CODECS
// if hardware frame, copy it to system memory
if ( picture & & picture - > hw_frames_ctx ) {
sw_picture = av_frame_alloc ( ) ;
//if (av_hwframe_map(sw_picture, picture, AV_HWFRAME_MAP_READ) < 0) {
if ( av_hwframe_transfer_data ( sw_picture , picture , 0 ) < 0 ) {
CV_LOG_ERROR ( NULL , " Error copying data from GPU to CPU (av_hwframe_transfer_data) " ) ;
return false ;
}
}
# endif
if ( ! sw_picture | | ! sw_picture - > data [ 0 ] )
2011-07-05 00:18:12 +08:00
return false ;
2022-12-01 06:29:43 +08:00
CV_LOG_DEBUG ( NULL , " Input picture format: " < < av_get_pix_fmt_name ( ( AVPixelFormat ) sw_picture - > format ) ) ;
const AVPixelFormat result_format = convertRGB ? AV_PIX_FMT_BGR24 : ( AVPixelFormat ) sw_picture - > format ;
switch ( result_format )
{
case AV_PIX_FMT_BGR24 : * depth = CV_8U ; * cn = 3 ; break ;
case AV_PIX_FMT_GRAY8 : * depth = CV_8U ; * cn = 1 ; break ;
case AV_PIX_FMT_GRAY16LE : * depth = CV_16U ; * cn = 1 ; break ;
default :
CV_LOG_WARNING ( NULL , " Unknown/unsupported picture format: " < < av_get_pix_fmt_name ( result_format )
< < " , will be treated as 8UC1. " ) ;
* depth = CV_8U ;
* cn = 1 ;
break ; // TODO: return false?
}
2012-04-30 22:33:52 +08:00
if ( img_convert_ctx = = NULL | |
2022-03-20 01:06:50 +08:00
frame . width ! = video_st - > CV_FFMPEG_CODEC_FIELD - > width | |
frame . height ! = video_st - > CV_FFMPEG_CODEC_FIELD - > height | |
2016-01-12 22:51:26 +08:00
frame . data = = NULL )
2012-04-30 22:33:52 +08:00
{
2016-01-12 22:51:26 +08:00
// 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)
2022-03-20 01:06:50 +08:00
int buffer_width = context - > coded_width , buffer_height = context - > coded_height ;
2012-04-30 22:33:52 +08:00
img_convert_ctx = sws_getCachedContext (
2016-01-12 22:51:26 +08:00
img_convert_ctx ,
buffer_width , buffer_height ,
2021-03-01 23:51:04 +08:00
( AVPixelFormat ) sw_picture - > format ,
2016-01-12 22:51:26 +08:00
buffer_width , buffer_height ,
2022-12-01 06:29:43 +08:00
result_format ,
2012-04-30 22:33:52 +08:00
SWS_BICUBIC ,
NULL , NULL , NULL
) ;
if ( img_convert_ctx = = NULL )
return false ; //CV_Error(0, "Cannot initialize the conversion context!");
2015-10-06 21:09:31 +08:00
2016-01-12 22:51:26 +08:00
# if USE_AV_FRAME_GET_BUFFER
av_frame_unref ( & rgb_picture ) ;
2022-12-01 06:29:43 +08:00
rgb_picture . format = result_format ;
2016-01-12 22:51:26 +08:00
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 ) ;
2015-10-06 21:09:31 +08:00
rgb_picture . data [ 0 ] = ( uint8_t * ) realloc ( rgb_picture . data [ 0 ] ,
2022-12-01 06:29:43 +08:00
_opencv_ffmpeg_av_image_get_buffer_size ( result_format ,
2016-01-12 22:51:26 +08:00
buffer_width , buffer_height ) ) ;
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_image_fill_arrays ( & rgb_picture , rgb_picture . data [ 0 ] ,
2022-12-01 06:29:43 +08:00
result_format , buffer_width , buffer_height ) ;
2016-01-12 22:51:26 +08:00
# endif
2022-03-20 01:06:50 +08:00
frame . width = video_st - > CV_FFMPEG_CODEC_FIELD - > width ;
frame . height = video_st - > CV_FFMPEG_CODEC_FIELD - > height ;
2015-10-06 21:09:31 +08:00
frame . data = rgb_picture . data [ 0 ] ;
2016-01-12 22:51:26 +08:00
frame . step = rgb_picture . linesize [ 0 ] ;
2012-04-30 22:33:52 +08:00
}
sws_scale (
img_convert_ctx ,
2021-03-01 23:51:04 +08:00
sw_picture - > data ,
sw_picture - > linesize ,
2022-06-26 22:21:40 +08:00
0 , sw_picture - > height ,
2012-04-30 22:33:52 +08:00
rgb_picture . data ,
rgb_picture . linesize
) ;
2011-07-05 00:18:12 +08:00
* data = frame . data ;
* step = frame . step ;
* width = frame . width ;
* height = frame . height ;
2021-03-02 15:59:08 +08:00
# if USE_AV_HW_CODECS
if ( sw_picture ! = picture )
{
2022-03-13 12:08:51 +08:00
av_frame_free ( & sw_picture ) ;
2021-03-01 23:51:04 +08:00
}
2021-03-02 15:59:08 +08:00
# endif
2011-07-05 00:18:12 +08:00
return true ;
}
2021-05-15 00:48:50 +08:00
bool CvCapture_FFMPEG : : retrieveHWFrame ( cv : : OutputArray output )
{
# if USE_AV_HW_CODECS
// check that we have HW frame in GPU memory
2022-03-20 01:06:50 +08:00
if ( ! picture | | ! picture - > hw_frames_ctx | | ! context ) {
2021-05-15 00:48:50 +08:00
return false ;
}
// GPU color conversion NV12->BGRA, from GPU media buffer to GPU OpenCL buffer
2022-03-20 01:06:50 +08:00
return hw_copy_frame_to_umat ( context - > hw_device_ctx , picture , output ) ;
2021-05-15 00:48:50 +08:00
# else
CV_UNUSED ( output ) ;
return false ;
# endif
}
2014-12-11 01:17:35 +08:00
double CvCapture_FFMPEG : : getProperty ( int property_id ) const
2011-07-05 00:18:12 +08:00
{
2022-03-20 01:06:50 +08:00
if ( ! video_st | | ! context ) return 0 ;
2011-07-05 00:18:12 +08:00
2018-09-30 01:43:01 +08:00
double codec_tag = 0 ;
2020-08-17 20:24:54 +08:00
CV_CODEC_ID codec_id = AV_CODEC_ID_NONE ;
2018-09-30 01:43:01 +08:00
const char * codec_fourcc = NULL ;
2011-07-05 00:18:12 +08:00
switch ( property_id )
{
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_MSEC :
2019-06-14 13:47:03 +08:00
if ( picture_pts = = AV_NOPTS_VALUE_ )
{
return 0 ;
}
return ( dts_to_sec ( picture_pts ) * 1000 ) ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_FRAMES :
2012-04-30 22:33:52 +08:00
return ( double ) frame_number ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_AVI_RATIO :
2012-04-30 22:33:52 +08:00
return r2d ( ic - > streams [ video_stream ] - > time_base ) ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_FRAME_COUNT :
2012-04-30 22:33:52 +08:00
return ( double ) get_total_frames ( ) ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_FRAME_WIDTH :
2021-03-03 04:41:05 +08:00
return ( double ) ( ( rotation_auto & & ( ( rotation_angle % 180 ) ! = 0 ) ) ? frame . height : frame . width ) ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_FRAME_HEIGHT :
2021-03-03 04:41:05 +08:00
return ( double ) ( ( rotation_auto & & ( ( rotation_angle % 180 ) ! = 0 ) ) ? frame . width : frame . height ) ;
2022-06-26 23:30:31 +08:00
case CAP_PROP_FRAME_TYPE :
return ( double ) av_get_picture_type_char ( picture - > pict_type ) ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_FPS :
2015-10-31 08:48:04 +08:00
return get_fps ( ) ;
2022-08-02 22:18:42 +08:00
case CAP_PROP_FOURCC : {
2022-03-20 01:06:50 +08:00
codec_id = video_st - > CV_FFMPEG_CODEC_FIELD - > codec_id ;
codec_tag = ( double ) video_st - > CV_FFMPEG_CODEC_FIELD - > codec_tag ;
2018-09-30 01:43:01 +08:00
if ( codec_tag | | codec_id = = AV_CODEC_ID_NONE )
{
return codec_tag ;
}
codec_fourcc = _opencv_avcodec_get_name ( codec_id ) ;
2022-08-02 22:18:42 +08:00
if ( ! codec_fourcc | | strcmp ( codec_fourcc , " unknown_codec " ) = = 0 | | strlen ( codec_fourcc ) ! = 4 )
2018-09-30 01:43:01 +08:00
{
2022-08-02 22:18:42 +08:00
const struct AVCodecTag * fallback_tags [ ] = {
// APIchanges:
// 2012-01-31 - dd6d3b0 - lavf 54.01.0
// Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags().
avformat_get_riff_video_tags ( ) ,
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100) && defined LIBAVFORMAT_VERSION_MICRO && LIBAVFORMAT_VERSION_MICRO >= 100
// APIchanges: ffmpeg only
// 2014-01-19 - 1a193c4 - lavf 55.25.100 - avformat.h
// Add avformat_get_mov_video_tags() and avformat_get_mov_audio_tags().
avformat_get_mov_video_tags ( ) ,
# endif
codec_bmp_tags , // fallback for avformat < 54.1
NULL } ;
return av_codec_get_tag ( fallback_tags , codec_id ) ;
2018-09-30 01:43:01 +08:00
}
2018-11-03 10:17:17 +08:00
return ( double ) CV_FOURCC ( codec_fourcc [ 0 ] , codec_fourcc [ 1 ] , codec_fourcc [ 2 ] , codec_fourcc [ 3 ] ) ;
2022-08-02 22:18:42 +08:00
}
2019-05-31 21:18:00 +08:00
case CAP_PROP_SAR_NUM :
2018-02-21 23:42:40 +08:00
return _opencv_ffmpeg_get_sample_aspect_ratio ( ic - > streams [ video_stream ] ) . num ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_SAR_DEN :
2018-02-21 23:42:40 +08:00
return _opencv_ffmpeg_get_sample_aspect_ratio ( ic - > streams [ video_stream ] ) . den ;
2019-11-18 22:07:06 +08:00
case CAP_PROP_CODEC_PIXEL_FORMAT :
{
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_CODECPAR
AVPixelFormat pix_fmt = ( AVPixelFormat ) video_st - > codecpar - > format ;
# else
2019-11-18 22:07:06 +08:00
AVPixelFormat pix_fmt = video_st - > codec - > pix_fmt ;
2022-03-20 01:06:50 +08:00
# endif
2019-11-18 22:07:06 +08:00
unsigned int fourcc_tag = avcodec_pix_fmt_to_codec_tag ( pix_fmt ) ;
return ( fourcc_tag = = 0 ) ? ( double ) - 1 : ( double ) fourcc_tag ;
}
case CAP_PROP_FORMAT :
if ( rawMode )
return - 1 ;
break ;
2022-12-01 06:29:43 +08:00
case CAP_PROP_CONVERT_RGB :
return convertRGB ;
2021-11-24 05:18:55 +08:00
case CAP_PROP_LRF_HAS_KEY_FRAME : {
const AVPacket & p = bsfc ? packet_filtered : packet ;
return ( ( p . flags & AV_PKT_FLAG_KEY ) ! = 0 ) ? 1 : 0 ;
}
case CAP_PROP_CODEC_EXTRADATA_INDEX :
return extraDataIdx ;
2020-03-11 06:44:14 +08:00
case CAP_PROP_BITRATE :
2020-03-10 21:44:22 +08:00
return static_cast < double > ( get_bitrate ( ) ) ;
2020-07-29 01:15:02 +08:00
case CAP_PROP_ORIENTATION_META :
2020-06-02 23:34:52 +08:00
return static_cast < double > ( rotation_angle ) ;
2020-07-29 01:15:02 +08:00
case CAP_PROP_ORIENTATION_AUTO :
2020-08-25 02:39:05 +08:00
# if LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
2020-06-02 23:34:52 +08:00
return static_cast < double > ( rotation_auto ) ;
2020-06-08 22:31:18 +08:00
# else
return 0 ;
# endif
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
case CAP_PROP_HW_ACCELERATION :
return static_cast < double > ( va_type ) ;
case CAP_PROP_HW_DEVICE :
return static_cast < double > ( hw_device ) ;
2021-05-15 00:48:50 +08:00
case CAP_PROP_HW_ACCELERATION_USE_OPENCL :
return static_cast < double > ( use_opencl ) ;
2021-03-01 23:51:04 +08:00
# endif // USE_AV_HW_CODECS
2021-09-10 05:23:49 +08:00
case CAP_PROP_STREAM_OPEN_TIME_USEC :
//ic->start_time_realtime is in microseconds
return ( ( double ) ic - > start_time_realtime ) ;
2022-09-12 14:12:28 +08:00
case CAP_PROP_N_THREADS :
return static_cast < double > ( context - > thread_count ) ;
2012-04-30 22:33:52 +08:00
default :
2012-04-14 05:50:59 +08:00
break ;
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
2011-07-05 00:18:12 +08:00
return 0 ;
}
2014-12-11 01:17:35 +08:00
double CvCapture_FFMPEG : : r2d ( AVRational r ) const
2012-04-30 22:33:52 +08:00
{
return r . num = = 0 | | r . den = = 0 ? 0. : ( double ) r . num / ( double ) r . den ;
}
2014-12-11 01:17:35 +08:00
double CvCapture_FFMPEG : : get_duration_sec ( ) const
2011-07-05 00:18:12 +08:00
{
2012-04-30 22:33:52 +08:00
double sec = ( double ) ic - > duration / ( double ) AV_TIME_BASE ;
if ( sec < eps_zero )
2011-07-05 00:18:12 +08:00
{
2012-04-30 22:33:52 +08:00
sec = ( double ) ic - > streams [ video_stream ] - > duration * r2d ( ic - > streams [ video_stream ] - > time_base ) ;
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
return sec ;
2011-07-05 00:18:12 +08:00
}
2020-03-10 21:44:22 +08:00
int64_t CvCapture_FFMPEG : : get_bitrate ( ) const
2012-02-18 20:56:00 +08:00
{
2020-03-10 21:44:22 +08:00
return ic - > bit_rate / 1000 ;
2012-04-30 22:33:52 +08:00
}
2014-12-11 01:17:35 +08:00
double CvCapture_FFMPEG : : get_fps ( ) const
2012-04-30 22:33:52 +08:00
{
2016-09-01 20:31:08 +08:00
#if 0 && LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 1, 100) && LIBAVFORMAT_VERSION_MICRO >= 100
double fps = r2d ( av_guess_frame_rate ( ic , ic - > streams [ video_stream ] , NULL ) ) ;
# else
double fps = r2d ( ic - > streams [ video_stream ] - > avg_frame_rate ) ;
2012-04-30 22:33:52 +08:00
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(52, 111, 0)
if ( fps < eps_zero )
{
fps = r2d ( ic - > streams [ video_stream ] - > avg_frame_rate ) ;
2012-02-18 20:56:00 +08:00
}
2012-06-08 01:21:29 +08:00
# endif
2012-04-30 22:33:52 +08:00
if ( fps < eps_zero )
{
2022-03-20 01:06:50 +08:00
fps = 1.0 / r2d ( ic - > streams [ video_stream ] - > time_base ) ;
2012-04-30 22:33:52 +08:00
}
2016-09-01 20:31:08 +08:00
# endif
2012-04-30 22:33:52 +08:00
return fps ;
}
2014-12-11 01:17:35 +08:00
int64_t CvCapture_FFMPEG : : get_total_frames ( ) const
2012-04-30 22:33:52 +08:00
{
int64_t nbf = ic - > streams [ video_stream ] - > nb_frames ;
if ( nbf = = 0 )
{
nbf = ( int64_t ) floor ( get_duration_sec ( ) * get_fps ( ) + 0.5 ) ;
}
return nbf ;
}
int64_t CvCapture_FFMPEG : : dts_to_frame_number ( int64_t dts )
{
double sec = dts_to_sec ( dts ) ;
return ( int64_t ) ( get_fps ( ) * sec + 0.5 ) ;
}
2019-06-14 13:47:03 +08:00
double CvCapture_FFMPEG : : dts_to_sec ( int64_t dts ) const
2012-04-30 22:33:52 +08:00
{
return ( double ) ( dts - ic - > streams [ video_stream ] - > start_time ) *
r2d ( ic - > streams [ video_stream ] - > time_base ) ;
}
2020-06-02 23:34:52 +08:00
void CvCapture_FFMPEG : : get_rotation_angle ( )
{
rotation_angle = 0 ;
2022-03-20 01:06:50 +08:00
# 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 )
{
2022-06-10 21:32:18 +08:00
rotation_angle = - cvRound ( av_display_rotation_get ( ( const int32_t * ) data ) ) ;
2022-03-20 01:06:50 +08:00
if ( rotation_angle < 0 )
rotation_angle + = 360 ;
}
# elif LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
2020-06-02 23:34:52 +08:00
AVDictionaryEntry * rotate_tag = av_dict_get ( video_st - > metadata , " rotate " , NULL , 0 ) ;
if ( rotate_tag ! = NULL )
rotation_angle = atoi ( rotate_tag - > value ) ;
2020-06-08 22:31:18 +08:00
# endif
2020-06-02 23:34:52 +08:00
}
2012-04-30 22:33:52 +08:00
void CvCapture_FFMPEG : : seek ( int64_t _frame_number )
{
2022-03-20 01:06:50 +08:00
CV_Assert ( context ) ;
2012-04-30 22:33:52 +08:00
_frame_number = std : : min ( _frame_number , get_total_frames ( ) ) ;
int delta = 16 ;
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
// if we have not grabbed a single frame before first seek, let's read the first frame
// and get some valuable information during the process
2012-05-05 22:43:03 +08:00
if ( first_frame_number < 0 & & get_total_frames ( ) > 1 )
2012-04-14 05:50:59 +08:00
grabFrame ( ) ;
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
for ( ; ; )
{
int64_t _frame_number_temp = std : : max ( _frame_number - delta , ( int64_t ) 0 ) ;
double sec = ( double ) _frame_number_temp / get_fps ( ) ;
int64_t time_stamp = ic - > streams [ video_stream ] - > start_time ;
double time_base = r2d ( ic - > streams [ video_stream ] - > time_base ) ;
time_stamp + = ( int64_t ) ( sec / time_base + 0.5 ) ;
2012-05-05 22:43:03 +08:00
if ( get_total_frames ( ) > 1 ) av_seek_frame ( ic , video_stream , time_stamp , AVSEEK_FLAG_BACKWARD ) ;
2022-03-20 01:06:50 +08:00
avcodec_flush_buffers ( context ) ;
2012-04-30 22:33:52 +08:00
if ( _frame_number > 0 )
{
grabFrame ( ) ;
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
if ( _frame_number > 1 )
{
frame_number = dts_to_frame_number ( picture_pts ) - first_frame_number ;
//printf("_frame_number = %d, frame_number = %d, delta = %d\n",
// (int)_frame_number, (int)frame_number, delta);
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
if ( frame_number < 0 | | frame_number > _frame_number - 1 )
{
if ( _frame_number_temp = = 0 | | delta > = INT_MAX / 4 )
break ;
delta = delta < 16 ? delta * 2 : delta * 3 / 2 ;
continue ;
}
while ( frame_number < _frame_number - 1 )
{
if ( ! grabFrame ( ) )
break ;
}
frame_number + + ;
break ;
}
else
{
frame_number = 1 ;
break ;
}
}
else
{
frame_number = 0 ;
break ;
}
2012-02-18 20:56:00 +08:00
}
2012-04-30 22:33:52 +08:00
}
void CvCapture_FFMPEG : : seek ( double sec )
{
seek ( ( int64_t ) ( sec * get_fps ( ) + 0.5 ) ) ;
2012-02-18 20:56:00 +08:00
}
2011-07-05 00:18:12 +08:00
bool CvCapture_FFMPEG : : setProperty ( int property_id , double value )
{
if ( ! video_st ) return false ;
switch ( property_id )
{
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_MSEC :
case CAP_PROP_POS_FRAMES :
case CAP_PROP_POS_AVI_RATIO :
2011-07-05 00:18:12 +08:00
{
switch ( property_id )
{
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_FRAMES :
2012-04-30 22:33:52 +08:00
seek ( ( int64_t ) value ) ;
2011-07-05 00:18:12 +08:00
break ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_MSEC :
2012-04-30 22:33:52 +08:00
seek ( value / 1000.0 ) ;
2011-07-05 00:18:12 +08:00
break ;
2019-05-31 21:18:00 +08:00
case CAP_PROP_POS_AVI_RATIO :
2012-04-30 22:33:52 +08:00
seek ( ( int64_t ) ( value * ic - > duration ) ) ;
2011-07-05 00:18:12 +08:00
break ;
}
picture_pts = ( int64_t ) value ;
}
break ;
2019-11-18 22:07:06 +08:00
case CAP_PROP_FORMAT :
if ( value = = - 1 )
return setRaw ( ) ;
return false ;
2022-12-01 06:29:43 +08:00
case CAP_PROP_CONVERT_RGB :
convertRGB = ( value ! = 0 ) ;
return true ;
2020-07-29 01:15:02 +08:00
case CAP_PROP_ORIENTATION_AUTO :
2020-08-25 02:39:05 +08:00
# if LIBAVUTIL_BUILD >= CALC_FFMPEG_VERSION(52, 94, 100)
2021-03-03 04:41:05 +08:00
rotation_auto = value ! = 0 ? true : false ;
2020-06-08 22:31:18 +08:00
return true ;
# else
2021-03-03 04:41:05 +08:00
rotation_auto = false ;
2020-06-08 22:31:18 +08:00
return false ;
# endif
2011-07-05 00:18:12 +08:00
default :
return false ;
}
return true ;
}
///////////////// FFMPEG CvVideoWriter implementation //////////////////////////
struct CvVideoWriter_FFMPEG
{
bool open ( const char * filename , int fourcc ,
2021-03-01 23:51:04 +08:00
double fps , int width , int height , const VideoWriterParameters & params ) ;
2011-07-05 00:18:12 +08:00
void close ( ) ;
bool writeFrame ( const unsigned char * data , int step , int width , int height , int cn , int origin ) ;
2021-05-15 00:48:50 +08:00
bool writeHWFrame ( cv : : InputArray input ) ;
2021-03-01 23:51:04 +08:00
double getProperty ( int propId ) const ;
2011-07-05 00:18:12 +08:00
void init ( ) ;
2022-03-20 01:06:50 +08:00
CV_FFMPEG_FMT_CONST AVOutputFormat * fmt ;
2012-04-30 22:33:52 +08:00
AVFormatContext * oc ;
2011-07-05 00:18:12 +08:00
uint8_t * outbuf ;
uint32_t outbuf_size ;
FILE * outfile ;
AVFrame * picture ;
AVFrame * input_picture ;
uint8_t * picbuf ;
AVStream * video_st ;
2022-03-20 01:06:50 +08:00
AVCodecContext * context ;
2021-03-01 23:51:04 +08:00
AVPixelFormat input_pix_fmt ;
2016-03-17 00:36:53 +08:00
unsigned char * aligned_input ;
2017-11-23 22:48:01 +08:00
size_t aligned_input_size ;
2012-04-30 22:33:52 +08:00
int frame_width , frame_height ;
2015-04-23 05:39:44 +08:00
int frame_idx ;
2012-04-30 22:33:52 +08:00
bool ok ;
2011-07-05 00:18:12 +08:00
struct SwsContext * img_convert_ctx ;
2021-03-01 23:51:04 +08:00
VideoAccelerationType va_type ;
int hw_device ;
2021-05-15 00:48:50 +08:00
int use_opencl ;
2011-07-05 00:18:12 +08:00
} ;
static const char * icvFFMPEGErrStr ( int err )
{
2011-07-29 22:17:42 +08:00
switch ( err ) {
2012-04-14 05:50:59 +08:00
case AVERROR_BSF_NOT_FOUND :
return " Bitstream filter not found " ;
case AVERROR_DECODER_NOT_FOUND :
return " Decoder not found " ;
case AVERROR_DEMUXER_NOT_FOUND :
return " Demuxer not found " ;
case AVERROR_ENCODER_NOT_FOUND :
return " Encoder not found " ;
case AVERROR_EOF :
return " End of file " ;
case AVERROR_EXIT :
return " Immediate exit was requested; the called function should not be restarted " ;
case AVERROR_FILTER_NOT_FOUND :
return " Filter not found " ;
case AVERROR_INVALIDDATA :
return " Invalid data found when processing input " ;
case AVERROR_MUXER_NOT_FOUND :
return " Muxer not found " ;
case AVERROR_OPTION_NOT_FOUND :
return " Option not found " ;
case AVERROR_PATCHWELCOME :
return " Not yet implemented in FFmpeg, patches welcome " ;
case AVERROR_PROTOCOL_NOT_FOUND :
return " Protocol not found " ;
case AVERROR_STREAM_NOT_FOUND :
return " Stream not found " ;
default :
break ;
2012-04-30 22:33:52 +08:00
}
2011-07-29 22:17:42 +08:00
2012-04-30 22:33:52 +08:00
return " Unspecified error " ;
2011-07-05 00:18:12 +08:00
}
/* function internal to FFMPEG (libavformat/riff.c) to lookup codec id by fourcc tag*/
extern " C " {
2013-04-04 15:27:43 +08:00
enum CV_CODEC_ID codec_get_bmp_id ( unsigned int tag ) ;
2011-07-05 00:18:12 +08:00
}
void CvVideoWriter_FFMPEG : : init ( )
{
fmt = 0 ;
oc = 0 ;
outbuf = 0 ;
outbuf_size = 0 ;
outfile = 0 ;
picture = 0 ;
input_picture = 0 ;
picbuf = 0 ;
video_st = 0 ;
2022-03-20 01:06:50 +08:00
context = 0 ;
2021-03-01 23:51:04 +08:00
input_pix_fmt = AV_PIX_FMT_NONE ;
2016-03-17 00:36:53 +08:00
aligned_input = NULL ;
2017-11-23 22:48:01 +08:00
aligned_input_size = 0 ;
2011-07-05 00:18:12 +08:00
img_convert_ctx = 0 ;
2012-04-30 22:33:52 +08:00
frame_width = frame_height = 0 ;
2015-04-23 05:39:44 +08:00
frame_idx = 0 ;
2021-03-01 23:51:04 +08:00
va_type = VIDEO_ACCELERATION_NONE ;
hw_device = - 1 ;
2021-05-15 00:48:50 +08:00
use_opencl = 0 ;
2012-04-30 22:33:52 +08:00
ok = false ;
2011-07-05 00:18:12 +08:00
}
/**
* the following function is a modified version of code
* found in ffmpeg - 0.4 .9 - pre1 / output_example . c
*/
static AVFrame * icv_alloc_picture_FFMPEG ( int pix_fmt , int width , int height , bool alloc )
{
2012-04-30 22:33:52 +08:00
AVFrame * picture ;
2017-06-29 21:40:22 +08:00
uint8_t * picture_buf = 0 ;
2012-04-30 22:33:52 +08:00
int size ;
2015-09-09 07:35:40 +08:00
# if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 55 , 45 , 101 ) : CALC_FFMPEG_VERSION ( 55 , 28 , 1 ) )
picture = av_frame_alloc ( ) ;
# else
2012-04-30 22:33:52 +08:00
picture = avcodec_alloc_frame ( ) ;
2015-09-09 07:35:40 +08:00
# endif
2012-04-30 22:33:52 +08:00
if ( ! picture )
return NULL ;
2015-10-02 23:27:18 +08:00
picture - > format = pix_fmt ;
picture - > width = width ;
picture - > height = height ;
2016-03-22 20:26:55 +08:00
size = _opencv_ffmpeg_av_image_get_buffer_size ( ( AVPixelFormat ) pix_fmt , width , height ) ;
2012-04-30 22:33:52 +08:00
if ( alloc ) {
picture_buf = ( uint8_t * ) malloc ( size ) ;
if ( ! picture_buf )
{
av_free ( picture ) ;
return NULL ;
}
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_image_fill_arrays ( picture , picture_buf ,
2015-09-09 07:35:40 +08:00
( AVPixelFormat ) pix_fmt , width , height ) ;
2012-04-30 22:33:52 +08:00
}
2017-06-29 21:40:22 +08:00
2012-04-30 22:33:52 +08:00
return picture ;
2011-07-05 00:18:12 +08:00
}
2021-03-01 23:51:04 +08:00
/* configure video stream */
2022-03-20 01:06:50 +08:00
static AVCodecContext * icv_configure_video_stream_FFMPEG ( AVFormatContext * oc ,
2021-03-01 23:51:04 +08:00
AVStream * st ,
const AVCodec * codec ,
int w , int h , int bitrate ,
2022-03-20 01:06:50 +08:00
double fps , AVPixelFormat pixel_format , int fourcc )
2011-07-05 00:18:12 +08:00
{
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_CODECPAR
AVCodecContext * c = avcodec_alloc_context3 ( codec ) ;
# else
2021-03-01 23:51:04 +08:00
AVCodecContext * c = st - > codec ;
2022-03-20 01:06:50 +08:00
# endif
CV_Assert ( c ) ;
2012-04-30 22:33:52 +08:00
int frame_rate , frame_rate_base ;
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
c - > codec_id = codec - > id ;
2012-04-30 22:33:52 +08:00
c - > codec_type = AVMEDIA_TYPE_VIDEO ;
2022-03-20 01:06:50 +08:00
c - > codec_tag = fourcc ;
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
# ifndef CV_FFMPEG_CODECPAR
2013-03-05 05:37:19 +08:00
// Set per-codec defaults
2020-08-17 20:24:54 +08:00
CV_CODEC_ID c_id = c - > codec_id ;
2013-03-05 05:37:19 +08:00
avcodec_get_context_defaults3 ( c , codec ) ;
// avcodec_get_context_defaults3 erases codec_id for some reason
c - > codec_id = c_id ;
2022-03-20 01:06:50 +08:00
# endif
2013-03-05 05:37:19 +08:00
2012-04-30 22:33:52 +08:00
/* put sample parameters */
int64_t lbit_rate = ( int64_t ) bitrate ;
lbit_rate + = ( bitrate / 2 ) ;
lbit_rate = std : : min ( lbit_rate , ( int64_t ) INT_MAX ) ;
c - > bit_rate = lbit_rate ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
// took advice from
// http://ffmpeg-users.933282.n4.nabble.com/warning-clipping-1-dct-coefficients-to-127-127-td934297.html
c - > qmin = 3 ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* resolution must be a multiple of two */
c - > width = w ;
c - > height = h ;
/* time base: this is the fundamental unit of time (in seconds) in terms
of which frame timestamps are represented . for fixed - fps content ,
2011-07-05 00:18:12 +08:00
timebase should be 1 / framerate and timestamp increments should be
identically 1. */
2012-04-30 22:33:52 +08:00
frame_rate = ( int ) ( fps + 0.5 ) ;
frame_rate_base = 1 ;
2018-10-01 04:24:02 +08:00
while ( fabs ( ( ( double ) frame_rate / frame_rate_base ) - fps ) > 0.001 ) {
2012-04-30 22:33:52 +08:00
frame_rate_base * = 10 ;
frame_rate = ( int ) ( fps * frame_rate_base + 0.5 ) ;
}
2011-07-05 00:18:12 +08:00
c - > time_base . den = frame_rate ;
c - > time_base . num = frame_rate_base ;
2012-04-30 22:33:52 +08:00
/* adjust time base for supported framerates */
if ( codec & & codec - > supported_framerates ) {
const AVRational * p = codec - > supported_framerates ;
2011-07-05 00:18:12 +08:00
AVRational req = { frame_rate , frame_rate_base } ;
2012-04-30 22:33:52 +08:00
const AVRational * best = NULL ;
AVRational best_error = { INT_MAX , 1 } ;
for ( ; p - > den ! = 0 ; p + + ) {
AVRational error = av_sub_q ( req , * p ) ;
if ( error . num < 0 ) error . num * = - 1 ;
if ( av_cmp_q ( error , best_error ) < 0 ) {
best_error = error ;
best = p ;
}
}
2017-06-29 21:40:22 +08:00
if ( best = = NULL )
2022-03-20 01:06:50 +08:00
{
# ifdef CV_FFMPEG_CODECPAR
avcodec_free_context ( & c ) ;
# endif
return NULL ;
}
2012-04-30 22:33:52 +08:00
c - > time_base . den = best - > num ;
c - > time_base . num = best - > den ;
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
c - > gop_size = 12 ; /* emit one intra frame every twelve frames at most */
2021-03-01 23:51:04 +08:00
c - > pix_fmt = pixel_format ;
2013-04-04 15:27:43 +08:00
if ( c - > codec_id = = CV_CODEC ( CODEC_ID_MPEG2VIDEO ) ) {
2011-07-05 00:18:12 +08:00
c - > max_b_frames = 2 ;
}
2013-04-04 15:27:43 +08:00
if ( c - > codec_id = = CV_CODEC ( CODEC_ID_MPEG1VIDEO ) | | c - > codec_id = = CV_CODEC ( CODEC_ID_MSMPEG4V3 ) ) {
2011-07-05 00:18:12 +08:00
/* needed to avoid using macroblocks in which some coeffs overflow
2018-02-14 00:28:11 +08:00
this doesn ' t happen with normal video , it just happens here as the
motion of the chroma plane doesn ' t match the luma plane */
2012-04-30 22:33:52 +08:00
/* avoid FFMPEG warning 'clipping 1 dct coefficients...' */
2011-07-05 00:18:12 +08:00
c - > mb_decision = 2 ;
}
2013-03-05 11:06:05 +08:00
2013-03-05 05:37:19 +08:00
/* Some settings for libx264 encoding, restore dummy values for gop_size
and qmin since they will be set to reasonable defaults by the libx264
2013-08-27 17:57:24 +08:00
preset system . Also , use a crf encode with the default quality rating ,
2013-03-05 05:37:19 +08:00
this seems easier than finding an appropriate default bitrate . */
2015-04-23 02:57:29 +08:00
if ( c - > codec_id = = AV_CODEC_ID_H264 ) {
2013-03-05 05:37:19 +08:00
c - > gop_size = - 1 ;
c - > qmin = - 1 ;
c - > bit_rate = 0 ;
2015-10-06 00:28:35 +08:00
if ( c - > priv_data )
av_opt_set ( c - > priv_data , " crf " , " 23 " , 0 ) ;
2013-03-05 05:37:19 +08:00
}
2013-03-05 11:06:05 +08:00
2018-02-14 00:28:11 +08:00
// some formats want stream headers to be separate
2011-07-05 00:18:12 +08:00
if ( oc - > oformat - > flags & AVFMT_GLOBALHEADER )
{
2020-08-25 02:39:05 +08:00
// flags were renamed: https://github.com/libav/libav/commit/7c6eb0a1b7bf1aac7f033a7ec6d8cacc3b5c2615
# if LIBAVCODEC_BUILD >= (LIBAVCODEC_VERSION_MICRO >= 100 \
? CALC_FFMPEG_VERSION ( 56 , 60 , 100 ) : CALC_FFMPEG_VERSION ( 56 , 35 , 0 ) )
2017-11-19 02:08:41 +08:00
c - > flags | = AV_CODEC_FLAG_GLOBAL_HEADER ;
# else
2011-07-05 00:18:12 +08:00
c - > flags | = CODEC_FLAG_GLOBAL_HEADER ;
2017-11-19 02:08:41 +08:00
# endif
2011-07-05 00:18:12 +08:00
}
2022-03-20 01:06:50 +08:00
st - > avg_frame_rate = av_make_q ( frame_rate , frame_rate_base ) ;
2018-05-29 19:28:38 +08:00
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 20, 0)
st - > time_base = c - > time_base ;
# endif
2016-09-08 19:42:01 +08:00
2022-03-20 01:06:50 +08:00
return c ;
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
static const int OPENCV_NO_FRAMES_WRITTEN_CODE = 1000 ;
2022-03-20 01:06:50 +08:00
static int icv_av_write_frame_FFMPEG ( AVFormatContext * oc , AVStream * video_st , AVCodecContext * c ,
2015-04-23 05:39:44 +08:00
uint8_t * , uint32_t ,
2021-03-01 23:51:04 +08:00
AVFrame * picture , int frame_idx )
2011-07-05 00:18:12 +08:00
{
2015-04-23 02:57:29 +08:00
int ret = OPENCV_NO_FRAMES_WRITTEN_CODE ;
2011-07-05 00:18:12 +08:00
2017-11-19 02:08:41 +08:00
# if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(57, 0, 0)
if ( oc - > oformat - > flags & AVFMT_RAWPICTURE )
{
2011-07-05 00:18:12 +08:00
/* raw video case. The API will change slightly in the near
futur for that */
AVPacket pkt ;
av_init_packet ( & pkt ) ;
2012-04-30 22:33:52 +08:00
pkt . flags | = PKT_FLAG_KEY ;
2011-07-05 00:18:12 +08:00
pkt . stream_index = video_st - > index ;
pkt . data = ( uint8_t * ) picture ;
pkt . size = sizeof ( AVPicture ) ;
ret = av_write_frame ( oc , & pkt ) ;
2017-11-19 02:08:41 +08:00
}
else
# endif
{
2011-07-05 00:18:12 +08:00
/* encode the image */
2020-08-07 05:43:47 +08:00
# if USE_AV_SEND_FRAME_API
2021-03-01 23:51:04 +08:00
if ( picture = = NULL & & frame_idx = = 0 ) {
ret = 0 ;
} else {
ret = avcodec_send_frame ( c , picture ) ;
if ( ret < 0 )
CV_LOG_ERROR ( NULL , " Error sending frame to encoder (avcodec_send_frame) " ) ;
}
2020-08-07 05:43:47 +08:00
while ( ret > = 0 )
{
AVPacket * pkt = av_packet_alloc ( ) ;
pkt - > stream_index = video_st - > index ;
ret = avcodec_receive_packet ( c , pkt ) ;
if ( ! ret )
{
av_packet_rescale_ts ( pkt , c - > time_base , video_st - > time_base ) ;
ret = av_write_frame ( oc , pkt ) ;
av_packet_free ( & pkt ) ;
continue ;
}
av_packet_free ( & pkt ) ;
break ;
}
# else
2021-03-01 23:51:04 +08:00
CV_UNUSED ( frame_idx ) ;
2015-04-23 02:57:29 +08:00
AVPacket pkt ;
av_init_packet ( & pkt ) ;
int got_output = 0 ;
pkt . data = NULL ;
pkt . size = 0 ;
ret = avcodec_encode_video2 ( c , & pkt , picture , & got_output ) ;
if ( ret < 0 )
2015-04-23 05:39:44 +08:00
;
2015-04-23 02:57:29 +08:00
else if ( got_output ) {
2015-04-23 05:39:44 +08:00
if ( pkt . pts ! = ( int64_t ) AV_NOPTS_VALUE )
pkt . pts = av_rescale_q ( pkt . pts , c - > time_base , video_st - > time_base ) ;
if ( pkt . dts ! = ( int64_t ) AV_NOPTS_VALUE )
pkt . dts = av_rescale_q ( pkt . dts , c - > time_base , video_st - > time_base ) ;
if ( pkt . duration )
pkt . duration = av_rescale_q ( pkt . duration , c - > time_base , video_st - > time_base ) ;
2015-04-23 02:57:29 +08:00
pkt . stream_index = video_st - > index ;
ret = av_write_frame ( oc , & pkt ) ;
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_packet_unref ( & pkt ) ;
2015-04-23 02:57:29 +08:00
}
else
ret = OPENCV_NO_FRAMES_WRITTEN_CODE ;
# endif
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
return ret ;
2011-07-05 00:18:12 +08:00
}
/// write a frame with FFMPEG
bool CvVideoWriter_FFMPEG : : writeFrame ( const unsigned char * data , int step , int width , int height , int cn , int origin )
{
2016-03-16 21:28:59 +08:00
// check parameters
if ( input_pix_fmt = = AV_PIX_FMT_BGR24 ) {
if ( cn ! = 3 ) {
return false ;
}
}
2022-12-01 06:29:43 +08:00
else if ( input_pix_fmt = = AV_PIX_FMT_GRAY8 | | input_pix_fmt = = AV_PIX_FMT_GRAY16LE ) {
2016-03-16 21:28:59 +08:00
if ( cn ! = 1 ) {
return false ;
}
}
else {
2022-12-01 06:29:43 +08:00
CV_LOG_WARNING ( NULL , " Input data does not match selected pixel format: "
< < av_get_pix_fmt_name ( input_pix_fmt )
< < " , number of channels: " < < cn ) ;
2021-11-28 02:34:52 +08:00
CV_Assert ( false ) ;
2016-03-16 21:28:59 +08:00
}
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
if ( ( width & - 2 ) ! = frame_width | | ( height & - 2 ) ! = frame_height | | ! data )
return false ;
width = frame_width ;
height = frame_height ;
2011-07-05 00:18:12 +08:00
2016-03-17 00:36:53 +08:00
// FFmpeg contains SIMD optimizations which can sometimes read data past
2017-11-23 22:48:01 +08:00
// the supplied input buffer.
// Related info: https://trac.ffmpeg.org/ticket/6763
// 1. To ensure that doesn't happen, we pad the step to a multiple of 32
// (that's the minimal alignment for which Valgrind doesn't raise any warnings).
// 2. (dataend - SIMD_SIZE) and (dataend + SIMD_SIZE) is from the same 4k page
const int CV_STEP_ALIGNMENT = 32 ;
const size_t CV_SIMD_SIZE = 32 ;
2021-03-01 23:51:04 +08:00
const size_t CV_PAGE_MASK = ~ ( size_t ) ( 4096 - 1 ) ;
2018-02-20 18:15:41 +08:00
const unsigned char * dataend = data + ( ( size_t ) height * step ) ;
2017-11-23 22:48:01 +08:00
if ( step % CV_STEP_ALIGNMENT ! = 0 | |
( ( ( size_t ) dataend - CV_SIMD_SIZE ) & CV_PAGE_MASK ) ! = ( ( ( size_t ) dataend + CV_SIMD_SIZE ) & CV_PAGE_MASK ) )
2012-02-22 20:16:01 +08:00
{
2017-11-23 22:48:01 +08:00
int aligned_step = ( step + CV_STEP_ALIGNMENT - 1 ) & ~ ( CV_STEP_ALIGNMENT - 1 ) ;
2016-03-17 00:36:53 +08:00
2017-11-23 22:48:01 +08:00
size_t new_size = ( aligned_step * height + CV_SIMD_SIZE ) ;
if ( ! aligned_input | | aligned_input_size < new_size )
2012-02-22 20:16:01 +08:00
{
2017-11-23 22:48:01 +08:00
if ( aligned_input )
av_freep ( & aligned_input ) ;
aligned_input_size = new_size ;
aligned_input = ( unsigned char * ) av_mallocz ( aligned_input_size ) ;
2012-02-22 20:16:01 +08:00
}
2016-03-17 00:36:53 +08:00
2012-02-22 20:16:01 +08:00
if ( origin = = 1 )
for ( int y = 0 ; y < height ; y + + )
2016-03-17 00:36:53 +08:00
memcpy ( aligned_input + y * aligned_step , data + ( height - 1 - y ) * step , step ) ;
2012-02-22 20:16:01 +08:00
else
for ( int y = 0 ; y < height ; y + + )
2016-03-17 00:36:53 +08:00
memcpy ( aligned_input + y * aligned_step , data + y * step , step ) ;
2011-07-05 00:18:12 +08:00
2016-03-17 00:36:53 +08:00
data = aligned_input ;
step = aligned_step ;
2011-07-05 00:18:12 +08:00
}
2022-03-20 01:06:50 +08:00
AVPixelFormat sw_pix_fmt = context - > pix_fmt ;
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
2022-03-20 01:06:50 +08:00
if ( context - > hw_frames_ctx )
sw_pix_fmt = ( ( AVHWFramesContext * ) context - > hw_frames_ctx - > data ) - > sw_format ;
2021-03-01 23:51:04 +08:00
# endif
if ( sw_pix_fmt ! = input_pix_fmt ) {
2021-11-28 02:34:52 +08:00
CV_Assert ( input_picture ) ;
2012-04-30 22:33:52 +08:00
// let input_picture point to the raw data buffer of 'image'
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_image_fill_arrays ( input_picture , ( uint8_t * ) data ,
2015-09-09 07:35:40 +08:00
( AVPixelFormat ) input_pix_fmt , width , height ) ;
2016-03-17 00:36:53 +08:00
input_picture - > linesize [ 0 ] = step ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
if ( ! img_convert_ctx )
{
img_convert_ctx = sws_getContext ( width ,
height ,
2015-09-09 07:35:40 +08:00
( AVPixelFormat ) input_pix_fmt ,
2022-03-20 01:06:50 +08:00
context - > width ,
context - > height ,
2021-03-01 23:51:04 +08:00
sw_pix_fmt ,
2012-04-30 22:33:52 +08:00
SWS_BICUBIC ,
NULL , NULL , NULL ) ;
if ( ! img_convert_ctx )
return false ;
}
2012-04-14 05:50:59 +08:00
if ( sws_scale ( img_convert_ctx , input_picture - > data ,
input_picture - > linesize , 0 ,
height ,
picture - > data , picture - > linesize ) < 0 )
return false ;
2012-04-30 22:33:52 +08:00
}
else {
2016-03-22 20:26:55 +08:00
_opencv_ffmpeg_av_image_fill_arrays ( picture , ( uint8_t * ) data ,
2015-09-09 07:35:40 +08:00
( AVPixelFormat ) input_pix_fmt , width , height ) ;
2016-03-17 00:36:53 +08:00
picture - > linesize [ 0 ] = step ;
2012-04-30 22:33:52 +08:00
}
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
bool ret ;
# if USE_AV_HW_CODECS
2022-03-20 01:06:50 +08:00
if ( context - > hw_device_ctx ) {
2021-03-01 23:51:04 +08:00
// 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 ;
}
2022-03-20 01:06:50 +08:00
if ( av_hwframe_get_buffer ( context - > hw_frames_ctx , hw_frame , 0 ) < 0 ) {
2021-03-01 23:51:04 +08:00
CV_LOG_ERROR ( NULL , " Error obtaining HW frame (av_hwframe_get_buffer) " ) ;
av_frame_free ( & hw_frame ) ;
return false ;
}
if ( av_hwframe_transfer_data ( hw_frame , picture , 0 ) < 0 ) {
CV_LOG_ERROR ( NULL , " Error copying data from CPU to GPU (av_hwframe_transfer_data) " ) ;
av_frame_free ( & hw_frame ) ;
return false ;
}
hw_frame - > pts = frame_idx ;
2022-03-20 01:06:50 +08:00
int ret_write = icv_av_write_frame_FFMPEG ( oc , video_st , context , outbuf , outbuf_size , hw_frame , frame_idx ) ;
2021-03-01 23:51:04 +08:00
ret = ret_write > = 0 ? true : false ;
av_frame_free ( & hw_frame ) ;
} else
# endif
{
picture - > pts = frame_idx ;
2022-03-20 01:06:50 +08:00
int ret_write = icv_av_write_frame_FFMPEG ( oc , video_st , context , outbuf , outbuf_size , picture , frame_idx ) ;
2021-03-01 23:51:04 +08:00
ret = ret_write > = 0 ? true : false ;
}
2015-04-23 05:39:44 +08:00
frame_idx + + ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
return ret ;
2011-07-05 00:18:12 +08:00
}
2021-05-15 00:48:50 +08:00
bool CvVideoWriter_FFMPEG : : writeHWFrame ( cv : : InputArray input ) {
# if USE_AV_HW_CODECS
2022-03-20 01:06:50 +08:00
if ( ! video_st | | ! context | | ! context - > hw_frames_ctx | | ! context - > hw_device_ctx )
2021-05-15 00:48:50 +08:00
return false ;
// Get hardware frame from frame pool
AVFrame * hw_frame = av_frame_alloc ( ) ;
if ( ! hw_frame ) {
return false ;
}
2022-03-20 01:06:50 +08:00
if ( av_hwframe_get_buffer ( context - > hw_frames_ctx , hw_frame , 0 ) < 0 ) {
2021-05-15 00:48:50 +08:00
av_frame_free ( & hw_frame ) ;
return false ;
}
// GPU to GPU copy
2022-03-20 01:06:50 +08:00
if ( ! hw_copy_umat_to_frame ( context - > hw_device_ctx , input , hw_frame ) ) {
2021-05-15 00:48:50 +08:00
av_frame_free ( & hw_frame ) ;
return false ;
}
// encode
hw_frame - > pts = frame_idx ;
2022-03-20 01:06:50 +08:00
icv_av_write_frame_FFMPEG ( oc , video_st , context , outbuf , outbuf_size , hw_frame , frame_idx ) ;
2021-05-15 00:48:50 +08:00
frame_idx + + ;
av_frame_free ( & hw_frame ) ;
return true ;
# else
CV_UNUSED ( input ) ;
return false ;
# endif
}
2021-03-01 23:51:04 +08:00
double CvVideoWriter_FFMPEG : : getProperty ( int propId ) const
{
CV_UNUSED ( propId ) ;
# if USE_AV_HW_CODECS
if ( propId = = VIDEOWRITER_PROP_HW_ACCELERATION )
{
return static_cast < double > ( va_type ) ;
}
else if ( propId = = VIDEOWRITER_PROP_HW_DEVICE )
{
return static_cast < double > ( hw_device ) ;
}
2021-05-15 00:48:50 +08:00
else if ( propId = = VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL )
{
return static_cast < double > ( use_opencl ) ;
}
2021-03-01 23:51:04 +08:00
# endif
return 0 ;
}
2011-07-05 00:18:12 +08:00
/// close video output stream and free associated memory
void CvVideoWriter_FFMPEG : : close ( )
{
2012-04-30 22:33:52 +08:00
/* no more frame to compress. The codec has a latency of a few
frames if using B frames , so we get the last frames by
passing the same picture again */
// TODO -- do we need to account for latency here?
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* write the trailer, if any */
2022-04-02 02:02:14 +08:00
if ( picture & & ok & & oc )
2012-04-30 22:33:52 +08:00
{
2017-11-19 02:08:41 +08:00
# if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(57, 0, 0)
if ( ! ( oc - > oformat - > flags & AVFMT_RAWPICTURE ) )
# endif
2012-04-30 22:33:52 +08:00
{
for ( ; ; )
{
2022-03-20 01:06:50 +08:00
int ret = icv_av_write_frame_FFMPEG ( oc , video_st , context , outbuf , outbuf_size , NULL , frame_idx ) ;
2012-04-30 22:33:52 +08:00
if ( ret = = OPENCV_NO_FRAMES_WRITTEN_CODE | | ret < 0 )
break ;
}
}
av_write_trailer ( oc ) ;
}
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
if ( img_convert_ctx )
{
sws_freeContext ( img_convert_ctx ) ;
img_convert_ctx = 0 ;
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
// free pictures
2022-04-02 02:02:14 +08:00
if ( picture & & context & & context - > pix_fmt ! = input_pix_fmt )
2012-04-30 22:33:52 +08:00
{
if ( picture - > data [ 0 ] )
free ( picture - > data [ 0 ] ) ;
picture - > data [ 0 ] = 0 ;
}
av_free ( picture ) ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
if ( input_picture )
av_free ( input_picture ) ;
2011-07-05 00:18:12 +08:00
2022-04-02 02:02:14 +08:00
# ifdef CV_FFMPEG_CODECPAR
avcodec_free_context ( & context ) ;
# else
2012-04-30 22:33:52 +08:00
/* close codec */
2022-04-02 02:02:14 +08:00
if ( context ) // fixed after https://github.com/FFmpeg/FFmpeg/commit/3e1f507f3e8f16b716aa115552d243b48ae809bd
avcodec_close ( context ) ;
context = NULL ;
# endif
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
av_free ( outbuf ) ;
2011-07-05 00:18:12 +08:00
2017-06-29 21:40:22 +08:00
if ( oc )
2012-04-30 22:33:52 +08:00
{
2017-06-29 21:40:22 +08:00
if ( ! ( fmt - > flags & AVFMT_NOFILE ) )
{
/* close the output file */
avio_close ( oc - > pb ) ;
}
2011-07-05 00:18:12 +08:00
2017-06-29 21:40:22 +08:00
/* free the stream */
avformat_free_context ( oc ) ;
}
2011-07-05 00:18:12 +08:00
2016-03-17 00:36:53 +08:00
av_freep ( & aligned_input ) ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
init ( ) ;
}
2011-07-05 00:18:12 +08:00
2015-06-03 22:01:45 +08:00
# define CV_PRINTABLE_CHAR(ch) ((ch) < 32 ? '?' : (ch))
# define CV_TAG_TO_PRINTABLE_CHAR4(tag) CV_PRINTABLE_CHAR((tag) & 255), CV_PRINTABLE_CHAR(((tag) >> 8) & 255), CV_PRINTABLE_CHAR(((tag) >> 16) & 255), CV_PRINTABLE_CHAR(((tag) >> 24) & 255)
2015-06-08 17:58:02 +08:00
static inline bool cv_ff_codec_tag_match ( const AVCodecTag * tags , CV_CODEC_ID id , unsigned int tag )
2015-06-03 22:01:45 +08:00
{
while ( tags - > id ! = AV_CODEC_ID_NONE )
{
if ( tags - > id = = id & & tags - > tag = = tag )
return true ;
tags + + ;
}
return false ;
}
2018-01-13 07:49:21 +08:00
2015-06-08 17:58:02 +08:00
static inline bool cv_ff_codec_tag_list_match ( const AVCodecTag * const * tags , CV_CODEC_ID id , unsigned int tag )
2015-06-03 22:01:45 +08:00
{
int i ;
for ( i = 0 ; tags & & tags [ i ] ; i + + ) {
bool res = cv_ff_codec_tag_match ( tags [ i ] , id , tag ) ;
if ( res )
return res ;
}
return false ;
}
2018-01-13 07:49:21 +08:00
static inline void cv_ff_codec_tag_dump ( const AVCodecTag * const * tags )
{
int i ;
for ( i = 0 ; tags & & tags [ i ] ; i + + ) {
const AVCodecTag * ptags = tags [ i ] ;
while ( ptags - > id ! = AV_CODEC_ID_NONE )
{
unsigned int tag = ptags - > tag ;
printf ( " fourcc tag 0x%08x/'%c%c%c%c' codec_id %04X \n " , tag , CV_TAG_TO_PRINTABLE_CHAR4 ( tag ) , ptags - > id ) ;
ptags + + ;
}
}
}
2012-04-30 22:33:52 +08:00
/// Create a video writer object that uses FFMPEG
bool CvVideoWriter_FFMPEG : : open ( const char * filename , int fourcc ,
2021-03-01 23:51:04 +08:00
double fps , int width , int height , const VideoWriterParameters & params )
2012-04-30 22:33:52 +08:00
{
2019-01-14 18:33:38 +08:00
InternalFFMpegRegister : : init ( ) ;
2021-03-03 04:41:05 +08:00
AutoLock lock ( _mutex ) ;
2013-04-04 15:27:43 +08:00
CV_CODEC_ID codec_id = CV_CODEC ( CODEC_ID_NONE ) ;
2021-03-01 23:51:04 +08:00
AVPixelFormat codec_pix_fmt ;
2012-04-30 22:33:52 +08:00
double bitrate_scale = 1 ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
close ( ) ;
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
const bool is_color = params . get ( VIDEOWRITER_PROP_IS_COLOR , true ) ;
2022-12-01 06:29:43 +08:00
const int depth = params . get ( VIDEOWRITER_PROP_DEPTH , CV_8U ) ;
const bool is_supported = depth = = CV_8U | | ( depth = = CV_16U & & ! is_color ) ;
if ( ! is_supported )
{
CV_LOG_WARNING ( NULL , " Unsupported depth/isColor combination is selected, "
" only CV_8UC1/CV_8UC3/CV_16UC1 are supported. " ) ;
return false ;
}
2021-03-01 23:51:04 +08:00
if ( params . has ( VIDEOWRITER_PROP_HW_ACCELERATION ) )
{
va_type = params . get < VideoAccelerationType > ( VIDEOWRITER_PROP_HW_ACCELERATION , VIDEO_ACCELERATION_NONE ) ;
# if !USE_AV_HW_CODECS
if ( va_type ! = VIDEO_ACCELERATION_NONE & & va_type ! = VIDEO_ACCELERATION_ANY )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: FFmpeg backend is build without acceleration support. Can't handle VIDEOWRITER_PROP_HW_ACCELERATION parameter. Bailout " ) ;
return false ;
}
# endif
}
if ( params . has ( VIDEOWRITER_PROP_HW_DEVICE ) )
{
hw_device = params . get < int > ( VIDEOWRITER_PROP_HW_DEVICE , - 1 ) ;
if ( va_type = = VIDEO_ACCELERATION_NONE & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Invalid usage of VIDEOWRITER_PROP_HW_DEVICE without requested H/W acceleration. Bailout " ) ;
return false ;
}
if ( va_type = = VIDEO_ACCELERATION_ANY & & hw_device ! = - 1 )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Invalid usage of VIDEOWRITER_PROP_HW_DEVICE with 'ANY' H/W acceleration. Bailout " ) ;
return false ;
}
}
2021-05-15 00:48:50 +08:00
if ( params . has ( VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL ) ) {
use_opencl = params . get < int > ( VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL ) ;
}
2021-03-01 23:51:04 +08:00
if ( params . warnUnusedParameters ( ) )
{
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: unsupported parameters in VideoWriter, see logger INFO channel for details " ) ;
return false ;
}
2012-04-30 22:33:52 +08:00
// check arguments
if ( ! filename )
return false ;
if ( fps < = 0 )
return false ;
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
// we allow frames of odd width or height, but in this case we truncate
// the rightmost column/the bottom row. Probably, this should be handled more elegantly,
// but some internal functions inside FFMPEG swscale require even width/height.
width & = - 2 ;
height & = - 2 ;
if ( width < = 0 | | height < = 0 )
return false ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* auto detect the output format from the name and fourcc code. */
2011-07-29 22:17:42 +08:00
2012-04-30 22:33:52 +08:00
fmt = av_guess_format ( NULL , filename , NULL ) ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
if ( ! fmt )
return false ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* determine optimal pixel format */
2022-12-01 06:29:43 +08:00
if ( is_color )
{
switch ( depth )
{
case CV_8U : input_pix_fmt = AV_PIX_FMT_BGR24 ; break ;
default :
CV_LOG_WARNING ( NULL , " Unsupported input depth for color image: " < < depth ) ;
return false ;
}
2012-04-30 22:33:52 +08:00
}
2022-12-01 06:29:43 +08:00
else
{
switch ( depth )
{
case CV_8U : input_pix_fmt = AV_PIX_FMT_GRAY8 ; break ;
case CV_16U : input_pix_fmt = AV_PIX_FMT_GRAY16LE ; break ;
default :
CV_LOG_WARNING ( NULL , " Unsupported input depth for grayscale image: " < < depth ) ;
return false ;
}
2012-04-30 22:33:52 +08:00
}
2022-12-01 06:29:43 +08:00
CV_LOG_DEBUG ( NULL , " Selected pixel format: " < < av_get_pix_fmt_name ( input_pix_fmt ) ) ;
2012-04-14 05:50:59 +08:00
2018-01-13 07:49:21 +08:00
if ( fourcc = = - 1 )
{
fprintf ( stderr , " OpenCV: FFMPEG: format %s / %s \n " , fmt - > name , fmt - > long_name ) ;
cv_ff_codec_tag_dump ( fmt - > codec_tag ) ;
return false ;
}
2012-04-30 22:33:52 +08:00
/* Lookup codec_id for given fourcc */
2015-06-03 22:01:45 +08:00
if ( ( codec_id = av_codec_get_id ( fmt - > codec_tag , fourcc ) ) = = CV_CODEC ( CODEC_ID_NONE ) )
{
const struct AVCodecTag * fallback_tags [ ] = {
2015-06-08 17:58:02 +08:00
// APIchanges:
// 2012-01-31 - dd6d3b0 - lavf 54.01.0
// Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags().
2015-06-03 22:01:45 +08:00
avformat_get_riff_video_tags ( ) ,
2015-06-08 17:58:02 +08:00
# if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100) && defined LIBAVFORMAT_VERSION_MICRO && LIBAVFORMAT_VERSION_MICRO >= 100
2015-06-03 22:01:45 +08:00
// APIchanges: ffmpeg only
// 2014-01-19 - 1a193c4 - lavf 55.25.100 - avformat.h
// Add avformat_get_mov_video_tags() and avformat_get_mov_audio_tags().
2015-06-08 17:58:02 +08:00
avformat_get_mov_video_tags ( ) ,
2015-06-03 22:01:45 +08:00
# endif
2015-06-08 17:58:02 +08:00
codec_bmp_tags , // fallback for avformat < 54.1
NULL } ;
2021-03-01 23:51:04 +08:00
if ( codec_id = = CV_CODEC ( CODEC_ID_NONE ) ) {
codec_id = av_codec_get_id ( fallback_tags , fourcc ) ;
}
if ( codec_id = = CV_CODEC ( CODEC_ID_NONE ) ) {
char * p = ( char * ) & fourcc ;
char name [ ] = { ( char ) tolower ( p [ 0 ] ) , ( char ) tolower ( p [ 1 ] ) , ( char ) tolower ( p [ 2 ] ) , ( char ) tolower ( p [ 3 ] ) , 0 } ;
const AVCodecDescriptor * desc = avcodec_descriptor_get_by_name ( name ) ;
if ( desc )
codec_id = desc - > id ;
}
if ( codec_id = = CV_CODEC ( CODEC_ID_NONE ) )
2015-06-03 22:01:45 +08:00
{
fflush ( stdout ) ;
fprintf ( stderr , " OpenCV: FFMPEG: tag 0x%08x/'%c%c%c%c' is not found (format '%s / %s')' \n " ,
fourcc , CV_TAG_TO_PRINTABLE_CHAR4 ( fourcc ) ,
fmt - > name , fmt - > long_name ) ;
return false ;
}
}
2018-01-13 07:49:21 +08:00
2015-06-03 22:01:45 +08:00
// validate tag
if ( cv_ff_codec_tag_list_match ( fmt - > codec_tag , codec_id , fourcc ) = = false )
{
fflush ( stdout ) ;
fprintf ( stderr , " OpenCV: FFMPEG: tag 0x%08x/'%c%c%c%c' is not supported with codec id %d and format '%s / %s' \n " ,
fourcc , CV_TAG_TO_PRINTABLE_CHAR4 ( fourcc ) ,
codec_id , fmt - > name , fmt - > long_name ) ;
int supported_tag ;
if ( ( supported_tag = av_codec_get_tag ( fmt - > codec_tag , codec_id ) ) ! = 0 )
{
fprintf ( stderr , " OpenCV: FFMPEG: fallback to use tag 0x%08x/'%c%c%c%c' \n " ,
supported_tag , CV_TAG_TO_PRINTABLE_CHAR4 ( supported_tag ) ) ;
fourcc = supported_tag ;
}
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
// alloc memory for context
oc = avformat_alloc_context ( ) ;
2021-12-03 20:32:49 +08:00
CV_Assert ( oc ) ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* set file name */
oc - > oformat = fmt ;
2022-03-20 01:06:50 +08:00
# ifndef CV_FFMPEG_URL
2012-04-30 22:33:52 +08:00
snprintf ( oc - > filename , sizeof ( oc - > filename ) , " %s " , filename ) ;
2022-03-20 01:06:50 +08:00
# 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
2012-04-30 22:33:52 +08:00
/* set some options */
oc - > max_delay = ( int ) ( 0.7 * AV_TIME_BASE ) ; /* This reduces buffer underrun warnings with MPEG */
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
// set a few optimal pixel formats for lossless codecs of interest..
switch ( codec_id ) {
2013-04-04 15:27:43 +08:00
case CV_CODEC ( CODEC_ID_JPEGLS ) :
2012-04-30 22:33:52 +08:00
// BGR24 or GRAY8 depending on is_color...
2018-01-13 07:49:21 +08:00
// supported: bgr24 rgb24 gray gray16le
// as of version 3.4.1
2012-04-30 22:33:52 +08:00
codec_pix_fmt = input_pix_fmt ;
break ;
2013-04-04 15:27:43 +08:00
case CV_CODEC ( CODEC_ID_HUFFYUV ) :
2018-01-13 07:49:21 +08:00
// supported: yuv422p rgb24 bgra
// as of version 3.4.1
switch ( input_pix_fmt )
{
case AV_PIX_FMT_RGB24 :
case AV_PIX_FMT_BGRA :
codec_pix_fmt = input_pix_fmt ;
break ;
case AV_PIX_FMT_BGR24 :
codec_pix_fmt = AV_PIX_FMT_RGB24 ;
break ;
default :
codec_pix_fmt = AV_PIX_FMT_YUV422P ;
break ;
}
break ;
case CV_CODEC ( CODEC_ID_PNG ) :
// supported: rgb24 rgba rgb48be rgba64be pal8 gray ya8 gray16be ya16be monob
// as of version 3.4.1
switch ( input_pix_fmt )
{
case AV_PIX_FMT_GRAY8 :
case AV_PIX_FMT_GRAY16BE :
case AV_PIX_FMT_RGB24 :
case AV_PIX_FMT_BGRA :
codec_pix_fmt = input_pix_fmt ;
break ;
case AV_PIX_FMT_GRAY16LE :
codec_pix_fmt = AV_PIX_FMT_GRAY16BE ;
break ;
case AV_PIX_FMT_BGR24 :
codec_pix_fmt = AV_PIX_FMT_RGB24 ;
break ;
default :
codec_pix_fmt = AV_PIX_FMT_YUV422P ;
break ;
}
break ;
case CV_CODEC ( CODEC_ID_FFV1 ) :
// supported: MANY
// as of version 3.4.1
switch ( input_pix_fmt )
{
case AV_PIX_FMT_GRAY8 :
case AV_PIX_FMT_GRAY16LE :
# ifdef AV_PIX_FMT_BGR0
case AV_PIX_FMT_BGR0 :
# endif
case AV_PIX_FMT_BGRA :
codec_pix_fmt = input_pix_fmt ;
break ;
case AV_PIX_FMT_GRAY16BE :
codec_pix_fmt = AV_PIX_FMT_GRAY16LE ;
break ;
case AV_PIX_FMT_BGR24 :
case AV_PIX_FMT_RGB24 :
# ifdef AV_PIX_FMT_BGR0
codec_pix_fmt = AV_PIX_FMT_BGR0 ;
# else
codec_pix_fmt = AV_PIX_FMT_BGRA ;
# endif
break ;
default :
codec_pix_fmt = AV_PIX_FMT_YUV422P ;
break ;
}
2012-04-30 22:33:52 +08:00
break ;
2013-04-04 15:27:43 +08:00
case CV_CODEC ( CODEC_ID_MJPEG ) :
case CV_CODEC ( CODEC_ID_LJPEG ) :
2015-09-09 07:35:40 +08:00
codec_pix_fmt = AV_PIX_FMT_YUVJ420P ;
2012-04-30 22:33:52 +08:00
bitrate_scale = 3 ;
break ;
2013-04-04 15:27:43 +08:00
case CV_CODEC ( CODEC_ID_RAWVIDEO ) :
2018-01-13 07:49:21 +08:00
// RGBA is the only RGB fourcc supported by AVI and MKV format
if ( fourcc = = CV_FOURCC ( ' R ' , ' G ' , ' B ' , ' A ' ) )
{
codec_pix_fmt = AV_PIX_FMT_RGBA ;
}
else
{
switch ( input_pix_fmt )
{
case AV_PIX_FMT_GRAY8 :
case AV_PIX_FMT_GRAY16LE :
case AV_PIX_FMT_GRAY16BE :
codec_pix_fmt = input_pix_fmt ;
break ;
default :
codec_pix_fmt = AV_PIX_FMT_YUV420P ;
break ;
}
}
2012-04-30 22:33:52 +08:00
break ;
default :
// good for lossy formats, MPEG, etc.
2015-09-09 07:35:40 +08:00
codec_pix_fmt = AV_PIX_FMT_YUV420P ;
2012-04-30 22:33:52 +08:00
break ;
}
2012-06-08 01:21:29 +08:00
2020-08-25 02:39:05 +08:00
double bitrate = std : : min ( bitrate_scale * fps * width * height , ( double ) INT_MAX / 2 ) ;
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
if ( codec_id = = AV_CODEC_ID_NONE ) {
2022-03-20 01:06:50 +08:00
codec_id = av_guess_codec ( oc - > oformat , NULL , filename , NULL , AVMEDIA_TYPE_VIDEO ) ;
2021-03-01 23:51:04 +08:00
}
// Add video stream to output file
video_st = avformat_new_stream ( oc , 0 ) ;
if ( ! video_st ) {
CV_WARN ( " Could not allocate stream " ) ;
return false ;
}
AVDictionary * dict = NULL ;
# if !defined(NO_GETENV) && (LIBAVUTIL_VERSION_MAJOR >= 53)
char * options = getenv ( " OPENCV_FFMPEG_WRITER_OPTIONS " ) ;
2022-10-27 06:27:17 +08:00
if ( options )
{
CV_LOG_DEBUG ( NULL , " VIDEOIO/FFMPEG: using writer options from environment: " < < options ) ;
2021-03-01 23:51:04 +08:00
av_dict_parse_string ( & dict , options , " ; " , " | " , 0 ) ;
}
# endif
// find and open encoder, try HW acceleration types specified in 'hw_acceleration' list (in order)
int err = - 1 ;
2022-03-20 01:06:50 +08:00
const AVCodec * codec = NULL ;
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
AVBufferRef * hw_device_ctx = NULL ;
HWAccelIterator accel_iter ( va_type , true /*isEncoder*/ , dict ) ;
while ( accel_iter . good ( ) )
{
# else
do {
# endif
# if USE_AV_HW_CODECS
accel_iter . parse_next ( ) ;
AVHWDeviceType hw_type = accel_iter . hw_type ( ) ;
codec = NULL ;
AVPixelFormat hw_format = AV_PIX_FMT_NONE ;
if ( hw_device_ctx )
av_buffer_unref ( & hw_device_ctx ) ;
if ( hw_type ! = AV_HWDEVICE_TYPE_NONE )
{
codec = hw_find_codec ( codec_id , hw_type , av_codec_is_encoder , accel_iter . disabled_codecs ( ) . c_str ( ) , & hw_format ) ;
if ( ! codec )
continue ;
2021-05-15 00:48:50 +08:00
hw_device_ctx = hw_create_device ( hw_type , hw_device , accel_iter . device_subname ( ) , use_opencl ! = 0 ) ;
2021-03-01 23:51:04 +08:00
if ( ! hw_device_ctx )
continue ;
}
else if ( hw_type = = AV_HWDEVICE_TYPE_NONE )
# endif
{
codec = avcodec_find_encoder ( codec_id ) ;
if ( ! codec ) {
CV_LOG_ERROR ( NULL , " Could not find encoder for codec_id= " < < ( int ) codec_id < < " , error: "
< < icvFFMPEGErrStr ( AVERROR_ENCODER_NOT_FOUND ) ) ;
}
}
if ( ! codec )
continue ;
# if USE_AV_HW_CODECS
AVPixelFormat format = ( hw_format ! = AV_PIX_FMT_NONE ) ? hw_format : codec_pix_fmt ;
# else
AVPixelFormat format = codec_pix_fmt ;
# endif
2022-03-20 01:06:50 +08:00
# ifdef CV_FFMPEG_CODECPAR
2022-04-02 02:02:14 +08:00
avcodec_free_context ( & context ) ;
2022-03-20 01:06:50 +08:00
# endif
context = icv_configure_video_stream_FFMPEG ( oc , video_st , codec ,
width , height , ( int ) ( bitrate + 0.5 ) ,
fps , format , fourcc ) ;
if ( ! context )
{
2021-03-01 23:51:04 +08:00
continue ;
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
#if 0
# if FF_API_DUMP_FORMAT
2021-03-01 23:51:04 +08:00
dump_format ( oc , 0 , filename , 1 ) ;
2011-07-05 00:18:12 +08:00
# else
2021-03-01 23:51:04 +08:00
av_dump_format ( oc , 0 , filename , 1 ) ;
2012-04-30 22:33:52 +08:00
# endif
2011-07-05 00:18:12 +08:00
# endif
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
if ( hw_device_ctx ) {
2022-03-20 01:06:50 +08:00
context - > hw_device_ctx = av_buffer_ref ( hw_device_ctx ) ;
2021-03-01 23:51:04 +08:00
if ( hw_format ! = AV_PIX_FMT_NONE ) {
2022-03-20 01:06:50 +08:00
context - > hw_frames_ctx = hw_create_frames ( NULL , hw_device_ctx , width , height , hw_format ) ;
if ( ! context - > hw_frames_ctx )
2021-03-01 23:51:04 +08:00
continue ;
}
}
# endif
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
int64_t lbit_rate = ( int64_t ) context - > bit_rate ;
2021-03-01 23:51:04 +08:00
lbit_rate + = ( int64_t ) ( bitrate / 2 ) ;
lbit_rate = std : : min ( lbit_rate , ( int64_t ) INT_MAX ) ;
2022-03-20 01:06:50 +08:00
context - > bit_rate_tolerance = ( int ) lbit_rate ;
context - > bit_rate = ( int ) lbit_rate ;
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
/* open the codec */
2022-03-20 01:06:50 +08:00
err = avcodec_open2 ( context , codec , NULL ) ;
2021-03-01 23:51:04 +08:00
if ( err > = 0 ) {
# if USE_AV_HW_CODECS
va_type = hw_type_to_va_type ( hw_type ) ;
if ( hw_type ! = AV_HWDEVICE_TYPE_NONE & & hw_device < 0 )
hw_device = 0 ;
# endif
break ;
} else {
2022-03-20 01:06:50 +08:00
CV_LOG_ERROR ( NULL , " Could not open codec " < < codec - > name < < " , error: " < < icvFFMPEGErrStr ( err ) < < " ( " < < err < < " ) " ) ;
2021-03-01 23:51:04 +08:00
}
# if USE_AV_HW_CODECS
} // while (accel_iter.good())
# else
} while ( 0 ) ;
# endif
2011-07-05 00:18:12 +08:00
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
if ( hw_device_ctx )
av_buffer_unref ( & hw_device_ctx ) ;
# endif
if ( dict ! = NULL )
av_dict_free ( & dict ) ;
if ( err < 0 ) {
CV_LOG_ERROR ( NULL , " VIDEOIO/FFMPEG: Failed to initialize VideoWriter " ) ;
2012-04-30 22:33:52 +08:00
return false ;
2012-04-14 05:50:59 +08:00
}
2011-07-05 00:18:12 +08:00
2022-03-20 01:06:50 +08:00
# 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
2012-04-30 22:33:52 +08:00
outbuf = NULL ;
2011-07-05 00:18:12 +08:00
2017-11-19 02:08:41 +08:00
# if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(57, 0, 0)
if ( ! ( oc - > oformat - > flags & AVFMT_RAWPICTURE ) )
# endif
{
2012-04-30 22:33:52 +08:00
/* allocate output buffer */
/* assume we will never get codec output with more than 4 bytes per pixel... */
outbuf_size = width * height * 4 ;
outbuf = ( uint8_t * ) av_malloc ( outbuf_size ) ;
2012-04-14 05:50:59 +08:00
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
bool need_color_convert ;
2022-03-20 01:06:50 +08:00
AVPixelFormat sw_pix_fmt = context - > pix_fmt ;
2021-03-01 23:51:04 +08:00
# if USE_AV_HW_CODECS
2022-03-20 01:06:50 +08:00
if ( context - > hw_frames_ctx )
sw_pix_fmt = ( ( AVHWFramesContext * ) context - > hw_frames_ctx - > data ) - > sw_format ;
2021-03-01 23:51:04 +08:00
# endif
need_color_convert = ( sw_pix_fmt ! = input_pix_fmt ) ;
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* allocate the encoded raw picture */
2022-03-20 01:06:50 +08:00
picture = icv_alloc_picture_FFMPEG ( sw_pix_fmt , context - > width , context - > height , need_color_convert ) ;
2012-04-30 22:33:52 +08:00
if ( ! picture ) {
return false ;
2012-04-14 05:50:59 +08:00
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* if the output format is not our input format, then a temporary
picture of the input format is needed too . It is then converted
to the required output format */
input_picture = NULL ;
if ( need_color_convert ) {
2022-03-20 01:06:50 +08:00
input_picture = icv_alloc_picture_FFMPEG ( input_pix_fmt , context - > width , context - > height , false ) ;
2012-04-30 22:33:52 +08:00
if ( ! input_picture ) {
return false ;
2012-04-14 05:50:59 +08:00
}
2011-07-05 00:18:12 +08:00
}
2012-04-30 22:33:52 +08:00
/* open the output file, if needed */
2020-08-25 02:39:05 +08:00
if ( ! ( fmt - > flags & AVFMT_NOFILE ) )
{
if ( avio_open ( & oc - > pb , filename , AVIO_FLAG_WRITE ) < 0 )
{
2012-04-30 22:33:52 +08:00
return false ;
}
2012-04-14 05:50:59 +08:00
}
2011-07-05 00:18:12 +08:00
2012-04-30 22:33:52 +08:00
/* write the stream header, if any */
err = avformat_write_header ( oc , NULL ) ;
2012-06-08 01:21:29 +08:00
2012-04-30 22:33:52 +08:00
if ( err < 0 )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
close ( ) ;
remove ( filename ) ;
return false ;
2012-04-17 15:12:16 +08:00
}
2012-04-30 22:33:52 +08:00
frame_width = width ;
frame_height = height ;
2015-04-23 05:39:44 +08:00
frame_idx = 0 ;
2012-04-30 22:33:52 +08:00
ok = true ;
2013-02-12 18:20:23 +08:00
2012-04-30 22:33:52 +08:00
return true ;
2012-04-17 15:12:16 +08:00
}
2021-01-28 14:00:38 +08:00
static
CvCapture_FFMPEG * cvCreateFileCaptureWithParams_FFMPEG ( const char * filename , const VideoCaptureParameters & params )
2012-04-17 15:12:16 +08:00
{
2021-01-28 14:00:38 +08:00
// FIXIT: remove unsafe malloc() approach
2012-04-30 22:33:52 +08:00
CvCapture_FFMPEG * capture = ( CvCapture_FFMPEG * ) malloc ( sizeof ( * capture ) ) ;
2017-06-29 21:40:22 +08:00
if ( ! capture )
return 0 ;
2012-04-30 22:33:52 +08:00
capture - > init ( ) ;
2021-01-28 14:00:38 +08:00
if ( capture - > open ( filename , params ) )
2012-04-30 22:33:52 +08:00
return capture ;
2013-02-12 18:20:23 +08:00
2012-04-30 22:33:52 +08:00
capture - > close ( ) ;
free ( capture ) ;
return 0 ;
2012-04-17 15:12:16 +08:00
}
2012-04-30 22:33:52 +08:00
void cvReleaseCapture_FFMPEG ( CvCapture_FFMPEG * * capture )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
if ( capture & & * capture )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
( * capture ) - > close ( ) ;
free ( * capture ) ;
* capture = 0 ;
2012-04-17 15:12:16 +08:00
}
}
2012-04-30 22:33:52 +08:00
int cvSetCaptureProperty_FFMPEG ( CvCapture_FFMPEG * capture , int prop_id , double value )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
return capture - > setProperty ( prop_id , value ) ;
2012-04-17 15:12:16 +08:00
}
2012-04-30 22:33:52 +08:00
double cvGetCaptureProperty_FFMPEG ( CvCapture_FFMPEG * capture , int prop_id )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
return capture - > getProperty ( prop_id ) ;
2012-04-17 15:12:16 +08:00
}
2012-04-30 22:33:52 +08:00
int cvGrabFrame_FFMPEG ( CvCapture_FFMPEG * capture )
2012-04-17 15:12:16 +08:00
{
2012-04-30 22:33:52 +08:00
return capture - > grabFrame ( ) ;
2012-04-17 15:12:16 +08:00
}
2012-04-18 19:18:25 +08:00
2012-04-30 22:33:52 +08:00
int cvRetrieveFrame_FFMPEG ( CvCapture_FFMPEG * capture , unsigned char * * data , int * step , int * width , int * height , int * cn )
2012-04-18 19:18:25 +08:00
{
2022-12-01 06:29:43 +08:00
int depth = CV_8U ;
return cvRetrieveFrame2_FFMPEG ( capture , data , step , width , height , cn , & depth ) ;
}
int cvRetrieveFrame2_FFMPEG ( CvCapture_FFMPEG * capture , unsigned char * * data , int * step , int * width , int * height , int * cn , int * depth )
{
return capture - > retrieveFrame ( 0 , data , step , width , height , cn , depth ) ;
2012-04-18 19:18:25 +08:00
}
2021-03-01 23:51:04 +08:00
static CvVideoWriter_FFMPEG * cvCreateVideoWriterWithParams_FFMPEG ( const char * filename , int fourcc , double fps ,
int width , int height , const VideoWriterParameters & params )
2012-04-18 19:18:25 +08:00
{
2012-04-30 22:33:52 +08:00
CvVideoWriter_FFMPEG * writer = ( CvVideoWriter_FFMPEG * ) malloc ( sizeof ( * writer ) ) ;
2017-06-29 21:40:22 +08:00
if ( ! writer )
return 0 ;
2012-04-30 22:33:52 +08:00
writer - > init ( ) ;
2021-03-01 23:51:04 +08:00
if ( writer - > open ( filename , fourcc , fps , width , height , params ) )
2012-04-30 22:33:52 +08:00
return writer ;
writer - > close ( ) ;
free ( writer ) ;
return 0 ;
2012-04-18 19:18:25 +08:00
}
2021-03-01 23:51:04 +08:00
CvVideoWriter_FFMPEG * cvCreateVideoWriter_FFMPEG ( const char * filename , int fourcc , double fps ,
int width , int height , int isColor )
{
VideoWriterParameters params ;
params . add ( VIDEOWRITER_PROP_IS_COLOR , isColor ) ;
return cvCreateVideoWriterWithParams_FFMPEG ( filename , fourcc , fps , width , height , params ) ;
}
2012-04-30 22:33:52 +08:00
void cvReleaseVideoWriter_FFMPEG ( CvVideoWriter_FFMPEG * * writer )
{
if ( writer & & * writer )
2012-04-18 19:18:25 +08:00
{
2012-04-30 22:33:52 +08:00
( * writer ) - > close ( ) ;
free ( * writer ) ;
* writer = 0 ;
2012-04-18 19:18:25 +08:00
}
}
2012-04-30 22:33:52 +08:00
int cvWriteFrame_FFMPEG ( CvVideoWriter_FFMPEG * writer ,
const unsigned char * data , int step ,
int width , int height , int cn , int origin )
2012-04-18 19:18:25 +08:00
{
2012-04-30 22:33:52 +08:00
return writer - > writeFrame ( data , step , width , height , cn , origin ) ;
2012-04-18 19:18:25 +08:00
}