From 9838b08919267da21bd93f2ec496b63bae1713c2 Mon Sep 17 00:00:00 2001
From: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
Date: Thu, 19 Mar 2020 12:02:23 -0400
Subject: [PATCH] video: fix incoming video for iOS

During incoming calls to iOS, video input failed to open sometimes.
It happens for two reasons:
1. Stopped by interrupt callback.
2. Keyframe callback was set two late.

This patch:
1. Completes decoder configuration after video loop start running.
2. Sets keyframe callback for video session before starting.

Change-Id: I00e1ace06a78ab2ec2fb3ce6fe6a5991c6593318
---
 src/media/video/video_receive_thread.cpp | 76 ++++++++++++++----------
 src/media/video/video_receive_thread.h   |  2 +
 src/sip/sipcall.cpp                      | 18 +++---
 3 files changed, 57 insertions(+), 39 deletions(-)

diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index 6a86e8f687..2924a326aa 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -52,6 +52,7 @@ VideoReceiveThread::VideoReceiveThread(const std::string& id,
     , stream_(sdp)
     , sdpContext_(stream_.str().size(), false, &readFunction, 0, 0, this)
     , sink_ {Manager::instance().createSinkClient(id)}
+    , isVideoConfigured_(false)
     , mtu_(mtu)
     , rotation_(0)
     , loop_(std::bind(&VideoReceiveThread::setup, this),
@@ -119,36 +120,6 @@ bool VideoReceiveThread::setup()
         // Now replace our custom AVIOContext with one that will read packets
         videoDecoder_->setIOContext(demuxContext_.get());
     }
-
-    if (videoDecoder_->setupVideo()) {
-        JAMI_ERR("decoder IO startup failed");
-        return false;
-    }
-
-    // Default size from input video
-    if (dstWidth_ == 0 and dstHeight_ == 0) {
-        dstWidth_ = videoDecoder_->getWidth();
-        dstHeight_ = videoDecoder_->getHeight();
-    }
-
-    if (not sink_->start()) {
-        JAMI_ERR("RX: sink startup failed");
-        return false;
-    }
-
-    auto conf = Manager::instance().getConferenceFromCallID(id_);
-    if (!conf)
-        exitConference();
-
-    // Send remote video codec in SmartInfo
-    Smartools::getInstance().setRemoteVideoCodec(videoDecoder_->getDecoderName(), id_);
-
-    // Send the resolution in smartInfo
-    Smartools::getInstance().setResolution(id_, dstWidth_, dstHeight_);
-
-    if (onSetupSuccess_)
-        onSetupSuccess_(MEDIA_VIDEO);
-
     return true;
 }
 
@@ -187,6 +158,9 @@ void VideoReceiveThread::addIOContext(SocketPair& socketPair)
 
 void VideoReceiveThread::decodeFrame()
 {
+    if (!configureVideoOutput()) {
+        return;
+    }
     auto status = videoDecoder_->decode();
     if (status == MediaDemuxer::Status::EndOfFile ||
         status == MediaDemuxer::Status::ReadError) {
@@ -198,6 +172,48 @@ void VideoReceiveThread::decodeFrame()
     }
 }
 
+bool VideoReceiveThread::configureVideoOutput()
+{
+    if (isVideoConfigured_) {
+        return true;
+    }
+    if (!loop_.isRunning()) {
+        return false;
+    }
+   if (videoDecoder_->setupVideo()) {
+        JAMI_ERR("decoder IO startup failed");
+        loop_.stop();
+        return false;
+    }
+
+    // Default size from input video
+    if (dstWidth_ == 0 and dstHeight_ == 0) {
+        dstWidth_ = videoDecoder_->getWidth();
+        dstHeight_ = videoDecoder_->getHeight();
+    }
+
+    if (not sink_->start()) {
+        JAMI_ERR("RX: sink startup failed");
+        loop_.stop();
+        return false;
+    }
+
+    auto conf = Manager::instance().getConferenceFromCallID(id_);
+    if (!conf)
+        exitConference();
+
+    // Send remote video codec in SmartInfo
+    Smartools::getInstance().setRemoteVideoCodec(videoDecoder_->getDecoderName(), id_);
+
+    // Send the resolution in smartInfo
+    Smartools::getInstance().setResolution(id_, dstWidth_, dstHeight_);
+
+    if (onSetupSuccess_)
+        onSetupSuccess_(MEDIA_VIDEO);
+    isVideoConfigured_ = true;
+    return true;
+}
+
 void VideoReceiveThread::enterConference()
 {
     if (!loop_.isRunning())
diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h
index 757bd8829d..9f1379e8e1 100644
--- a/src/media/video/video_receive_thread.h
+++ b/src/media/video/video_receive_thread.h
@@ -91,6 +91,7 @@ private:
     std::unique_ptr<MediaIOHandle> demuxContext_;
     std::shared_ptr<SinkClient> sink_;
     bool isReset_;
+    bool isVideoConfigured_;
     uint16_t mtu_;
     int rotation_;
     std::shared_ptr<AVBufferRef> displayMatrix_;
@@ -99,6 +100,7 @@ private:
     void decodeFrame();
     static int interruptCb(void *ctx);
     static int readFunction(void *opaque, uint8_t *buf, int buf_size);
+    bool configureVideoOutput();
 
     std::function<void(MediaType)> onSetupSuccess_;
 
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index 174b149d4f..f90d5488b9 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -982,6 +982,15 @@ SIPCall::startAllMedia()
 
         rtp->setSuccessfulSetupCb([this](MediaType type){ rtpSetupSuccess(type); });
 
+#ifdef ENABLE_VIDEO
+        videortp_->setRequestKeyFrameCallback([wthis = weak()] {
+            runOnMainThread([wthis] {
+                if (auto this_ = wthis.lock())
+                    this_->requestKeyframe();
+            });
+        });
+#endif
+
         // Not restarting media loop on hold as it's a huge waste of CPU ressources
         // because of the audio loop
         if (getState() != CallState::HOLD) {
@@ -1006,15 +1015,6 @@ SIPCall::startAllMedia()
         }
     }
 
-#ifdef ENABLE_VIDEO
-    videortp_->setRequestKeyFrameCallback([wthis = weak()] {
-        runOnMainThread([wthis] {
-            if (auto this_ = wthis.lock())
-                this_->requestKeyframe();
-        });
-    });
-#endif
-
     if (not isSubcall() and peerHolding_ != peer_holding) {
         peerHolding_ = peer_holding;
         emitSignal<DRing::CallSignal::PeerHold>(getCallId(), peerHolding_);
-- 
GitLab