From 950ce8f142bedd2d6b8e5319f3519f897b01d9cf Mon Sep 17 00:00:00 2001
From: philippegorley <philippe.gorley@savoirfairelinux.com>
Date: Sun, 26 Nov 2017 19:53:32 -0500
Subject: [PATCH] video: request keyframe on missed packet

Keyframes will only be sent on request instead of a timely interval.
This may interfere with clients that do not yet have this patch.

Change-Id: Ie786160744257052ef9d103d50fe46cd90138927
Reviewed-by: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
---
 src/media/media_encoder.cpp              |  2 ++
 src/media/rtp_session.h                  |  2 +-
 src/media/video/video_receive_thread.cpp | 18 ++++++++++++++++--
 src/media/video/video_receive_thread.h   |  3 ++-
 src/media/video/video_rtp_session.cpp    |  6 ++----
 src/media/video/video_sender.cpp         |  4 ++--
 src/media/video/video_sender.h           |  3 ++-
 7 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index d9e288f950..5d66871b29 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -285,8 +285,10 @@ MediaEncoder::encode(VideoFrame& input, bool is_keyframe,
 
     if (is_keyframe) {
         frame->pict_type = AV_PICTURE_TYPE_I;
+        frame->key_frame = 1;
     } else {
         frame->pict_type = AV_PICTURE_TYPE_NONE;
+        frame->key_frame = 0;
     }
 
     AVPacket pkt;
diff --git a/src/media/rtp_session.h b/src/media/rtp_session.h
index 2a80154ccd..7f17dc9560 100644
--- a/src/media/rtp_session.h
+++ b/src/media/rtp_session.h
@@ -57,7 +57,7 @@ public:
 
 protected:
     std::recursive_mutex mutex_;
-    std::unique_ptr<SocketPair> socketPair_;
+    std::shared_ptr<SocketPair> socketPair_;
     const std::string callID_;
 
     MediaDescription send_;
diff --git a/src/media/video/video_receive_thread.cpp b/src/media/video/video_receive_thread.cpp
index f9397beef1..b40fed421a 100644
--- a/src/media/video/video_receive_thread.cpp
+++ b/src/media/video/video_receive_thread.cpp
@@ -36,6 +36,8 @@ namespace ring { namespace video {
 
 using std::string;
 
+static constexpr uint32_t RTCP_RR_FRACTION_MASK = 0xFF000000;
+
 VideoReceiveThread::VideoReceiveThread(const std::string& id,
                                        const std::string &sdp,
                                        const bool isReset,
@@ -162,15 +164,27 @@ int VideoReceiveThread::readFunction(void *opaque, uint8_t *buf, int buf_size)
     return is.gcount();
 }
 
-void VideoReceiveThread::addIOContext(SocketPair &socketPair)
+void VideoReceiveThread::addIOContext(std::shared_ptr<SocketPair> socketPair)
 {
-    demuxContext_.reset(socketPair.createIOContext(mtu_));
+    demuxContext_.reset(socketPair->createIOContext(mtu_));
+    socketPair_ = socketPair;
 }
 
 bool VideoReceiveThread::decodeFrame()
 {
     const auto ret = videoDecoder_->decode(getNewFrame());
 
+    if (requestKeyFrameCallback_) {
+        auto rtcpPackets = socketPair_->getRtcpInfo();
+        if (rtcpPackets.size() != 0) {
+            // recent packet loss detected, ask for keyframe
+            if (ntohl(rtcpPackets.back().fraction_lost) & RTCP_RR_FRACTION_MASK) {
+                RING_DBG("Sending keyframe request");
+                requestKeyFrameCallback_(id_);
+            }
+        }
+    }
+
     switch (ret) {
         case MediaDecoder::Status::FrameFinished:
             publishFrame();
diff --git a/src/media/video/video_receive_thread.h b/src/media/video/video_receive_thread.h
index 5b2253023a..08d84cdaaa 100644
--- a/src/media/video/video_receive_thread.h
+++ b/src/media/video/video_receive_thread.h
@@ -51,7 +51,7 @@ public:
     ~VideoReceiveThread();
     void startLoop();
 
-    void addIOContext(SocketPair &socketPair);
+    void addIOContext(std::shared_ptr<SocketPair> socketPair);
     void setRequestKeyFrameCallback(void (*)(const std::string &));
     void enterConference();
     void exitConference();
@@ -81,6 +81,7 @@ private:
     std::atomic_bool restartDecoder_;
     bool isReset_;
     uint16_t mtu_;
+    std::shared_ptr<SocketPair> socketPair_;
 
     void (*requestKeyFrameCallback_)(const std::string &);
     void openDecoder();
diff --git a/src/media/video/video_rtp_session.cpp b/src/media/video/video_rtp_session.cpp
index db9cad6ddf..5d35eb9018 100644
--- a/src/media/video/video_rtp_session.cpp
+++ b/src/media/video/video_rtp_session.cpp
@@ -154,12 +154,10 @@ void VideoRtpSession::startReceiver()
                              new VideoReceiveThread(callID_, receive_.receiving_sdp, isReset, mtu_)
         );
 
-        /* ebail: keyframe requests can lead to timeout if they are not answered.
-         * we decided so to disable them for the moment
+        // XXX keyframe requests can timeout if unanswered
         receiveThread_->setRequestKeyFrameCallback(&SIPVoIPLink::enqueueKeyframeRequest);
-        */
         receiverRestartThread_.start();
-        receiveThread_->addIOContext(*socketPair_);
+        receiveThread_->addIOContext(socketPair_);
         receiveThread_->startLoop();
     } else {
         RING_DBG("Video receiving disabled");
diff --git a/src/media/video/video_sender.cpp b/src/media/video/video_sender.cpp
index c8246c29f4..b0c3bae157 100644
--- a/src/media/video/video_sender.cpp
+++ b/src/media/video/video_sender.cpp
@@ -61,7 +61,7 @@ VideoSender::~VideoSender()
 void
 VideoSender::encodeAndSendVideo(VideoFrame& input_frame)
 {
-    bool is_keyframe = forceKeyFrame_ > 0 \
+    bool is_keyframe = forceKeyFrame_ > 0
         or (keyFrameFreq_ > 0 and (frameNumber_ % keyFrameFreq_) == 0);
 
     if (is_keyframe)
@@ -84,7 +84,7 @@ VideoSender::update(Observable<std::shared_ptr<VideoFrame>>* /*obs*/,
 void
 VideoSender::forceKeyFrame()
 {
-    RING_DBG("keyframe requested");
+    RING_DBG("Peer has requested a key frame");
     ++forceKeyFrame_;
 }
 
diff --git a/src/media/video/video_sender.h b/src/media/video/video_sender.h
index 42562e6c0b..01419a65d1 100644
--- a/src/media/video/video_sender.h
+++ b/src/media/video/video_sender.h
@@ -64,7 +64,7 @@ public:
 
 private:
     static constexpr int KEYFRAMES_AT_START {4}; // Number of keyframes to enforce at stream startup
-    static constexpr unsigned KEY_FRAME_PERIOD {5}; // seconds before forcing a keyframe
+    static constexpr unsigned KEY_FRAME_PERIOD {0}; // seconds before forcing a keyframe
 
     NON_COPYABLE(VideoSender);
 
@@ -74,6 +74,7 @@ private:
     std::unique_ptr<MediaIOHandle> muxContext_ = nullptr;
     std::unique_ptr<MediaEncoder> videoEncoder_ = nullptr;
 
+    // XXX forceKeyFrame_ is always at -1, incremented to 0 when a keyframe is requested (still works though)
     std::atomic<int> forceKeyFrame_ {KEYFRAMES_AT_START};
     int keyFrameFreq_ {0}; // Set keyframe rate, 0 to disable auto-keyframe. Computed in constructor
     int64_t frameNumber_ = 0;
-- 
GitLab