#define AVSEEK_FLAG_ANY 1
#endif
-static void icvInitFFMPEG_internal()
+class ImplMutex
{
- static volatile bool initialized = false;
- if( !initialized )
+public:
+ ImplMutex();
+ ~ImplMutex();
+
+ 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
+{
+ Impl() { InitializeCriticalSection(&cs); refcount = 1; }
+ ~Impl() { 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 <libkern/OSAtomic.h>
+
+struct ImplMutex::Impl
+{
+ Impl() { sl = OS_SPINLOCK_INIT; refcount = 1; }
+ ~Impl() {}
+
+ 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
+{
+ Impl() { pthread_spin_init(&sl, 0); refcount = 1; }
+ ~Impl() { 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
+{
+ Impl() { pthread_mutex_init(&sl, 0); refcount = 1; }
+ ~Impl() { 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
+
+ImplMutex::ImplMutex()
+{
+ impl = new ImplMutex::Impl;
+}
+
+ImplMutex::~ImplMutex()
+{
+ delete impl;
+ impl = 0;
+}
+
+void ImplMutex::lock() { impl->lock(); }
+void ImplMutex::unlock() { impl->unlock(); }
+bool ImplMutex::trylock() { return impl->trylock(); }
+
+static int LockCallBack(void **mutex, AVLockOp op)
+{
+ switch (op)
{
- #if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 13, 0)
+ case AV_LOCK_CREATE:
+ *mutex = reinterpret_cast<void*>(new ImplMutex());
+ if (!*mutex)
+ return 1;
+ break;
+
+ case AV_LOCK_OBTAIN:
+ reinterpret_cast<ImplMutex*>(*mutex)->lock();
+ break;
+
+ case AV_LOCK_RELEASE:
+ reinterpret_cast<ImplMutex*>(*mutex)->unlock();
+ break;
+
+ case AV_LOCK_DESTROY:
+ ImplMutex* cv_mutex = reinterpret_cast<ImplMutex*>(*mutex);
+ delete cv_mutex;
+ cv_mutex = NULL;
+ break;
+ }
+ return 0;
+}
+
+static ImplMutex _InternalFFMpegRegister_mutex;
+
+class InternalFFMpegRegister
+{
+public:
+ static void Register()
+ {
+ _InternalFFMpegRegister_mutex.lock();
+ static InternalFFMpegRegister init;
+ _InternalFFMpegRegister_mutex.unlock();
+ }
+
+ ~InternalFFMpegRegister()
+ {
+ av_lockmgr_register(NULL);
+ }
+
+private:
+ InternalFFMpegRegister()
+ {
+#if LIBAVFORMAT_BUILD >= CALC_FFMPEG_VERSION(53, 13, 0)
avformat_network_init();
- #endif
+#endif
/* register all codecs, demux and protocols */
av_register_all();
- av_log_set_level(AV_LOG_ERROR);
+ /* register a callback function for synchronization */
+ av_lockmgr_register(&LockCallBack);
- initialized = true;
+ av_log_set_level(AV_LOG_ERROR);
}
-}
+};
bool CvCapture_FFMPEG::open( const char* _filename )
{
- icvInitFFMPEG_internal();
-
+ InternalFFMpegRegister::Register();
unsigned i;
bool valid = false;
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;
}
#else
av_find_stream_info(ic);
#endif
- if (err < 0) {
+ if (err < 0)
+ {
CV_WARN("Could not find codec parameters");
goto exit_func;
}
#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)
#else
avcodec_open(enc, codec)
#endif
- < 0) goto exit_func;
+ < 0)
+ goto exit_func;
video_stream = i;
video_st = ic->streams[i];
bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
double fps, int width, int height, bool is_color )
{
- icvInitFFMPEG_internal();
+ InternalFFMpegRegister::Register();
CodecID codec_id = CODEC_ID_NONE;
int err, codec_pix_fmt;
frame_width = width;
frame_height = height;
ok = true;
+
return true;
}
capture->init();
if( capture->open( filename ))
return capture;
+
capture->close();
free(capture);
return 0;
return 0;
}
-
void cvReleaseVideoWriter_FFMPEG( CvVideoWriter_FFMPEG** writer )
{
if( writer && *writer )
bool OutputMediaStream_FFMPEG::open(const char* fileName, int width, int height, double fps)
{
+ InternalFFMpegRegister::Register();
+
fmt_ = 0;
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);
bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma_format, int* width, int* height)
{
+ InternalFFMpegRegister::Register();
+
int err;
ctx_ = 0;
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
if (ret < 0)
{
- if (ret == AVERROR_EOF)
+ if (ret == (int)AVERROR_EOF)
*endOfFile = true;
return false;
}
#include "test_precomp.hpp"
#include "opencv2/highgui/highgui.hpp"
+using namespace cv;
+
#ifdef HAVE_FFMPEG
#include "ffmpeg_codecs.hpp"
-using namespace cv;
using namespace std;
class CV_FFmpegWriteBigVideoTest : public cvtest::BaseTest
else
{
Mat img(frame_s, CV_8UC3, Scalar::all(0));
- const int coeff = cvRound(cv::min(frame_s.width, frame_s.height)/(fps0 * time_sec));
+ const int coeff = cvRound(min(frame_s.width, frame_s.height)/(fps0 * time_sec));
for (int i = 0 ; i < static_cast<int>(fps * time_sec); i++ )
{
- //circle(img, Point2i(img_c / 2, img_r / 2), cv::min(img_r, img_c) / 2 * (i + 1), Scalar(255, 0, 0, 0), 2);
+ //circle(img, Point2i(img_c / 2, img_r / 2), min(img_r, img_c) / 2 * (i + 1), Scalar(255, 0, 0, 0), 2);
rectangle(img, Point2i(coeff * i, coeff * i), Point2i(coeff * (i + 1), coeff * (i + 1)),
Scalar::all(255 * (1.0 - static_cast<double>(i) / (fps * time_sec * 2) )), -1);
writer << img;
TEST(Highgui_Video, ffmpeg_image) { CV_FFmpegReadImageTest test; test.safe_run(); }
#endif
+
+#if defined(HAVE_FFMPEG) || defined(WIN32) || defined(_WIN32)
+
+//////////////////////////////// Parallel VideoWriters and VideoCaptures ////////////////////////////////////
+
+class CreateVideoWriterInvoker :
+ public ParallelLoopBody
+{
+public:
+ const static Size FrameSize;
+ static std::string TmpDirectory;
+
+ CreateVideoWriterInvoker(std::vector<VideoWriter*>& _writers, std::vector<std::string>& _files) :
+ ParallelLoopBody(), writers(&_writers), files(&_files)
+ {
+ }
+
+ virtual void operator() (const Range& range) const
+ {
+ for (int i = range.start; i != range.end; ++i)
+ {
+ std::ostringstream stream;
+ stream << i << ".avi";
+ std::string fileName = tempfile(stream.str().c_str());
+
+ files->operator[](i) = fileName;
+ writers->operator[](i) = new VideoWriter(fileName, CV_FOURCC('X','V','I','D'), 25.0f, FrameSize);
+
+ CV_Assert(writers->operator[](i)->isOpened());
+ }
+ }
+
+
+private:
+ std::vector<VideoWriter*>* writers;
+ std::vector<std::string>* files;
+};
+
+std::string CreateVideoWriterInvoker::TmpDirectory;
+const Size CreateVideoWriterInvoker::FrameSize(1020, 900);
+
+class WriteVideo_Invoker :
+ public ParallelLoopBody
+{
+public:
+ enum { FrameCount = 300 };
+
+ static const Scalar ObjectColor;
+ static const Point Center;
+
+ WriteVideo_Invoker(const std::vector<VideoWriter*>& _writers) :
+ ParallelLoopBody(), writers(&_writers)
+ {
+ }
+
+ static void GenerateFrame(Mat& frame, unsigned int i)
+ {
+ frame = Scalar::all(i % 255);
+
+ std::string text = to_string(i);
+ putText(frame, text, Point(50, Center.y), FONT_HERSHEY_SIMPLEX, 5.0, ObjectColor, 5, CV_AA);
+ circle(frame, Center, i + 2, ObjectColor, 2, CV_AA);
+ }
+
+ virtual void operator() (const Range& range) const
+ {
+ CV_Assert((range.start + 1) == range.end);
+ 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)
+ {
+ GenerateFrame(frame, i);
+ writer->operator<< (frame);
+ }
+ }
+
+protected:
+ static std::string to_string(unsigned int i)
+ {
+ std::stringstream stream(std::ios::out);
+ stream << "frame #" << i;
+ return stream.str();
+ }
+
+private:
+ const std::vector<VideoWriter*>* writers;
+};
+
+const Scalar WriteVideo_Invoker::ObjectColor(Scalar::all(0));
+const Point WriteVideo_Invoker::Center(CreateVideoWriterInvoker::FrameSize.height / 2,
+ CreateVideoWriterInvoker::FrameSize.width / 2);
+
+class CreateVideoCaptureInvoker :
+ public ParallelLoopBody
+{
+public:
+ CreateVideoCaptureInvoker(std::vector<VideoCapture*>& _readers, const std::vector<std::string>& _files) :
+ ParallelLoopBody(), readers(&_readers), files(&_files)
+ {
+ }
+
+ virtual void operator() (const Range& range) const
+ {
+ for (int i = range.start; i != range.end; ++i)
+ {
+ readers->operator[](i) = new VideoCapture(files->operator[](i));
+ CV_Assert(readers->operator[](i)->isOpened());
+ }
+ }
+private:
+ std::vector<VideoCapture*>* readers;
+ const std::vector<std::string>* files;
+};
+
+class ReadImageAndTest :
+ public ParallelLoopBody
+{
+public:
+ ReadImageAndTest(const std::vector<VideoCapture*>& _readers, cvtest::TS* _ts) :
+ ParallelLoopBody(), readers(&_readers), ts(_ts)
+ {
+ }
+
+ virtual void operator() (const Range& range) const
+ {
+ CV_Assert(range.start + 1 == range.end);
+ VideoCapture* capture = readers->operator[](range.start);
+ CV_Assert(capture != NULL);
+ CV_Assert(capture->isOpened());
+
+ const static double eps = 23.0;
+ unsigned int frameCount = static_cast<unsigned int>(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;
+
+ 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());
+
+ 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);
+
+ EXPECT_EQ(countNonZero(diff.reshape(1) > 1), 0);
+
+ next = false;
+ }
+ }
+ }
+
+ static bool next;
+
+private:
+ const std::vector<VideoCapture*>* readers;
+ cvtest::TS* ts;
+};
+
+bool ReadImageAndTest::next;
+
+TEST(Highgui_Video_parallel_writers_and_readers, accuracy)
+{
+ const unsigned int threadsCount = 4;
+ cvtest::TS* ts = cvtest::TS::ptr();
+
+ // creating VideoWriters
+ std::vector<VideoWriter*> writers(threadsCount);
+ Range range(0, threadsCount);
+ std::vector<std::string> files(threadsCount);
+ CreateVideoWriterInvoker invoker1(writers, files);
+ parallel_for_(range, invoker1);
+
+ // write a video
+ parallel_for_(range, WriteVideo_Invoker(writers));
+
+ // deleting the writers
+ for (std::vector<VideoWriter*>::iterator i = writers.begin(), end = writers.end(); i != end; ++i)
+ delete *i;
+ writers.clear();
+
+ std::vector<VideoCapture*> readers(threadsCount);
+ CreateVideoCaptureInvoker invoker2(readers, files);
+ parallel_for_(range, invoker2);
+
+ ReadImageAndTest::next = true;
+
+ parallel_for_(range, ReadImageAndTest(readers, ts));
+
+ // deleting tmp video files
+ for (std::vector<std::string>::const_iterator i = files.begin(), end = files.end(); i != end; ++i)
+ {
+ int code = remove(i->c_str());
+ if (code == 1)
+ std::cerr << "Couldn't delete " << *i << std::endl;
+ }
+}
+
+#endif