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

Unified Diff: content/renderer/media/video_track_recorder.cc

Issue 1313603004: MediaRecorderHandler (video part) and unittests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved DCHECK_EQ() Created 5 years, 3 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: 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);
}
« no previous file with comments | « content/renderer/media/video_track_recorder.h ('k') | content/renderer/media/video_track_recorder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698