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"
|
|
|
|
|
|
|
|
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
|
2011-07-05 00:18:12 +08:00
|
|
|
#include <assert.h>
|
2012-03-15 15:22:31 +08:00
|
|
|
#include <algorithm>
|
2012-04-30 22:33:52 +08:00
|
|
|
#include <limits>
|
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>
|
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>
|
2011-07-05 00:18:12 +08:00
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#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
|
|
|
|
2012-06-08 01:21:29 +08:00
|
|
|
static int get_number_of_cpus(void)
|
2011-08-02 22:27:21 +08:00
|
|
|
{
|
2020-08-25 02:39:05 +08:00
|
|
|
#if defined _WIN32
|
2011-08-02 22:27:21 +08:00
|
|
|
SYSTEM_INFO sysinfo;
|
|
|
|
GetSystemInfo( &sysinfo );
|
2012-04-30 22:33:52 +08:00
|
|
|
|
2011-08-02 22:27:21 +08:00
|
|
|
return (int)sysinfo.dwNumberOfProcessors;
|
2018-03-14 22:09:10 +08:00
|
|
|
#elif defined __linux__ || defined __HAIKU__
|
2011-08-02 22:27:21 +08:00
|
|
|
return (int)sysconf( _SC_NPROCESSORS_ONLN );
|
|
|
|
#elif defined __APPLE__
|
|
|
|
int numCPU=0;
|
|
|
|
int mib[4];
|
2012-04-30 22:33:52 +08:00
|
|
|
size_t len = sizeof(numCPU);
|
|
|
|
|
|
|
|
// set the mib for hw.ncpu
|
2011-08-02 22:27:21 +08:00
|
|
|
mib[0] = CTL_HW;
|
|
|
|
mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU;
|
2012-04-30 22:33:52 +08:00
|
|
|
|
|
|
|
// get the number of CPUs from the system
|
2011-08-02 22:27:21 +08:00
|
|
|
sysctl(mib, 2, &numCPU, &len, NULL, 0);
|
2012-04-30 22:33:52 +08:00
|
|
|
|
|
|
|
if( numCPU < 1 )
|
2011-08-02 22:27:21 +08:00
|
|
|
{
|
|
|
|
mib[1] = HW_NCPU;
|
|
|
|
sysctl( mib, 2, &numCPU, &len, NULL, 0 );
|
2012-04-30 22:33:52 +08:00
|
|
|
|
2011-08-02 22:27:21 +08:00
|
|
|
if( numCPU < 1 )
|
|
|
|
numCPU = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (int)numCPU;
|
|
|
|
#else
|
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-05 00:18:12 +08:00
|
|
|
struct Image_FFMPEG
|
|
|
|
{
|
|
|
|
unsigned char* data;
|
|
|
|
int step;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int cn;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
assert(metadata);
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn);
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
2011-07-05 00:18:12 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void CvCapture_FFMPEG::init()
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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;
|
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 )
|
|
|
|
{
|
|
|
|
avcodec_close( video_st->codec );
|
|
|
|
video_st = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
static ImplMutex _mutex;
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2021-03-03 04:41:05 +08:00
|
|
|
/* register all codecs, demux and protocols */
|
|
|
|
av_register_all();
|
|
|
|
|
|
|
|
/* register a callback function for synchronization */
|
|
|
|
av_lockmgr_register(&LockCallBack);
|
2012-04-30 22:33:52 +08:00
|
|
|
}
|
2013-02-12 18:20:23 +08:00
|
|
|
~InternalFFMpegRegister()
|
|
|
|
{
|
|
|
|
av_lockmgr_register(NULL);
|
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
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
close();
|
2012-06-08 01:21:29 +08:00
|
|
|
|
2021-01-28 14:00:38 +08:00
|
|
|
if (!params.empty())
|
|
|
|
{
|
|
|
|
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
|
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)
|
|
|
|
{
|
|
|
|
av_dict_set(&dict, "rtsp_transport", "tcp", 0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#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
|
2019-07-20 02:33:22 +08:00
|
|
|
AVInputFormat* input_format = NULL;
|
|
|
|
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)
|
|
|
|
{
|
2012-04-30 22:33:52 +08:00
|
|
|
CV_WARN("Could not find codec parameters");
|
|
|
|
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++)
|
|
|
|
{
|
2020-08-25 02:39:05 +08:00
|
|
|
AVCodecContext* enc = ic->streams[i]->codec;
|
2011-07-05 00:18:12 +08:00
|
|
|
|
2013-02-12 18:20:23 +08:00
|
|
|
//#ifdef FF_API_THREAD_INIT
|
|
|
|
// avcodec_thread_init(enc, get_number_of_cpus());
|
|
|
|
//#else
|
2012-04-30 22:33:52 +08:00
|
|
|
enc->thread_count = get_number_of_cpus();
|
2013-02-12 18:20:23 +08:00
|
|
|
//#endif
|
2011-07-29 22:17:42 +08:00
|
|
|
|
2020-01-26 15:19:09 +08:00
|
|
|
AVDictionaryEntry* avdiscard_entry = av_dict_get(dict, "avdiscard", NULL, 0);
|
|
|
|
|
2020-08-25 02:39:05 +08:00
|
|
|
if (avdiscard_entry) {
|
2020-01-26 15:19:09 +08:00
|
|
|
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;
|
2020-08-25 02:39:05 +08:00
|
|
|
// 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)
|
2020-01-26 15:19:09 +08:00
|
|
|
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;
|
|
|
|
}
|
2012-04-30 22:33:52 +08:00
|
|
|
|
2013-02-12 18:20:23 +08:00
|
|
|
if( AVMEDIA_TYPE_VIDEO == enc->codec_type && video_stream < 0)
|
|
|
|
{
|
2021-03-01 23:51:04 +08:00
|
|
|
CV_LOG_DEBUG(NULL, "FFMPEG: stream[" << i << "] is video stream with codecID=" << (int)enc->codec_id
|
|
|
|
<< " width=" << enc->width
|
|
|
|
<< " height=" << enc->height
|
|
|
|
);
|
|
|
|
|
2013-02-20 21:19:55 +08:00
|
|
|
// backup encoder' width/height
|
|
|
|
int enc_width = enc->width;
|
|
|
|
int enc_height = enc->height;
|
|
|
|
|
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)
|
|
|
|
AVCodec *codec = NULL;
|
|
|
|
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();
|
|
|
|
enc->get_format = avcodec_default_get_format;
|
|
|
|
if (enc->hw_device_ctx) {
|
|
|
|
av_buffer_unref(&enc->hw_device_ctx);
|
|
|
|
}
|
|
|
|
if (hw_type != AV_HWDEVICE_TYPE_NONE)
|
|
|
|
{
|
|
|
|
CV_LOG_DEBUG(NULL, "FFMPEG: trying to configure H/W acceleration: '" << accel_iter.hw_type_device_string() << "'");
|
|
|
|
AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE;
|
|
|
|
codec = hw_find_codec(enc->codec_id, hw_type, av_codec_is_decoder, accel_iter.disabled_codecs().c_str(), &hw_pix_fmt);
|
|
|
|
if (codec) {
|
|
|
|
if (hw_pix_fmt != AV_PIX_FMT_NONE)
|
|
|
|
enc->get_format = hw_get_format_callback; // set callback to select HW pixel format, not SW format
|
2021-05-15 00:48:50 +08:00
|
|
|
enc->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 (!enc->hw_device_ctx)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
codec = avcodec_find_decoder(enc->codec_id);
|
|
|
|
if (!codec)
|
|
|
|
{
|
|
|
|
CV_LOG_ERROR(NULL, "Could not find decoder for codec_id=" << (int)enc->codec_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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 << "'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!codec)
|
|
|
|
continue;
|
|
|
|
err = avcodec_open2(enc, codec, NULL);
|
|
|
|
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)
|
2021-03-01 23:51:04 +08:00
|
|
|
if (enc_width && (enc->width != enc_width))
|
|
|
|
enc->width = enc_width;
|
|
|
|
if (enc_height && (enc->height != enc_height))
|
|
|
|
enc->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
|
|
|
|
|
|
|
frame.width = enc->width;
|
|
|
|
frame.height = enc->height;
|
|
|
|
frame.cn = 3;
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
{
|
|
|
|
// check start code prefixed mode (as defined in the Annex B H.264 / H.265 specification)
|
|
|
|
if (packet.size >= 5
|
|
|
|
&& !(packet.data[0] == 0 && packet.data[1] == 0 && packet.data[2] == 0 && packet.data[3] == 1)
|
|
|
|
&& !(packet.data[0] == 0 && packet.data[1] == 0 && packet.data[2] == 1)
|
|
|
|
)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2012-04-30 22:33:52 +08:00
|
|
|
if( !ic || !video_st ) 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
|
|
|
|
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
|
|
|
|
valid = avcodec_receive_frame(video_st->codec, picture) >= 0;
|
|
|
|
#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
|
|
|
|
if (avcodec_send_packet(video_st->codec, &packet) < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ret = avcodec_receive_frame(video_st->codec, picture);
|
|
|
|
#else
|
|
|
|
int got_picture = 0;
|
2020-08-25 02:39:05 +08:00
|
|
|
avcodec_decode_video2(video_st->codec, 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
|
|
|
//picture_pts = picture->best_effort_timestamp;
|
|
|
|
if( picture_pts == AV_NOPTS_VALUE_ )
|
2016-05-19 21:29:28 +08:00
|
|
|
picture_pts = picture->pkt_pts != AV_NOPTS_VALUE_ && picture->pkt_pts != 0 ? picture->pkt_pts : picture->pkt_dts;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-18 22:07:06 +08:00
|
|
|
if (valid)
|
|
|
|
frame_number++;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CvCapture_FFMPEG::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn)
|
|
|
|
{
|
2019-11-18 22:07:06 +08:00
|
|
|
if (!video_st)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (rawMode)
|
|
|
|
{
|
|
|
|
AVPacket& p = bsfc ? packet_filtered : packet;
|
|
|
|
*data = p.data;
|
|
|
|
*step = p.size;
|
|
|
|
*width = p.size;
|
|
|
|
*height = 1;
|
|
|
|
*cn = 1;
|
|
|
|
return p.data != NULL;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2012-04-30 22:33:52 +08:00
|
|
|
if( img_convert_ctx == NULL ||
|
|
|
|
frame.width != video_st->codec->width ||
|
2016-01-12 22:51:26 +08:00
|
|
|
frame.height != video_st->codec->height ||
|
|
|
|
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)
|
|
|
|
int buffer_width = video_st->codec->coded_width, buffer_height = video_st->codec->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,
|
2015-09-09 07:35:40 +08:00
|
|
|
AV_PIX_FMT_BGR24,
|
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);
|
|
|
|
rgb_picture.format = AV_PIX_FMT_BGR24;
|
|
|
|
rgb_picture.width = buffer_width;
|
|
|
|
rgb_picture.height = buffer_height;
|
|
|
|
if (0 != av_frame_get_buffer(&rgb_picture, 32))
|
|
|
|
{
|
|
|
|
CV_WARN("OutOfMemory");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int aligns[AV_NUM_DATA_POINTERS];
|
|
|
|
avcodec_align_dimensions2(video_st->codec, &buffer_width, &buffer_height, aligns);
|
2015-10-06 21:09:31 +08:00
|
|
|
rgb_picture.data[0] = (uint8_t*)realloc(rgb_picture.data[0],
|
2016-03-22 20:26:55 +08:00
|
|
|
_opencv_ffmpeg_av_image_get_buffer_size( AV_PIX_FMT_BGR24,
|
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],
|
2016-01-12 22:51:26 +08:00
|
|
|
AV_PIX_FMT_BGR24, buffer_width, buffer_height );
|
|
|
|
#endif
|
|
|
|
frame.width = video_st->codec->width;
|
|
|
|
frame.height = video_st->codec->height;
|
|
|
|
frame.cn = 3;
|
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,
|
2016-01-12 22:51:26 +08:00
|
|
|
0, video_st->codec->coded_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;
|
|
|
|
*cn = frame.cn;
|
|
|
|
|
2021-03-02 15:59:08 +08:00
|
|
|
#if USE_AV_HW_CODECS
|
|
|
|
if (sw_picture != picture)
|
|
|
|
{
|
2021-03-01 23:51:04 +08:00
|
|
|
av_frame_unref(sw_picture);
|
|
|
|
}
|
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
|
|
|
|
if (!picture || !picture->hw_frames_ctx) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GPU color conversion NV12->BGRA, from GPU media buffer to GPU OpenCL buffer
|
|
|
|
return hw_copy_frame_to_umat(video_st->codec->hw_device_ctx, picture, output);
|
|
|
|
#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
|
|
|
{
|
|
|
|
if( !video_st ) return 0;
|
|
|
|
|
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);
|
2019-05-31 21:18:00 +08:00
|
|
|
case CAP_PROP_FPS:
|
2015-10-31 08:48:04 +08:00
|
|
|
return get_fps();
|
2019-05-31 21:18:00 +08:00
|
|
|
case CAP_PROP_FOURCC:
|
2018-09-30 01:43:01 +08:00
|
|
|
codec_id = video_st->codec->codec_id;
|
|
|
|
codec_tag = (double) video_st->codec->codec_tag;
|
|
|
|
|
|
|
|
if(codec_tag || codec_id == AV_CODEC_ID_NONE)
|
|
|
|
{
|
|
|
|
return codec_tag;
|
|
|
|
}
|
|
|
|
|
|
|
|
codec_fourcc = _opencv_avcodec_get_name(codec_id);
|
|
|
|
if(!codec_fourcc || strlen(codec_fourcc) < 4 || strcmp(codec_fourcc, "unknown_codec") == 0)
|
|
|
|
{
|
|
|
|
return codec_tag;
|
|
|
|
}
|
|
|
|
|
2018-11-03 10:17:17 +08:00
|
|
|
return (double) CV_FOURCC(codec_fourcc[0], codec_fourcc[1], codec_fourcc[2], codec_fourcc[3]);
|
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:
|
|
|
|
{
|
|
|
|
AVPixelFormat pix_fmt = video_st->codec->pix_fmt;
|
|
|
|
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;
|
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);
|
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)
|
|
|
|
{
|
|
|
|
fps = 1.0 / r2d(ic->streams[video_stream]->codec->time_base);
|
|
|
|
}
|
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;
|
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
|
|
|
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)
|
|
|
|
{
|
|
|
|
_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);
|
2012-04-30 22:33:52 +08:00
|
|
|
avcodec_flush_buffers(ic->streams[video_stream]->codec);
|
|
|
|
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;
|
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();
|
|
|
|
|
2012-06-08 01:21:29 +08:00
|
|
|
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;
|
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;
|
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 */
|
|
|
|
static bool icv_configure_video_stream_FFMPEG(AVFormatContext *oc,
|
|
|
|
AVStream *st,
|
|
|
|
const AVCodec* codec,
|
|
|
|
int w, int h, int bitrate,
|
|
|
|
double fps, AVPixelFormat pixel_format)
|
2011-07-05 00:18:12 +08:00
|
|
|
{
|
2021-03-01 23:51:04 +08:00
|
|
|
AVCodecContext *c = st->codec;
|
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;
|
2011-07-05 00:18:12 +08:00
|
|
|
|
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;
|
|
|
|
|
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)
|
2021-03-01 23:51:04 +08:00
|
|
|
return false;
|
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;
|
2011-07-05 00:18:12 +08:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2019-04-11 22:44:28 +08:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
AVRational avg_frame_rate = {frame_rate, frame_rate_base};
|
|
|
|
st->avg_frame_rate = avg_frame_rate;
|
|
|
|
#else
|
2016-09-08 19:42:01 +08:00
|
|
|
st->avg_frame_rate = (AVRational){frame_rate, frame_rate_base};
|
|
|
|
#endif
|
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
|
|
|
|
2021-03-01 23:51:04 +08:00
|
|
|
return true;
|
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;
|
|
|
|
|
2015-04-23 05:39:44 +08:00
|
|
|
static int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st,
|
|
|
|
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
|
|
|
{
|
2020-08-25 02:39:05 +08:00
|
|
|
AVCodecContext* c = video_st->codec;
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (input_pix_fmt == AV_PIX_FMT_GRAY8) {
|
|
|
|
if (cn != 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(false);
|
|
|
|
}
|
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
|
|
|
|
2012-04-30 22:33:52 +08:00
|
|
|
// typecast from opaque data type to implemented struct
|
2020-08-25 02:39:05 +08:00
|
|
|
AVCodecContext* c = video_st->codec;
|
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
|
|
|
}
|
|
|
|
|
2021-03-01 23:51:04 +08:00
|
|
|
AVPixelFormat sw_pix_fmt = c->pix_fmt;
|
|
|
|
#if USE_AV_HW_CODECS
|
|
|
|
if (c->hw_frames_ctx)
|
|
|
|
sw_pix_fmt = ((AVHWFramesContext*)c->hw_frames_ctx->data)->sw_format;
|
|
|
|
#endif
|
|
|
|
if ( sw_pix_fmt != input_pix_fmt ) {
|
2012-04-30 22:33:52 +08:00
|
|
|
assert( input_picture );
|
|
|
|
// 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,
|
2012-04-30 22:33:52 +08:00
|
|
|
c->width,
|
|
|
|
c->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
|
|
|
|
if (video_st->codec->hw_device_ctx) {
|
|
|
|
// copy data to HW frame
|
|
|
|
AVFrame* hw_frame = av_frame_alloc();
|
|
|
|
if (!hw_frame) {
|
|
|
|
CV_LOG_ERROR(NULL, "Error allocating AVFrame (av_frame_alloc)");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (av_hwframe_get_buffer(video_st->codec->hw_frames_ctx, hw_frame, 0) < 0) {
|
|
|
|
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;
|
|
|
|
int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, outbuf, outbuf_size, hw_frame, frame_idx);
|
|
|
|
ret = ret_write >= 0 ? true : false;
|
|
|
|
av_frame_free(&hw_frame);
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
picture->pts = frame_idx;
|
|
|
|
int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, outbuf, outbuf_size, picture, frame_idx);
|
|
|
|
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
|
|
|
|
if (!video_st->codec->hw_frames_ctx)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Get hardware frame from frame pool
|
|
|
|
AVFrame* hw_frame = av_frame_alloc();
|
|
|
|
if (!hw_frame) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (av_hwframe_get_buffer(video_st->codec->hw_frames_ctx, hw_frame, 0) < 0) {
|
|
|
|
av_frame_free(&hw_frame);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// GPU to GPU copy
|
|
|
|
if (!hw_copy_umat_to_frame(video_st->codec->hw_device_ctx, input, hw_frame)) {
|
|
|
|
av_frame_free(&hw_frame);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// encode
|
|
|
|
hw_frame->pts = frame_idx;
|
|
|
|
icv_av_write_frame_FFMPEG( oc, video_st, outbuf, outbuf_size, hw_frame, frame_idx);
|
|
|
|
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
|
|
|
// nothing to do if already released
|
|
|
|
if ( !picture )
|
|
|
|
return;
|
2012-06-08 01:21:29 +08:00
|
|
|
|
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 */
|
|
|
|
if(ok && oc)
|
|
|
|
{
|
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(;;)
|
|
|
|
{
|
2021-03-01 23:51:04 +08:00
|
|
|
int ret = icv_av_write_frame_FFMPEG( oc, video_st, 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
|
|
|
|
if( video_st->codec->pix_fmt != input_pix_fmt)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2012-04-30 22:33:52 +08:00
|
|
|
/* close codec */
|
|
|
|
avcodec_close(video_st->codec);
|
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);
|
|
|
|
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 */
|
|
|
|
if (is_color) {
|
2015-09-09 07:35:40 +08:00
|
|
|
input_pix_fmt = AV_PIX_FMT_BGR24;
|
2012-04-30 22:33:52 +08:00
|
|
|
}
|
|
|
|
else {
|
2015-09-09 07:35:40 +08:00
|
|
|
input_pix_fmt = AV_PIX_FMT_GRAY8;
|
2012-04-30 22:33:52 +08:00
|
|
|
}
|
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();
|
|
|
|
assert (oc);
|
2011-07-05 00:18:12 +08:00
|
|
|
|
2012-04-30 22:33:52 +08:00
|
|
|
/* set file name */
|
|
|
|
oc->oformat = fmt;
|
|
|
|
snprintf(oc->filename, sizeof(oc->filename), "%s", filename);
|
2011-07-05 00:18:12 +08:00
|
|
|
|
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) {
|
|
|
|
codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, AVMEDIA_TYPE_VIDEO);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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");
|
|
|
|
if (options) {
|
|
|
|
av_dict_parse_string(&dict, options, ";", "|", 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
AVCodecContext *c = video_st->codec;
|
|
|
|
|
|
|
|
// find and open encoder, try HW acceleration types specified in 'hw_acceleration' list (in order)
|
|
|
|
int err = -1;
|
|
|
|
AVCodec* codec = NULL;
|
|
|
|
#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
|
|
|
|
|
|
|
|
if (!icv_configure_video_stream_FFMPEG(oc, video_st, codec,
|
|
|
|
width, height, (int) (bitrate + 0.5),
|
|
|
|
fps, format)) {
|
|
|
|
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
|
|
|
c->codec_tag = fourcc;
|
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) {
|
|
|
|
c->hw_device_ctx = av_buffer_ref(hw_device_ctx);
|
|
|
|
if (hw_format != AV_PIX_FMT_NONE) {
|
|
|
|
c->hw_frames_ctx = hw_create_frames(NULL, hw_device_ctx, width, height, hw_format);
|
|
|
|
if (!c->hw_frames_ctx)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2011-07-05 00:18:12 +08:00
|
|
|
|
2021-03-01 23:51:04 +08:00
|
|
|
int64_t lbit_rate = (int64_t) c->bit_rate;
|
|
|
|
lbit_rate += (int64_t)(bitrate / 2);
|
|
|
|
lbit_rate = std::min(lbit_rate, (int64_t) INT_MAX);
|
|
|
|
c->bit_rate_tolerance = (int) lbit_rate;
|
|
|
|
c->bit_rate = (int) lbit_rate;
|
2011-07-05 00:18:12 +08:00
|
|
|
|
2021-03-01 23:51:04 +08:00
|
|
|
/* open the codec */
|
|
|
|
err = avcodec_open2(c, codec, NULL);
|
|
|
|
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: " << icvFFMPEGErrStr(err));
|
|
|
|
}
|
|
|
|
#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
|
|
|
|
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;
|
2021-03-01 23:51:04 +08:00
|
|
|
AVPixelFormat sw_pix_fmt = c->pix_fmt;
|
|
|
|
#if USE_AV_HW_CODECS
|
|
|
|
if (c->hw_frames_ctx)
|
|
|
|
sw_pix_fmt = ((AVHWFramesContext*)c->hw_frames_ctx->data)->sw_format;
|
|
|
|
#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 */
|
2021-03-01 23:51:04 +08:00
|
|
|
picture = icv_alloc_picture_FFMPEG(sw_pix_fmt, c->width, c->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 ) {
|
|
|
|
input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, c->width, c->height, false);
|
|
|
|
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
|
|
|
{
|
2012-04-30 22:33:52 +08:00
|
|
|
return capture->retrieveFrame(0, data, step, width, height, cn);
|
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
|
|
|
}
|