thread-safe VideoWriter and VideoCapture
authorIlya Lavrenov <ilya.lavrenov@itseez.com>
Mon, 19 Nov 2012 12:44:23 +0000 (16:44 +0400)
committerIlya Lavrenov <ilya.lavrenov@itseez.com>
Mon, 19 Nov 2012 12:44:23 +0000 (16:44 +0400)
modules/highgui/src/cap.cpp
modules/highgui/src/cap_ffmpeg.cpp
modules/highgui/src/cap_ffmpeg_impl.hpp
modules/highgui/src/cap_gstreamer.cpp
modules/highgui/test/test_ffmpeg.cpp

index e818cfe..f8d32e7 100644 (file)
@@ -424,7 +424,6 @@ CV_IMPL CvVideoWriter* cvCreateVideoWriter( const char* filename, int fourcc,
 
 CV_IMPL int cvWriteFrame( CvVideoWriter* writer, const IplImage* image )
 {
-
     return writer ? writer->writeFrame(image) : 0;
 }
 
index 0cc60e3..040adbd 100644 (file)
@@ -57,11 +57,32 @@ 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"
@@ -71,7 +92,7 @@ icvInitFFMPEG(void)
         #endif
             ".dll";
 
-        static HMODULE icvFFOpenCV = LoadLibrary( module_name );
+        icvFFOpenCV = LoadLibrary( module_name );
         if( icvFFOpenCV )
         {
             icvCreateFileCapture_FFMPEG_p =
@@ -123,10 +144,8 @@ 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
@@ -161,9 +180,9 @@ public:
     }
     virtual bool open( const char* filename )
     {
+        icvInitFFMPEG::Init();
         close();
 
-        icvInitFFMPEG();
         if( !icvCreateFileCapture_FFMPEG_p )
             return false;
         ffmpegCapture = icvCreateFileCapture_FFMPEG_p( filename );
@@ -196,7 +215,6 @@ CvCapture* cvCreateFileCapture_FFMPEG_proxy(const char * filename)
 #endif
 }
 
-
 class CvVideoWriter_FFMPEG_proxy : public CvVideoWriter
 {
 public:
@@ -214,8 +232,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 );
index 445a9e6..d6ccdab 100644 (file)
@@ -328,28 +328,179 @@ 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();
+    ~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;
 
@@ -361,7 +512,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 +523,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;
     }
@@ -393,7 +546,8 @@ bool CvCapture_FFMPEG::open( const char* _filename )
 #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 +555,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,7 +1430,7 @@ void CvVideoWriter_FFMPEG::close()
 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;
@@ -1495,6 +1650,7 @@ bool CvVideoWriter_FFMPEG::open( const char * filename, int fourcc,
     frame_width = width;
     frame_height = height;
     ok = true;
+
     return true;
 }
 
@@ -1506,6 +1662,7 @@ CvCapture_FFMPEG* cvCreateFileCapture_FFMPEG( const char* filename )
     capture->init();
     if( capture->open( filename ))
         return capture;
+
     capture->close();
     free(capture);
     return 0;
@@ -1554,7 +1711,6 @@ CvVideoWriter_FFMPEG* cvCreateVideoWriter_FFMPEG( const char* filename, int four
     return 0;
 }
 
-
 void cvReleaseVideoWriter_FFMPEG( CvVideoWriter_FFMPEG** writer )
 {
     if( writer && *writer )
@@ -1741,15 +1897,12 @@ AVStream* OutputMediaStream_FFMPEG::addVideoStream(AVFormatContext *oc, CodecID
 
 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);
@@ -1920,6 +2073,8 @@ private:
 
 bool InputMediaStream_FFMPEG::open(const char* fileName, int* codec, int* chroma_format, int* width, int* height)
 {
+    InternalFFMpegRegister::Register();
+
     int err;
 
     ctx_ = 0;
@@ -1930,11 +2085,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 +2204,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;
         }
index e8cf213..60ba885 100644 (file)
 #define CV_WARN(message) fprintf(stderr, "warning: %s (%s:%d)\n", message, __FILE__, __LINE__)
 #endif
 
-static bool isInited = false;
+static cv::Mutex gst_initializer_mutex;
+
+class gst_initializer
+{
+public:
+    static void init()
+    {
+        gst_initializer_mutex.lock();
+        static gst_initializer init;
+        gst_initializer_mutex.unlock();
+    }
+private:
+    gst_initializer()
+    {
+        gst_init(NULL, NULL);
+    }
+};
+
 class CvCapture_GStreamer : public CvCapture
 {
 public:
@@ -298,16 +315,18 @@ bool CvCapture_GStreamer::open( int type, const char* filename )
 
     __BEGIN__;
 
-    if(!isInited) {
+    gst_initializer::init();
+
+//    if(!isInited) {
 //        printf("gst_init\n");
-        gst_init (NULL, NULL);
+//        gst_init (NULL, NULL);
 
 //        gst_debug_set_active(TRUE);
 //        gst_debug_set_colored(TRUE);
 //        gst_debug_set_default_threshold(GST_LEVEL_WARNING);
 
-        isInited = true;
-    }
+//        isInited = true;
+//    }
     bool stream = false;
     bool manualpipeline = false;
     char *uri = NULL;
@@ -477,10 +496,11 @@ bool CvVideoWriter_GStreamer::open( const char * filename, int fourcc,
     encit=encs.find(fourcc);
     if (encit==encs.end())
         CV_ERROR( CV_StsUnsupportedFormat,"Gstreamer Opencv backend doesn't support this codec acutally.");
-    if(!isInited) {
-        gst_init (NULL, NULL);
-        isInited = true;
-    }
+//    if(!isInited) {
+//        gst_init (NULL, NULL);
+//        isInited = true;
+//    }
+    gst_initializer::init();
     close();
     source=gst_element_factory_make("appsrc",NULL);
     file=gst_element_factory_make("filesink", NULL);
index fb32e01..ed06a2c 100644 (file)
 #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
@@ -118,11 +119,11 @@ public:
             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;
@@ -174,3 +175,221 @@ public:
 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