From d0744b3252cc836c83373a2880e0c8c7b5b5f334 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Thu, 14 Nov 2019 15:35:14 -0500
Subject: [PATCH] video_input: handle EBUSY error from ffmpeg

This solves some bugs:
+ If we have two calls and the client is holding one of these till the other
is creating, the camera will start for the second call
+ If another process is using the camera, Jami will retry to open the camera
till the call ends
+ If one video input runs cleanup() and the other one createDecoder(), the
shmPath_ is set back correctly

Change-Id: Id6b02e453dffbe0b231884c2942786407af5eac3
---
 src/media/video/video_input.cpp | 25 +++++++++++++++++++++----
 src/media/video/video_input.h   |  1 +
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/src/media/video/video_input.cpp b/src/media/video/video_input.cpp
index 0ebefcca88..8d081330d4 100644
--- a/src/media/video/video_input.cpp
+++ b/src/media/video/video_input.cpp
@@ -66,6 +66,7 @@ VideoInput::VideoInput()
 
 VideoInput::~VideoInput()
 {
+    isStopped_ = true;
 #if VIDEO_CLIENT_INPUT
     emitSignal<DRing::VideoSignal::StopCapture>();
     capturing_ = false;
@@ -210,10 +211,26 @@ VideoInput::createDecoder()
         [](void* data) -> int { return not static_cast<VideoInput*>(data)->isCapturing(); },
         this);
 
-    if (decoder->openInput(decOpts_) < 0) {
-        JAMI_ERR("Could not open input \"%s\"", decOpts_.input.c_str());
-        foundDecOpts(decOpts_);
-        return;
+    bool ready = false, restartSink = false;
+    while (!ready && !isStopped_) {
+        // Retry to open the video till the input is opened
+        auto ret = decoder->openInput(decOpts_);
+        ready = ret >= 0;
+        if (ret < 0 && -ret != EBUSY) {
+            JAMI_ERR("Could not open input \"%s\" with status %i", decOpts_.input.c_str(), ret);
+            foundDecOpts(decOpts_);
+            return;
+        } else if (-ret == EBUSY) {
+            // If the device is busy, this means that it can be used by another call.
+            // If this is the case, cleanup() can occurs and this will erase shmPath_
+            // So, be sure to regenerate a correct shmPath for clients.
+            restartSink = true;
+        }
+        std::this_thread::sleep_for(std::chrono::milliseconds(10));
+    }
+
+    if (restartSink && !isStopped_) {
+        sink_->start();
     }
 
     /* Data available, finish the decoding */
diff --git a/src/media/video/video_input.h b/src/media/video/video_input.h
index f77c717e66..f7f67f0d50 100644
--- a/src/media/video/video_input.h
+++ b/src/media/video/video_input.h
@@ -85,6 +85,7 @@ private:
 
     std::string currentResource_;
     std::atomic<bool> switchPending_ = {false};
+    std::atomic_bool  isStopped_ = {false};
 
     DeviceParams decOpts_;
     std::promise<DeviceParams> foundDecOpts_;
-- 
GitLab