From e0b772a392509bdaaccb512393e5bf04b7c5a12e Mon Sep 17 00:00:00 2001 From: Alexander Reshetnikov Date: Mon, 12 Mar 2012 17:07:54 +0000 Subject: [PATCH] Added a first version of new ffmpeg wrapper --- CMakeLists.txt | 5 + modules/highgui/CMakeLists.txt | 14 + modules/highgui/src/cap_ffmpeg_api.hpp | 13 +- modules/highgui/src/cap_ffmpeg_impl_v2.hpp | 1470 ++++++++++++++++++++++++++++ modules/highgui/src/cap_ffmpeg_v2.cpp | 250 +++++ 5 files changed, 1750 insertions(+), 2 deletions(-) create mode 100755 modules/highgui/src/cap_ffmpeg_impl_v2.hpp create mode 100644 modules/highgui/src/cap_ffmpeg_v2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 96a48c4..3cb2898 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -385,6 +385,7 @@ if(UNIX) set(HAVE_FFMPEG 1) endif() endif() + # Find the bzip2 library because it is required on some systems FIND_LIBRARY(BZIP2_LIBRARIES NAMES bz2 bzip2) if(NOT BZIP2_LIBRARIES) @@ -393,6 +394,10 @@ if(UNIX) endif() endif() + if(HAVE_FFMPEG) + CHECK_MODULE(libavformat>=53 NEW_FFMPEG) + endif() + if(WITH_1394) CHECK_MODULE(libdc1394-2 HAVE_DC1394_2) if(NOT HAVE_DC1394_2) diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index 59505a2..cb3028b 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -53,6 +53,18 @@ set(grfmt_srcs src/bitstrm.cpp ${grfmt_srcs}) source_group("Src\\grfmts" FILES ${grfmt_hdrs} ${grfmt_srcs}) set(highgui_hdrs src/precomp.hpp src/utils.hpp) + +if(NEW_FFMPEG) +set(highgui_srcs + src/cap.cpp + src/cap_images.cpp + src/cap_ffmpeg_v2.cpp + src/loadsave.cpp + src/precomp.cpp + src/utils.cpp + src/window.cpp + ) +else() set(highgui_srcs src/cap.cpp src/cap_images.cpp @@ -62,6 +74,8 @@ set(highgui_srcs src/utils.cpp src/window.cpp ) +endif() + file(GLOB highgui_ext_hdrs "include/opencv2/${name}/*.hpp" "include/opencv2/${name}/*.h") #YV diff --git a/modules/highgui/src/cap_ffmpeg_api.hpp b/modules/highgui/src/cap_ffmpeg_api.hpp index 57d5f19..de9ad67 100644 --- a/modules/highgui/src/cap_ffmpeg_api.hpp +++ b/modules/highgui/src/cap_ffmpeg_api.hpp @@ -26,16 +26,25 @@ enum OPENCV_FFMPEG_API struct CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG(const char* filename); +OPENCV_FFMPEG_API struct CvCapture_FFMPEG_2* cvCreateFileCapture_FFMPEG_2(const char* filename); OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop, double value); +OPENCV_FFMPEG_API int cvSetCaptureProperty_FFMPEG_2(struct CvCapture_FFMPEG_2* cap, + int prop, double value); OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG(struct CvCapture_FFMPEG* cap, int prop); +OPENCV_FFMPEG_API double cvGetCaptureProperty_FFMPEG_2(struct CvCapture_FFMPEG_2* cap, int prop); OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG(struct CvCapture_FFMPEG* cap); -OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(CvCapture_FFMPEG* capture, unsigned char** data, +OPENCV_FFMPEG_API int cvGrabFrame_FFMPEG_2(struct CvCapture_FFMPEG_2* cap); +OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG(struct CvCapture_FFMPEG* capture, unsigned char** data, + int* step, int* width, int* height, int* cn); +OPENCV_FFMPEG_API int cvRetrieveFrame_FFMPEG_2(struct CvCapture_FFMPEG_2* capture, unsigned char** data, int* step, int* width, int* height, int* cn); OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG(struct CvCapture_FFMPEG** cap); - +OPENCV_FFMPEG_API void cvReleaseCapture_FFMPEG_2(struct CvCapture_FFMPEG_2** cap); OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG(const char* filename, int fourcc, double fps, int width, int height, int isColor ); +OPENCV_FFMPEG_API struct CvVideoWriter_FFMPEG_2* cvCreateVideoWriter_FFMPEG_2(const char* filename, + int fourcc, double fps, int width, int height, int isColor ); OPENCV_FFMPEG_API int cvWriteFrame_FFMPEG(struct CvVideoWriter_FFMPEG* writer, const unsigned char* data, int step, int width, int height, int cn, int origin); diff --git a/modules/highgui/src/cap_ffmpeg_impl_v2.hpp b/modules/highgui/src/cap_ffmpeg_impl_v2.hpp new file mode 100755 index 0000000..fa41502 --- /dev/null +++ b/modules/highgui/src/cap_ffmpeg_impl_v2.hpp @@ -0,0 +1,1470 @@ +/*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*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#undef UINT64_C +#define UINT64_C(val) val ## LL +#include +#include +#include + +#ifdef __cplusplus +} +#endif + +#include "cap_ffmpeg_api.hpp" +#include "assert.h" + +#define CALC_FFMPEG_VERSION(a,b,c) ( a<<16 | b<<8 | c ) + +struct Image_FFMPEG +{ + unsigned char* data; + int step; + int width; + int height; + int cn; +}; + +struct CvCapture_FFMPEG_2 +{ + CvCapture_FFMPEG_2(const char* filename); + CvCapture_FFMPEG_2(const CvCapture_FFMPEG_2& mf); + CvCapture_FFMPEG_2& operator=(const CvCapture_FFMPEG_2& mf); + + ~CvCapture_FFMPEG_2(); + + bool open( const char* filename ); + void close(); + bool setProperty(int, double); + double getProperty(int); + bool grabFrame(); + bool retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn); + void init(); + bool reopen(); + + cv::Mat read(); + + void seek(int64_t frame_number); + void seek(double sec); + + int64_t get_total_frames(); + int64_t get_frame_number(); + + private: + AVFormatContext * ic; + AVCodecContext * avcodec_context; + AVCodec * avcodec; + AVFrame * picture; + AVFrame * rgb_picture; + AVStream * video_st; + AVPacket packet; + Image_FFMPEG frame; + + #if defined(HAVE_FFMPEG_SWSCALE) + struct SwsContext *img_convert_ctx; + #endif + + char* filename; + + int video_stream; + int64_t picture_pts; + + size_t width, height; + int64_t frame_number; + + double eps_zero; + + double get_duration_sec(); + double get_fps(); + int get_bitrate(); + + double r2d(AVRational r); + int64_t dts_to_frame_number(int64_t dts); + double dts_to_sec(int64_t dts); +}; + +CvCapture_FFMPEG_2::CvCapture_FFMPEG_2(const char* filename) : +ic(0), avcodec_context(0), avcodec(0), +picture(0), rgb_picture(0), video_stream(-1), +width(0), height(0), frame_number(0), eps_zero(0.000025) +{ + av_register_all(); + + avformat_network_init(); + + // Open video file + avformat_open_input(&ic, filename, NULL, NULL); + + // Find the first video stream + for(int i = 0; i < static_cast(ic->nb_streams); i++) + { + struct AVCodecContext * cc = ic->streams[i]->codec; + // set number of threads + cc->thread_count = 2; + + if(cc->codec_type == AVMEDIA_TYPE_VIDEO && video_stream == -1) + { + AVCodec * codec = avcodec_find_decoder(cc->codec_id); + + if (codec == NULL) + CV_Error(0, "Unsupported codec !!!"); + + avcodec_open2(cc, codec, NULL); + video_stream = i; + break; + } + } + + if(video_stream == -1) + CV_Error(0, "Didn't find a video stream"); + + // Get a pointer to the codec context for the video stream + avcodec_context = ic->streams[video_stream]->codec; + + // Allocate video frame + picture = avcodec_alloc_frame(); +} + +CvCapture_FFMPEG_2::CvCapture_FFMPEG_2(const CvCapture_FFMPEG_2& vr) : + ic(vr.ic), + avcodec_context (vr.avcodec_context), + avcodec(0), + picture(0), + rgb_picture(0), + video_stream(-1), + width(0), height(0), + frame_number(0), + eps_zero(0.000001) {} + +CvCapture_FFMPEG_2& CvCapture_FFMPEG_2::operator=(const CvCapture_FFMPEG_2& mf) +{ + ic = mf.ic; + avcodec_context = mf.avcodec_context; + return *this; +} + +bool CvCapture_FFMPEG_2::open(const char* filename) +{ + CvCapture_FFMPEG_2 cap(filename); + *this = cap; +} + +void CvCapture_FFMPEG_2::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] ) + { + free( rgb_picture->data[0] ); + rgb_picture->data[0] = 0; + } + + // free last packet if exist + if (packet.data) { + av_free_packet (&packet); + } + + init(); +} + +bool CvCapture_FFMPEG_2::grabFrame() +{ + bool valid = false; + static bool bFirstTime = true; + 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; + packet.data = NULL; + } + + if( !ic || !video_st ) + return false; + + // free last packet if exist + if (packet.data != NULL) { + av_free_packet (&packet); + } + + // get the next frame + while (!valid) { + int ret = av_read_frame(ic, &packet); + if (ret == AVERROR(EAGAIN)) + continue; + if (ret < 0) + break; + + if( packet.stream_index != video_stream ) { + av_free_packet (&packet); + continue; + } + + avcodec_decode_video2(video_st->codec, picture, &got_picture, &packet); + + if (got_picture) { + // we have a new picture, so memorize it + picture_pts = packet.pts; + valid = 1; + } + } + + // return if we have a new picture or not + return valid; +} + +bool CvCapture_FFMPEG_2::retrieveFrame(int, unsigned char** data, int* step, int* width, int* height, int* cn) +{ + if( !video_st || !picture->data[0] ) + return false; + +#if !defined(HAVE_FFMPEG_SWSCALE) +#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 +#else + img_convert_ctx = sws_getContext(video_st->codec->width, + video_st->codec->height, + video_st->codec->pix_fmt, + video_st->codec->width, + video_st->codec->height, + PIX_FMT_BGR24, + SWS_BICUBIC, + NULL, NULL, NULL); + + sws_scale(img_convert_ctx, picture->data, + picture->linesize, 0, + video_st->codec->height, + rgb_picture->data, rgb_picture->linesize); + sws_freeContext(img_convert_ctx); +#endif + *data = frame.data; + *step = frame.step; + *width = frame.width; + *height = frame.height; + *cn = frame.cn; + + return true; +} + +void CvCapture_FFMPEG_2::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; + packet.data = NULL; +#if defined(HAVE_FFMPEG_SWSCALE) + img_convert_ctx = 0; +#endif +} + +bool CvCapture_FFMPEG_2::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 +#if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(52, 111, 0) + av_open_input_file(&ic, filename, NULL, 0, NULL); +#else + avformat_open_input(&ic, filename, NULL, NULL); +#endif + + 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 + +#if FF_API_THREAD_INIT + avcodec_thread_init(enc, std::min(get_number_of_cpus(), 16)); +#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; +} + +int64_t CvCapture_FFMPEG_2::get_frame_number() +{ + return frame_number; +} + +cv::Mat CvCapture_FFMPEG_2::read() +{ + int frame_finished = 0; + AVPacket packet; + + int count_errs = 0; + const int max_number_of_attempts = 32; + + while(true) + { + av_read_frame(ic, &packet); + + if(packet.stream_index == video_stream) + { + // Decode video frame + avcodec_decode_video2(avcodec_context, picture, &frame_finished, &packet); + + // Did we get a video frame? + if(frame_finished) + { + rgb_picture = avcodec_alloc_frame(); + + cv::Mat img(static_cast(avcodec_context->height), static_cast(avcodec_context->width), CV_8UC3); + + uint8_t * buffer = reinterpret_cast(img.ptr(0)); + + avpicture_fill(reinterpret_cast(rgb_picture), buffer, PIX_FMT_RGB24, avcodec_context->width, avcodec_context->height); + + width = picture->width; + height = picture->height; + + struct SwsContext * img_convert_ctx = sws_getContext( + width, height, + avcodec_context->pix_fmt, + width, height, + PIX_FMT_BGR24, + SWS_BICUBIC, + NULL, NULL, NULL + ); + + img_convert_ctx = sws_getCachedContext( + img_convert_ctx, + width, height, + avcodec_context->pix_fmt, + width, height, + PIX_FMT_BGR24, + SWS_BICUBIC, + NULL, NULL, NULL + ); + + if (img_convert_ctx == NULL) + CV_Error(0, "Cannot initialize the conversion context!"); + + sws_scale( + img_convert_ctx, + picture->data, + picture->linesize, + 0, height, + rgb_picture->data, + rgb_picture->linesize + ); + + sws_freeContext(img_convert_ctx); + + av_free(rgb_picture); + + frame_number++; + + //std::cout << "cur dts: " << ic->streams[video_stream]->cur_dts << std::endl; + + return img; + } + else + { + count_errs ++; + if (count_errs > max_number_of_attempts) + break; + } + } + else + { + count_errs ++; + if (count_errs > max_number_of_attempts) + break; + } + } + + // Free the packet that was allocated by av_read_frame + av_free_packet(&packet); + + return cv::Mat(); +} + +double CvCapture_FFMPEG_2::r2d(AVRational r) +{ + if (r.num == 0 || r.den == 0) + { + return 0.0; + } + else + { + return static_cast(r.num) / static_cast(r.den); + } +} + +double CvCapture_FFMPEG_2::get_duration_sec() +{ + double sec = static_cast(ic->duration) / static_cast(AV_TIME_BASE); + + if (sec < eps_zero) + { + sec = static_cast(ic->streams[video_stream]->duration) * r2d(ic->streams[video_stream]->time_base); + } + + if (sec < eps_zero) + { + sec = static_cast(static_cast(ic->streams[video_stream]->duration)) * r2d(ic->streams[video_stream]->time_base); + } + return sec; +} + +int CvCapture_FFMPEG_2::get_bitrate() +{ + return ic->bit_rate; +} + +double CvCapture_FFMPEG_2::get_fps() +{ + double fps = r2d(ic->streams[video_stream]->r_frame_rate); + + if (fps < eps_zero) + { + fps = r2d(ic->streams[video_stream]->avg_frame_rate); + } + + // may be this is wrong + if (fps < eps_zero) + { + fps = 1.0 / r2d(ic->streams[video_stream]->codec->time_base); + } + + return fps; +} + +int64_t CvCapture_FFMPEG_2::get_total_frames() +{ + int64_t nbf = ic->streams[video_stream]->nb_frames; + + if (nbf == 0) + { + nbf = static_cast(get_duration_sec() * get_fps()); + } + return nbf; +} + +//#include + +double round(double d) +{ + return std::floor(d + 0.5); +} + +int64_t CvCapture_FFMPEG_2::dts_to_frame_number(int64_t dts) +{ + double sec = dts_to_sec(dts); + return static_cast(get_fps() * sec); +} + +double CvCapture_FFMPEG_2::dts_to_sec(int64_t dts) +{ + return static_cast(dts - ic->streams[video_stream]->start_time) * r2d(ic->streams[video_stream]->time_base); +} + +void CvCapture_FFMPEG_2::seek(int64_t frame_number) +{ + double sec = static_cast(frame_number) / static_cast(get_fps()); + this->frame_number = std::min(frame_number, get_total_frames()); + seek(sec); + /* int64_t dts = dts_to_frame_number(ic->streams[video_stream]->cur_dts); + + if (abs(dts - 2 - frame_number) > 16) + { + double sec = static_cast(frame_number) / static_cast(get_fps()); + int64_t time_stamp = ic->streams[video_stream]->start_time; + double time_base = r2d(ic->streams[video_stream]->time_base); + time_stamp += static_cast(sec / time_base); + av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD); + } + + while(dts - 2 < frame_number) + { + cv::Mat i = read(); + if (i.empty()) + break; + + dts = dts_to_frame_number(ic->streams[video_stream]->cur_dts); + + //std::cout << "cur dts: " << ic->streams[video_stream]->cur_dts << " f: " << dts << std::endl; + } */ +} + +void CvCapture_FFMPEG_2::seek(double sec) +{ + // seek(static_cast(sec * get_fps())); + + int64_t time_stamp = ic->streams[video_stream]->start_time; + double time_base = av_q2d(ic->streams[video_stream]->time_base); + time_stamp += static_cast(sec / time_base); + av_seek_frame(ic, video_stream, time_stamp, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD); +} + +CvCapture_FFMPEG_2::~CvCapture_FFMPEG_2() +{ + avformat_close_input(&ic); +} + +bool CvCapture_FFMPEG_2::setProperty( int property_id, double value ) +{ + if (!video_stream) return false; + + switch( property_id ) + { + case CV_FFMPEG_CAP_PROP_POS_MSEC: + case CV_FFMPEG_CAP_PROP_POS_FRAMES: + case CV_FFMPEG_CAP_PROP_POS_AVI_RATIO: + { + + switch( property_id ) + { + case CV_FFMPEG_CAP_PROP_POS_FRAMES: + seek(value/1000.0); break; + break; + + case CV_FFMPEG_CAP_PROP_POS_MSEC: + seek(value); + break; + + case CV_FFMPEG_CAP_PROP_POS_AVI_RATIO: + seek(value*this->get_bitrate()); + 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 flags = AVSEEK_FLAG_ANY; + if (timestamp < ic->streams[video_stream]->cur_dts) + flags |= AVSEEK_FLAG_BACKWARD; + int ret = av_seek_frame(ic, video_stream, timestamp, flags); + 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; + +} + +#if defined(__APPLE__) +#define AV_NOPTS_VALUE_ ((int64_t)0x8000000000000000LL) +#else +#define AV_NOPTS_VALUE_ ((int64_t)AV_NOPTS_VALUE) +#endif + +double CvCapture_FFMPEG_2::getProperty( int property_id ) +{ + // if( !capture || !video_st || !picture->data[0] ) return 0; + if( !video_stream ) return 0; + + double frameScale = av_q2d (video_st->time_base) * av_q2d (video_st->r_frame_rate); + int64_t timestamp; + timestamp = picture_pts; + + switch( property_id ) + { + case CV_FFMPEG_CAP_PROP_POS_MSEC: + return 1000.0*static_cast(dts_to_sec(frame_number)); + break; + + case CV_FFMPEG_CAP_PROP_POS_FRAMES: + return (double)static_cast(get_frame_number()); + break; + + case CV_FFMPEG_CAP_PROP_POS_AVI_RATIO: + return static_cast(dts_to_frame_number(frame_number))/static_cast(dts_to_sec(frame_number)); + break; + + case CV_FFMPEG_CAP_PROP_FRAME_COUNT: + return (double)static_cast(get_total_frames()); + break; + + case CV_FFMPEG_CAP_PROP_FRAME_WIDTH: + return (double)static_cast(width); + break; + + case CV_FFMPEG_CAP_PROP_FRAME_HEIGHT: + return (double)static_cast(height); + break; + + case CV_FFMPEG_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_FFMPEG_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; +} + +///////////////// FFMPEG CvVideoWriter implementation ////////////////////////// +struct CvVideoWriter_FFMPEG +{ + bool open( const char* filename, int fourcc, + double fps, int width, int height, bool isColor ); + void close(); + bool writeFrame( const unsigned char* data, int step, int width, int height, int cn, int origin ); + + 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; + Image_FFMPEG temp_image; +#if defined(HAVE_FFMPEG_SWSCALE) + struct SwsContext *img_convert_ctx; +#endif +}; + +static const char * icvFFMPEGErrStr(int err) +{ +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) + switch(err) { + case AVERROR_BSF_NOT_FOUND: + return "Bitstream filter not found"; + case AVERROR_DECODER_NOT_FOUND: + return "Decoder not found"; + case AVERROR_DEMUXER_NOT_FOUND: + return "Demuxer not found"; + case AVERROR_ENCODER_NOT_FOUND: + return "Encoder not found"; + case AVERROR_EOF: + return "End of file"; + case AVERROR_EXIT: + return "Immediate exit was requested; the called function should not be restarted"; + case AVERROR_FILTER_NOT_FOUND: + return "Filter not found"; + case AVERROR_INVALIDDATA: + return "Invalid data found when processing input"; + case AVERROR_MUXER_NOT_FOUND: + return "Muxer not found"; + case AVERROR_OPTION_NOT_FOUND: + return "Option not found"; + case AVERROR_PATCHWELCOME: + return "Not yet implemented in FFmpeg, patches welcome"; + case AVERROR_PROTOCOL_NOT_FOUND: + return "Protocol not found"; + case AVERROR_STREAM_NOT_FOUND: + return "Stream not found"; + default: + break; + } +#else + 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; + } +#endif + + 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; + memset(&temp_image, 0, sizeof(temp_image)); +#if defined(HAVE_FFMPEG_SWSCALE) + img_convert_ctx = 0; +#endif +} + +/** + * 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( (PixelFormat) pix_fmt, width, height); + if(alloc){ + picture_buf = (uint8_t *) malloc(size); + if (!picture_buf) + { + av_free(picture); + return NULL; + } + avpicture_fill((AVPicture *)picture, picture_buf, + (PixelFormat) 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, AVMEDIA_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 = AVMEDIA_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=(int)(fps+0.5); + frame_rate_base=1; + while (fabs((double)frame_rate/frame_rate_base) - fps > 0.001){ + frame_rate_base*=10; + frame_rate=(int)(fps*frame_rate_base + 0.5); + } +#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 ) +{ + +#if LIBAVFORMAT_BUILD > 4628 + AVCodecContext * c = video_st->codec; +#else + AVCodecContext * c = &(video_st->codec); +#endif + int out_size; + int ret; + + 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); + + #ifndef PKT_FLAG_KEY + #define PKT_FLAG_KEY AV_PKT_FLAG_KEY + #endif + + 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) return -1; + + return 0; +} + +/// write a frame with FFMPEG +bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int width, int height, int cn, int origin ) +{ + bool ret = false; + + // 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 LIBAVFORMAT_BUILD < 5231 + // It is not needed in the latest versions of the ffmpeg + if( c->codec_id == CODEC_ID_RAWVIDEO && origin != 1 ) + { + if( !temp_image.data ) + { + temp_image.step = (width*cn + 3) & -4; + temp_image.width = width; + temp_image.height = height; + temp_image.cn = cn; + temp_image.data = (unsigned char*)malloc(temp_image.step*temp_image.height); + } + for( int y = 0; y < height; y++ ) + memcpy(temp_image.data + y*temp_image.step, data + (height-1-y)*step, width*cn); + data = temp_image.data; + step = temp_image.step; + } +#else + if( width*cn != step ) + { + if( !temp_image.data ) + { + temp_image.step = width*cn; + temp_image.width = width; + temp_image.height = height; + temp_image.cn = cn; + temp_image.data = (unsigned char*)malloc(temp_image.step*temp_image.height); + } + if (origin == 1) + for( int y = 0; y < height; y++ ) + memcpy(temp_image.data + y*temp_image.step, data + (height-1-y)*step, temp_image.step); + else + for( int y = 0; y < height; y++ ) + memcpy(temp_image.data + y*temp_image.step, data + y*step, temp_image.step); + data = temp_image.data; + step = temp_image.step; + } +#endif + + // check parameters + if (input_pix_fmt == PIX_FMT_BGR24) { + if (cn != 3) { + return false; + } + } + else if (input_pix_fmt == PIX_FMT_GRAY8) { + if (cn != 1) { + return false; + } + } + else { + assert(false); + } + + // check if buffer sizes match, i.e. image has expected format (size, channels, bitdepth, alignment) +/*#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(37<<8)+0) + assert (image->imageSize == avpicture_get_size( (PixelFormat)input_pix_fmt, image->width, image->height )); +#else + assert (image->imageSize == avpicture_get_size( input_pix_fmt, image->width, image->height )); +#endif*/ + + 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 *) data, + (PixelFormat)input_pix_fmt, width, height); + +#if !defined(HAVE_FFMPEG_SWSCALE) + // convert to the color format needed by the codec + if( img_convert((AVPicture *)picture, c->pix_fmt, + (AVPicture *)input_picture, (PixelFormat)input_pix_fmt, + width, height) < 0){ + return false; + } +#else + img_convert_ctx = sws_getContext(width, + height, + (PixelFormat)input_pix_fmt, + c->width, + c->height, + c->pix_fmt, + SWS_BICUBIC, + NULL, NULL, NULL); + + if ( sws_scale(img_convert_ctx, input_picture->data, + input_picture->linesize, 0, + height, + picture->data, picture->linesize) < 0 ) + { + return false; + } + sws_freeContext(img_convert_ctx); +#endif + } + else{ + avpicture_fill((AVPicture *)picture, (uint8_t *) data, + (PixelFormat)input_pix_fmt, width, height); + } + + ret = icv_av_write_frame_FFMPEG( oc, video_st, outbuf, outbuf_size, picture) >= 0; + + 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 + if(picture->data[0]) + free(picture->data[0]); + picture->data[0] = 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) && LIBAVCODEC_VERSION_INT <= ((54<<16)+(5<<8)+0) + url_fclose(oc->pb); + #else + #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0) + url_fclose(&oc->pb); +#endif + #endif + + } + + /* free the stream */ + av_free(oc); + + if( temp_image.data ) + { + free(temp_image.data); + temp_image.data = 0; + } + + init(); +} + +/// Create a video writer object that uses FFMPEG +bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, + double fps, int width, int height, bool is_color ) +{ + CodecID codec_id = CODEC_ID_NONE; + int err, codec_pix_fmt, bitrate_scale=64; + + close(); + + // check arguments + assert (filename); + assert (fps > 0); + assert (width > 0 && height > 0); + + // tell FFMPEG to register codecs + av_register_all (); + + /* auto detect the output format from the name and fourcc code. */ + +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) + fmt = av_guess_format(NULL, filename, NULL); +#else + fmt = guess_format(NULL, filename, NULL); +#endif + + if (!fmt) + return false; + + /* determine optimal pixel format */ + if (is_color) { + input_pix_fmt = PIX_FMT_BGR24; + } + else { + input_pix_fmt = PIX_FMT_GRAY8; + } + + /* Lookup codec_id for given fourcc */ +#if LIBAVCODEC_VERSION_INT<((51<<16)+(49<<8)+0) + if( (codec_id = codec_get_bmp_id( fourcc )) == CODEC_ID_NONE ) + return false; +#else + /* const struct AVCodecTag * tags[] = { codec_bmp_tags, NULL}; + if( (codec_id = av_codec_get_id(tags, fourcc)) == CODEC_ID_NONE ) + return false; */ +#endif + + // alloc memory for context +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) + oc = avformat_alloc_context(); +#else + oc = av_alloc_format_context(); +#endif + 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 */ + + // set a few optimal pixel formats for lossless codecs of interest.. + 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_HUFFYUV: + codec_pix_fmt = PIX_FMT_YUV422P; + break; + case CODEC_ID_MJPEG: + case CODEC_ID_LJPEG: + codec_pix_fmt = PIX_FMT_YUVJ420P; + bitrate_scale = 128; + break; + case CODEC_ID_RAWVIDEO: + codec_pix_fmt = input_pix_fmt; + break; + 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, + width, height, width*height*bitrate_scale, + fps, codec_pix_fmt); + + + /* set the output parameters (must be done even if no + parameters). */ + #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0) + + if (av_set_parameters(oc, NULL) < 0) { + return false; + } + + #endif + +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) + av_dump_format(oc, 0, filename, 1); +#else + dump_format(oc, 0, filename, 1); +#endif + + /* now that all the parameters are set, we can open the audio and + video codecs and allocate the necessary encode buffers */ + if (!video_st){ + return false; + } + + 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) { + return false; + } + + c->bit_rate_tolerance = c->bit_rate; + + /* 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)); + return false; + } + + 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 = width*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) { + return false; + } + + /* if the output format is not our input format, then a temporary + picture of the input format is needed too. It is then converted + to the required output format */ + input_picture = NULL; + if ( need_color_convert ) { + input_picture = icv_alloc_picture_FFMPEG(input_pix_fmt, c->width, c->height, false); + if (!input_picture) { + return false; + } + } + + /* open the output file, if needed */ + #ifndef URL_RDONLY + #define URL_RDONLY 1 + #endif + #ifndef URL_WRONLY + #define URL_WRONLY 2 + #endif + #ifndef URL_RWONLY + #define URL_RWONLY (URL_RDONLY|URL_WRONLY) + #endif + + if (!(fmt->flags & AVFMT_NOFILE)) + { + #if LIBAVCODEC_VERSION_INT <= ((54<<16)+(5<<8)+0) + if (url_fopen(&oc->pb, filename, URL_WRONLY) < 0) + { + return false; + } + #endif + } + + /* write the stream header, if any */ +#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 2, 0) + avformat_write_header(oc, NULL); +#else + av_write_header( oc ); +#endif + return true; +} + + +CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int fourcc, double fps, + int width, int height, int isColor ) +{ + CvVideoWriter_FFMPEG* writer = (CvVideoWriter_FFMPEG*)malloc(sizeof(*writer)); + writer->init(); + if( writer->open( filename, fourcc, fps, width, height, isColor != 0 )) + return writer; + writer->close(); + free(writer); + return 0; +} + + +void cvReleaseVideoWriter_FFMPEG( CvVideoWriter_FFMPEG** writer ) +{ + if( writer && *writer ) + { + (*writer)->close(); + free(*writer); + *writer = 0; + } +} + + +int cvWriteFrame_FFMPEG( CvVideoWriter_FFMPEG* writer, + const unsigned char* data, int step, + int width, int height, int cn, int origin) +{ + return writer->writeFrame(data, step, width, height, cn, origin); +} + +int cvSetCaptureProperty_FFMPEG_2(CvCapture_FFMPEG_2* capture, int prop_id, double value) +{ + return capture->setProperty(prop_id, value); +} + +double cvGetCaptureProperty_FFMPEG_2(CvCapture_FFMPEG_2* capture, int prop_id) +{ + return capture->getProperty(prop_id); +} + +int cvGrabFrame_FFMPEG_2(CvCapture_FFMPEG_2* capture) +{ + return capture->grabFrame(); +} + +int cvRetrieveFrame_FFMPEG_2(CvCapture_FFMPEG_2* capture, unsigned char** data, int* step, int* width, int* height, int* cn) +{ + return capture->retrieveFrame(0, data, step, width, height, cn); +} + +CvCapture_FFMPEG_2* cvCreateFileCapture_FFMPEG_2( const char* filename ) +{ + CvCapture_FFMPEG_2* capture = (CvCapture_FFMPEG_2*)malloc(sizeof(*capture)); + capture->init(); + if( capture->open( filename )) + return capture; + capture->close(); + free(capture); + return 0; +} + +void cvReleaseCapture_FFMPEG_2(CvCapture_FFMPEG_2** capture) +{ + if( capture && *capture ) + { + (*capture)->close(); + free(*capture); + *capture = 0; + } +} + diff --git a/modules/highgui/src/cap_ffmpeg_v2.cpp b/modules/highgui/src/cap_ffmpeg_v2.cpp new file mode 100644 index 0000000..2ae7bc8 --- /dev/null +++ b/modules/highgui/src/cap_ffmpeg_v2.cpp @@ -0,0 +1,250 @@ +/*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 "precomp.hpp" + +#ifdef HAVE_FFMPEG +#include "cap_ffmpeg_impl_v2.hpp" +#else +#include "cap_ffmpeg_api.hpp" +#endif + +static CvCreateFileCapture_Plugin icvCreateFileCapture_FFMPEG_p = 0; +static CvReleaseCapture_Plugin icvReleaseCapture_FFMPEG_p = 0; +static CvGrabFrame_Plugin icvGrabFrame_FFMPEG_p = 0; +static CvRetrieveFrame_Plugin icvRetrieveFrame_FFMPEG_p = 0; +static CvSetCaptureProperty_Plugin icvSetCaptureProperty_FFMPEG_p = 0; +static CvGetCaptureProperty_Plugin icvGetCaptureProperty_FFMPEG_p = 0; +static CvCreateVideoWriter_Plugin icvCreateVideoWriter_FFMPEG_p = 0; +static CvReleaseVideoWriter_Plugin icvReleaseVideoWriter_FFMPEG_p = 0; +static CvWriteFrame_Plugin icvWriteFrame_FFMPEG_p = 0; + +static void +icvInitFFMPEG(void) +{ + static int ffmpegInitialized = 0; + if( !ffmpegInitialized ) + { + #if defined WIN32 || defined _WIN32 + const char* module_name = "opencv_ffmpeg" + #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) + "_64" + #endif + ".dll"; + + static HMODULE icvFFOpenCV = LoadLibrary( module_name ); + if( icvFFOpenCV ) + { + icvCreateFileCapture_FFMPEG_p = + (CvCreateFileCapture_Plugin)GetProcAddress(icvFFOpenCV, "cvCreateFileCapture_FFMPEG_2"); + icvReleaseCapture_FFMPEG_p = + (CvReleaseCapture_Plugin)GetProcAddress(icvFFOpenCV, "cvReleaseCapture_FFMPEG_2"); + icvGrabFrame_FFMPEG_p = + (CvGrabFrame_Plugin)GetProcAddress(icvFFOpenCV, "cvGrabFrame_FFMPEG_2"); + icvRetrieveFrame_FFMPEG_p = + (CvRetrieveFrame_Plugin)GetProcAddress(icvFFOpenCV, "cvRetrieveFrame_FFMPEG_2"); + icvSetCaptureProperty_FFMPEG_p = + (CvSetCaptureProperty_Plugin)GetProcAddress(icvFFOpenCV, "cvSetCaptureProperty_FFMPEG_2"); + icvGetCaptureProperty_FFMPEG_p = + (CvGetCaptureProperty_Plugin)GetProcAddress(icvFFOpenCV, "cvGetCaptureProperty_FFMPEG_2"); + icvCreateVideoWriter_FFMPEG_p = + (CvCreateVideoWriter_Plugin)GetProcAddress(icvFFOpenCV, "cvCreateVideoWriter_FFMPEG"); + icvReleaseVideoWriter_FFMPEG_p = + (CvReleaseVideoWriter_Plugin)GetProcAddress(icvFFOpenCV, "cvReleaseVideoWriter_FFMPEG"); + icvWriteFrame_FFMPEG_p = + (CvWriteFrame_Plugin)GetProcAddress(icvFFOpenCV, "cvWriteFrame_FFMPEG"); + +#if 0 + if( icvCreateFileCapture_FFMPEG_p != 0 && + icvReleaseCapture_FFMPEG_p != 0 && + icvGrabFrame_FFMPEG_p != 0 && + icvRetrieveFrame_FFMPEG_p != 0 && + icvSetCaptureProperty_FFMPEG_p != 0 && + icvGetCaptureProperty_FFMPEG_p != 0 && + icvCreateVideoWriter_FFMPEG_p != 0 && + icvReleaseVideoWriter_FFMPEG_p != 0 && + icvWriteFrame_FFMPEG_p != 0 ) + { + printf("Successfully initialized ffmpeg plugin!\n"); + } + else + { + printf("Failed to load FFMPEG plugin: module handle=%p\n", icvFFOpenCV); + } +#endif + } + #elif defined HAVE_FFMPEG + icvCreateFileCapture_FFMPEG_p = (CvCreateFileCapture_Plugin)cvCreateFileCapture_FFMPEG_2; + icvReleaseCapture_FFMPEG_p = (CvReleaseCapture_Plugin)cvReleaseCapture_FFMPEG_2; + icvGrabFrame_FFMPEG_p = (CvGrabFrame_Plugin)cvGrabFrame_FFMPEG_2; + icvRetrieveFrame_FFMPEG_p = (CvRetrieveFrame_Plugin)cvRetrieveFrame_FFMPEG_2; + icvSetCaptureProperty_FFMPEG_p = (CvSetCaptureProperty_Plugin)cvSetCaptureProperty_FFMPEG_2; + icvGetCaptureProperty_FFMPEG_p = (CvGetCaptureProperty_Plugin)cvGetCaptureProperty_FFMPEG_2; + icvCreateVideoWriter_FFMPEG_p = (CvCreateVideoWriter_Plugin)cvCreateVideoWriter_FFMPEG; + icvReleaseVideoWriter_FFMPEG_p = (CvReleaseVideoWriter_Plugin)cvReleaseVideoWriter_FFMPEG; + icvWriteFrame_FFMPEG_p = (CvWriteFrame_Plugin)cvWriteFrame_FFMPEG; + #endif + + ffmpegInitialized = 1; + } +} + + +class CvCapture_FFMPEG_proxy : public CvCapture +{ +public: + CvCapture_FFMPEG_proxy() { ffmpegCapture = 0; } + virtual ~CvCapture_FFMPEG_proxy() { close(); } + + virtual double getProperty(int propId) + { + return ffmpegCapture ? icvGetCaptureProperty_FFMPEG_p(ffmpegCapture, propId) : 0; + } + virtual bool setProperty(int propId, double value) + { + return ffmpegCapture ? icvSetCaptureProperty_FFMPEG_p(ffmpegCapture, propId, value)!=0 : false; + } + virtual bool grabFrame() + { + return ffmpegCapture ? icvGrabFrame_FFMPEG_p(ffmpegCapture)!=0 : false; + } + virtual IplImage* retrieveFrame(int) + { + unsigned char* data = 0; + int step=0, width=0, height=0, cn=0; + + if(!ffmpegCapture || + !icvRetrieveFrame_FFMPEG_p(ffmpegCapture, &data, &step, &width, &height, &cn)) + return 0; + cvInitImageHeader(&frame, cvSize(width, height), 8, cn); + cvSetData(&frame, data, step); + return &frame; + } + virtual bool open( const char* filename ) + { + close(); + + icvInitFFMPEG(); + if( !icvCreateFileCapture_FFMPEG_p ) + return false; + ffmpegCapture = icvCreateFileCapture_FFMPEG_p( filename ); + return ffmpegCapture != 0; + } + virtual void close() + { + if( ffmpegCapture && icvReleaseCapture_FFMPEG_p ) + icvReleaseCapture_FFMPEG_p( &ffmpegCapture ); + assert( ffmpegCapture == 0 ); + ffmpegCapture = 0; + } + +protected: + void* ffmpegCapture; + IplImage frame; +}; + + +CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char * filename) +{ + CvCapture_FFMPEG_proxy* result = new CvCapture_FFMPEG_proxy; + if( result->open( filename )) + return result; + delete result; +#if defined WIN32 || defined _WIN32 + return cvCreateFileCapture_VFW(filename); +#else + return 0; +#endif +} + + +class CvVideoWriter_FFMPEG_proxy : public CvVideoWriter +{ +public: + CvVideoWriter_FFMPEG_proxy() { ffmpegWriter = 0; } + virtual ~CvVideoWriter_FFMPEG_proxy() { close(); } + + virtual bool writeFrame( const IplImage* image ) + { + if(!ffmpegWriter) + return false; + CV_Assert(image->depth == 8); + + return icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image->imageData, + image->widthStep, image->width, image->height, image->nChannels, image->origin) !=0; + } + virtual bool open( const char* filename, int fourcc, double fps, CvSize frameSize, bool isColor ) + { + close(); + icvInitFFMPEG(); + if( !icvCreateVideoWriter_FFMPEG_p ) + return false; + ffmpegWriter = icvCreateVideoWriter_FFMPEG_p( filename, fourcc, fps, frameSize.width, frameSize.height, isColor ); + return ffmpegWriter != 0; + } + + virtual void close() + { + if( ffmpegWriter && icvReleaseVideoWriter_FFMPEG_p ) + icvReleaseVideoWriter_FFMPEG_p( &ffmpegWriter ); + assert( ffmpegWriter == 0 ); + ffmpegWriter = 0; + } + +protected: + void* ffmpegWriter; +}; + + +CvVideoWriter* cvCreateVideoWriter_FFMPEG_proxy( const char* filename, int fourcc, + double fps, CvSize frameSize, int isColor ) +{ + CvVideoWriter_FFMPEG_proxy* result = new CvVideoWriter_FFMPEG_proxy; + + if( result->open( filename, fourcc, fps, frameSize, isColor != 0 )) + return result; + delete result; +#if defined WIN32 || defined _WIN32 + return cvCreateVideoWriter_VFW(filename, fourcc, fps, frameSize, isColor); +#else + return 0; +#endif +} -- 2.7.4