Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1914)

Unified Diff: chrome/renderer/media/cast_rtp_stream.cc

Issue 1849003002: Add video frame refresh to MediaStream and VideoCapture stacks. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/renderer/media/cast_rtp_stream.cc
diff --git a/chrome/renderer/media/cast_rtp_stream.cc b/chrome/renderer/media/cast_rtp_stream.cc
index aaa9f691d00ed952726fd598d2f236abfcaf28da..b4f92f4697a97588a48d65829e29ceb82666962d 100644
--- a/chrome/renderer/media/cast_rtp_stream.cc
+++ b/chrome/renderer/media/cast_rtp_stream.cc
@@ -9,13 +9,17 @@
#include <utility>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/timer/timer.h"
#include "base/trace_event/trace_event.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/renderer/media/cast_session.h"
@@ -49,6 +53,14 @@ const char kCodecNameH264[] = "H264";
// To convert from kilobits per second to bits to per second.
const int kBitrateMultiplier = 1000;
+// The maximum number of milliseconds that should elapse since the last video
+// frame was received from the video source, before requesting refresh frames.
+const int kRefreshIntervalMilliseconds = 250;
+
+// The maximum number of refresh video frames to request/receive. After this
+// limit (60 * 250ms = 15 seconds), refresh frame requests will stop being made.
+const int kMaxConsecutiveRefreshFrames = 60;
+
CastRtpPayloadParams DefaultOpusPayload() {
CastRtpPayloadParams payload;
payload.payload_type = media::cast::kDefaultRtpAudioPayloadType;
@@ -301,11 +313,17 @@ bool ToVideoSenderConfig(const CastRtpParams& params,
} // namespace
// This class receives MediaStreamTrack events and video frames from a
-// MediaStreamTrack.
+// MediaStreamVideoTrack. It also includes a timer to request refresh frames
+// when the capturer halts (e.g., a screen capturer stops delivering frames
+// because the screen is not being updated). When a halt is detected, refresh
+// frames will be requested at regular intervals for a short period of time.
+// This provides the video encoder, downstream, several copies of the last frame
+// so that it may clear up lossy encoding artifacts.
//
// Threading: Video frames are received on the IO thread and then
-// forwarded to media::cast::VideoFrameInput through a static method.
-// Member variables of this class are only accessed on the render thread.
+// forwarded to media::cast::VideoFrameInput. The inner class, Deliverer,
+// handles this. Otherwise, all methods and member variables of the outer class
+// must only be accessed on the render thread.
class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>,
public content::MediaStreamVideoSink {
public:
@@ -313,66 +331,130 @@ class CastVideoSink : public base::SupportsWeakPtr<CastVideoSink>,
// |error_callback| is called if video formats don't match.
CastVideoSink(const blink::WebMediaStreamTrack& track,
const CastRtpStream::ErrorCallback& error_callback)
- : track_(track),
- sink_added_(false),
- error_callback_(error_callback) {}
+ : track_(track), deliverer_(new Deliverer(error_callback)),
+ consecutive_refresh_count_(0),
+ expecting_a_refresh_frame_(false) {}
~CastVideoSink() override {
- if (sink_added_)
- RemoveFromVideoTrack(this, track_);
- }
-
- // This static method is used to forward video frames to |frame_input|.
- static void OnVideoFrame(
- // These parameters are already bound when callback is created.
- const CastRtpStream::ErrorCallback& error_callback,
- const scoped_refptr<media::cast::VideoFrameInput> frame_input,
- // These parameters are passed for each frame.
- const scoped_refptr<media::VideoFrame>& video_frame,
- base::TimeTicks estimated_capture_time) {
- const base::TimeTicks timestamp = estimated_capture_time.is_null()
- ? base::TimeTicks::Now()
- : estimated_capture_time;
-
- if (!(video_frame->format() == media::PIXEL_FORMAT_I420 ||
- video_frame->format() == media::PIXEL_FORMAT_YV12 ||
- video_frame->format() == media::PIXEL_FORMAT_YV12A)) {
- NOTREACHED();
- return;
- }
- scoped_refptr<media::VideoFrame> frame = video_frame;
- // Drop alpha channel since we do not support it yet.
- if (frame->format() == media::PIXEL_FORMAT_YV12A)
- frame = media::WrapAsI420VideoFrame(video_frame);
-
- // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
- TRACE_EVENT_INSTANT2(
- "cast_perf_test", "MediaStreamVideoSink::OnVideoFrame",
- TRACE_EVENT_SCOPE_THREAD,
- "timestamp", timestamp.ToInternalValue(),
- "time_delta", frame->timestamp().ToInternalValue());
- frame_input->InsertRawVideoFrame(frame, timestamp);
+ MediaStreamVideoSink::DisconnectFromTrack();
}
// Attach this sink to a video track represented by |track_|.
// Data received from the track will be submitted to |frame_input|.
void AddToTrack(
const scoped_refptr<media::cast::VideoFrameInput>& frame_input) {
- DCHECK(!sink_added_);
- sink_added_ = true;
- AddToVideoTrack(
- this,
- base::Bind(
- &CastVideoSink::OnVideoFrame,
- error_callback_,
- frame_input),
- track_);
+ DCHECK(deliverer_);
+ deliverer_->WillConnectToTrack(AsWeakPtr(), frame_input);
+ refresh_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kRefreshIntervalMilliseconds),
+ base::Bind(&CastVideoSink::OnRefreshTimerFired,
+ base::Unretained(this)));
+ MediaStreamVideoSink::ConnectToTrack(track_,
+ base::Bind(&Deliverer::OnVideoFrame,
+ deliverer_));
}
private:
- blink::WebMediaStreamTrack track_;
- bool sink_added_;
- CastRtpStream::ErrorCallback error_callback_;
+ class Deliverer : public base::RefCountedThreadSafe<Deliverer> {
+ public:
+ explicit Deliverer(const CastRtpStream::ErrorCallback& error_callback)
+ : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ error_callback_(error_callback) {}
+
+ void WillConnectToTrack(
+ base::WeakPtr<CastVideoSink> sink,
+ scoped_refptr<media::cast::VideoFrameInput> frame_input) {
+ DCHECK(main_task_runner_->RunsTasksOnCurrentThread());
+ sink_ = sink;
+ frame_input_ = std::move(frame_input);
+ }
+
+ void OnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame,
+ base::TimeTicks estimated_capture_time) {
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&CastVideoSink::DidReceiveFrame, sink_));
+
+ const base::TimeTicks timestamp = estimated_capture_time.is_null()
+ ? base::TimeTicks::Now()
+ : estimated_capture_time;
+
+ if (!(video_frame->format() == media::PIXEL_FORMAT_I420 ||
+ video_frame->format() == media::PIXEL_FORMAT_YV12 ||
+ video_frame->format() == media::PIXEL_FORMAT_YV12A)) {
+ error_callback_.Run("Incompatible video frame format.");
+ return;
+ }
+ scoped_refptr<media::VideoFrame> frame = video_frame;
+ // Drop alpha channel since we do not support it yet.
+ if (frame->format() == media::PIXEL_FORMAT_YV12A)
+ frame = media::WrapAsI420VideoFrame(video_frame);
+
+ // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
+ TRACE_EVENT_INSTANT2(
+ "cast_perf_test", "MediaStreamVideoSink::OnVideoFrame",
+ TRACE_EVENT_SCOPE_THREAD,
+ "timestamp", timestamp.ToInternalValue(),
+ "time_delta", frame->timestamp().ToInternalValue());
+ frame_input_->InsertRawVideoFrame(frame, timestamp);
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<Deliverer>;
+ ~Deliverer() {}
+
+ const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+ const CastRtpStream::ErrorCallback error_callback_;
+
+ // These are set on the main thread after construction, and before the first
+ // call to OnVideoFrame() on the IO thread. |sink_| may be passed around on
+ // any thread, but must only be dereferenced on the main renderer thread.
+ base::WeakPtr<CastVideoSink> sink_;
+ scoped_refptr<media::cast::VideoFrameInput> frame_input_;
+
+ DISALLOW_COPY_AND_ASSIGN(Deliverer);
+ };
+
+ private:
+ void OnRefreshTimerFired() {
+ ++consecutive_refresh_count_;
+ if (consecutive_refresh_count_ >= kMaxConsecutiveRefreshFrames)
+ refresh_timer_.Stop(); // Stop timer until receiving a non-refresh frame.
+
+ DVLOG(1) << "CastVideoSink is requesting another refresh frame "
+ "(consecutive count=" << consecutive_refresh_count_ << ").";
+ expecting_a_refresh_frame_ = true;
+ MediaStreamVideoSink::RequestRefreshFrame();
+ }
+
+ void DidReceiveFrame() {
+ if (expecting_a_refresh_frame_) {
+ // There is uncertainty as to whether the video frame was in response to a
+ // refresh request. However, if it was not, more video frames will soon
+ // follow, and before the refresh timer can fire again. Thus, the
+ // behavior resulting from this logic will be correct.
+ expecting_a_refresh_frame_ = false;
+ } else {
+ consecutive_refresh_count_ = 0;
+ // The following re-starts the timer, scheduling it to fire at
+ // kRefreshIntervalMilliseconds from now.
+ refresh_timer_.Reset();
+ }
+ }
+
+ const blink::WebMediaStreamTrack track_;
+ const scoped_refptr<Deliverer> deliverer_;
+
+ // Requests refresh frames at a constant rate while the source is paused, up
+ // to a consecutive maximum.
+ base::RepeatingTimer refresh_timer_;
+
+ // Counter for the number of consecutive "refresh frames" received.
xjz 2016/04/01 19:10:04 nit: s/received/requested.
miu 2016/04/01 23:24:27 Done. Good catch.
+ int consecutive_refresh_count_;
+
+ // Set to true when a request for a refresh frame has been made. This is
+ // cleared once the next frame is received.
+ bool expecting_a_refresh_frame_;
DISALLOW_COPY_AND_ASSIGN(CastVideoSink);
};

Powered by Google App Engine
This is Rietveld 408576698