Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |