796 lines
23 KiB
C++
796 lines
23 KiB
C++
/*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.
|
|
//
|
|
//
|
|
// Intel License Agreement
|
|
// For Open Source Computer Vision Library
|
|
//
|
|
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
|
// 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.
|
|
//
|
|
// * The name of Intel Corporation may not be used to endorse or promote products
|
|
// 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*/
|
|
|
|
#include "_highgui.h"
|
|
|
|
|
|
extern "C" {
|
|
#include <ffmpeg/avformat.h>
|
|
}
|
|
|
|
#ifdef NDEBUG
|
|
#define CV_WARN(message)
|
|
#else
|
|
#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
|
|
#endif
|
|
|
|
typedef struct CvCaptureAVI_FFMPEG
|
|
{
|
|
CvCaptureVTable * vtable;
|
|
|
|
AVFormatContext * ic;
|
|
int video_stream;
|
|
AVStream * video_st;
|
|
AVFrame * picture;
|
|
int64_t picture_pts;
|
|
AVFrame rgb_picture;
|
|
|
|
IplImage frame;
|
|
} CvCaptureAVI_FFMPEG;
|
|
|
|
static void icvCloseAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture )
|
|
{
|
|
//cvFree( (void**)&(capture->entries) );
|
|
|
|
if( capture->picture )
|
|
av_free(capture->picture);
|
|
|
|
if( capture->video_st )
|
|
{
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
avcodec_close( capture->video_st->codec );
|
|
#else
|
|
avcodec_close( &capture->video_st->codec );
|
|
#endif
|
|
capture->video_st = NULL;
|
|
}
|
|
|
|
if( capture->ic )
|
|
{
|
|
av_close_input_file(capture->ic);
|
|
capture->ic = NULL;
|
|
}
|
|
|
|
if( capture->rgb_picture.data[0] )
|
|
cvFree( &capture->rgb_picture.data[0] );
|
|
|
|
memset( &capture->frame, 0, sizeof(capture->frame));
|
|
}
|
|
|
|
|
|
static int icvOpenAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, const char* filename )
|
|
{
|
|
int err, valid = 0, video_index = -1, i;
|
|
AVFormatContext *ic;
|
|
|
|
capture->ic = NULL;
|
|
capture->video_stream = -1;
|
|
capture->video_st = NULL;
|
|
/* register all codecs, demux and protocols */
|
|
av_register_all();
|
|
|
|
err = av_open_input_file(&ic, filename, NULL, 0, NULL);
|
|
if (err < 0) {
|
|
CV_WARN("Error opening file");
|
|
goto exit_func;
|
|
}
|
|
capture->ic = ic;
|
|
err = av_find_stream_info(ic);
|
|
if (err < 0) {
|
|
CV_WARN("Could not find codec parameters");
|
|
goto exit_func;
|
|
}
|
|
for(i = 0; i < ic->nb_streams; i++) {
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
AVCodecContext *enc = ic->streams[i]->codec;
|
|
#else
|
|
AVCodecContext *enc = &ic->streams[i]->codec;
|
|
#endif
|
|
AVCodec *codec;
|
|
if( CODEC_TYPE_VIDEO == enc->codec_type && video_index < 0) {
|
|
video_index = i;
|
|
codec = avcodec_find_decoder(enc->codec_id);
|
|
if (!codec ||
|
|
avcodec_open(enc, codec) < 0)
|
|
goto exit_func;
|
|
capture->video_stream = i;
|
|
capture->video_st = ic->streams[i];
|
|
capture->picture = avcodec_alloc_frame();
|
|
|
|
capture->rgb_picture.data[0] = (uchar*)cvAlloc(
|
|
avpicture_get_size( PIX_FMT_BGR24,
|
|
enc->width, enc->height ));
|
|
avpicture_fill( (AVPicture*)&capture->rgb_picture, capture->rgb_picture.data[0],
|
|
PIX_FMT_BGR24, enc->width, enc->height );
|
|
|
|
cvInitImageHeader( &capture->frame, cvSize( enc->width,
|
|
enc->height ), 8, 3, 0, 4 );
|
|
cvSetData( &capture->frame, capture->rgb_picture.data[0],
|
|
capture->rgb_picture.linesize[0] );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
if(video_index >= 0)
|
|
valid = 1;
|
|
|
|
exit_func:
|
|
|
|
if( !valid )
|
|
icvCloseAVI_FFMPEG( capture );
|
|
|
|
return valid;
|
|
}
|
|
|
|
|
|
static int icvGrabFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture )
|
|
{
|
|
int valid=0;
|
|
static bool bFirstTime = true;
|
|
static AVPacket pkt;
|
|
int got_picture;
|
|
|
|
// First time we're called, set packet.data to NULL to indicate it
|
|
// doesn't have to be freed
|
|
if (bFirstTime) {
|
|
bFirstTime = false;
|
|
pkt.data = NULL;
|
|
}
|
|
|
|
if( !capture || !capture->ic || !capture->video_st )
|
|
return 0;
|
|
|
|
// free last packet if exist
|
|
if (pkt.data != NULL) {
|
|
av_free_packet (&pkt);
|
|
}
|
|
|
|
// get the next frame
|
|
while ((0 == valid) && (av_read_frame(capture->ic, &pkt) >= 0)) {
|
|
if( pkt.stream_index != capture->video_stream ) continue;
|
|
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
avcodec_decode_video(capture->video_st->codec,
|
|
capture->picture, &got_picture,
|
|
pkt.data, pkt.size);
|
|
#else
|
|
avcodec_decode_video(&capture->video_st->codec,
|
|
capture->picture, &got_picture,
|
|
pkt.data, pkt.size);
|
|
#endif
|
|
|
|
if (got_picture) {
|
|
// we have a new picture, so memorize it
|
|
capture->picture_pts = pkt.pts;
|
|
valid = 1;
|
|
}
|
|
}
|
|
|
|
// return if we have a new picture or not
|
|
return valid;
|
|
}
|
|
|
|
|
|
static const IplImage* icvRetrieveFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture )
|
|
{
|
|
if( !capture || !capture->video_st || !capture->picture->data[0] )
|
|
return 0;
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
img_convert( (AVPicture*)&capture->rgb_picture, PIX_FMT_BGR24,
|
|
(AVPicture*)capture->picture,
|
|
capture->video_st->codec->pix_fmt,
|
|
capture->video_st->codec->width,
|
|
capture->video_st->codec->height );
|
|
#else
|
|
img_convert( (AVPicture*)&capture->rgb_picture, PIX_FMT_BGR24,
|
|
(AVPicture*)capture->picture,
|
|
capture->video_st->codec.pix_fmt,
|
|
capture->video_st->codec.width,
|
|
capture->video_st->codec.height );
|
|
#endif
|
|
return &capture->frame;
|
|
}
|
|
|
|
|
|
static int icvSetPropertyAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture,
|
|
int property_id, double value );
|
|
|
|
static double icvGetPropertyAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, int property_id )
|
|
{
|
|
if( !capture || !capture->video_st || !capture->picture->data[0] )
|
|
return 0;
|
|
|
|
int64_t timestamp;
|
|
timestamp = capture->picture_pts;
|
|
|
|
switch( property_id )
|
|
{
|
|
case CV_CAP_PROP_POS_MSEC:
|
|
if(capture->ic->start_time != static_cast<double>(AV_NOPTS_VALUE))
|
|
return (double)(timestamp - capture->ic->start_time)*1000/(double)AV_TIME_BASE;
|
|
break;
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
if(capture->video_st->cur_dts != static_cast<double>(AV_NOPTS_VALUE))
|
|
return (double)capture->video_st->cur_dts-1;
|
|
break;
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
if(capture->ic->start_time != static_cast<double>(AV_NOPTS_VALUE) && capture->ic->duration != static_cast<double>(AV_NOPTS_VALUE))
|
|
return (double)(timestamp-capture->ic->start_time)/(double)capture->ic->duration;
|
|
break;
|
|
case CV_CAP_PROP_FRAME_WIDTH:
|
|
return capture->frame.width;
|
|
break;
|
|
case CV_CAP_PROP_FRAME_HEIGHT:
|
|
return capture->frame.height;
|
|
break;
|
|
case CV_CAP_PROP_FPS:
|
|
#if LIBAVCODEC_BUILD > 4753
|
|
return av_q2d (capture->video_st->r_frame_rate);
|
|
#else
|
|
return (double)capture->video_st->codec.frame_rate
|
|
/ (double)capture->video_st->codec.frame_rate_base;
|
|
#endif
|
|
break;
|
|
case CV_CAP_PROP_FOURCC:
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
return (double)capture->video_st->codec->codec_tag;
|
|
#else
|
|
return (double)capture->video_st->codec.codec_tag;
|
|
#endif
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int icvSetPropertyAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture,
|
|
int property_id, double value )
|
|
{
|
|
if( !capture || !capture->video_st || !capture->picture->data[0] )
|
|
return 0;
|
|
switch( property_id )
|
|
{
|
|
#if 0
|
|
case CV_CAP_PROP_POS_MSEC:
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
{
|
|
int64_t timestamp = AV_NOPTS_VALUE;
|
|
switch( property_id )
|
|
{
|
|
case CV_CAP_PROP_POS_FRAMES:
|
|
if(capture->ic->start_time != AV_NOPTS_VALUE) {
|
|
value *= (double)capture->video_st->codec.frame_rate_base
|
|
/ (double)capture->video_st->codec.frame_rate;
|
|
timestamp = capture->ic->start_time+(int64_t)(value*AV_TIME_BASE);
|
|
}
|
|
break;
|
|
case CV_CAP_PROP_POS_MSEC:
|
|
if(capture->ic->start_time != AV_NOPTS_VALUE)
|
|
timestamp = capture->ic->start_time+(int64_t)(value*AV_TIME_BASE/1000);
|
|
break;
|
|
case CV_CAP_PROP_POS_AVI_RATIO:
|
|
if(capture->ic->start_time != AV_NOPTS_VALUE && capture->ic->duration != AV_NOPTS_VALUE)
|
|
timestamp = capture->ic->start_time+(int64_t)(value*capture->ic->duration);
|
|
break;
|
|
}
|
|
if(timestamp != AV_NOPTS_VALUE) {
|
|
//printf("timestamp=%g\n",(double)timestamp);
|
|
int ret = av_seek_frame(capture->ic, -1, timestamp, 0);
|
|
if (ret < 0) {
|
|
fprintf(stderr, "HIGHGUI ERROR: AVI: could not seek to position %0.3f\n",
|
|
(double)timestamp / AV_TIME_BASE);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static CvCaptureVTable captureAVI_FFMPEG_vtable =
|
|
{
|
|
6,
|
|
(CvCaptureCloseFunc)icvCloseAVI_FFMPEG,
|
|
(CvCaptureGrabFrameFunc)icvGrabFrameAVI_FFMPEG,
|
|
(CvCaptureRetrieveFrameFunc)icvRetrieveFrameAVI_FFMPEG,
|
|
(CvCaptureGetPropertyFunc)icvGetPropertyAVI_FFMPEG,
|
|
(CvCaptureSetPropertyFunc)icvSetPropertyAVI_FFMPEG,
|
|
(CvCaptureGetDescriptionFunc)0
|
|
};
|
|
|
|
|
|
CvCapture* cvCaptureFromFile_FFMPEG( const char* filename )
|
|
{
|
|
CvCaptureAVI_FFMPEG* capture = 0;
|
|
|
|
if( filename )
|
|
{
|
|
capture = (CvCaptureAVI_FFMPEG*)cvAlloc( sizeof(*capture));
|
|
memset( capture, 0, sizeof(*capture));
|
|
|
|
capture->vtable = &captureAVI_FFMPEG_vtable;
|
|
|
|
if( !icvOpenAVI_FFMPEG( capture, filename ))
|
|
cvReleaseCapture( (CvCapture**)&capture );
|
|
}
|
|
|
|
return (CvCapture*)capture;
|
|
}
|
|
|
|
///////////////// FFMPEG CvVideoWriter implementation //////////////////////////
|
|
typedef struct CvAVI_FFMPEG_Writer
|
|
{
|
|
AVOutputFormat *fmt;
|
|
AVFormatContext *oc;
|
|
uint8_t * outbuf;
|
|
uint32_t outbuf_size;
|
|
FILE * outfile;
|
|
AVFrame * picture;
|
|
AVFrame * rgb_picture;
|
|
uint8_t * picbuf;
|
|
AVStream * video_st;
|
|
} CvAVI_FFMPEG_Writer;
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
AVFrame * picture;
|
|
uint8_t * picture_buf;
|
|
int size;
|
|
|
|
picture = avcodec_alloc_frame();
|
|
if (!picture)
|
|
return NULL;
|
|
size = avpicture_get_size(pix_fmt, width, height);
|
|
if(alloc){
|
|
picture_buf = (uint8_t *) cvAlloc(size);
|
|
if (!picture_buf)
|
|
{
|
|
av_free(picture);
|
|
return NULL;
|
|
}
|
|
avpicture_fill((AVPicture *)picture, picture_buf,
|
|
pix_fmt, width, height);
|
|
}
|
|
else {
|
|
}
|
|
return picture;
|
|
}
|
|
|
|
/* add a video output stream */
|
|
static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, int codec_tag, int w, int h, int bitrate, double fps, int pixel_format)
|
|
{
|
|
AVCodecContext *c;
|
|
AVStream *st;
|
|
int codec_id;
|
|
int frame_rate, frame_rate_base;
|
|
AVCodec *codec;
|
|
|
|
|
|
st = av_new_stream(oc, 0);
|
|
if (!st) {
|
|
CV_WARN("Could not allocate stream");
|
|
return NULL;
|
|
}
|
|
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
c = st->codec;
|
|
#else
|
|
c = &(st->codec);
|
|
#endif
|
|
#if LIBAVFORMAT_BUILD > 4621
|
|
codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, CODEC_TYPE_VIDEO);
|
|
#else
|
|
codec_id = oc->oformat->video_codec;
|
|
#endif
|
|
|
|
if(codec_tag) c->codec_tag=codec_tag;
|
|
|
|
c->codec_id = (CodecID) codec_id;
|
|
codec = avcodec_find_encoder(c->codec_id);
|
|
|
|
c->codec_type = CODEC_TYPE_VIDEO;
|
|
|
|
/* put sample parameters */
|
|
c->bit_rate = bitrate;
|
|
|
|
/* 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,
|
|
timebase should be 1/framerate and timestamp increments should be
|
|
identically 1. */
|
|
frame_rate=cvRound(fps);
|
|
frame_rate_base=1;
|
|
while (fabs((double)frame_rate/frame_rate_base) - fps > 0.001){
|
|
frame_rate_base*=10;
|
|
frame_rate=cvRound(fps*frame_rate_base);
|
|
}
|
|
#if LIBAVFORMAT_BUILD > 4752
|
|
c->time_base.den = frame_rate;
|
|
c->time_base.num = frame_rate_base;
|
|
/* adjust time base for supported framerates */
|
|
if(codec && codec->supported_framerates){
|
|
const AVRational *p= codec->supported_framerates;
|
|
AVRational req= (AVRational){frame_rate, frame_rate_base};
|
|
const AVRational *best=NULL;
|
|
AVRational best_error= (AVRational){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;
|
|
}
|
|
}
|
|
c->time_base.den= best->num;
|
|
c->time_base.num= best->den;
|
|
}
|
|
#else
|
|
c->frame_rate = frame_rate;
|
|
c->frame_rate_base = frame_rate_base;
|
|
#endif
|
|
|
|
c->gop_size = 12; /* emit one intra frame every twelve frames at most */
|
|
c->pix_fmt = (PixelFormat) pixel_format;
|
|
if (c->codec_id == CODEC_ID_MPEG2VIDEO) {
|
|
/* just for testing, we also add B frames */
|
|
c->max_b_frames = 2;
|
|
}
|
|
if (c->codec_id == CODEC_ID_MPEG1VIDEO){
|
|
/* needed to avoid using macroblocks in which some coeffs overflow
|
|
this doesnt happen with normal video, it just happens here as the
|
|
motion of the chroma plane doesnt match the luma plane */
|
|
c->mb_decision=2;
|
|
}
|
|
// some formats want stream headers to be seperate
|
|
if(!strcmp(oc->oformat->name, "mp4") || !strcmp(oc->oformat->name, "mov") || !strcmp(oc->oformat->name, "3gp"))
|
|
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
|
|
|
return st;
|
|
}
|
|
|
|
/// Create a video writer object that uses FFMPEG
|
|
CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char * filename, int fourcc,
|
|
double fps, CvSize frameSize, int /*is_color*/ )
|
|
{
|
|
CV_FUNCNAME("cvCreateVideoWriter");
|
|
|
|
CvAVI_FFMPEG_Writer * writer = NULL;
|
|
|
|
__BEGIN__;
|
|
|
|
// check arguments
|
|
assert (filename);
|
|
assert (fps > 0);
|
|
assert (frameSize.width > 0 && frameSize.height > 0);
|
|
|
|
// allocate memory for structure...
|
|
writer = (CvAVI_FFMPEG_Writer *) cvAlloc( sizeof(CvAVI_FFMPEG_Writer));
|
|
memset (writer, 0, sizeof (*writer));
|
|
|
|
// tell FFMPEG to register codecs
|
|
av_register_all ();
|
|
|
|
/* auto detect the output format from the name. default is mpeg. */
|
|
writer->fmt = guess_format(NULL, filename, NULL);
|
|
if (!writer->fmt) {
|
|
CV_ERROR( CV_StsUnsupportedFormat, "Could not deduce output format from file extension");
|
|
//writer->fmt = guess_format("mpeg", NULL, NULL);
|
|
}
|
|
|
|
// alloc memory for context
|
|
writer->oc = av_alloc_format_context();
|
|
assert (writer->oc);
|
|
|
|
/* set file name */
|
|
writer->oc->oformat = writer->fmt;
|
|
snprintf(writer->oc->filename, sizeof(writer->oc->filename), "%s", filename);
|
|
|
|
// TODO -- safe to ignore output audio stream?
|
|
writer->video_st = icv_add_video_stream_FFMPEG(writer->oc, fourcc, frameSize.width, frameSize.height, 800000, fps, PIX_FMT_YUV420P);
|
|
|
|
|
|
/* set the output parameters (must be done even if no
|
|
parameters). */
|
|
if (av_set_parameters(writer->oc, NULL) < 0) {
|
|
CV_ERROR(CV_StsBadArg, "Invalid output format parameters");
|
|
}
|
|
|
|
dump_format(writer->oc, 0, filename, 1);
|
|
|
|
/* now that all the parameters are set, we can open the audio and
|
|
video codecs and allocate the necessary encode buffers */
|
|
if (!writer->video_st){
|
|
CV_ERROR(CV_StsBadArg, "Couldn't open video stream");
|
|
}
|
|
|
|
AVCodec *codec;
|
|
AVCodecContext *c;
|
|
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
c = (writer->video_st->codec);
|
|
#else
|
|
c = &(writer->video_st->codec);
|
|
#endif
|
|
|
|
/* find the video encoder */
|
|
codec = avcodec_find_encoder(c->codec_id);
|
|
if (!codec) {
|
|
CV_ERROR(CV_StsBadArg, "codec not found");
|
|
}
|
|
|
|
/* open the codec */
|
|
if (avcodec_open(c, codec) < 0) {
|
|
char errtext[256];
|
|
sprintf(errtext, "Could not open codec '%s'", codec->name);
|
|
CV_ERROR(CV_StsBadArg, errtext);
|
|
}
|
|
|
|
// printf("Using codec %s\n", codec->name);
|
|
writer->outbuf = NULL;
|
|
|
|
if (!(writer->oc->oformat->flags & AVFMT_RAWPICTURE)) {
|
|
/* allocate output buffer */
|
|
/* XXX: API change will be done */
|
|
writer->outbuf_size = 200000;
|
|
writer->outbuf = (uint8_t *) malloc(writer->outbuf_size);
|
|
}
|
|
|
|
bool need_color_convert;
|
|
need_color_convert = c->pix_fmt != PIX_FMT_BGR24;
|
|
|
|
/* allocate the encoded raw picture */
|
|
writer->picture = icv_alloc_picture_FFMPEG(c->pix_fmt, c->width, c->height, need_color_convert);
|
|
if (!writer->picture) {
|
|
CV_ERROR(CV_StsNoMem, "Could not allocate picture");
|
|
}
|
|
|
|
/* if the output format is not YUV420P, then a temporary YUV420P
|
|
picture is needed too. It is then converted to the required
|
|
output format */
|
|
writer->rgb_picture = NULL;
|
|
if ( need_color_convert ) {
|
|
writer->rgb_picture = icv_alloc_picture_FFMPEG(PIX_FMT_BGR24, c->width, c->height, false);
|
|
if (!writer->rgb_picture) {
|
|
CV_ERROR(CV_StsNoMem, "Could not allocate picture");
|
|
}
|
|
}
|
|
|
|
/* open the output file, if needed */
|
|
if (!(writer->fmt->flags & AVFMT_NOFILE)) {
|
|
if (url_fopen(&writer->oc->pb, filename, URL_WRONLY) < 0) {
|
|
CV_ERROR(CV_StsBadArg, "Couldn't open output file for writing");
|
|
}
|
|
}
|
|
|
|
/* write the stream header, if any */
|
|
av_write_header( writer->oc );
|
|
|
|
|
|
__END__;
|
|
|
|
// return what we got
|
|
return (CvVideoWriter *) writer;
|
|
}
|
|
|
|
int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st, uint8_t * outbuf, uint32_t outbuf_size, AVFrame * picture ){
|
|
CV_FUNCNAME("icv_av_write_frame_FFMPEG");
|
|
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
AVCodecContext * c = video_st->codec;
|
|
#else
|
|
AVCodecContext * c = &(video_st->codec);
|
|
#endif
|
|
int out_size;
|
|
int ret;
|
|
|
|
__BEGIN__;
|
|
|
|
if (oc->oformat->flags & AVFMT_RAWPICTURE) {
|
|
/* raw video case. The API will change slightly in the near
|
|
futur for that */
|
|
AVPacket pkt;
|
|
av_init_packet(&pkt);
|
|
|
|
pkt.flags |= PKT_FLAG_KEY;
|
|
pkt.stream_index= video_st->index;
|
|
pkt.data= (uint8_t *)picture;
|
|
pkt.size= sizeof(AVPicture);
|
|
|
|
ret = av_write_frame(oc, &pkt);
|
|
} else {
|
|
/* encode the image */
|
|
out_size = avcodec_encode_video(c, outbuf, outbuf_size, picture);
|
|
/* if zero size, it means the image was buffered */
|
|
if (out_size > 0) {
|
|
AVPacket pkt;
|
|
av_init_packet(&pkt);
|
|
#if LIBAVFORMAT_BUILD > 4752
|
|
pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base);
|
|
#else
|
|
pkt.pts = c->coded_frame->pts;
|
|
#endif
|
|
if(c->coded_frame->key_frame)
|
|
pkt.flags |= PKT_FLAG_KEY;
|
|
pkt.stream_index= video_st->index;
|
|
pkt.data= outbuf;
|
|
pkt.size= out_size;
|
|
|
|
/* write the compressed frame in the media file */
|
|
ret = av_write_frame(oc, &pkt);
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
}
|
|
if (ret != 0) {
|
|
CV_ERROR(CV_StsError, "Error while writing video frame");
|
|
}
|
|
|
|
__END__;
|
|
return CV_StsOk;
|
|
}
|
|
|
|
/// write a frame with FFMPEG
|
|
CV_IMPL int cvWriteFrame( CvVideoWriter * writer, const IplImage * image )
|
|
{
|
|
int ret = 0;
|
|
|
|
CV_FUNCNAME("cvWriteFrame");
|
|
|
|
__BEGIN__;
|
|
|
|
// typecast from opaque data type to implemented struct
|
|
CvAVI_FFMPEG_Writer * mywriter = (CvAVI_FFMPEG_Writer*) writer;
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
AVCodecContext *c = mywriter->video_st->codec;
|
|
#else
|
|
AVCodecContext *c = &(mywriter->video_st->codec);
|
|
#endif
|
|
// check parameters
|
|
assert ( image );
|
|
assert ( image->nChannels == 3 );
|
|
assert ( image->depth == IPL_DEPTH_8U );
|
|
|
|
|
|
// check if buffer sizes match, i.e. image has expected format (size, channels, bitdepth, alignment)
|
|
assert (image->imageSize == avpicture_get_size (PIX_FMT_BGR24, image->width, image->height));
|
|
|
|
if (c->pix_fmt != PIX_FMT_BGR24 ) {
|
|
assert( mywriter->rgb_picture );
|
|
// let rgb_picture point to the raw data buffer of 'image'
|
|
avpicture_fill((AVPicture *)mywriter->rgb_picture, (uint8_t *) image->imageData,
|
|
PIX_FMT_BGR24, image->width, image->height);
|
|
|
|
// convert to the color format needed by the codec
|
|
if( img_convert((AVPicture *)mywriter->picture, c->pix_fmt,
|
|
(AVPicture *)mywriter->rgb_picture, PIX_FMT_BGR24,
|
|
image->width, image->height) < 0){
|
|
CV_ERROR(CV_StsUnsupportedFormat, "FFMPEG::img_convert pixel format conversion from BGR24 not handled");
|
|
}
|
|
}
|
|
else{
|
|
avpicture_fill((AVPicture *)mywriter->picture, (uint8_t *) image->imageData,
|
|
PIX_FMT_BGR24, image->width, image->height);
|
|
}
|
|
|
|
ret = icv_av_write_frame_FFMPEG( mywriter->oc, mywriter->video_st, mywriter->outbuf, mywriter->outbuf_size, mywriter->picture);
|
|
|
|
__END__;
|
|
return ret;
|
|
}
|
|
|
|
/// close video output stream and free associated memory
|
|
CV_IMPL void cvReleaseVideoWriter( CvVideoWriter ** writer )
|
|
{
|
|
int i;
|
|
|
|
// nothing to do if already released
|
|
if ( !(*writer) )
|
|
return;
|
|
|
|
// release data structures in reverse order
|
|
CvAVI_FFMPEG_Writer * mywriter = (CvAVI_FFMPEG_Writer*)(*writer);
|
|
|
|
/* 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?
|
|
|
|
/* write the trailer, if any */
|
|
av_write_trailer(mywriter->oc);
|
|
|
|
// free pictures
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
if( mywriter->video_st->codec->pix_fmt != PIX_FMT_BGR24){
|
|
#else
|
|
if( mywriter->video_st->codec.pix_fmt != PIX_FMT_BGR24){
|
|
#endif
|
|
cvFree(&(mywriter->picture->data[0]));
|
|
}
|
|
av_free(mywriter->picture);
|
|
|
|
if (mywriter->rgb_picture) {
|
|
av_free(mywriter->rgb_picture);
|
|
}
|
|
|
|
/* close codec */
|
|
#if LIBAVFORMAT_BUILD > 4628
|
|
avcodec_close(mywriter->video_st->codec);
|
|
#else
|
|
avcodec_close(&(mywriter->video_st->codec));
|
|
#endif
|
|
|
|
av_free(mywriter->outbuf);
|
|
|
|
/* free the streams */
|
|
for(i = 0; i < mywriter->oc->nb_streams; i++) {
|
|
av_freep(&mywriter->oc->streams[i]->codec);
|
|
av_freep(&mywriter->oc->streams[i]);
|
|
}
|
|
|
|
if (!(mywriter->fmt->flags & AVFMT_NOFILE)) {
|
|
/* close the output file */
|
|
url_fclose(&mywriter->oc->pb);
|
|
}
|
|
|
|
/* free the stream */
|
|
av_free(mywriter->oc);
|
|
|
|
/* free cvVideoWriter */
|
|
cvFree ( writer );
|
|
|
|
// mark as released
|
|
(*writer) = 0;
|
|
}
|