From f6c3205d65f9873353b662759249bf685a24cdfe Mon Sep 17 00:00:00 2001
From: Philippe Gorley <gorley.philippe@gmail.com>
Date: Mon, 26 Sep 2016 14:52:59 -0400
Subject: [PATCH] video: fallback from hardware to software decoding

This patch also implements a way for the Call object to restart
the media receiver

Change-Id: I1090575f14425d383eb5471a388c4c9e8d888b0e
(cherry picked from commit 660bc3404d949947c9e521786381560a970959a3)
---
 src/call.h                               |  2 ++
 src/media/audio/audio_rtp_session.cpp    | 11 ++++++++
 src/media/audio/audio_rtp_session.h      |  1 +
 src/media/media_decoder.cpp              |  2 +-
 src/media/media_decoder.h                |  3 +-
 src/media/rtp_session.h                  |  1 +
 src/media/video/video_receive_thread.cpp | 15 ++++++++--
 src/media/video/video_receive_thread.h   |  5 +++-
 src/media/video/video_rtp_session.cpp    | 36 ++++++++++++++++++++++--
 src/media/video/video_rtp_session.h      |  2 ++
 src/sip/sipcall.cpp                      | 10 +++++++
 src/sip/sipcall.h                        |  2 ++
 12 files changed, 82 insertions(+), 8 deletions(-)

diff --git a/src/call.h b/src/call.h
index 54c264a698..5eb618fcf3 100644
--- a/src/call.h
+++ b/src/call.h
@@ -331,6 +331,8 @@ class Call : public Recordable, public std::enable_shared_from_this<Call> {
 
         virtual void restartMediaSender() = 0;
 
+        virtual void restartMediaReceiver() = 0;
+
     protected:
         /**
          * Constructor of a call
diff --git a/src/media/audio/audio_rtp_session.cpp b/src/media/audio/audio_rtp_session.cpp
index 7474886ae5..f0bf91a610 100644
--- a/src/media/audio/audio_rtp_session.cpp
+++ b/src/media/audio/audio_rtp_session.cpp
@@ -422,6 +422,17 @@ AudioRtpSession::startReceiver()
     receiveThread_->startLoop();
 }
 
+void
+AudioRtpSession::restartReceiver()
+{
+    std::lock_guard<std::recursive_mutex> lock(mutex_);
+    // ensure that start has been called before restart
+    if (not socketPair_)
+        return;
+
+    startReceiver();
+}
+
 void
 AudioRtpSession::start(std::unique_ptr<IceSocket> rtp_sock, std::unique_ptr<IceSocket> rtcp_sock)
 {
diff --git a/src/media/audio/audio_rtp_session.h b/src/media/audio/audio_rtp_session.h
index 8259d36714..add163b1d2 100644
--- a/src/media/audio/audio_rtp_session.h
+++ b/src/media/audio/audio_rtp_session.h
@@ -44,6 +44,7 @@ class AudioRtpSession : public RtpSession {
         void start(std::unique_ptr<IceSocket> rtp_sock = nullptr,
                    std::unique_ptr<IceSocket> rtcp_sock = nullptr) override;
         void restartSender() override;
+        void restartReceiver() override;
         void stop() override;
         void setMuted(bool isMuted);
 
diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index d939b99a49..959e41aca7 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -340,7 +340,7 @@ MediaDecoder::decode(VideoFrame& result)
             if (!accel_->hasFailed())
                 accel_->extractData(decoderCtx_, result);
             else
-                return Status::DecodeError;
+                return Status::RestartRequired;
         }
 #endif // RING_ACCEL
         if (emulateRate_ and frame->pkt_pts != AV_NOPTS_VALUE) {
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index fa22ab2b2d..6340f3a916 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -64,7 +64,8 @@ class MediaDecoder {
             FrameFinished,
             EOFError,
             ReadError,
-            DecodeError
+            DecodeError,
+            RestartRequired
         };
 
         MediaDecoder();
diff --git a/src/media/rtp_session.h b/src/media/rtp_session.h
index d70c4b00f5..def4987967 100644
--- a/src/media/rtp_session.h
+++ b/src/media/rtp_session.h
@@ -39,6 +39,7 @@ public:
     virtual void start(std::unique_ptr<IceSocket> rtp_sock = nullptr,
                        std::unique_ptr<IceSocket> rtcp_sock = nullptr) = 0;
     virtual void restartSender() = 0;
+    virtual void restartReceiver() = 0;
     virtual void stop() = 0;
 
     virtual void updateMedia(const MediaDescription& send,
diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index 286f781e2e..e3ab0bd559 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -37,13 +37,15 @@ namespace ring { namespace video {
 using std::string;
 
 VideoReceiveThread::VideoReceiveThread(const std::string& id,
-                                       const std::string &sdp) :
+                                       const std::string &sdp,
+                                       const DeviceParams& args) :
     VideoGenerator::VideoGenerator()
-    , args_()
+    , args_(args)
     , dstWidth_(0)
     , dstHeight_(0)
     , id_(id)
     , stream_(sdp)
+    , restartDecoder_(false)
     , sdpContext_(stream_.str().size(), false, &readFunction, 0, 0, this)
     , sink_ {Manager::instance().createSinkClient(id)}
     , requestKeyFrameCallback_(0)
@@ -175,6 +177,11 @@ bool VideoReceiveThread::decodeFrame()
         case MediaDecoder::Status::ReadError:
             RING_ERR("fatal error, read failed");
             loop_.stop();
+            break;
+
+        case MediaDecoder::Status::RestartRequired:
+            restartDecoder_ = true;
+            break;
 
         case MediaDecoder::Status::Success:
         case MediaDecoder::Status::EOFError:
@@ -216,4 +223,8 @@ int VideoReceiveThread::getHeight() const
 int VideoReceiveThread::getPixelFormat() const
 { return videoDecoder_->getPixelFormat(); }
 
+bool
+VideoReceiveThread::restartDecoder() const
+{ return restartDecoder_.load(); }
+
 }} // namespace ring::video
diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h
index a8a3f3ccfc..57d1032de8 100644
--- a/src/media/video/video_receive_thread.h
+++ b/src/media/video/video_receive_thread.h
@@ -34,6 +34,7 @@
 #include <climits>
 #include <sstream>
 #include <memory>
+#include <atomic>
 
 namespace ring {
 class SocketPair;
@@ -46,7 +47,7 @@ class SinkClient;
 
 class VideoReceiveThread : public VideoGenerator {
 public:
-    VideoReceiveThread(const std::string &id, const std::string &sdp);
+    VideoReceiveThread(const std::string &id, const std::string &sdp, const DeviceParams& args);
     ~VideoReceiveThread();
     void startLoop();
 
@@ -59,6 +60,7 @@ public:
     int getWidth() const;
     int getHeight() const;
     int getPixelFormat() const;
+    bool restartDecoder() const;
 
 private:
     NON_COPYABLE(VideoReceiveThread);
@@ -76,6 +78,7 @@ private:
     MediaIOHandle sdpContext_;
     std::unique_ptr<MediaIOHandle> demuxContext_;
     std::shared_ptr<SinkClient> sink_;
+    std::atomic_bool restartDecoder_;
     void (*requestKeyFrameCallback_)(const std::string &);
     void openDecoder();
     bool decodeFrame();
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index 40d5f0ab74..077dff5363 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -129,10 +129,13 @@ VideoRtpSession::restartSender()
 void VideoRtpSession::startReceiver()
 {
     if (receive_.enabled and not receive_.holding) {
-        if (receiveThread_)
-            RING_WARN("restarting video receiver");
+        DeviceParams receiverArgs = {};
+        if (receiveThread_) {
+            RING_WARN("Restarting video receiver");
+            receiverArgs.enableAccel = "0"; // most likely cause of this restart
+        }
         receiveThread_.reset(
-            new VideoReceiveThread(callID_, receive_.receiving_sdp)
+            new VideoReceiveThread(callID_, receive_.receiving_sdp, receiverArgs)
         );
         /* ebail: keyframe requests can lead to timeout if they are not answered.
          * we decided so to disable them for the moment
@@ -147,6 +150,19 @@ void VideoRtpSession::startReceiver()
         receiveThread_.reset();
     }
 }
+
+void
+VideoRtpSession::restartReceiver()
+{
+    std::lock_guard<std::recursive_mutex> lock(mutex_);
+
+    // ensure that start has been called before restart
+    if (not socketPair_)
+        return;
+
+    startReceiver(); // disable accel
+}
+
 void VideoRtpSession::start(std::unique_ptr<IceSocket> rtp_sock,
                             std::unique_ptr<IceSocket> rtcp_sock)
 {
@@ -526,9 +542,23 @@ VideoRtpSession::setupRtcpChecker()
     return true;
 }
 
+void
+VideoRtpSession::checkReceiver()
+{
+    if (receiveThread_ && receiveThread_->restartDecoder()) {
+        const auto& cid = callID_;
+        runOnMainThread([cid]{
+            if (auto call = Manager::instance().callFactory.getCall(cid)) {
+                call->restartMediaReceiver();
+            }
+        });
+    }
+}
+
 void
 VideoRtpSession::processRtcpChecker()
 {
+    checkReceiver();
     adaptQualityAndBitrate();
     rtcpCheckerThread_.wait_for(std::chrono::seconds(RTCP_CHECKING_INTERVAL));
 }
diff --git a/src/media/video/video_rtp_session.h b/src/media/video/video_rtp_session.h
index 4b303651de..407b5da78c 100644
--- a/src/media/video/video_rtp_session.h
+++ b/src/media/video/video_rtp_session.h
@@ -62,6 +62,7 @@ public:
     void start(std::unique_ptr<IceSocket> rtp_sock = nullptr,
                std::unique_ptr<IceSocket> rtcp_sock = nullptr) override;
     void restartSender() override;
+    void restartReceiver() override;
     void stop() override;
 
     void forceKeyFrame();
@@ -100,6 +101,7 @@ private:
     void adaptQualityAndBitrate();
     void storeVideoBitrateInfo();
     void getVideoBitrateInfo();
+    void checkReceiver();
 
 
     // interval in seconds between RTCP checkings
diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp
index d7b1081873..f234be540b 100644
--- a/src/sip/sipcall.cpp
+++ b/src/sip/sipcall.cpp
@@ -889,6 +889,16 @@ SIPCall::restartMediaSender()
 #endif
 }
 
+void
+SIPCall::restartMediaReceiver()
+{
+    RING_DBG("[call:%s] restarting RX media streams", getCallId().c_str());
+    avformatrtp_->restartReceiver();
+#ifdef RING_VIDEO
+    videortp_.restartReceiver();
+#endif
+}
+
 void
 SIPCall::stopAllMedia()
 {
diff --git a/src/sip/sipcall.h b/src/sip/sipcall.h
index 74f35b1980..2448dba06e 100644
--- a/src/sip/sipcall.h
+++ b/src/sip/sipcall.h
@@ -203,6 +203,8 @@ class SIPCall : public Call
 
         void restartMediaSender() override;
 
+        void restartMediaReceiver() override;
+
         bool useVideoCodec(const AccountVideoCodecInfo* codec) const override;
 
         virtual std::map<std::string, std::string> getDetails() const override;
-- 
GitLab