videoio: VideoWriter H264/.mp4 support via ffmpeg/libav
This commit is contained in:
parent
8ad6ba82fc
commit
1f3043f618
@ -1540,6 +1540,30 @@ void CvVideoWriter_FFMPEG::close()
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
static inline bool cv_ff_codec_tag_match(const AVCodecTag *tags, enum AVCodecID id, unsigned int tag)
|
||||||
|
{
|
||||||
|
while (tags->id != AV_CODEC_ID_NONE)
|
||||||
|
{
|
||||||
|
if (tags->id == id && tags->tag == tag)
|
||||||
|
return true;
|
||||||
|
tags++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static inline bool cv_ff_codec_tag_list_match(const AVCodecTag *const *tags, enum AVCodecID id, unsigned int tag)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a video writer object that uses FFMPEG
|
/// Create a video writer object that uses FFMPEG
|
||||||
bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
|
bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
|
||||||
double fps, int width, int height, bool is_color )
|
double fps, int width, int height, bool is_color )
|
||||||
@ -1587,6 +1611,45 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
|
|||||||
#if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0)
|
#if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0)
|
||||||
if( (codec_id = codec_get_bmp_id( fourcc )) == CV_CODEC(CODEC_ID_NONE) )
|
if( (codec_id = codec_get_bmp_id( fourcc )) == CV_CODEC(CODEC_ID_NONE) )
|
||||||
return false;
|
return false;
|
||||||
|
#elif LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(54, 1, 0)
|
||||||
|
// APIchnages:
|
||||||
|
// 2012-01-31 - dd6d3b0 - lavf 54.01.0
|
||||||
|
// Add avformat_get_riff_video_tags() and avformat_get_riff_audio_tags().
|
||||||
|
if( (codec_id = av_codec_get_id(fmt->codec_tag, fourcc)) == CV_CODEC(CODEC_ID_NONE) )
|
||||||
|
{
|
||||||
|
const struct AVCodecTag * fallback_tags[] = {
|
||||||
|
avformat_get_riff_video_tags(),
|
||||||
|
#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(55, 25, 100)
|
||||||
|
// APIchanges: ffmpeg only
|
||||||
|
// 2014-01-19 - 1a193c4 - lavf 55.25.100 - avformat.h
|
||||||
|
// Add avformat_get_mov_video_tags() and avformat_get_mov_audio_tags().
|
||||||
|
// TODO ffmpeg only, need to skip libav: avformat_get_mov_video_tags(),
|
||||||
|
#endif
|
||||||
|
codec_bmp_tags, NULL };
|
||||||
|
if( (codec_id = av_codec_get_id(fallback_tags, fourcc)) == CV_CODEC(CODEC_ID_NONE) )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL};
|
const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL};
|
||||||
if( (codec_id = av_codec_get_id(tags, fourcc)) == CV_CODEC(CODEC_ID_NONE) )
|
if( (codec_id = av_codec_get_id(tags, fourcc)) == CV_CODEC(CODEC_ID_NONE) )
|
||||||
|
@ -49,8 +49,28 @@ using namespace cv;
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
static const char* AVI_EXT = ".avi";
|
||||||
|
static const char* MP4_EXT = ".mp4";
|
||||||
|
|
||||||
class CV_FFmpegWriteBigVideoTest : public cvtest::BaseTest
|
class CV_FFmpegWriteBigVideoTest : public cvtest::BaseTest
|
||||||
{
|
{
|
||||||
|
struct TestFormatEntry {
|
||||||
|
int tag;
|
||||||
|
const char* ext;
|
||||||
|
bool required;
|
||||||
|
};
|
||||||
|
|
||||||
|
static long int getFileSize(string filename)
|
||||||
|
{
|
||||||
|
FILE *p_file = NULL;
|
||||||
|
p_file = fopen(filename.c_str(), "rb");
|
||||||
|
if (p_file == NULL)
|
||||||
|
return -1;
|
||||||
|
fseek(p_file, 0, SEEK_END);
|
||||||
|
long int size = ftell(p_file);
|
||||||
|
fclose(p_file);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
void run(int)
|
void run(int)
|
||||||
{
|
{
|
||||||
@ -59,36 +79,35 @@ public:
|
|||||||
const double fps0 = 15;
|
const double fps0 = 15;
|
||||||
const double time_sec = 1;
|
const double time_sec = 1;
|
||||||
|
|
||||||
const int tags[] = {
|
const TestFormatEntry entries[] = {
|
||||||
0,
|
{0, AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('D', 'I', 'V', '3'),
|
//{VideoWriter::fourcc('D', 'I', 'V', '3'), AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('D', 'I', 'V', 'X'),
|
//{VideoWriter::fourcc('D', 'I', 'V', 'X'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('D', 'X', '5', '0'),
|
{VideoWriter::fourcc('D', 'X', '5', '0'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('F', 'L', 'V', '1'),
|
{VideoWriter::fourcc('F', 'L', 'V', '1'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('H', '2', '6', '1'),
|
{VideoWriter::fourcc('H', '2', '6', '1'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('H', '2', '6', '3'),
|
{VideoWriter::fourcc('H', '2', '6', '3'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('I', '4', '2', '0'),
|
{VideoWriter::fourcc('I', '4', '2', '0'), AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('j', 'p', 'e', 'g'),
|
//{VideoWriter::fourcc('j', 'p', 'e', 'g'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('M', 'J', 'P', 'G'),
|
{VideoWriter::fourcc('M', 'J', 'P', 'G'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('m', 'p', '4', 'v'),
|
{VideoWriter::fourcc('m', 'p', '4', 'v'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('M', 'P', 'E', 'G'),
|
{VideoWriter::fourcc('M', 'P', 'E', 'G'), AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('W', 'M', 'V', '1'),
|
//{VideoWriter::fourcc('W', 'M', 'V', '1'), AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('W', 'M', 'V', '2'),
|
//{VideoWriter::fourcc('W', 'M', 'V', '2'), AVI_EXT, true},
|
||||||
VideoWriter::fourcc('X', 'V', 'I', 'D'),
|
{VideoWriter::fourcc('X', 'V', 'I', 'D'), AVI_EXT, true},
|
||||||
//VideoWriter::fourcc('Y', 'U', 'Y', '2'),
|
//{VideoWriter::fourcc('Y', 'U', 'Y', '2'), AVI_EXT, true},
|
||||||
|
{VideoWriter::fourcc('H', '2', '6', '4'), MP4_EXT, false}
|
||||||
};
|
};
|
||||||
|
|
||||||
const size_t n = sizeof(tags)/sizeof(tags[0]);
|
const size_t n = sizeof(entries)/sizeof(entries[0]);
|
||||||
|
|
||||||
bool created = false;
|
|
||||||
|
|
||||||
for (size_t j = 0; j < n; ++j)
|
for (size_t j = 0; j < n; ++j)
|
||||||
{
|
{
|
||||||
int tag = tags[j];
|
int tag = entries[j].tag;
|
||||||
stringstream s;
|
const char* ext = entries[j].ext;
|
||||||
s << tag;
|
string s = cv::format("%08x%s", tag, ext);
|
||||||
|
|
||||||
const string filename = tempfile((s.str()+".avi").c_str());
|
const string filename = tempfile(s.c_str());
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -113,10 +132,11 @@ public:
|
|||||||
|
|
||||||
if (writer.isOpened() == false)
|
if (writer.isOpened() == false)
|
||||||
{
|
{
|
||||||
ts->printf(ts->LOG, "\n\nFile name: %s\n", filename.c_str());
|
fprintf(stderr, "\n\nFile name: %s\n", filename.c_str());
|
||||||
ts->printf(ts->LOG, "Codec id: %d Codec tag: %c%c%c%c\n", j,
|
fprintf(stderr, "Codec id: %d Codec tag: %c%c%c%c\n", (int)j,
|
||||||
tag & 255, (tag >> 8) & 255, (tag >> 16) & 255, (tag >> 24) & 255);
|
tag & 255, (tag >> 8) & 255, (tag >> 16) & 255, (tag >> 24) & 255);
|
||||||
ts->printf(ts->LOG, "Error: cannot create video file.");
|
fprintf(stderr, "Error: cannot create video file.");
|
||||||
|
if (entries[j].required)
|
||||||
ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
|
ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -133,8 +153,23 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
writer.release();
|
writer.release();
|
||||||
if (!created) created = true;
|
long int sz = getFileSize(filename);
|
||||||
else remove(filename.c_str());
|
if (sz < 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: File name: %s was not created\n", filename.c_str());
|
||||||
|
if (entries[j].required)
|
||||||
|
ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (sz < 8192)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: File name: %s is very small (data write problems?)\n", filename.c_str());
|
||||||
|
if (entries[j].required)
|
||||||
|
ts->set_failed_test_info(ts->FAIL_INVALID_OUTPUT);
|
||||||
|
}
|
||||||
|
remove(filename.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user