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

Side by Side Diff: content/renderer/media/video_track_recorder.cc

Issue 1384483005: MediaStream Recorder: Support VP9 encoder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: miu@s comments Created 5 years, 2 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/video_track_recorder.h" 5 #include "content/renderer/media/video_track_recorder.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/sys_info.h" 9 #include "base/sys_info.h"
10 #include "base/threading/thread.h" 10 #include "base/threading/thread.h"
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 // which is cached on first frame arrival, and is supposed to be the render IO 68 // which is cached on first frame arrival, and is supposed to be the render IO
69 // thread, but this is not enforced; 69 // thread, but this is not enforced;
70 // - uses an internal |encoding_thread_| for libvpx interactions, notably for 70 // - uses an internal |encoding_thread_| for libvpx interactions, notably for
71 // encoding (which might take some time). 71 // encoding (which might take some time).
72 class VideoTrackRecorder::VpxEncoder final 72 class VideoTrackRecorder::VpxEncoder final
73 : public base::RefCountedThreadSafe<VpxEncoder> { 73 : public base::RefCountedThreadSafe<VpxEncoder> {
74 public: 74 public:
75 static void ShutdownEncoder(scoped_ptr<base::Thread> encoding_thread, 75 static void ShutdownEncoder(scoped_ptr<base::Thread> encoding_thread,
76 ScopedVpxCodecCtxPtr encoder); 76 ScopedVpxCodecCtxPtr encoder);
77 77
78 explicit VpxEncoder(const OnEncodedVideoCB& on_encoded_video_callback); 78 VpxEncoder(bool use_vp9, const OnEncodedVideoCB& on_encoded_video_callback);
79 79
80 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame, 80 void StartFrameEncode(const scoped_refptr<VideoFrame>& frame,
81 base::TimeTicks capture_timestamp); 81 base::TimeTicks capture_timestamp);
82 82
83 private: 83 private:
84 friend class base::RefCountedThreadSafe<VpxEncoder>; 84 friend class base::RefCountedThreadSafe<VpxEncoder>;
85 ~VpxEncoder(); 85 ~VpxEncoder();
86 86
87 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame, 87 void EncodeOnEncodingThread(const scoped_refptr<VideoFrame>& frame,
88 base::TimeTicks capture_timestamp); 88 base::TimeTicks capture_timestamp);
89 89
90 void ConfigureVp8Encoding(const gfx::Size& size); 90 void ConfigureEncoding(const gfx::Size& size);
91 91
92 // Returns true if |codec_config_| has been filled in at least once. 92 // Returns true if |codec_config_| has been filled in at least once.
93 bool IsInitialized() const; 93 bool IsInitialized() const;
94 94
95 // Estimate the frame duration from |frame| and |last_frame_timestamp_|. 95 // Estimate the frame duration from |frame| and |last_frame_timestamp_|.
96 base::TimeDelta CalculateFrameDuration( 96 base::TimeDelta CalculateFrameDuration(
97 const scoped_refptr<VideoFrame>& frame); 97 const scoped_refptr<VideoFrame>& frame);
98 98
99 // Force usage of VP9 for encoding, instead of VP8 which is the default.
100 const bool use_vp9_;
101
99 // Used to shutdown properly on the same thread we were created. 102 // Used to shutdown properly on the same thread we were created.
100 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; 103 const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
101 104
102 // Task runner where frames to encode and reply callbacks must happen. 105 // Task runner where frames to encode and reply callbacks must happen.
103 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; 106 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
104 107
105 // This callback should be exercised on IO thread. 108 // This callback should be exercised on IO thread.
106 const OnEncodedVideoCB on_encoded_video_callback_; 109 const OnEncodedVideoCB on_encoded_video_callback_;
107 110
108 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables 111 // Thread for encoding. Active for the lifetime of VpxEncoder. All variables
(...skipping 15 matching lines...) Expand all
124 // static 127 // static
125 void VideoTrackRecorder::VpxEncoder::ShutdownEncoder( 128 void VideoTrackRecorder::VpxEncoder::ShutdownEncoder(
126 scoped_ptr<base::Thread> encoding_thread, 129 scoped_ptr<base::Thread> encoding_thread,
127 ScopedVpxCodecCtxPtr encoder) { 130 ScopedVpxCodecCtxPtr encoder) {
128 DCHECK(encoding_thread->IsRunning()); 131 DCHECK(encoding_thread->IsRunning());
129 encoding_thread->Stop(); 132 encoding_thread->Stop();
130 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope. 133 // Both |encoding_thread| and |encoder| will be destroyed at end-of-scope.
131 } 134 }
132 135
133 VideoTrackRecorder::VpxEncoder::VpxEncoder( 136 VideoTrackRecorder::VpxEncoder::VpxEncoder(
137 bool use_vp9,
134 const OnEncodedVideoCB& on_encoded_video_callback) 138 const OnEncodedVideoCB& on_encoded_video_callback)
135 : main_task_runner_(base::MessageLoop::current()->task_runner()), 139 : use_vp9_(use_vp9),
140 main_task_runner_(base::MessageLoop::current()->task_runner()),
136 on_encoded_video_callback_(on_encoded_video_callback), 141 on_encoded_video_callback_(on_encoded_video_callback),
137 encoding_thread_(new base::Thread("EncodingThread")) { 142 encoding_thread_(new base::Thread("EncodingThread")) {
138 DCHECK(!on_encoded_video_callback_.is_null()); 143 DCHECK(!on_encoded_video_callback_.is_null());
139 144
140 codec_config_.g_timebase.den = 0; // Not initialized. 145 codec_config_.g_timebase.den = 0; // Not initialized.
141 146
142 DCHECK(!encoding_thread_->IsRunning()); 147 DCHECK(!encoding_thread_->IsRunning());
143 encoding_thread_->Start(); 148 encoding_thread_->Start();
144 } 149 }
145 150
(...skipping 20 matching lines...) Expand all
166 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread( 171 void VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread(
167 const scoped_refptr<VideoFrame>& frame, 172 const scoped_refptr<VideoFrame>& frame,
168 base::TimeTicks capture_timestamp) { 173 base::TimeTicks capture_timestamp) {
169 TRACE_EVENT0("video", 174 TRACE_EVENT0("video",
170 "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread"); 175 "VideoTrackRecorder::VpxEncoder::EncodeOnEncodingThread");
171 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread()); 176 DCHECK(encoding_thread_->task_runner()->BelongsToCurrentThread());
172 177
173 const gfx::Size frame_size = frame->visible_rect().size(); 178 const gfx::Size frame_size = frame->visible_rect().size();
174 if (!IsInitialized() || 179 if (!IsInitialized() ||
175 gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) { 180 gfx::Size(codec_config_.g_w, codec_config_.g_h) != frame_size) {
176 ConfigureVp8Encoding(frame_size); 181 ConfigureEncoding(frame_size);
177 } 182 }
178 183
179 vpx_image_t vpx_image; 184 vpx_image_t vpx_image;
180 vpx_image_t* const result = vpx_img_wrap(&vpx_image, 185 vpx_image_t* const result = vpx_img_wrap(&vpx_image,
181 VPX_IMG_FMT_I420, 186 VPX_IMG_FMT_I420,
182 frame_size.width(), 187 frame_size.width(),
183 frame_size.height(), 188 frame_size.height(),
184 1 /* align */, 189 1 /* align */,
185 frame->data(VideoFrame::kYPlane)); 190 frame->data(VideoFrame::kYPlane));
186 DCHECK_EQ(result, &vpx_image); 191 DCHECK_EQ(result, &vpx_image);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 } 223 }
219 origin_task_runner_->PostTask(FROM_HERE, 224 origin_task_runner_->PostTask(FROM_HERE,
220 base::Bind(OnFrameEncodeCompleted, 225 base::Bind(OnFrameEncodeCompleted,
221 on_encoded_video_callback_, 226 on_encoded_video_callback_,
222 frame, 227 frame,
223 base::Passed(&data), 228 base::Passed(&data),
224 capture_timestamp, 229 capture_timestamp,
225 keyframe)); 230 keyframe));
226 } 231 }
227 232
228 void VideoTrackRecorder::VpxEncoder::ConfigureVp8Encoding( 233 void VideoTrackRecorder::VpxEncoder::ConfigureEncoding(const gfx::Size& size) {
229 const gfx::Size& size) {
230 if (IsInitialized()) { 234 if (IsInitialized()) {
231 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less- 235 // TODO(mcasas) VP8 quirk/optimisation: If the new |size| is strictly less-
232 // than-or-equal than the old size, in terms of area, the existing encoder 236 // than-or-equal than the old size, in terms of area, the existing encoder
233 // instance could be reused after changing |codec_config_.{g_w,g_h}|. 237 // instance could be reused after changing |codec_config_.{g_w,g_h}|.
234 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: " 238 DVLOG(1) << "Destroying/Re-Creating encoder for new frame size: "
235 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString() 239 << gfx::Size(codec_config_.g_w, codec_config_.g_h).ToString()
236 << " --> " << size.ToString(); 240 << " --> " << size.ToString() << (use_vp9_ ? " vp9" : " vp8");
237 encoder_.reset(); 241 encoder_.reset();
238 } 242 }
239 243
240 const vpx_codec_iface_t* interface = vpx_codec_vp8_cx(); 244 const vpx_codec_iface_t* interface =
245 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
241 const vpx_codec_err_t result = vpx_codec_enc_config_default(interface, 246 const vpx_codec_err_t result = vpx_codec_enc_config_default(interface,
242 &codec_config_, 247 &codec_config_,
243 0 /* reserved */); 248 0 /* reserved */);
244 DCHECK_EQ(VPX_CODEC_OK, result); 249 DCHECK_EQ(VPX_CODEC_OK, result);
245 250
246 // Adjust default bit rate to account for the actual size. 251 // Adjust default bit rate to account for the actual size.
247 DCHECK_EQ(320u, codec_config_.g_w); 252 DCHECK_EQ(320u, codec_config_.g_w);
248 DCHECK_EQ(240u, codec_config_.g_h); 253 DCHECK_EQ(240u, codec_config_.g_h);
249 DCHECK_EQ(256u, codec_config_.rc_target_bitrate); 254 DCHECK_EQ(256u, codec_config_.rc_target_bitrate);
250 codec_config_.rc_target_bitrate = size.GetArea() * 255 codec_config_.rc_target_bitrate = size.GetArea() *
251 codec_config_.rc_target_bitrate / codec_config_.g_w / codec_config_.g_h; 256 codec_config_.rc_target_bitrate /
257 codec_config_.g_w / codec_config_.g_h;
258 // Both VP8/VP9 configuration should be Variable BitRate by default.
259 DCHECK_EQ(VPX_VBR, codec_config_.rc_end_usage);
260 if (use_vp9_) {
261 // Number of frames to consume before producing output.
262 codec_config_.g_lag_in_frames = 0;
263
264 // DCHECK that the profile selected by default is I420 (magic number 0).
265 DCHECK_EQ(0u, codec_config_.g_profile);
266 } else {
267 // VP8 usually produces frames instantaneously.
miu 2015/10/07 21:56:56 nit: s/usually/always/
mcasas 2015/10/08 18:08:45 Done.
268 DCHECK_EQ(0u, codec_config_.g_lag_in_frames);
269 }
252 270
253 DCHECK(size.width()); 271 DCHECK(size.width());
254 DCHECK(size.height()); 272 DCHECK(size.height());
255 codec_config_.g_w = size.width(); 273 codec_config_.g_w = size.width();
256 codec_config_.g_h = size.height(); 274 codec_config_.g_h = size.height();
257 codec_config_.g_pass = VPX_RC_ONE_PASS; 275 codec_config_.g_pass = VPX_RC_ONE_PASS;
258 276
259 // Timebase is the smallest interval used by the stream, can be set to the 277 // Timebase is the smallest interval used by the stream, can be set to the
260 // frame rate or to e.g. microseconds. 278 // frame rate or to e.g. microseconds.
261 codec_config_.g_timebase.num = 1; 279 codec_config_.g_timebase.num = 1;
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 } 329 }
312 last_frame_timestamp_ = frame->timestamp(); 330 last_frame_timestamp_ = frame->timestamp();
313 // Make sure |predicted_frame_duration| is in a safe range of values. 331 // Make sure |predicted_frame_duration| is in a safe range of values.
314 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8); 332 const TimeDelta kMaxFrameDuration = TimeDelta::FromSecondsD(1.0 / 8);
315 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1); 333 const TimeDelta kMinFrameDuration = TimeDelta::FromMilliseconds(1);
316 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration, 334 return std::min(kMaxFrameDuration, std::max(predicted_frame_duration,
317 kMinFrameDuration)); 335 kMinFrameDuration));
318 } 336 }
319 337
320 VideoTrackRecorder::VideoTrackRecorder( 338 VideoTrackRecorder::VideoTrackRecorder(
339 bool use_vp9,
321 const blink::WebMediaStreamTrack& track, 340 const blink::WebMediaStreamTrack& track,
322 const OnEncodedVideoCB& on_encoded_video_callback) 341 const OnEncodedVideoCB& on_encoded_video_callback)
323 : track_(track), 342 : track_(track),
324 encoder_(new VpxEncoder(on_encoded_video_callback)) { 343 encoder_(new VpxEncoder(use_vp9, on_encoded_video_callback)) {
325 DCHECK(main_render_thread_checker_.CalledOnValidThread()); 344 DCHECK(main_render_thread_checker_.CalledOnValidThread());
326 DCHECK(!track_.isNull()); 345 DCHECK(!track_.isNull());
327 DCHECK(track_.extraData()); 346 DCHECK(track_.extraData());
328 347
329 // StartFrameEncode() will be called on Render IO thread. 348 // StartFrameEncode() will be called on Render IO thread.
330 AddToVideoTrack(this, 349 AddToVideoTrack(this,
331 base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode, 350 base::Bind(&VideoTrackRecorder::VpxEncoder::StartFrameEncode,
332 encoder_), 351 encoder_),
333 track_); 352 track_);
334 } 353 }
335 354
336 VideoTrackRecorder::~VideoTrackRecorder() { 355 VideoTrackRecorder::~VideoTrackRecorder() {
337 DCHECK(main_render_thread_checker_.CalledOnValidThread()); 356 DCHECK(main_render_thread_checker_.CalledOnValidThread());
338 RemoveFromVideoTrack(this, track_); 357 RemoveFromVideoTrack(this, track_);
339 track_.reset(); 358 track_.reset();
340 } 359 }
341 360
342 void VideoTrackRecorder::OnVideoFrameForTesting( 361 void VideoTrackRecorder::OnVideoFrameForTesting(
343 const scoped_refptr<media::VideoFrame>& frame, 362 const scoped_refptr<media::VideoFrame>& frame,
344 base::TimeTicks timestamp) { 363 base::TimeTicks timestamp) {
345 encoder_->StartFrameEncode(frame, timestamp); 364 encoder_->StartFrameEncode(frame, timestamp);
346 } 365 }
347 366
348 } // namespace content 367 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698