diff --git a/modules/highgui/src/cap_ffmpeg.cpp b/modules/highgui/src/cap_ffmpeg.cpp index 0a2f0a308d..657502acf4 100644 --- a/modules/highgui/src/cap_ffmpeg.cpp +++ b/modules/highgui/src/cap_ffmpeg.cpp @@ -57,21 +57,42 @@ 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 cv::Mutex _icvInitFFMPEG_mutex; + +class icvInitFFMPEG { - static int ffmpegInitialized = 0; - if( !ffmpegInitialized ) +public: + static void Init() + { + cv::AutoLock al(_icvInitFFMPEG_mutex); + static icvInitFFMPEG init; + } + +private: + #if defined WIN32 || defined _WIN32 + HMODULE icvFFOpenCV; + + ~icvInitFFMPEG() + { + if (icvFFOpenCV) + { + FreeLibrary(icvFFOpenCV); + icvFFOpenCV = 0; + } + } + #endif + + icvInitFFMPEG() { #if defined WIN32 || defined _WIN32 const char* module_name = "opencv_ffmpeg" - CVAUX_STR(CV_VERSION_EPOCH) CVAUX_STR(CV_VERSION_MAJOR) CVAUX_STR(CV_VERSION_MINOR) + CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION) #if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__) "_64" #endif ".dll"; - static HMODULE icvFFOpenCV = LoadLibrary( module_name ); + icvFFOpenCV = LoadLibrary( module_name ); if( icvFFOpenCV ) { icvCreateFileCapture_FFMPEG_p = @@ -93,18 +114,24 @@ icvInitFFMPEG(void) icvWriteFrame_FFMPEG_p = (CvWriteFrame_Plugin)GetProcAddress(icvFFOpenCV, "cvWriteFrame_FFMPEG"); - if( icvCreateFileCapture_FFMPEG_p == NULL || - icvReleaseCapture_FFMPEG_p == NULL || - icvGrabFrame_FFMPEG_p == NULL || - icvRetrieveFrame_FFMPEG_p == NULL || - icvSetCaptureProperty_FFMPEG_p == NULL || - icvGetCaptureProperty_FFMPEG_p == NULL || - icvCreateVideoWriter_FFMPEG_p == NULL || - icvReleaseVideoWriter_FFMPEG_p == NULL || - icvWriteFrame_FFMPEG_p == NULL ) +#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 { - fprintf(stderr, "Failed to load FFMPEG plugin: module handle=%p\n", icvFFOpenCV); + printf("Failed to load FFMPEG plugin: module handle=%p\n", icvFFOpenCV); } +#endif } #elif defined HAVE_FFMPEG icvCreateFileCapture_FFMPEG_p = (CvCreateFileCapture_Plugin)cvCreateFileCapture_FFMPEG; @@ -117,13 +144,12 @@ icvInitFFMPEG(void) icvReleaseVideoWriter_FFMPEG_p = (CvReleaseVideoWriter_Plugin)cvReleaseVideoWriter_FFMPEG; icvWriteFrame_FFMPEG_p = (CvWriteFrame_Plugin)cvWriteFrame_FFMPEG; #endif - - ffmpegInitialized = 1; } -} +}; -class CvCapture_FFMPEG_proxy : public CvCapture +class CvCapture_FFMPEG_proxy : + public CvCapture { public: CvCapture_FFMPEG_proxy() { ffmpegCapture = 0; } @@ -146,18 +172,18 @@ public: 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; + 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 ) { + icvInitFFMPEG::Init(); close(); - icvInitFFMPEG(); if( !icvCreateFileCapture_FFMPEG_p ) return false; ffmpegCapture = icvCreateFileCapture_FFMPEG_p( filename ); @@ -190,8 +216,8 @@ CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char * filename) #endif } - -class CvVideoWriter_FFMPEG_proxy : public CvVideoWriter +class CvVideoWriter_FFMPEG_proxy : + public CvVideoWriter { public: CvVideoWriter_FFMPEG_proxy() { ffmpegWriter = 0; } @@ -208,8 +234,8 @@ public: } virtual bool open( const char* filename, int fourcc, double fps, CvSize frameSize, bool isColor ) { + icvInitFFMPEG::Init(); close(); - icvInitFFMPEG(); if( !icvCreateVideoWriter_FFMPEG_p ) return false; ffmpegWriter = icvCreateVideoWriter_FFMPEG_p( filename, fourcc, fps, frameSize.width, frameSize.height, isColor ); diff --git a/modules/highgui/src/cap_ffmpeg_impl.hpp b/modules/highgui/src/cap_ffmpeg_impl.hpp index 445a9e6208..b7e1d90605 100644 --- a/modules/highgui/src/cap_ffmpeg_impl.hpp +++ b/modules/highgui/src/cap_ffmpeg_impl.hpp @@ -328,28 +328,187 @@ void CvCapture_FFMPEG::close() #define AVSEEK_FLAG_ANY 1 #endif -static void icvInitFFMPEG_internal() +class ImplMutex { - static volatile bool initialized = false; - if( !initialized ) +public: + ImplMutex() { init(); } + ~ImplMutex() { destroy(); } + + void init(); + void destroy(); + + void lock(); + bool trylock(); + void unlock(); + + struct Impl; +protected: + Impl* impl; + +private: + ImplMutex(const ImplMutex&); + ImplMutex& operator = (const ImplMutex& m); +}; + +#if defined WIN32 || defined _WIN32 || defined WINCE + +struct ImplMutex::Impl +{ + void init() { InitializeCriticalSection(&cs); refcount = 1; } + void destroy() { DeleteCriticalSection(&cs); } + + void lock() { EnterCriticalSection(&cs); } + bool trylock() { return TryEnterCriticalSection(&cs) != 0; } + void unlock() { LeaveCriticalSection(&cs); } + + CRITICAL_SECTION cs; + int refcount; +}; + +#ifndef __GNUC__ +static int _interlockedExchangeAdd(int* addr, int delta) +{ +#if defined _MSC_VER && _MSC_VER >= 1500 + return (int)_InterlockedExchangeAdd((long volatile*)addr, delta); +#else + return (int)InterlockedExchangeAdd((long volatile*)addr, delta); +#endif +} +#endif // __GNUC__ + +#elif defined __APPLE__ + +#include + +struct ImplMutex::Impl +{ + void init() { sl = OS_SPINLOCK_INIT; refcount = 1; } + void destroy() { } + + void lock() { OSSpinLockLock(&sl); } + bool trylock() { return OSSpinLockTry(&sl); } + void unlock() { OSSpinLockUnlock(&sl); } + + OSSpinLock sl; + int refcount; +}; + +#elif defined __linux__ && !defined ANDROID + +struct ImplMutex::Impl +{ + void init() { pthread_spin_init(&sl, 0); refcount = 1; } + void destroy() { pthread_spin_destroy(&sl); } + + void lock() { pthread_spin_lock(&sl); } + bool trylock() { return pthread_spin_trylock(&sl) == 0; } + void unlock() { pthread_spin_unlock(&sl); } + + pthread_spinlock_t sl; + int refcount; +}; + +#else + +struct ImplMutex::Impl +{ + void init() { pthread_mutex_init(&sl, 0); refcount = 1; } + void destroy() { pthread_mutex_destroy(&sl); } + + void lock() { pthread_mutex_lock(&sl); } + bool trylock() { return pthread_mutex_trylock(&sl) == 0; } + void unlock() { pthread_mutex_unlock(&sl); } + + pthread_mutex_t sl; + int refcount; +}; + +#endif + +void ImplMutex::init() +{ + impl = (Impl*)malloc(sizeof(Impl)); + impl->init(); +} +void ImplMutex::destroy() +{ + impl->destroy(); + free(impl); + impl = NULL; +} +void ImplMutex::lock() { impl->lock(); } +void ImplMutex::unlock() { impl->unlock(); } +bool ImplMutex::trylock() { return impl->trylock(); } + +static int LockCallBack(void **mutex, AVLockOp op) +{ + ImplMutex* localMutex = reinterpret_cast(*mutex); + switch (op) + { + case AV_LOCK_CREATE: + localMutex = reinterpret_cast(malloc(sizeof(ImplMutex))); + localMutex->init(); + *mutex = localMutex; + if (!*mutex) + return 1; + break; + + case AV_LOCK_OBTAIN: + localMutex->lock(); + break; + + case AV_LOCK_RELEASE: + localMutex->unlock(); + break; + + case AV_LOCK_DESTROY: + localMutex->destroy(); + free(localMutex); + localMutex = NULL; + break; + } + return 0; +} + +static ImplMutex _mutex; +static bool _initialized = false; + +class InternalFFMpegRegister +{ +public: + InternalFFMpegRegister() { + _mutex.lock(); + if (!_initialized) + { #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 13, 0) - avformat_network_init(); + avformat_network_init(); #endif - /* register all codecs, demux and protocols */ - av_register_all(); + /* register all codecs, demux and protocols */ + av_register_all(); + + /* register a callback function for synchronization */ + av_lockmgr_register(&LockCallBack); + + av_log_set_level(AV_LOG_ERROR); - av_log_set_level(AV_LOG_ERROR); + _initialized = true; + } + _mutex.unlock(); + } - initialized = true; + ~InternalFFMpegRegister() + { + _initialized = false; + av_lockmgr_register(NULL); } -} +}; + +static InternalFFMpegRegister _init; bool CvCapture_FFMPEG::open( const char* _filename ) { - icvInitFFMPEG_internal(); - unsigned i; bool valid = false; @@ -361,7 +520,8 @@ bool CvCapture_FFMPEG::open( const char* _filename ) int err = av_open_input_file(&ic, _filename, NULL, 0, NULL); #endif - if (err < 0) { + if (err < 0) + { CV_WARN("Error opening file"); goto exit_func; } @@ -371,7 +531,8 @@ bool CvCapture_FFMPEG::open( const char* _filename ) #else av_find_stream_info(ic); #endif - if (err < 0) { + if (err < 0) + { CV_WARN("Could not find codec parameters"); goto exit_func; } @@ -383,17 +544,18 @@ bool CvCapture_FFMPEG::open( const char* _filename ) AVCodecContext *enc = &ic->streams[i]->codec; #endif -#ifdef FF_API_THREAD_INIT - avcodec_thread_init(enc, get_number_of_cpus()); -#else +//#ifdef FF_API_THREAD_INIT +// avcodec_thread_init(enc, get_number_of_cpus()); +//#else enc->thread_count = get_number_of_cpus(); -#endif +//#endif #if LIBAVFORMAT_BUILD < CALC_FFMPEG_VERSION(53, 2, 0) #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO #endif - if( AVMEDIA_TYPE_VIDEO == enc->codec_type && video_stream < 0) { + if( AVMEDIA_TYPE_VIDEO == enc->codec_type && video_stream < 0) + { AVCodec *codec = avcodec_find_decoder(enc->codec_id); if (!codec || #if LIBAVCODEC_VERSION_INT >= ((53<<16)+(8<<8)+0) @@ -401,7 +563,8 @@ bool CvCapture_FFMPEG::open( const char* _filename ) #else avcodec_open(enc, codec) #endif - < 0) goto exit_func; + < 0) + goto exit_func; video_stream = i; video_st = ic->streams[i]; @@ -1275,8 +1438,6 @@ void CvVideoWriter_FFMPEG::close() bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, double fps, int width, int height, bool is_color ) { - icvInitFFMPEG_internal(); - CodecID codec_id = CODEC_ID_NONE; int err, codec_pix_fmt; double bitrate_scale = 1; @@ -1495,6 +1656,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc, frame_width = width; frame_height = height; ok = true; + return true; } @@ -1506,6 +1668,7 @@ CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG( const char* filename ) capture->init(); if( capture->open( filename )) return capture; + capture->close(); free(capture); return 0; @@ -1554,7 +1717,6 @@ CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int four return 0; } - void cvReleaseVideoWriter_FFMPEG( CvVideoWriter_FFMPEG** writer ) { if( writer && *writer ) @@ -1745,11 +1907,6 @@ bool OutputMediaStream_FFMPEG::open(const char* fileName, int width, int height, oc_ = 0; video_st_ = 0; - // tell FFMPEG to register codecs - av_register_all(); - - av_log_set_level(AV_LOG_ERROR); - // 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); @@ -1930,11 +2087,6 @@ bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma avformat_network_init(); #endif - // register all codecs, demux and protocols - av_register_all(); - - av_log_set_level(AV_LOG_ERROR); - #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 6, 0) err = avformat_open_input(&ctx_, fileName, 0, 0); #else @@ -2054,7 +2206,7 @@ bool InputMediaStream_FFMPEG::read(unsigned char** data, int* size, int* endOfFi if (ret < 0) { - if (ret == AVERROR_EOF) + if (ret == (int)AVERROR_EOF) *endOfFile = true; return false; } diff --git a/modules/highgui/test/test_ffmpeg.cpp b/modules/highgui/test/test_ffmpeg.cpp index 0702cb1de4..53065462a2 100644 --- a/modules/highgui/test/test_ffmpeg.cpp +++ b/modules/highgui/test/test_ffmpeg.cpp @@ -207,7 +207,6 @@ public: } } - private: std::vector* writers; std::vector* files; @@ -241,18 +240,18 @@ public: virtual void operator() (const Range& range) const { - if((range.start + 1) != range.end) - return; - - VideoWriter* writer = writers->operator[](range.start); - CV_Assert(writer != NULL); - CV_Assert(writer->isOpened()); - - Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3); - for (unsigned int i = 0; i < FrameCount; ++i) + for (int j = range.start; j < range.end; ++j) { - GenerateFrame(frame, i); - writer->operator<< (frame); + VideoWriter* writer = writers->operator[](j); + CV_Assert(writer != NULL); + CV_Assert(writer->isOpened()); + + Mat frame(CreateVideoWriterInvoker::FrameSize, CV_8UC3); + for (unsigned int i = 0; i < FrameCount; ++i) + { + GenerateFrame(frame, i); + writer->operator<< (frame); + } } } @@ -305,47 +304,47 @@ public: virtual void operator() (const Range& range) const { - if((range.start + 1) != range.end) - return; - - VideoCapture* capture = readers->operator[](range.start); - CV_Assert(capture != NULL); - CV_Assert(capture->isOpened()); + for (int j = range.start; j < range.end; ++j) + { + VideoCapture* capture = readers->operator[](j); + CV_Assert(capture != NULL); + CV_Assert(capture->isOpened()); - const static double eps = 23.0; - unsigned int frameCount = static_cast(capture->get(CV_CAP_PROP_FRAME_COUNT)); - CV_Assert(frameCount == WriteVideo_Invoker::FrameCount); - Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3); + const static double eps = 23.0; + unsigned int frameCount = static_cast(capture->get(CV_CAP_PROP_FRAME_COUNT)); + CV_Assert(frameCount == WriteVideo_Invoker::FrameCount); + Mat reference(CreateVideoWriterInvoker::FrameSize, CV_8UC3); - for (unsigned int i = 0; i < frameCount && next; ++i) - { - Mat actual; - (*capture) >> actual; + for (unsigned int i = 0; i < frameCount && next; ++i) + { + Mat actual; + (*capture) >> actual; - WriteVideo_Invoker::GenerateFrame(reference, i); + WriteVideo_Invoker::GenerateFrame(reference, i); - EXPECT_EQ(reference.cols, actual.cols); - EXPECT_EQ(reference.rows, actual.rows); - EXPECT_EQ(reference.depth(), actual.depth()); - EXPECT_EQ(reference.channels(), actual.channels()); + EXPECT_EQ(reference.cols, actual.cols); + EXPECT_EQ(reference.rows, actual.rows); + EXPECT_EQ(reference.depth(), actual.depth()); + EXPECT_EQ(reference.channels(), actual.channels()); - double psnr = PSNR(actual, reference); - if (psnr < eps) - { -#define SUM cvtest::TS::SUMMARY - ts->printf(SUM, "\nPSNR: %lf\n", psnr); - ts->printf(SUM, "Video #: %d\n", range.start); - ts->printf(SUM, "Frame #: %d\n", i); -#undef SUM - ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); - ts->set_gtest_status(); + double psnr = PSNR(actual, reference); + if (psnr < eps) + { + #define SUM cvtest::TS::SUMMARY + ts->printf(SUM, "\nPSNR: %lf\n", psnr); + ts->printf(SUM, "Video #: %d\n", range.start); + ts->printf(SUM, "Frame #: %d\n", i); + #undef SUM + ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); + ts->set_gtest_status(); - Mat diff; - absdiff(actual, reference, diff); + Mat diff; + absdiff(actual, reference, diff); - EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0); + EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0); - next = false; + next = false; + } } } } @@ -359,7 +358,7 @@ private: bool ReadImageAndTest::next; -TEST(Highgui_Video_parallel_writers_and_readers, DISABLED_accuracy) +TEST(Highgui_Video_parallel_writers_and_readers, accuracy) { const unsigned int threadsCount = 4; cvtest::TS* ts = cvtest::TS::ptr();