CAP_MFX: surface pool with timeout, more parameters
authorMaksim Shabunin <maksim.shabunin@gmail.com>
Tue, 17 Dec 2019 12:55:45 +0000 (15:55 +0300)
committerMaksim Shabunin <maksim.shabunin@gmail.com>
Tue, 12 May 2020 15:43:04 +0000 (18:43 +0300)
modules/videoio/src/cap_mfx_common.cpp
modules/videoio/src/cap_mfx_common.hpp
modules/videoio/src/cap_mfx_reader.cpp
modules/videoio/src/cap_mfx_writer.cpp

index 55c2450..268d8c6 100644 (file)
 using namespace std;
 using namespace cv;
 
+static mfxIMPL getImpl()
+{
+    static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_IMPL", MFX_IMPL_AUTO_ANY);
+    return (mfxIMPL)res;
+}
+
+static size_t getExtraSurfaceNum()
+{
+    static const size_t res = cv::utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_EXTRA_SURFACE_NUM", 1);
+    return res;
+}
+
+static size_t getPoolTimeoutSec()
+{
+    static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_POOL_TIMEOUT", 1);
+    return res;
+}
+
+//==================================================================================================
+
 bool DeviceHandler::init(MFXVideoSession &session)
 {
     mfxStatus res = MFX_ERR_NONE;
-    mfxIMPL impl = MFX_IMPL_AUTO_ANY;
+    mfxIMPL impl = getImpl();
     mfxVersion ver = { {19, 1} };
 
     res = session.Init(impl, &ver);
@@ -114,11 +134,26 @@ SurfacePool::~SurfacePool()
 {
 }
 
+SurfacePool * SurfacePool::_create(const mfxFrameAllocRequest &request, const mfxVideoParam &params)
+{
+    return new SurfacePool(request.Info.Width,
+                           request.Info.Height,
+                           saturate_cast<ushort>((size_t)request.NumFrameSuggested + getExtraSurfaceNum()),
+                           params.mfx.FrameInfo);
+}
+
 mfxFrameSurface1 *SurfacePool::getFreeSurface()
 {
-    for(std::vector<mfxFrameSurface1>::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
-        if (!i->Data.Locked)
-            return &(*i);
+    const int64 start = cv::getTickCount();
+    do
+    {
+        for(std::vector<mfxFrameSurface1>::iterator i = surfaces.begin(); i != surfaces.end(); ++i)
+            if (!i->Data.Locked)
+                return &(*i);
+        sleep_ms(10);
+    }
+    while((cv::getTickCount() - start) / cv::getTickFrequency() < getPoolTimeoutSec()); // seconds
+    DBG(cout << "No free surface!" << std::endl);
     return 0;
 }
 
index b4d2d9a..bef6d34 100644 (file)
@@ -6,6 +6,7 @@
 #define MFXHELPER_H
 
 #include "opencv2/core.hpp"
+#include "opencv2/core/utils/configuration.private.hpp"
 
 #include <iostream>
 #include <fstream>
@@ -259,12 +260,11 @@ public:
         DBG(std::cout << "MFX QueryIOSurf: " << res << std::endl);
         if (res < MFX_ERR_NONE)
             return 0;
-        return new SurfacePool(request.Info.Width,
-                               request.Info.Height,
-                               request.NumFrameSuggested,
-                               params.mfx.FrameInfo);
+        return _create(request, params);
     }
 private:
+    static SurfacePool* _create(const mfxFrameAllocRequest& request, const mfxVideoParam& params);
+private:
     SurfacePool(const SurfacePool &);
     SurfacePool &operator=(const SurfacePool &);
 public:
@@ -285,6 +285,29 @@ protected:
 };
 
 
+// TODO: move to core::util?
+#ifdef CV_CXX11
+#include <thread>
+static void sleep_ms(int64 ms)
+{
+    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+}
+#elif defined(__linux__)
+#include <time.h>
+static void sleep_ms(int64 ms)
+{
+    nanosleep(ms * 1000 * 1000);
+}
+#elif defined _WIN32
+static void sleep_ms(int64 ms)
+{
+    Sleep(ms);
+}
+#else
+#error "Can not detect sleep_ms() implementation"
+#endif
+
+
 // Linux specific
 #ifdef __linux__
 
@@ -310,7 +333,6 @@ private:
 #ifdef _WIN32
 
 #include <Windows.h>
-inline void sleep(unsigned long sec) { Sleep(1000 * sec); }
 
 class DXHandle : public DeviceHandler {
 public:
index f2ec1c4..7df2cf5 100644 (file)
@@ -214,7 +214,7 @@ bool VideoCapture_IntelMFX::grabFrame()
         else if (res == MFX_WRN_DEVICE_BUSY)
         {
             DBG(cout << "Waiting for device" << endl);
-            sleep(1);
+            sleep_ms(1000);
             continue;
         }
         else if (res == MFX_WRN_VIDEO_PARAM_CHANGED)
index 8a93fc7..1279141 100644 (file)
 using namespace std;
 using namespace cv;
 
+static size_t getBitrateDivisor()
+{
+    static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_BITRATE_DIVISOR", 300);
+    return res;
+}
+
+static mfxU32 getWriterTimeoutMS()
+{
+    static const size_t res = utils::getConfigurationParameterSizeT("OPENCV_VIDEOIO_MFX_WRITER_TIMEOUT", 1);
+    return saturate_cast<mfxU32>(res * 1000); // convert from seconds
+}
+
 inline mfxU32 codecIdByFourCC(int fourcc)
 {
     const int CC_MPG2 = FourCC('M', 'P', 'G', '2').vali32;
@@ -77,7 +89,7 @@ VideoWriter_IntelMFX::VideoWriter_IntelMFX(const String &filename, int _fourcc,
     memset(&params, 0, sizeof(params));
     params.mfx.CodecId = codecId;
     params.mfx.TargetUsage = MFX_TARGETUSAGE_BALANCED;
-    params.mfx.TargetKbps = (mfxU16)cvRound(frameSize.area() * fps / 500); // TODO: set in options
+    params.mfx.TargetKbps = saturate_cast<mfxU16>((frameSize.area() * fps) / (42.6666 * getBitrateDivisor())); // TODO: set in options
     params.mfx.RateControlMethod = MFX_RATECONTROL_VBR;
     params.mfx.FrameInfo.FrameRateExtN = cvRound(fps * 1000);
     params.mfx.FrameInfo.FrameRateExtD = 1000;
@@ -210,7 +222,7 @@ bool VideoWriter_IntelMFX::write_one(cv::InputArray bgr)
         res = encoder->EncodeFrameAsync(NULL, workSurface, &bs->stream, &sync);
         if (res == MFX_ERR_NONE)
         {
-            res = session->SyncOperation(sync, 1000); // 1 sec, TODO: provide interface to modify timeout
+            res = session->SyncOperation(sync, getWriterTimeoutMS()); // TODO: provide interface to modify timeout
             if (res == MFX_ERR_NONE)
             {
                 // ready to write
@@ -239,7 +251,7 @@ bool VideoWriter_IntelMFX::write_one(cv::InputArray bgr)
         else if (res == MFX_WRN_DEVICE_BUSY)
         {
             DBG(cout << "Waiting for device" << endl);
-            sleep(1);
+            sleep_ms(1000);
             continue;
         }
         else