+++ /dev/null
-/*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"
-
-#if defined _MSC_VER && _MSC_VER >= 1200
-#pragma warning( disable: 4244 4510 4512 4610 )
-#endif
-
-extern "C" {
-#ifndef WIN32
-#define INT64_C
-#define __STDC_CONSTANT_MACROS
-#include <stdint.h>
-#include <errno.h>
-#endif
-
-#include <ffmpeg/avformat.h>
-#include <ffmpeg/avcodec.h>
-}
-
-#if defined _MSC_VER && _MSC_VER >= 1200
-#pragma warning( default: 4244 4510 4512 4610 )
-#endif
-
-#ifdef NDEBUG
-#define CV_WARN(message)
-#else
-#define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
-#endif
-
-
-#ifndef MKTAG
-#define MKTAG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
-#endif
-
-
-
-
-char * FOURCC2str( int fourcc )
-{
- char * mystr=(char*)malloc(5);
- 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;
-}
-
-
-// required to look up the correct codec ID depending on the FOURCC code,
-// this is just a snipped from the file riff.c from ffmpeg/libavformat
-typedef struct AVCodecTag {
- int id;
- unsigned int tag;
-} AVCodecTag;
-
-const AVCodecTag codec_bmp_tags[] = {
- { CODEC_ID_H264, MKTAG('H', '2', '6', '4') },
- { CODEC_ID_H264, MKTAG('h', '2', '6', '4') },
- { CODEC_ID_H264, MKTAG('X', '2', '6', '4') },
- { CODEC_ID_H264, MKTAG('x', '2', '6', '4') },
- { CODEC_ID_H264, MKTAG('a', 'v', 'c', '1') },
- { CODEC_ID_H264, MKTAG('V', 'S', 'S', 'H') },
-
- { CODEC_ID_H263, MKTAG('H', '2', '6', '3') },
- { CODEC_ID_H263P, MKTAG('H', '2', '6', '3') },
- { CODEC_ID_H263I, MKTAG('I', '2', '6', '3') }, /* intel h263 */
- { CODEC_ID_H261, MKTAG('H', '2', '6', '1') },
-
- /* added based on MPlayer */
- { CODEC_ID_H263P, MKTAG('U', '2', '6', '3') },
- { CODEC_ID_H263P, MKTAG('v', 'i', 'v', '1') },
-
- { CODEC_ID_MPEG4, MKTAG('F', 'M', 'P', '4') },
- { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') },
- { CODEC_ID_MPEG4, MKTAG('D', 'X', '5', '0') },
- { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') },
- { CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') },
- { CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') },
- { CODEC_ID_MPEG4, MKTAG(0x04, 0, 0, 0) }, /* some broken avi use this */
-
- /* added based on MPlayer */
- { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', '1') },
- { CODEC_ID_MPEG4, MKTAG('B', 'L', 'Z', '0') },
- { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') },
- { CODEC_ID_MPEG4, MKTAG('U', 'M', 'P', '4') },
- { CODEC_ID_MPEG4, MKTAG('W', 'V', '1', 'F') },
- { CODEC_ID_MPEG4, MKTAG('S', 'E', 'D', 'G') },
-
- { CODEC_ID_MPEG4, MKTAG('R', 'M', 'P', '4') },
-
- { CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '3') }, /* default signature when using MSMPEG4 */
- { CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') },
-
- /* added based on MPlayer */
- { CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', 'G', '3') },
- { CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '5') },
- { CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '6') },
- { CODEC_ID_MSMPEG4V3, MKTAG('D', 'I', 'V', '4') },
- { CODEC_ID_MSMPEG4V3, MKTAG('A', 'P', '4', '1') },
- { CODEC_ID_MSMPEG4V3, MKTAG('C', 'O', 'L', '1') },
- { CODEC_ID_MSMPEG4V3, MKTAG('C', 'O', 'L', '0') },
-
- { CODEC_ID_MSMPEG4V2, MKTAG('M', 'P', '4', '2') },
-
- /* added based on MPlayer */
- { CODEC_ID_MSMPEG4V2, MKTAG('D', 'I', 'V', '2') },
-
- { CODEC_ID_MSMPEG4V1, MKTAG('M', 'P', 'G', '4') },
-
- { CODEC_ID_WMV1, MKTAG('W', 'M', 'V', '1') },
-
- /* added based on MPlayer */
- { CODEC_ID_WMV2, MKTAG('W', 'M', 'V', '2') },
- { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'd') },
- { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'h', 'd') },
- { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 's', 'l') },
- { CODEC_ID_DVVIDEO, MKTAG('d', 'v', '2', '5') },
- { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '1') },
- { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'g', '2') },
- { CODEC_ID_MPEG2VIDEO, MKTAG('m', 'p', 'g', '2') },
- { CODEC_ID_MPEG2VIDEO, MKTAG('M', 'P', 'E', 'G') },
- { CODEC_ID_MPEG1VIDEO, MKTAG('P', 'I', 'M', '1') },
- { CODEC_ID_MPEG1VIDEO, MKTAG('V', 'C', 'R', '2') },
- { CODEC_ID_MPEG1VIDEO, 0x10000001 },
- { CODEC_ID_MPEG2VIDEO, 0x10000002 },
- { CODEC_ID_MPEG2VIDEO, MKTAG('D', 'V', 'R', ' ') },
- { CODEC_ID_MPEG2VIDEO, MKTAG('M', 'M', 'E', 'S') },
- { CODEC_ID_MJPEG, MKTAG('M', 'J', 'P', 'G') },
- { CODEC_ID_MJPEG, MKTAG('L', 'J', 'P', 'G') },
- { CODEC_ID_LJPEG, MKTAG('L', 'J', 'P', 'G') },
- { CODEC_ID_MJPEG, MKTAG('J', 'P', 'G', 'L') }, /* Pegasus lossless JPEG */
- { CODEC_ID_MJPEG, MKTAG('M', 'J', 'L', 'S') }, /* JPEG-LS custom FOURCC for avi - decoder */
- { CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') },
- { CODEC_ID_MJPEG, MKTAG('I', 'J', 'P', 'G') },
- { CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') },
- { CODEC_ID_HUFFYUV, MKTAG('H', 'F', 'Y', 'U') },
- { CODEC_ID_FFVHUFF, MKTAG('F', 'F', 'V', 'H') },
- { CODEC_ID_CYUV, MKTAG('C', 'Y', 'U', 'V') },
- { CODEC_ID_RAWVIDEO, 0 },
- { CODEC_ID_RAWVIDEO, MKTAG('I', '4', '2', '0') },
- { CODEC_ID_RAWVIDEO, MKTAG('Y', 'U', 'Y', '2') },
- { CODEC_ID_RAWVIDEO, MKTAG('Y', '4', '2', '2') },
- { CODEC_ID_RAWVIDEO, MKTAG('Y', 'V', '1', '2') },
- { CODEC_ID_RAWVIDEO, MKTAG('U', 'Y', 'V', 'Y') },
- { CODEC_ID_RAWVIDEO, MKTAG('I', 'Y', 'U', 'V') },
- { CODEC_ID_RAWVIDEO, MKTAG('Y', '8', '0', '0') },
- { CODEC_ID_RAWVIDEO, MKTAG('H', 'D', 'Y', 'C') },
- { CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '1') },
- { CODEC_ID_INDEO3, MKTAG('I', 'V', '3', '2') },
- { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') },
- { CODEC_ID_VP3, MKTAG('V', 'P', '3', '0') },
- { CODEC_ID_ASV1, MKTAG('A', 'S', 'V', '1') },
- { CODEC_ID_ASV2, MKTAG('A', 'S', 'V', '2') },
- { CODEC_ID_VCR1, MKTAG('V', 'C', 'R', '1') },
- { CODEC_ID_FFV1, MKTAG('F', 'F', 'V', '1') },
- { CODEC_ID_XAN_WC4, MKTAG('X', 'x', 'a', 'n') },
- { CODEC_ID_MSRLE, MKTAG('m', 'r', 'l', 'e') },
- { CODEC_ID_MSRLE, MKTAG(0x1, 0x0, 0x0, 0x0) },
- { CODEC_ID_MSVIDEO1, MKTAG('M', 'S', 'V', 'C') },
- { CODEC_ID_MSVIDEO1, MKTAG('m', 's', 'v', 'c') },
- { CODEC_ID_MSVIDEO1, MKTAG('C', 'R', 'A', 'M') },
- { CODEC_ID_MSVIDEO1, MKTAG('c', 'r', 'a', 'm') },
- { CODEC_ID_MSVIDEO1, MKTAG('W', 'H', 'A', 'M') },
- { CODEC_ID_MSVIDEO1, MKTAG('w', 'h', 'a', 'm') },
- { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') },
- { CODEC_ID_TRUEMOTION1, MKTAG('D', 'U', 'C', 'K') },
- { CODEC_ID_MSZH, MKTAG('M', 'S', 'Z', 'H') },
- { CODEC_ID_ZLIB, MKTAG('Z', 'L', 'I', 'B') },
- { CODEC_ID_SNOW, MKTAG('S', 'N', 'O', 'W') },
- { CODEC_ID_4XM, MKTAG('4', 'X', 'M', 'V') },
- { CODEC_ID_FLV1, MKTAG('F', 'L', 'V', '1') },
- { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') },
- { CODEC_ID_TSCC, MKTAG('t', 's', 'c', 'c') },
- { CODEC_ID_ULTI, MKTAG('U', 'L', 'T', 'I') },
- { CODEC_ID_VIXL, MKTAG('V', 'I', 'X', 'L') },
- { CODEC_ID_QPEG, MKTAG('Q', 'P', 'E', 'G') },
- { CODEC_ID_QPEG, MKTAG('Q', '1', '.', '0') },
- { CODEC_ID_QPEG, MKTAG('Q', '1', '.', '1') },
- { CODEC_ID_WMV3, MKTAG('W', 'M', 'V', '3') },
- { CODEC_ID_LOCO, MKTAG('L', 'O', 'C', 'O') },
- { CODEC_ID_THEORA, MKTAG('t', 'h', 'e', 'o') },
-#if LIBAVCODEC_VERSION_INT>0x000409
- { 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') },
-#endif
-#if LIBAVCODEC_VERSION_INT>((51<<16)+(11<<8)+0)
- { CODEC_ID_VP5, MKTAG('V', 'P', '5', '0') },
- { CODEC_ID_VP6, MKTAG('V', 'P', '6', '0') },
- { CODEC_ID_VP6, MKTAG('V', 'P', '6', '1') },
- { CODEC_ID_VP6, MKTAG('V', 'P', '6', '2') },
- { CODEC_ID_VP6F, MKTAG('V', 'P', '6', 'F') },
- { CODEC_ID_JPEG2000, MKTAG('M', 'J', '2', 'C') },
- { CODEC_ID_VMNC, MKTAG('V', 'M', 'n', 'c') },
-#endif
-#if LIBAVCODEC_VERSION_INT>=((51<<16)+(49<<8)+0)
-// this tag seems not to exist in older versions of FFMPEG
- { CODEC_ID_TARGA, MKTAG('t', 'g', 'a', ' ') },
-#endif
- { CODEC_ID_NONE, 0 },
-};
-
-
-class CvCapture_FFMPEG : public CvCapture
-{
-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;
- AVStream * video_st;
- AVFrame * picture;
- int64_t picture_pts;
- AVFrame rgb_picture;
- IplImage frame;
-/*
- '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;
-};
-
-
-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;
-}
-
-
-void CvCapture_FFMPEG::close()
-{
- if( picture )
- av_free(picture);
-
- if( video_st )
- {
-#if LIBAVFORMAT_BUILD > 4628
- avcodec_close( video_st->codec );
-#else
- avcodec_close( &video_st->codec );
-#endif
- video_st = NULL;
- }
-
- if( ic )
- {
- av_close_input_file(ic);
- ic = NULL;
- }
-
- if( rgb_picture.data[0] )
- cvFree( &rgb_picture.data[0] );
-
- init();
-}
-
-
-/*
- Used to reopen a video if the slower fallback function for seeking is used.
-*/
-bool CvCapture_FFMPEG::reopen()
-{
- if ( filename==NULL ) return false;
-
-#if LIBAVFORMAT_BUILD > 4628
- avcodec_close( video_st->codec );
-#else
- avcodec_close( &video_st->codec );
-#endif
- av_close_input_file(ic);
-
- // reopen video
- av_open_input_file(&ic, filename, NULL, 0, NULL);
- av_find_stream_info(ic);
-#if LIBAVFORMAT_BUILD > 4628
- AVCodecContext *enc = ic->streams[video_stream]->codec;
-#else
- AVCodecContext *enc = &ic->streams[video_stream]->codec;
-#endif
- AVCodec *codec = avcodec_find_decoder(enc->codec_id);
- avcodec_open(enc, codec);
- video_st = ic->streams[video_stream];
-
- // reset framenumber to zero
- picture_pts=0;
-
- return true;
-}
-
-
-
-bool CvCapture_FFMPEG::open( const char* _filename )
-{
- unsigned i;
- bool valid = false;
-
- close();
-
- /* register all codecs, demux and protocols */
- av_register_all();
-
-#ifndef _DEBUG
- // av_log_level = AV_LOG_QUIET;
-#endif
-
- int err = av_open_input_file(&ic, _filename, NULL, 0, NULL);
- if (err < 0) {
- CV_WARN("Error opening file");
- goto exit_func;
- }
- 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
-
- 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_stream >= 0) valid = true;
-
- // perform check if source is seekable via ffmpeg's seek function av_seek_frame(...)
- err = av_seek_frame(ic, video_stream, 10, 0);
- if (err < 0)
- {
- filename=(char*)malloc(strlen(_filename)+1);
- strcpy(filename, _filename);
- // reopen videofile to 'seek' back to first frame
- reopen();
- }
- else
- {
- // seek seems to work, so we don't need the filename,
- // but we still need to seek back to filestart
- filename=NULL;
- av_seek_frame(ic, video_stream, 0, 0);
- }
-exit_func:
-
- if( !valid )
- close();
-
- return valid;
-}
-
-
-bool CvCapture_FFMPEG::grabFrame()
-{
- bool valid = false;
- 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( !ic || !video_st )
- return false;
-
- // free last packet if exist
- if (pkt.data != NULL) {
- av_free_packet (&pkt);
- }
-
- // get the next frame
- while (!valid && (av_read_frame(ic, &pkt) >= 0)) {
- if( pkt.stream_index != video_stream ) continue;
-#if LIBAVFORMAT_BUILD > 4628
- avcodec_decode_video(video_st->codec,
- picture, &got_picture,
- pkt.data, pkt.size);
-#else
- 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
- picture_pts = pkt.pts;
- valid = 1;
- }
- }
-
- // return if we have a new picture or not
- return valid;
-}
-
-
-IplImage* CvCapture_FFMPEG::retrieveFrame()
-{
- if( !video_st || !picture->data[0] )
- return 0;
-
-
-#if LIBAVFORMAT_BUILD > 4628
- 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*)&rgb_picture, PIX_FMT_BGR24,
- (AVPicture*)picture,
- video_st->codec.pix_fmt,
- video_st->codec.width,
- video_st->codec.height );
-#endif
- return &frame;
-}
-
-
-double CvCapture_FFMPEG::getProperty( int property_id )
-{
- // if( !capture || !video_st || !picture->data[0] ) return 0;
- if( !video_st ) return 0;
-
-
- int64_t timestamp;
- timestamp = picture_pts;
-
- switch( property_id )
- {
- case CV_CAP_PROP_POS_MSEC:
- // if(ic->start_time != static_cast<double>(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(video_st->cur_dts != static_cast<double>(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(ic->start_time != static_cast<double>(AV_NOPTS_VALUE) && ic->duration != static_cast<double>(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)frame.width;
- break;
- case CV_CAP_PROP_FRAME_HEIGHT:
- return (double)frame.height;
- break;
- case CV_CAP_PROP_FPS:
-#if LIBAVCODEC_BUILD > 4753
- return av_q2d (video_st->r_frame_rate);
-#else
- 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)video_st->codec->codec_tag;
-#else
- return (double)video_st->codec.codec_tag;
-#endif
- break;
- }
- return 0;
-}
-
-
-
-// this is a VERY slow fallback function, ONLY used if ffmpeg's av_seek_frame delivers no correct result!
-bool CvCapture_FFMPEG::slowSeek( int framenumber )
-{
- if ( framenumber>picture_pts )
- {
- while ( picture_pts<framenumber )
- if ( !grabFrame() ) return false;
- }
- else if ( framenumber<picture_pts )
- {
- reopen();
- while ( picture_pts<framenumber )
- if ( !grabFrame() ) return false;
- }
- return true;
-}
-
-
-bool CvCapture_FFMPEG::setProperty( int property_id, double value )
-{
- 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:
- {
- int64_t timestamp = 0;
- AVRational time_base;
- switch( property_id )
- {
- case CV_CAP_PROP_POS_FRAMES:
- timestamp=(int64_t)value;
- if(ic->start_time != AV_NOPTS_VALUE)
- timestamp += ic->start_time;
- break;
-
- 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(ic->start_time != AV_NOPTS_VALUE)
- timestamp += ic->start_time;
- break;
-
- 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 ( filename )
- {
- // ffmpeg's seek doesn't work...
- if (!slowSeek((int)timestamp))
- {
- fprintf(stderr, "HIGHGUI ERROR: AVI: could not (slow) seek to position %0.3f\n",
- (double)timestamp / AV_TIME_BASE);
- return false;
- }
- }
- else
- {
- 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 false;
- }
- }
- picture_pts=(int64_t)value;
- }
- break;
-
- default:
- return false;
- }
-
- return true;
-}
-
-
-
-CvCapture* cvCreateFileCapture_FFMPEG( const char* filename )
-{
- CvCapture_FFMPEG* capture = new CvCapture_FFMPEG;
- if( capture->open( filename ))
- return capture;
- delete capture;
- return 0;
-}
-
-
-///////////////// FFMPEG CvVideoWriter implementation //////////////////////////
-class CvVideoWriter_FFMPEG : public CvVideoWriter
-{
-public:
- CvVideoWriter_FFMPEG() { init(); }
- virtual ~CvVideoWriter_FFMPEG() { close(); }
-
- virtual bool open( const char* filename, int fourcc,
- double fps, CvSize frameSize, bool isColor );
- virtual void close();
- virtual bool writeFrame( const IplImage* image );
-
-protected:
- void init();
-
- 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;
- IplImage * temp_image;
-};
-
-static const char * icvFFMPEGErrStr(int err)
-{
- switch(err) {
- case AVERROR_NUMEXPECTED:
- return "Incorrect filename syntax";
- case AVERROR_INVALIDDATA:
- return "Invalid data in header";
- case AVERROR_NOFMT:
- return "Unknown format";
- case AVERROR_IO:
- return "I/O error occurred";
- case AVERROR_NOMEM:
- return "Memory allocation error";
- default:
- break;
- }
- return "Unspecified error";
-}
-
-/* function internal to FFMPEG (libavformat/riff.c) to lookup codec id by fourcc tag*/
-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
- */
-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 to the container */
-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) {
- CV_WARN("Could not allocate stream");
- return NULL;
- }
-
-#if LIBAVFORMAT_BUILD > 4628
- c = st->codec;
-#else
- c = &(st->codec);
-#endif
-
-#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;
-#endif
-
- if(codec_id != CODEC_ID_NONE){
- c->codec_id = codec_id;
- }
-
- //if(codec_tag) c->codec_tag=codec_tag;
- 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 = {frame_rate, frame_rate_base};
- 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;
- }
- }
- 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) {
- c->max_b_frames = 2;
- }
- if (c->codec_id == CODEC_ID_MPEG1VIDEO || c->codec_id == CODEC_ID_MSMPEG4V3){
- /* 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 */
- /* avoid FFMPEG warning 'clipping 1 dct coefficients...' */
- c->mb_decision=2;
- }
-#if LIBAVCODEC_VERSION_INT>0x000409
- // some formats want stream headers to be seperate
- if(oc->oformat->flags & AVFMT_GLOBALHEADER)
- {
- c->flags |= CODEC_FLAG_GLOBAL_HEADER;
- }
-#endif
-
- return st;
-}
-
-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
-bool CvVideoWriter_FFMPEG::writeFrame( const IplImage * image )
-{
- bool ret = false;
-
- CV_FUNCNAME("CvVideoWriter_FFMPEG::writerFrame");
-
- __BEGIN__;
-
- // typecast from opaque data type to implemented struct
-#if LIBAVFORMAT_BUILD > 4628
- AVCodecContext *c = video_st->codec;
-#else
- AVCodecContext *c = &(video_st->codec);
-#endif
-
- if( c->codec_id == CODEC_ID_RAWVIDEO && image->origin != IPL_ORIGIN_BL )
- {
- if( !temp_image )
- temp_image = cvCreateImage( cvGetSize(image),
- image->depth, image->nChannels );
- cvFlip( image, temp_image, 0 );
- image = temp_image;
- }
-
- // check parameters
- 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 (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( input_pix_fmt, image->width, image->height ));
-
- if ( c->pix_fmt != input_pix_fmt ) {
- assert( input_picture );
- // let input_picture point to the raw data buffer of 'image'
- 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 *)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 *)picture, (uint8_t *) image->imageData,
- input_pix_fmt, image->width, image->height);
- }
-
- 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
-void CvVideoWriter_FFMPEG::close()
-{
- unsigned i;
-
- // nothing to do if already released
- if ( !picture )
- return;
-
- /* 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(oc);
-
- // free pictures
-#if LIBAVFORMAT_BUILD > 4628
- if( video_st->codec->pix_fmt != input_pix_fmt){
-#else
- if( video_st->codec.pix_fmt != input_pix_fmt){
-#endif
- cvFree(&(picture->data[0]));
- }
- av_free(picture);
-
- if (input_picture) {
- av_free(input_picture);
- }
-
- /* close codec */
-#if LIBAVFORMAT_BUILD > 4628
- avcodec_close(video_st->codec);
-#else
- avcodec_close(&(video_st->codec));
-#endif
-
- av_free(outbuf);
-
- /* free the streams */
- for(i = 0; i < oc->nb_streams; i++) {
- av_freep(&oc->streams[i]->codec);
- av_freep(&oc->streams[i]);
- }
-
- if (!(fmt->flags & AVFMT_NOFILE)) {
- /* close the output file */
-
-
-#if LIBAVCODEC_VERSION_INT==((51<<16)+(49<<8)+0)
- url_fclose(oc->pb);
-#else
- url_fclose(&oc->pb);
-#endif
-
- }
-
- /* free the stream */
- av_free(oc);
-
- 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__;
-
- close();
-
- // 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;
-}