X-Git-Url: http://git.maemo.org/git/?p=opencv;a=blobdiff_plain;f=otherlibs%2Fhighgui%2Fcvcap_ffmpeg.cpp;fp=otherlibs%2Fhighgui%2Fcvcap_ffmpeg.cpp;h=3c84214aca85ccdb15de02577bd492e4672d09e2;hp=801209750f3fc728a355bbc30254808350017aa2;hb=80cd7b93506cc1926882d5fd08a2c74ee9359e29;hpb=467a270adf12425827305759c0c4ea8f5b2b3854 diff --git a/otherlibs/highgui/cvcap_ffmpeg.cpp b/otherlibs/highgui/cvcap_ffmpeg.cpp index 8012097..3c84214 100644 --- a/otherlibs/highgui/cvcap_ffmpeg.cpp +++ b/otherlibs/highgui/cvcap_ffmpeg.cpp @@ -41,25 +41,34 @@ #include "_highgui.h" +#if defined _MSC_VER && _MSC_VER >= 1200 +#pragma warning( disable: 4244 4510 4512 4610 ) +#endif + extern "C" { -#include -#include #ifndef WIN32 +#define INT64_C +#define __STDC_CONSTANT_MACROS +#include #include #endif -} +#include +#include +} -#define INT64_C (long long) +#if defined _MSC_VER && _MSC_VER >= 1200 +#pragma warning( default: 4244 4510 4512 4610 ) +#endif #ifdef NDEBUG -#define CV_WARN(message) +#define CV_WARN(message) #else #define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__) #endif -#if LIBAVCODEC_VERSION_INT<=0x000409 +#ifndef MKTAG #define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24)) #endif @@ -69,10 +78,10 @@ extern "C" { char * FOURCC2str( int fourcc ) { char * mystr=(char*)malloc(5); - mystr[0]=(fourcc )&255; - mystr[1]=(fourcc>> 8)&255; - mystr[2]=(fourcc>>16)&255; - mystr[3]=(fourcc>>24)&255; + mystr[0]=(char)((fourcc )&255); + mystr[1]=(char)((fourcc>> 8)&255); + mystr[2]=(char)((fourcc>>16)&255); + mystr[3]=(char)((fourcc>>24)&255); mystr[4]=0; return mystr; } @@ -212,15 +221,17 @@ const AVCodecTag codec_bmp_tags[] = { { CODEC_ID_LOCO, MKTAG('L', 'O', 'C', 'O') }, { CODEC_ID_THEORA, MKTAG('t', 'h', 'e', 'o') }, #if LIBAVCODEC_VERSION_INT>0x000409 - { CODEC_ID_JPEGLS,MKTAG('M', 'J', 'L', 'S') }, /* JPEG-LS custom FOURCC for avi - encoder */ - { CODEC_ID_FLASHSV, MKTAG('F', 'S', 'V', '1') }, - { CODEC_ID_VC1, MKTAG('W', 'V', 'C', '1') }, - { CODEC_ID_VC1, MKTAG('W', 'M', 'V', 'A') }, { CODEC_ID_WNV1, MKTAG('W', 'N', 'V', '1') }, { CODEC_ID_AASC, MKTAG('A', 'A', 'S', 'C') }, { CODEC_ID_INDEO2, MKTAG('R', 'T', '2', '1') }, { CODEC_ID_FRAPS, MKTAG('F', 'P', 'S', '1') }, { CODEC_ID_TRUEMOTION2, MKTAG('T', 'M', '2', '0') }, +#endif +#if LIBAVCODEC_VERSION_INT>((50<<16)+(1<<8)+0) + { CODEC_ID_FLASHSV, MKTAG('F', 'S', 'V', '1') }, + { CODEC_ID_JPEGLS,MKTAG('M', 'J', 'L', 'S') }, /* JPEG-LS custom FOURCC for avi - encoder */ + { CODEC_ID_VC1, MKTAG('W', 'V', 'C', '1') }, + { CODEC_ID_VC1, MKTAG('W', 'M', 'V', 'A') }, { CODEC_ID_CSCD, MKTAG('C', 'S', 'C', 'D') }, { CODEC_ID_ZMBV, MKTAG('Z', 'M', 'B', 'V') }, { CODEC_ID_KMVC, MKTAG('K', 'M', 'V', 'C') }, @@ -242,9 +253,24 @@ const AVCodecTag codec_bmp_tags[] = { }; -typedef struct CvCaptureAVI_FFMPEG +class CvCapture_FFMPEG : public CvCapture { - CvCaptureVTable * vtable; +public: + CvCapture_FFMPEG() { init(); } + virtual ~CvCapture_FFMPEG() { close(); } + + virtual bool open( const char* filename ); + virtual void close(); + + virtual double getProperty(int); + virtual bool setProperty(int, double); + virtual bool grabFrame(); + virtual IplImage* retrieveFrame(); + +protected: + void init(); + bool reopen(); + bool slowSeek( int framenumber ); AVFormatContext * ic; int video_stream; @@ -259,85 +285,93 @@ typedef struct CvCaptureAVI_FFMPEG 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; -} CvCaptureAVI_FFMPEG; +}; + + +void CvCapture_FFMPEG::init() +{ + ic = 0; + video_stream = -1; + video_st = 0; + picture = 0; + picture_pts = 0; + memset( &rgb_picture, 0, sizeof(rgb_picture) ); + memset( &frame, 0, sizeof(frame) ); + filename = 0; +} -static void icvCloseAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) +void CvCapture_FFMPEG::close() { - //cvFree( (void**)&(capture->entries) ); - if( capture->picture ) - av_free(capture->picture); + if( picture ) + av_free(picture); - if( capture->video_st ) + if( video_st ) { #if LIBAVFORMAT_BUILD > 4628 - avcodec_close( capture->video_st->codec ); + avcodec_close( video_st->codec ); #else - avcodec_close( &capture->video_st->codec ); + avcodec_close( &video_st->codec ); #endif - capture->video_st = NULL; + video_st = NULL; } - if( capture->ic ) + if( ic ) { - av_close_input_file(capture->ic); - capture->ic = NULL; + av_close_input_file(ic); + ic = NULL; } - if( capture->rgb_picture.data[0] ) - cvFree( &capture->rgb_picture.data[0] ); + if( rgb_picture.data[0] ) + cvFree( &rgb_picture.data[0] ); - memset( &capture->frame, 0, sizeof(capture->frame)); + init(); } /* Used to reopen a video if the slower fallback function for seeking is used. */ -static int icvReopenFileAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) +bool CvCapture_FFMPEG::reopen() { - if ( capture->filename==NULL ) return 0; + if ( filename==NULL ) return false; #if LIBAVFORMAT_BUILD > 4628 - avcodec_close( capture->video_st->codec ); + avcodec_close( video_st->codec ); #else - avcodec_close( &capture->video_st->codec ); + avcodec_close( &video_st->codec ); #endif - av_close_input_file(capture->ic); + av_close_input_file(ic); // reopen video - av_open_input_file(&capture->ic, capture->filename, NULL, 0, NULL); - av_find_stream_info(capture->ic); + av_open_input_file(&ic, filename, NULL, 0, NULL); + av_find_stream_info(ic); #if LIBAVFORMAT_BUILD > 4628 - AVCodecContext *enc = capture->ic->streams[capture->video_stream]->codec; + AVCodecContext *enc = ic->streams[video_stream]->codec; #else - AVCodecContext *enc = &capture->ic->streams[capture->video_stream]->codec; + AVCodecContext *enc = &ic->streams[video_stream]->codec; #endif AVCodec *codec = avcodec_find_decoder(enc->codec_id); avcodec_open(enc, codec); - capture->video_st = capture->ic->streams[capture->video_stream]; + video_st = ic->streams[video_stream]; // reset framenumber to zero - capture->picture_pts=0; + picture_pts=0; - return 1; + return true; } -// forward -static int icvCheckSeekAVI_FFMPEG( CvCaptureAVI_FFMPEG *capture); - -static int icvOpenAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, const char* filename ) +bool CvCapture_FFMPEG::open( const char* _filename ) { - int err, valid = 0, video_index = -1, i; - AVFormatContext *ic; + unsigned i; + bool valid = false; + + close(); - capture->ic = NULL; - capture->video_stream = -1; - capture->video_st = NULL; /* register all codecs, demux and protocols */ av_register_all(); @@ -345,12 +379,11 @@ static int icvOpenAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, const char* filename // av_log_level = AV_LOG_QUIET; #endif - err = av_open_input_file(&ic, filename, NULL, 0, NULL); + int 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"); @@ -362,66 +395,60 @@ static int icvOpenAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, const char* filename #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] = (uint8_t*)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( CODEC_TYPE_VIDEO == enc->codec_type && video_stream < 0) { + AVCodec *codec = avcodec_find_decoder(enc->codec_id); + if (!codec || + avcodec_open(enc, codec) < 0) + goto exit_func; + video_stream = i; + video_st = ic->streams[i]; + picture = avcodec_alloc_frame(); + + rgb_picture.data[0] = (uint8_t*)cvAlloc( + avpicture_get_size( PIX_FMT_BGR24, + enc->width, enc->height )); + avpicture_fill( (AVPicture*)&rgb_picture, rgb_picture.data[0], + PIX_FMT_BGR24, enc->width, enc->height ); + + cvInitImageHeader( &frame, cvSize( enc->width, + enc->height ), 8, 3, 0, 4 ); + cvSetData( &frame, rgb_picture.data[0], + rgb_picture.linesize[0] ); + break; + } } - if(video_index >= 0) valid = 1; + if(video_stream >= 0) valid = true; // perform check if source is seekable via ffmpeg's seek function av_seek_frame(...) - err = av_seek_frame(capture->ic, capture->video_stream, 10, 0); + err = av_seek_frame(ic, video_stream, 10, 0); if (err < 0) { - int length=0; - while ( filename[length] ) length++; - // remark filename - capture->filename=(char*)malloc(length+1); - for ( int i=0; ifilename[i]=filename[i]; + filename=(char*)malloc(strlen(_filename)+1); + strcpy(filename, _filename); // reopen videofile to 'seek' back to first frame - icvReopenFileAVI_FFMPEG( capture ); + reopen(); } else { // seek seems to work, so we don't need the filename, // but we still need to seek back to filestart - capture->filename=NULL; - av_seek_frame(capture->ic, capture->video_stream, 0, 0); + filename=NULL; + av_seek_frame(ic, video_stream, 0, 0); } exit_func: if( !valid ) - icvCloseAVI_FFMPEG( capture ); + close(); return valid; } - - -static int icvGrabFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) +bool CvCapture_FFMPEG::grabFrame() { - int valid=0; + bool valid = false; static bool bFirstTime = true; static AVPacket pkt; int got_picture; @@ -433,8 +460,8 @@ static int icvGrabFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) pkt.data = NULL; } - if( !capture || !capture->ic || !capture->video_st ) - return 0; + if( !ic || !video_st ) + return false; // free last packet if exist if (pkt.data != NULL) { @@ -442,21 +469,21 @@ static int icvGrabFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) } // get the next frame - while ((0 == valid) && (av_read_frame(capture->ic, &pkt) >= 0)) { - if( pkt.stream_index != capture->video_stream ) continue; + while (!valid && (av_read_frame(ic, &pkt) >= 0)) { + if( pkt.stream_index != video_stream ) continue; #if LIBAVFORMAT_BUILD > 4628 - avcodec_decode_video(capture->video_st->codec, - capture->picture, &got_picture, + avcodec_decode_video(video_st->codec, + picture, &got_picture, pkt.data, pkt.size); #else - avcodec_decode_video(&capture->video_st->codec, - capture->picture, &got_picture, + avcodec_decode_video(&video_st->codec, + picture, &got_picture, pkt.data, pkt.size); #endif if (got_picture) { // we have a new picture, so memorize it - capture->picture_pts = pkt.pts; + picture_pts = pkt.pts; valid = 1; } } @@ -466,74 +493,74 @@ static int icvGrabFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) } -static const IplImage* icvRetrieveFrameAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture ) +IplImage* CvCapture_FFMPEG::retrieveFrame() { - if( !capture || !capture->video_st || !capture->picture->data[0] ) - return 0; + if( !video_st || !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 ); + img_convert( (AVPicture*)&rgb_picture, PIX_FMT_BGR24, + (AVPicture*)picture, + video_st->codec->pix_fmt, + video_st->codec->width, + 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 ); + img_convert( (AVPicture*)&rgb_picture, PIX_FMT_BGR24, + (AVPicture*)picture, + video_st->codec.pix_fmt, + video_st->codec.width, + video_st->codec.height ); #endif - return &capture->frame; + return &frame; } -static double icvGetPropertyAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, int property_id ) +double CvCapture_FFMPEG::getProperty( int property_id ) { - // if( !capture || !capture->video_st || !capture->picture->data[0] ) return 0; - if( !capture || !capture->video_st ) return 0; + // if( !capture || !video_st || !picture->data[0] ) return 0; + if( !video_st ) return 0; int64_t timestamp; - timestamp = capture->picture_pts; + timestamp = picture_pts; switch( property_id ) { case CV_CAP_PROP_POS_MSEC: - // if(capture->ic->start_time != static_cast(AV_NOPTS_VALUE)) - if(capture->ic->start_time != AV_NOPTS_VALUE) - return (double)(timestamp - capture->ic->start_time)*1000/(double)AV_TIME_BASE; + // if(ic->start_time != static_cast(AV_NOPTS_VALUE)) + if(ic->start_time != AV_NOPTS_VALUE) + return (double)(timestamp - ic->start_time)*1000/(double)AV_TIME_BASE; break; case CV_CAP_PROP_POS_FRAMES: - //if(capture->video_st->cur_dts != static_cast(AV_NOPTS_VALUE)) - if(capture->video_st->cur_dts != AV_NOPTS_VALUE) - return (double)capture->video_st->cur_dts-1; + //if(video_st->cur_dts != static_cast(AV_NOPTS_VALUE)) + if(video_st->cur_dts != AV_NOPTS_VALUE) + return (double)video_st->cur_dts-1; break; case CV_CAP_PROP_POS_AVI_RATIO: - // if(capture->ic->start_time != static_cast(AV_NOPTS_VALUE) && capture->ic->duration != static_cast(AV_NOPTS_VALUE)) - if(capture->ic->start_time != AV_NOPTS_VALUE && capture->ic->duration != AV_NOPTS_VALUE) - return (double)(timestamp-capture->ic->start_time)/(double)capture->ic->duration; + // if(ic->start_time != static_cast(AV_NOPTS_VALUE) && ic->duration != static_cast(AV_NOPTS_VALUE)) + if(ic->start_time != AV_NOPTS_VALUE && ic->duration != AV_NOPTS_VALUE) + return (double)(timestamp-ic->start_time)/(double)ic->duration; break; case CV_CAP_PROP_FRAME_WIDTH: - return (double)capture->frame.width; + return (double)frame.width; break; case CV_CAP_PROP_FRAME_HEIGHT: - return (double)capture->frame.height; + return (double)frame.height; break; case CV_CAP_PROP_FPS: #if LIBAVCODEC_BUILD > 4753 - return av_q2d (capture->video_st->r_frame_rate); + return av_q2d (video_st->r_frame_rate); #else - return (double)capture->video_st->codec.frame_rate - / (double)capture->video_st->codec.frame_rate_base; + return (double)video_st->codec.frame_rate + / (double)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; + return (double)video_st->codec->codec_tag; #else - return (double)capture->video_st->codec.codec_tag; + return (double)video_st->codec.codec_tag; #endif break; } @@ -543,144 +570,129 @@ static double icvGetPropertyAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, int proper // this is a VERY slow fallback function, ONLY used if ffmpeg's av_seek_frame delivers no correct result! -static int icvSlowSeekAVI_FFMPEG( CvCaptureAVI_FFMPEG* capture, const int framenumber ) +bool CvCapture_FFMPEG::slowSeek( int framenumber ) { - if ( framenumber>capture->picture_pts ) + if ( framenumber>picture_pts ) { - while ( capture->picture_ptspicture_pts ) + else if ( framenumberpicture_ptsvideo_st ) return 0; + if( !video_st ) return false; switch( property_id ) { - case CV_CAP_PROP_POS_MSEC: - case CV_CAP_PROP_POS_FRAMES: - case CV_CAP_PROP_POS_AVI_RATIO: + case CV_CAP_PROP_POS_MSEC: + case CV_CAP_PROP_POS_FRAMES: + case CV_CAP_PROP_POS_AVI_RATIO: { - int64_t timestamp; + int64_t timestamp = 0; AVRational time_base; switch( property_id ) { - case CV_CAP_PROP_POS_FRAMES: + case CV_CAP_PROP_POS_FRAMES: timestamp=(int64_t)value; - if(capture->ic->start_time != AV_NOPTS_VALUE) - timestamp += capture->ic->start_time; + if(ic->start_time != AV_NOPTS_VALUE) + timestamp += ic->start_time; break; - case CV_CAP_PROP_POS_MSEC: - time_base=capture->ic->streams[capture->video_stream]->time_base; + case CV_CAP_PROP_POS_MSEC: + time_base=ic->streams[video_stream]->time_base; timestamp=(int64_t)(value*(float(time_base.den)/float(time_base.num))/1000); - if(capture->ic->start_time != AV_NOPTS_VALUE) - timestamp += capture->ic->start_time; + if(ic->start_time != AV_NOPTS_VALUE) + timestamp += ic->start_time; break; - case CV_CAP_PROP_POS_AVI_RATIO: - timestamp=(int64_t)(value*capture->ic->duration); - if(capture->ic->start_time != AV_NOPTS_VALUE && capture->ic->duration != AV_NOPTS_VALUE) - timestamp += capture->ic->start_time; + case CV_CAP_PROP_POS_AVI_RATIO: + timestamp=(int64_t)(value*ic->duration); + if(ic->start_time != AV_NOPTS_VALUE && ic->duration != AV_NOPTS_VALUE) + timestamp += ic->start_time; break; } - if ( capture->filename ) + if ( filename ) { // ffmpeg's seek doesn't work... - if (icvSlowSeekAVI_FFMPEG(capture, timestamp) < 0) + if (!slowSeek((int)timestamp)) { - fprintf(stderr, "HIGHGUI ERROR: AVI: could not (slow) seek to position %0.3f\n", + fprintf(stderr, "HIGHGUI ERROR: AVI: could not (slow) seek to position %0.3f\n", (double)timestamp / AV_TIME_BASE); - return 0; + return false; } } else { - int ret = av_seek_frame(capture->ic, capture->video_stream, timestamp, 0); + int ret = av_seek_frame(ic, video_stream, 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; + fprintf(stderr, "HIGHGUI ERROR: AVI: could not seek to position %0.3f\n", + (double)timestamp / AV_TIME_BASE); + return false; } } - capture->picture_pts=value; + picture_pts=(int64_t)value; } break; - default: - return 0; + default: + return false; } - return 1; + return true; } -static CvCaptureVTable captureAVI_FFMPEG_vtable = +CvCapture* cvCreateFileCapture_FFMPEG( const char* filename ) { - 6, - (CvCaptureCloseFunc)icvCloseAVI_FFMPEG, - (CvCaptureGrabFrameFunc)icvGrabFrameAVI_FFMPEG, - (CvCaptureRetrieveFrameFunc)icvRetrieveFrameAVI_FFMPEG, - (CvCaptureGetPropertyFunc)icvGetPropertyAVI_FFMPEG, - (CvCaptureSetPropertyFunc)icvSetPropertyAVI_FFMPEG, - (CvCaptureGetDescriptionFunc)0 -}; + CvCapture_FFMPEG* capture = new CvCapture_FFMPEG; + if( capture->open( filename )) + return capture; + delete capture; + return 0; +} -CvCapture* cvCaptureFromFile_FFMPEG( const char* filename ) +///////////////// FFMPEG CvVideoWriter implementation ////////////////////////// +class CvVideoWriter_FFMPEG : public CvVideoWriter { - CvCaptureAVI_FFMPEG* capture = 0; +public: + CvVideoWriter_FFMPEG() { init(); } + virtual ~CvVideoWriter_FFMPEG() { close(); } - if( filename ) - { - capture = (CvCaptureAVI_FFMPEG*)cvAlloc( sizeof(*capture)); - memset( capture, 0, sizeof(*capture)); + virtual bool open( const char* filename, int fourcc, + double fps, CvSize frameSize, bool isColor ); + virtual void close(); + virtual bool writeFrame( const IplImage* image ); - capture->vtable = &captureAVI_FFMPEG_vtable; +protected: + void init(); - if( !icvOpenAVI_FFMPEG( capture, filename )) - { - capture->vtable->close((CvCapture*)capture); - cvFree( &capture ); - } - } - - return (CvCapture*)capture; -} - - -///////////////// FFMPEG CvVideoWriter implementation ////////////////////////// -typedef struct CvAVI_FFMPEG_Writer -{ - AVOutputFormat *fmt; - AVFormatContext *oc; + AVOutputFormat *fmt; + AVFormatContext *oc; uint8_t * outbuf; uint32_t outbuf_size; FILE * outfile; AVFrame * picture; AVFrame * input_picture; uint8_t * picbuf; - AVStream * video_st; - int input_pix_fmt; + AVStream * video_st; + int input_pix_fmt; IplImage * temp_image; -} CvAVI_FFMPEG_Writer; +}; -const char * icv_FFMPEG_ErrStr(int err) +static const char * icvFFMPEGErrStr(int err) { switch(err) { case AVERROR_NUMEXPECTED: @@ -704,6 +716,21 @@ extern "C" { enum CodecID codec_get_bmp_id(unsigned int tag); } +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; + input_pix_fmt = 0; + temp_image = 0; +} + /** * the following function is a modified version of code * found in ffmpeg-0.4.9-pre1/output_example.c @@ -720,12 +747,12 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo size = avpicture_get_size(pix_fmt, width, height); if(alloc){ picture_buf = (uint8_t *) cvAlloc(size); - if (!picture_buf) + if (!picture_buf) { av_free(picture); return NULL; } - avpicture_fill((AVPicture *)picture, picture_buf, + avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height); } else { @@ -734,16 +761,16 @@ static AVFrame * icv_alloc_picture_FFMPEG(int pix_fmt, int width, int height, bo } /* add a video output stream to the container */ -static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, - CodecID codec_id, - int w, int h, int bitrate, +static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, + CodecID codec_id, + int w, int h, int bitrate, double fps, int pixel_format) { AVCodecContext *c; AVStream *st; int frame_rate, frame_rate_base; AVCodec *codec; - + st = av_new_stream(oc, 0); if (!st) { @@ -757,7 +784,7 @@ static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, c = &(st->codec); #endif -#if LIBAVFORMAT_BUILD > 4621 +#if LIBAVFORMAT_BUILD > 4621 c->codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, CODEC_TYPE_VIDEO); #else c->codec_id = oc->oformat->video_codec; @@ -838,184 +865,6 @@ static AVStream *icv_add_video_stream_FFMPEG(AVFormatContext *oc, 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; - CodecID codec_id = CODEC_ID_NONE; - int err; - - __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 and fourcc code. */ - writer->fmt = guess_format(NULL, filename, NULL); - if (!writer->fmt) { - CV_ERROR( CV_StsUnsupportedFormat, "FFMPEG does not recognize the given file extension"); - } - - /* determine optimal pixel format */ - if (is_color) { - writer->input_pix_fmt = PIX_FMT_BGR24; - } - else { - writer->input_pix_fmt = PIX_FMT_GRAY8; - } - - // 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); - - /* set some options */ - writer->oc->max_delay = (int)(0.7*AV_TIME_BASE); /* This reduces buffer underrun warnings with MPEG */ - - /* Lookup codec id for given fourcc */ - if(fourcc!=CV_FOURCC_DEFAULT){ -#if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) - if( (codec_id = codec_get_bmp_id( fourcc )) == CODEC_ID_NONE ){ - CV_ERROR( CV_StsUnsupportedFormat, - "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); - } - } -#else - if( (codec_id = av_codec_get_id((const AVCodecTag**)(&codec_bmp_tags), fourcc)) == CODEC_ID_NONE ){ - CV_ERROR( CV_StsUnsupportedFormat, - "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); - } - } -#endif - - // set a few optimal pixel formats for lossless codecs of interest.. - int codec_pix_fmt; - switch (codec_id) { -#if LIBAVCODEC_VERSION_INT>0x000409 - case CODEC_ID_JPEGLS: - // BGR24 or GRAY8 depending on is_color... - codec_pix_fmt = writer->input_pix_fmt; - break; -#endif - case CODEC_ID_FFV1: - // no choice... other supported formats are YUV only - codec_pix_fmt = PIX_FMT_RGBA32; - break; - case CODEC_ID_MJPEG: - case CODEC_ID_LJPEG: - codec_pix_fmt = PIX_FMT_YUVJ420P; - break; - case CODEC_ID_RAWVIDEO: - default: - // good for lossy formats, MPEG, etc. - codec_pix_fmt = PIX_FMT_YUV420P; - break; - } - - // TODO -- safe to ignore output audio stream? - writer->video_st = icv_add_video_stream_FFMPEG(writer->oc, codec_id, - frameSize.width, frameSize.height, frameSize.width*frameSize.height*64, - fps, codec_pix_fmt); - - - /* 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 - - c->codec_tag = fourcc; - /* find the video encoder */ - codec = avcodec_find_encoder(c->codec_id); - if (!codec) { - CV_ERROR(CV_StsBadArg, "codec not found"); - } - - /* open the codec */ - if ( (err=avcodec_open(c, codec)) < 0) { - char errtext[256]; - sprintf(errtext, "Could not open codec '%s': %s", codec->name, icv_FFMPEG_ErrStr(err)); - CV_ERROR(CV_StsBadArg, errtext); - } - - writer->outbuf = NULL; - - if (!(writer->oc->oformat->flags & AVFMT_RAWPICTURE)) { - /* allocate output buffer */ - /* assume we will never get codec output with more than 4 bytes per pixel... */ - writer->outbuf_size = frameSize.width*frameSize.height*4; - writer->outbuf = (uint8_t *) av_malloc(writer->outbuf_size); - } - - bool need_color_convert; - need_color_convert = (c->pix_fmt != writer->input_pix_fmt); - - /* 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 our input format, then a temporary - picture of the input format is needed too. It is then converted - to the required output format */ - writer->input_picture = NULL; - if ( need_color_convert ) { - writer->input_picture = icv_alloc_picture_FFMPEG(writer->input_pix_fmt, c->width, c->height, false); - if (!writer->input_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"); @@ -1049,9 +898,9 @@ int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st, uint8_ AVPacket pkt; av_init_packet(&pkt); -#if LIBAVFORMAT_BUILD > 4752 +#if LIBAVFORMAT_BUILD > 4752 pkt.pts = av_rescale_q(c->coded_frame->pts, c->time_base, video_st->time_base); -#else +#else pkt.pts = c->coded_frame->pts; #endif if(c->coded_frame->key_frame) @@ -1075,142 +924,318 @@ int icv_av_write_frame_FFMPEG( AVFormatContext * oc, AVStream * video_st, uint8_ } /// write a frame with FFMPEG -CV_IMPL int cvWriteFrame( CvVideoWriter * writer, const IplImage * image ) +bool CvVideoWriter_FFMPEG::writeFrame( const IplImage * image ) { - int ret = 0; + bool ret = false; - CV_FUNCNAME("cvWriteFrame"); + CV_FUNCNAME("CvVideoWriter_FFMPEG::writerFrame"); __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; + AVCodecContext *c = video_st->codec; #else - AVCodecContext *c = &(mywriter->video_st->codec); + AVCodecContext *c = &(video_st->codec); #endif - + if( c->codec_id == CODEC_ID_RAWVIDEO && image->origin != IPL_ORIGIN_BL ) { - if( !mywriter->temp_image ) - mywriter->temp_image = cvCreateImage( cvGetSize(image), + if( !temp_image ) + temp_image = cvCreateImage( cvGetSize(image), image->depth, image->nChannels ); - cvFlip( image, mywriter->temp_image, 0 ); - image = mywriter->temp_image; + cvFlip( image, temp_image, 0 ); + image = temp_image; } // check parameters - if (mywriter->input_pix_fmt == PIX_FMT_BGR24) { + if (input_pix_fmt == PIX_FMT_BGR24) { if (image->nChannels != 3 || image->depth != IPL_DEPTH_8U) { CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 3."); } - } - else if (mywriter->input_pix_fmt == PIX_FMT_GRAY8) { + } + else if (input_pix_fmt == PIX_FMT_GRAY8) { if (image->nChannels != 1 || image->depth != IPL_DEPTH_8U) { CV_ERROR(CV_StsUnsupportedFormat, "cvWriteFrame() needs images with depth = IPL_DEPTH_8U and nChannels = 1."); } - } + } else { assert(false); } // check if buffer sizes match, i.e. image has expected format (size, channels, bitdepth, alignment) - assert (image->imageSize == avpicture_get_size( mywriter->input_pix_fmt, image->width, image->height )); + assert (image->imageSize == avpicture_get_size( input_pix_fmt, image->width, image->height )); - if ( c->pix_fmt != mywriter->input_pix_fmt ) { - assert( mywriter->input_picture ); + if ( c->pix_fmt != input_pix_fmt ) { + assert( input_picture ); // let input_picture point to the raw data buffer of 'image' - avpicture_fill((AVPicture *)mywriter->input_picture, (uint8_t *) image->imageData, - mywriter->input_pix_fmt, image->width, image->height); + avpicture_fill((AVPicture *)input_picture, (uint8_t *) image->imageData, + input_pix_fmt, image->width, image->height); // convert to the color format needed by the codec - if( img_convert((AVPicture *)mywriter->picture, c->pix_fmt, - (AVPicture *)mywriter->input_picture, mywriter->input_pix_fmt, + if( img_convert((AVPicture *)picture, c->pix_fmt, + (AVPicture *)input_picture, input_pix_fmt, 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, - mywriter->input_pix_fmt, image->width, image->height); + avpicture_fill((AVPicture *)picture, (uint8_t *) image->imageData, + input_pix_fmt, image->width, image->height); } - ret = icv_av_write_frame_FFMPEG( mywriter->oc, mywriter->video_st, mywriter->outbuf, mywriter->outbuf_size, mywriter->picture); + ret = icv_av_write_frame_FFMPEG( oc, video_st, outbuf, outbuf_size, picture) >= 0; __END__; return ret; } /// close video output stream and free associated memory -CV_IMPL void cvReleaseVideoWriter( CvVideoWriter ** writer ) +void CvVideoWriter_FFMPEG::close() { - int i; + unsigned i; // nothing to do if already released - if ( !(*writer) ) + if ( !picture ) 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? + // TODO -- do we need to account for latency here? /* write the trailer, if any */ - av_write_trailer(mywriter->oc); + av_write_trailer(oc); // free pictures #if LIBAVFORMAT_BUILD > 4628 - if( mywriter->video_st->codec->pix_fmt != mywriter->input_pix_fmt){ + if( video_st->codec->pix_fmt != input_pix_fmt){ #else - if( mywriter->video_st->codec.pix_fmt != mywriter->input_pix_fmt){ + if( video_st->codec.pix_fmt != input_pix_fmt){ #endif - cvFree(&(mywriter->picture->data[0])); + cvFree(&(picture->data[0])); } - av_free(mywriter->picture); + av_free(picture); - if (mywriter->input_picture) { - av_free(mywriter->input_picture); + if (input_picture) { + av_free(input_picture); } /* close codec */ #if LIBAVFORMAT_BUILD > 4628 - avcodec_close(mywriter->video_st->codec); + avcodec_close(video_st->codec); #else - avcodec_close(&(mywriter->video_st->codec)); + avcodec_close(&(video_st->codec)); #endif - - av_free(mywriter->outbuf); + + av_free(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]); + for(i = 0; i < oc->nb_streams; i++) { + av_freep(&oc->streams[i]->codec); + av_freep(&oc->streams[i]); } - if (!(mywriter->fmt->flags & AVFMT_NOFILE)) { + if (!(fmt->flags & AVFMT_NOFILE)) { /* close the output file */ #if LIBAVCODEC_VERSION_INT==((51<<16)+(49<<8)+0) - url_fclose(mywriter->oc->pb); + url_fclose(oc->pb); #else - url_fclose(&mywriter->oc->pb); + url_fclose(&oc->pb); #endif } /* free the stream */ - av_free(mywriter->oc); + av_free(oc); - cvReleaseImage( &mywriter->temp_image ); + cvReleaseImage( &temp_image ); + + init(); +} + +/// Create a video writer object that uses FFMPEG +bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, + double fps, CvSize frameSize, bool is_color ) +{ + CV_FUNCNAME("CvVideoWriter_FFMPEG::open"); + + CodecID codec_id = CODEC_ID_NONE; + int err; + + __BEGIN__; - /* free cvVideoWriter */ - cvFree ( writer ); + close(); - // mark as released - (*writer) = 0; + // check arguments + assert (filename); + assert (fps > 0); + assert (frameSize.width > 0 && frameSize.height > 0); + + // tell FFMPEG to register codecs + av_register_all (); + + /* auto detect the output format from the name and fourcc code. */ + fmt = guess_format(NULL, filename, NULL); + if (!fmt) { + CV_ERROR( CV_StsUnsupportedFormat, "FFMPEG does not recognize the given file extension"); + } + + /* determine optimal pixel format */ + if (is_color) { + input_pix_fmt = PIX_FMT_BGR24; + } + else { + input_pix_fmt = PIX_FMT_GRAY8; + } + + // alloc memory for context + oc = av_alloc_format_context(); + assert (oc); + + /* set file name */ + oc->oformat = fmt; + snprintf(oc->filename, sizeof(oc->filename), "%s", filename); + + /* set some options */ + oc->max_delay = (int)(0.7*AV_TIME_BASE); /* This reduces buffer underrun warnings with MPEG */ + + /* Lookup codec_id for given fourcc */ + if(fourcc!=CV_FOURCC_DEFAULT){ +#if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) + if( (codec_id = codec_get_bmp_id( fourcc )) == CODEC_ID_NONE ){ + CV_ERROR( CV_StsUnsupportedFormat, + "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); + } + } +#else + if( (codec_id = av_codec_get_id((const AVCodecTag**)(&codec_bmp_tags), fourcc)) == CODEC_ID_NONE ){ + CV_ERROR( CV_StsUnsupportedFormat, + "FFMPEG could not find a codec matching the given FOURCC code. Use fourcc=CV_FOURCC_DEFAULT for auto selection." ); + } + } +#endif + + // set a few optimal pixel formats for lossless codecs of interest.. + int codec_pix_fmt; + switch (codec_id) { +#if LIBAVCODEC_VERSION_INT>((50<<16)+(1<<8)+0) + case CODEC_ID_JPEGLS: + // BGR24 or GRAY8 depending on is_color... + codec_pix_fmt = input_pix_fmt; + break; +#endif + case CODEC_ID_FFV1: + // no choice... other supported formats are YUV only + codec_pix_fmt = PIX_FMT_RGBA32; + break; + case CODEC_ID_MJPEG: + case CODEC_ID_LJPEG: + codec_pix_fmt = PIX_FMT_YUVJ420P; + break; + case CODEC_ID_RAWVIDEO: + default: + // good for lossy formats, MPEG, etc. + codec_pix_fmt = PIX_FMT_YUV420P; + break; + } + + // TODO -- safe to ignore output audio stream? + video_st = icv_add_video_stream_FFMPEG(oc, codec_id, + frameSize.width, frameSize.height, frameSize.width*frameSize.height*64, + fps, codec_pix_fmt); + + + /* set the output parameters (must be done even if no + parameters). */ + if (av_set_parameters(oc, NULL) < 0) { + CV_ERROR(CV_StsBadArg, "Invalid output format parameters"); + } + + dump_format(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 (!video_st){ + CV_ERROR(CV_StsBadArg, "Couldn't open video stream"); + } + + AVCodec *codec; + AVCodecContext *c; + +#if LIBAVFORMAT_BUILD > 4628 + c = (video_st->codec); +#else + c = &(video_st->codec); +#endif + + c->codec_tag = fourcc; + /* find the video encoder */ + codec = avcodec_find_encoder(c->codec_id); + if (!codec) { + CV_ERROR(CV_StsBadArg, "codec not found"); + } + + /* open the codec */ + if ( (err=avcodec_open(c, codec)) < 0) { + char errtext[256]; + sprintf(errtext, "Could not open codec '%s': %s", codec->name, icvFFMPEGErrStr(err)); + CV_ERROR(CV_StsBadArg, errtext); + } + + outbuf = NULL; + + if (!(oc->oformat->flags & AVFMT_RAWPICTURE)) { + /* allocate output buffer */ + /* assume we will never get codec output with more than 4 bytes per pixel... */ + outbuf_size = frameSize.width*frameSize.height*4; + outbuf = (uint8_t *) av_malloc(outbuf_size); + } + + bool need_color_convert; + need_color_convert = (c->pix_fmt != input_pix_fmt); + + /* allocate the encoded raw picture */ + picture = icv_alloc_picture_FFMPEG(c->pix_fmt, c->width, c->height, need_color_convert); + if (!picture) { + CV_ERROR(CV_StsNoMem, "Could not allocate picture"); + } + + /* 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) { + CV_ERROR(CV_StsNoMem, "Could not allocate picture"); + } + } + + /* open the output file, if needed */ + if (!(fmt->flags & AVFMT_NOFILE)) { + if (url_fopen(&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( oc ); + + + __END__; + + return true; +} + +CvVideoWriter* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps, + CvSize frameSize, int isColor ) +{ + CvVideoWriter_FFMPEG* writer = new CvVideoWriter_FFMPEG; + if( writer->open( filename, fourcc, fps, frameSize, isColor != 0 )) + return writer; + delete writer; + return 0; }