| Index: content/renderer/media/video_track_recorder.cc
|
| diff --git a/content/renderer/media/video_track_recorder.cc b/content/renderer/media/video_track_recorder.cc
|
| index b5aaa72e0b1ab43fe8f68c94946f626ed889271f..84ce844ca5adac2a0e12149e9d46854cc39cb1ac 100644
|
| --- a/content/renderer/media/video_track_recorder.cc
|
| +++ b/content/renderer/media/video_track_recorder.cc
|
| @@ -10,8 +10,6 @@
|
| #include "base/threading/thread.h"
|
| #include "base/time/time.h"
|
| #include "base/trace_event/trace_event.h"
|
| -#include "content/child/child_process.h"
|
| -#include "media/base/bind_to_current_loop.h"
|
| #include "media/base/video_frame.h"
|
|
|
| extern "C" {
|
| @@ -28,16 +26,31 @@ using media::VideoFrameMetadata;
|
| namespace content {
|
|
|
| namespace {
|
| +
|
| const vpx_codec_flags_t kNoFlags = 0;
|
|
|
| +// Originally from remoting/codec/scoped_vpx_codec.h.
|
| +// TODO(mcasas): Refactor into a common location.
|
| +struct VpxCodecDeleter {
|
| + void operator()(vpx_codec_ctx_t* codec) {
|
| + if (!codec)
|
| + return;
|
| + vpx_codec_err_t ret = vpx_codec_destroy(codec);
|
| + CHECK_EQ(ret, VPX_CODEC_OK);
|
| + delete codec;
|
| + }
|
| +};
|
| +
|
| +typedef scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> ScopedVpxCodecCtxPtr;
|
| +
|
| void OnFrameEncodeCompleted(
|
| - const content::VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
|
| + const VideoTrackRecorder::OnEncodedVideoCB& on_encoded_video_cb,
|
| const scoped_refptr<VideoFrame>& frame,
|
| scoped_ptr<std::string> data,
|
| base::TimeTicks capture_timestamp,
|
| bool keyframe) {
|
| - DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "
|
| - << capture_timestamp << " ms - " << data->length() << "B ";
|
| + DVLOG(1) << (keyframe ? "" : "non ") << "keyframe "<< data->length() << "B, "
|
| + << capture_timestamp << " ms";
|
| on_encoded_video_cb.Run(frame, base::StringPiece(*data), capture_timestamp,
|
| keyframe);
|
| }
|
| @@ -45,23 +58,32 @@ void OnFrameEncodeCompleted(
|
| } // anonymous namespace
|
|
|
| // Inner class encapsulating all libvpx interactions and the encoding+delivery
|
| -// of received frames. This class is:
|
| -// - created and destroyed on its parent's thread (usually the main render
|
| -// thread),
|
| -// - receives VideoFrames and Run()s the callbacks on another thread (supposedly
|
| -// the render IO thread), which is cached on first frame arrival,
|
| +// of received frames. Limitation: Only VP8 is supported for the time being.
|
| +// This class must be ref-counted because the MediaStreamVideoTrack will hold a
|
| +// reference to it, via the callback MediaStreamVideoSink passes along, and it's
|
| +// unknown when exactly it will release that reference. This class:
|
| +// - is created and destroyed on its parent's thread (usually the main Render
|
| +// thread);
|
| +// - receives VideoFrames and Run()s the callbacks on |origin_task_runner_|,
|
| +// which is cached on first frame arrival, and is supposed to be the render IO
|
| +// thread, but this is not enforced;
|
| // - uses an internal |encoding_thread_| for libvpx interactions, notably for
|
| // encoding (which might take some time).
|
| -// Only VP8 is supported for the time being.
|
| -class VideoTrackRecorder::VpxEncoder final {
|
| +class VideoTrackRecorder::VpxEncoder final
|
| + : public base::RefCountedThreadSafe<VpxEncoder> {
|
| public:
|
| + static void ShutdownEncoder(scoped_ptr<base::Thread> encoding_thread,
|
| + ScopedVpxCodecCtxPtr encoder);
|
| +
|
| explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback);
|
| - ~VpxEncoder();
|
|
|
| void StartFrameEncode(const scoped_refptr<VideoFrame>& frame,
|
| base::TimeTicks capture_timestamp);
|
|
|
| private:
|
| + friend class base::RefCountedThreadSafe<VpxEncoder>;
|
| + ~VpxEncoder();
|
| +
|
| void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame,
|
| base::TimeTicks capture_timestamp);
|
|
|
| @@ -74,8 +96,8 @@ class VideoTrackRecorder::VpxEncoder final {
|
| base::TimeDelta CalculateFrameDuration(
|
| const scoped_refptr<VideoFrame>& frame);
|
|
|
| - // Used to check that we are destroyed on the same thread we were created.
|
| - base::ThreadChecker main_render_thread_checker_;
|
| + // Used to shutdown properly on the same thread we were created.
|
| + const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
|
|
|
| // Task runner where frames to encode and reply callbacks must happen.
|
| scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
|
| @@ -83,12 +105,14 @@ class VideoTrackRecorder::VpxEncoder final {
|
| // This callback should be exercised on IO thread.
|
| const OnEncodedVideoCB on_encoded_video_callback_;
|
|
|
| - // Thread for encoding. Active as long as VpxEncoder exists. All variables
|
| + // Thread for encoding. Active for the lifetime of VpxEncoder. All variables
|
| // below this are used in this thread.
|
| - base::Thread encoding_thread_;
|
| - // VP8 internal objects: configuration, encoder and Vpx Image wrapper.
|
| + scoped_ptr<base::Thread> encoding_thread_;
|
| + // VP8 internal objects: configuration and encoder.
|
| vpx_codec_enc_cfg_t codec_config_;
|
| - vpx_codec_ctx_t encoder_;
|
| + // |encoder_| is a special scoped pointer to guarantee proper destruction.
|
| + // Again, it should only be accessed on |encoding_thread_|.
|
| + ScopedVpxCodecCtxPtr encoder_;
|
|
|
| // The |VideoFrame::timestamp()| of the last encoded frame. This is used to
|
| // predict the duration of the next frame.
|
| @@ -97,23 +121,33 @@ class VideoTrackRecorder::VpxEncoder final {
|
| DISALLOW_COPY_AND_ASSIGN(VpxEncoder);
|
| };
|
|
|
| +// static
|
| +void VideoTrackRecorder::VpxEncoder::ShutdownEncoder(
|
| + scoped_ptr<base::Thread> encoding_thread,
|
| + ScopedVpxCodecCtxPtr encoder) {
|
| + DCHECK(encoding_thread->IsRunning());
|
| + encoding_thread->Stop();
|
| + // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope.
|
| +}
|
| +
|
| VideoTrackRecorder::VpxEncoder::VpxEncoder(
|
| const OnEncodedVideoCB& on_encoded_video_callback)
|
| - : on_encoded_video_callback_(on_encoded_video_callback),
|
| - encoding_thread_("EncodingThread") {
|
| + : main_task_runner_(base::MessageLoop::current()->task_runner()),
|
| + on_encoded_video_callback_(on_encoded_video_callback),
|
| + encoding_thread_(new base::Thread("EncodingThread")) {
|
| DCHECK(!on_encoded_video_callback_.is_null());
|
|
|
| codec_config_.g_timebase.den = 0; // Not initialized.
|
|
|
| - DCHECK(!encoding_thread_.IsRunning());
|
| - encoding_thread_.Start();
|
| + DCHECK(!encoding_thread_->IsRunning());
|
| + encoding_thread_->Start();
|
| }
|
|
|
| VideoTrackRecorder::VpxEncoder::~VpxEncoder() {
|
| - DCHECK(main_render_thread_checker_.CalledOnValidThread());
|
| - DCHECK(encoding_thread_.IsRunning());
|
| - encoding_thread_.Stop();
|
| - vpx_codec_destroy(&encoder_);
|
| + main_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&VpxEncoder::ShutdownEncoder,
|
| + base::Passed(&encoding_thread_),
|
| + base::Passed(&encoder_)));
|
| }
|
|
|
| void VideoTrackRecorder::VpxEncoder::StartFrameEncode(
|
| @@ -124,9 +158,9 @@ void VideoTrackRecorder::VpxEncoder::StartFrameEncode(
|
| origin_task_runner_ = base::MessageLoop::current()->task_runner();
|
| DCHECK(origin_task_runner_->BelongsToCurrentThread());
|
|
|
| - encoding_thread_.task_runner()->PostTask(
|
| + encoding_thread_->task_runner()->PostTask(
|
| FROM_HERE, base::Bind(&VpxEncoder::EncodeOnEncodingThread,
|
| - base::Unretained(this), frame, capture_timestamp));
|
| + this, frame, capture_timestamp));
|
| }
|
|
|
| void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread(
|
| @@ -134,7 +168,7 @@ void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread(
|
| base::TimeTicks capture_timestamp) {
|
| TRACE_EVENT0("video",
|
| "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread");
|
| - DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread());
|
| + DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread());
|
|
|
| const gfx::Size frame_size = frame->visible_rect().size();
|
| if (!IsInitialized() ||
|
| @@ -161,21 +195,21 @@ void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread(
|
| // Encode the frame. The presentation time stamp argument here is fixed to
|
| // zero to force the encoder to base its single-frame bandwidth calculations
|
| // entirely on |predicted_frame_duration|.
|
| - const vpx_codec_err_t ret = vpx_codec_encode(&encoder_,
|
| + const vpx_codec_err_t ret = vpx_codec_encode(encoder_.get(),
|
| &vpx_image,
|
| 0 /* pts */,
|
| duration.InMicroseconds(),
|
| kNoFlags,
|
| VPX_DL_REALTIME);
|
| DCHECK_EQ(ret, VPX_CODEC_OK) << vpx_codec_err_to_string(ret) << ", #"
|
| - << vpx_codec_error(&encoder_) << " -"
|
| - << vpx_codec_error_detail(&encoder_);
|
| + << vpx_codec_error(encoder_.get()) << " -"
|
| + << vpx_codec_error_detail(encoder_.get());
|
|
|
| scoped_ptr<std::string> data(new std::string);
|
| bool keyframe = false;
|
| vpx_codec_iter_t iter = NULL;
|
| const vpx_codec_cx_pkt_t* pkt = NULL;
|
| - while ((pkt = vpx_codec_get_cx_data(&encoder_, &iter)) != NULL) {
|
| + while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) {
|
| if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
|
| continue;
|
| data->assign(static_cast<char*>(pkt->data.frame.buf), pkt->data.frame.sz);
|
| @@ -183,7 +217,7 @@ void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread(
|
| break;
|
| }
|
| origin_task_runner_->PostTask(FROM_HERE,
|
| - base::Bind(&OnFrameEncodeCompleted,
|
| + base::Bind(OnFrameEncodeCompleted,
|
| on_encoded_video_callback_,
|
| frame,
|
| base::Passed(&data),
|
| @@ -200,7 +234,7 @@ void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding(
|
| DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: "
|
| << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString()
|
| << " --> " << size.ToString();
|
| - vpx_codec_destroy(&encoder_);
|
| + encoder_.reset();
|
| }
|
|
|
| const vpx_codec_iface_t* interface = vpx_codec_vp8_cx();
|
| @@ -246,19 +280,21 @@ void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding(
|
| // Number of frames to consume before producing output.
|
| codec_config_.g_lag_in_frames = 0;
|
|
|
| - const vpx_codec_err_t ret = vpx_codec_enc_init(&encoder_, interface,
|
| + DCHECK(!encoder_);
|
| + encoder_.reset(new vpx_codec_ctx_t);
|
| + const vpx_codec_err_t ret = vpx_codec_enc_init(encoder_.get(), interface,
|
| &codec_config_, kNoFlags);
|
| DCHECK_EQ(VPX_CODEC_OK, ret);
|
| }
|
|
|
| bool VideoTrackRecorder::VpxEncoder::IsInitialized() const {
|
| - DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread());
|
| + DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread());
|
| return codec_config_.g_timebase.den != 0;
|
| }
|
|
|
| base::TimeDelta VideoTrackRecorder::VpxEncoder::CalculateFrameDuration(
|
| const scoped_refptr<VideoFrame>& frame) {
|
| - DCHECK(encoding_thread_.task_runner()->BelongsToCurrentThread());
|
| + DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread());
|
|
|
| using base::TimeDelta;
|
| TimeDelta predicted_frame_duration;
|
| @@ -285,28 +321,27 @@ VideoTrackRecorder::VideoTrackRecorder(
|
| const blink::WebMediaStreamTrack& track,
|
| const OnEncodedVideoCB& on_encoded_video_callback)
|
| : track_(track),
|
| - encoder_(new VpxEncoder(on_encoded_video_callback)),
|
| - weak_factory_(this) {
|
| + encoder_(new VpxEncoder(on_encoded_video_callback)) {
|
| DCHECK(main_render_thread_checker_.CalledOnValidThread());
|
| DCHECK(!track_.isNull());
|
| DCHECK(track.extraData());
|
| +
|
| + // StartFrameEncode() will be called on Render IO thread.
|
| AddToVideoTrack(this,
|
| - media::BindToCurrentLoop(
|
| - base::Bind(&VideoTrackRecorder::OnVideoFrame,
|
| - weak_factory_.GetWeakPtr())),
|
| + base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode,
|
| + encoder_),
|
| track_);
|
| }
|
|
|
| VideoTrackRecorder::~VideoTrackRecorder() {
|
| DCHECK(main_render_thread_checker_.CalledOnValidThread());
|
| RemoveFromVideoTrack(this, track_);
|
| - weak_factory_.InvalidateWeakPtrs();
|
| track_.reset();
|
| }
|
|
|
| -void VideoTrackRecorder::OnVideoFrame(const scoped_refptr<VideoFrame>& frame,
|
| - base::TimeTicks timestamp) {
|
| - DCHECK(main_render_thread_checker_.CalledOnValidThread());
|
| +void VideoTrackRecorder::OnVideoFrameForTesting(
|
| + const scoped_refptr<media::VideoFrame>& frame,
|
| + base::TimeTicks timestamp) {
|
| encoder_->StartFrameEncode(frame, timestamp);
|
| }
|
|
|
|
|