OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 // TODO (pwestin): add a link to the design document describing the generic | 5 // TODO (pwestin): add a link to the design document describing the generic |
6 // protocol and the VP8 specific details. | 6 // protocol and the VP8 specific details. |
7 #include "media/cast/video_sender/codecs/vp8/vp8_encoder.h" | 7 #include "media/cast/video_sender/codecs/vp8/vp8_encoder.h" |
8 | 8 |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 | 21 |
22 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, | 22 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, |
23 uint8 max_unacked_frames) | 23 uint8 max_unacked_frames) |
24 : cast_config_(video_config), | 24 : cast_config_(video_config), |
25 use_multiple_video_buffers_( | 25 use_multiple_video_buffers_( |
26 cast_config_.max_number_of_video_buffers_used == | 26 cast_config_.max_number_of_video_buffers_used == |
27 kNumberOfVp8VideoBuffers), | 27 kNumberOfVp8VideoBuffers), |
28 max_number_of_repeated_buffers_in_a_row_( | 28 max_number_of_repeated_buffers_in_a_row_( |
29 (max_unacked_frames > kNumberOfVp8VideoBuffers) ? | 29 (max_unacked_frames > kNumberOfVp8VideoBuffers) ? |
30 ((max_unacked_frames - 1) / kNumberOfVp8VideoBuffers) : 0), | 30 ((max_unacked_frames - 1) / kNumberOfVp8VideoBuffers) : 0), |
31 config_(new vpx_codec_enc_cfg_t()), | |
32 encoder_(new vpx_codec_ctx_t()), | |
33 // Creating a wrapper to the image - setting image data to NULL. Actual | |
34 // pointer will be set during encode. Setting align to 1, as it is | |
35 // meaningless (actual memory is not allocated). | |
36 raw_image_(vpx_img_wrap(NULL, IMG_FMT_I420, video_config.width, | |
37 video_config.height, 1, NULL)), | |
38 key_frame_requested_(true), | 31 key_frame_requested_(true), |
39 timestamp_(0), | 32 timestamp_(0), |
40 last_encoded_frame_id_(kStartFrameId), | 33 last_encoded_frame_id_(kStartFrameId), |
41 number_of_repeated_buffers_(0) { | 34 number_of_repeated_buffers_(0) { |
42 // TODO(pwestin): we need to figure out how to synchronize the acking with the | 35 // TODO(pwestin): we need to figure out how to synchronize the acking with the |
43 // internal state of the encoder, ideally the encoder will tell if we can | 36 // internal state of the encoder, ideally the encoder will tell if we can |
44 // send another frame. | 37 // send another frame. |
45 DCHECK(!use_multiple_video_buffers_ || | 38 DCHECK(!use_multiple_video_buffers_ || |
46 max_number_of_repeated_buffers_in_a_row_ == 0) << "Invalid config"; | 39 max_number_of_repeated_buffers_in_a_row_ == 0) << "Invalid config"; |
47 | 40 |
48 // VP8 have 3 buffers available for prediction, with | 41 // VP8 have 3 buffers available for prediction, with |
49 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency | 42 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency |
50 // however in this mode we can not skip frames in the receiver to catch up | 43 // however in this mode we can not skip frames in the receiver to catch up |
51 // after a temporary network outage; with max_number_of_video_buffers_used | 44 // after a temporary network outage; with max_number_of_video_buffers_used |
52 // set to 3 we allow 2 frames to be skipped by the receiver without error | 45 // set to 3 we allow 2 frames to be skipped by the receiver without error |
53 // propagation. | 46 // propagation. |
54 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || | 47 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || |
55 cast_config_.max_number_of_video_buffers_used == | 48 cast_config_.max_number_of_video_buffers_used == |
56 kNumberOfVp8VideoBuffers) << "Invalid argument"; | 49 kNumberOfVp8VideoBuffers) << "Invalid argument"; |
57 | 50 |
58 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 51 thread_checker_.DetachFromThread(); |
59 acked_frame_buffers_[i] = true; | |
60 used_buffers_frame_id_[i] = kStartFrameId; | |
61 } | |
62 InitEncode(video_config.number_of_cores); | |
63 } | 52 } |
64 | 53 |
65 Vp8Encoder::~Vp8Encoder() { | 54 Vp8Encoder::~Vp8Encoder() { |
66 vpx_codec_destroy(encoder_.get()); | 55 vpx_codec_destroy(encoder_.get()); |
67 vpx_img_free(raw_image_); | 56 vpx_img_free(raw_image_); |
68 } | 57 } |
69 | 58 |
| 59 void Vp8Encoder::Initialize() { |
| 60 DCHECK(thread_checker_.CalledOnValidThread()); |
| 61 config_.reset(new vpx_codec_enc_cfg_t()); |
| 62 encoder_.reset(new vpx_codec_ctx_t()); |
| 63 |
| 64 // Creating a wrapper to the image - setting image data to NULL. Actual |
| 65 // pointer will be set during encode. Setting align to 1, as it is |
| 66 // meaningless (actual memory is not allocated). |
| 67 raw_image_ = vpx_img_wrap(NULL, IMG_FMT_I420, cast_config_.width, |
| 68 cast_config_.height, 1, NULL); |
| 69 |
| 70 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
| 71 acked_frame_buffers_[i] = true; |
| 72 used_buffers_frame_id_[i] = kStartFrameId; |
| 73 } |
| 74 InitEncode(cast_config_.number_of_cores); |
| 75 } |
| 76 |
70 void Vp8Encoder::InitEncode(int number_of_cores) { | 77 void Vp8Encoder::InitEncode(int number_of_cores) { |
| 78 DCHECK(thread_checker_.CalledOnValidThread()); |
71 // Populate encoder configuration with default values. | 79 // Populate encoder configuration with default values. |
72 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) { | 80 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) { |
73 DCHECK(false) << "Invalid return value"; | 81 DCHECK(false) << "Invalid return value"; |
74 } | 82 } |
75 config_->g_w = cast_config_.width; | 83 config_->g_w = cast_config_.width; |
76 config_->g_h = cast_config_.height; | 84 config_->g_h = cast_config_.height; |
77 config_->rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s. | 85 config_->rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s. |
78 | 86 |
79 // Setting the codec time base. | 87 // Setting the codec time base. |
80 config_->g_timebase.num = 1; | 88 config_->g_timebase.num = 1; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 } | 130 } |
123 vpx_codec_control(encoder_.get(), VP8E_SET_STATIC_THRESHOLD, 1); | 131 vpx_codec_control(encoder_.get(), VP8E_SET_STATIC_THRESHOLD, 1); |
124 vpx_codec_control(encoder_.get(), VP8E_SET_NOISE_SENSITIVITY, 0); | 132 vpx_codec_control(encoder_.get(), VP8E_SET_NOISE_SENSITIVITY, 0); |
125 vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, -6); | 133 vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, -6); |
126 vpx_codec_control(encoder_.get(), VP8E_SET_MAX_INTRA_BITRATE_PCT, | 134 vpx_codec_control(encoder_.get(), VP8E_SET_MAX_INTRA_BITRATE_PCT, |
127 rc_max_intra_target); | 135 rc_max_intra_target); |
128 } | 136 } |
129 | 137 |
130 bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, | 138 bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, |
131 transport::EncodedVideoFrame* encoded_image) { | 139 transport::EncodedVideoFrame* encoded_image) { |
| 140 DCHECK(thread_checker_.CalledOnValidThread()); |
132 // Image in vpx_image_t format. | 141 // Image in vpx_image_t format. |
133 // Input image is const. VP8's raw image is not defined as const. | 142 // Input image is const. VP8's raw image is not defined as const. |
134 raw_image_->planes[PLANE_Y] = | 143 raw_image_->planes[PLANE_Y] = |
135 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane)); | 144 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane)); |
136 raw_image_->planes[PLANE_U] = | 145 raw_image_->planes[PLANE_U] = |
137 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane)); | 146 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane)); |
138 raw_image_->planes[PLANE_V] = | 147 raw_image_->planes[PLANE_V] = |
139 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane)); | 148 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane)); |
140 | 149 |
141 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane); | 150 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane); |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 case kNoBuffer: | 336 case kNoBuffer: |
328 *flags |= VP8_EFLAG_NO_UPD_ARF; | 337 *flags |= VP8_EFLAG_NO_UPD_ARF; |
329 *flags |= VP8_EFLAG_NO_UPD_GF; | 338 *flags |= VP8_EFLAG_NO_UPD_GF; |
330 *flags |= VP8_EFLAG_NO_UPD_LAST; | 339 *flags |= VP8_EFLAG_NO_UPD_LAST; |
331 *flags |= VP8_EFLAG_NO_UPD_ENTROPY; | 340 *flags |= VP8_EFLAG_NO_UPD_ENTROPY; |
332 break; | 341 break; |
333 } | 342 } |
334 } | 343 } |
335 | 344 |
336 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { | 345 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { |
| 346 DCHECK(thread_checker_.CalledOnValidThread()); |
337 uint32 new_bitrate_kbit = new_bitrate / 1000; | 347 uint32 new_bitrate_kbit = new_bitrate / 1000; |
338 if (config_->rc_target_bitrate == new_bitrate_kbit) return; | 348 if (config_->rc_target_bitrate == new_bitrate_kbit) return; |
339 | 349 |
340 config_->rc_target_bitrate = new_bitrate_kbit; | 350 config_->rc_target_bitrate = new_bitrate_kbit; |
341 | 351 |
342 // Update encoder context. | 352 // Update encoder context. |
343 if (vpx_codec_enc_config_set(encoder_.get(), config_.get())) { | 353 if (vpx_codec_enc_config_set(encoder_.get(), config_.get())) { |
344 DCHECK(false) << "Invalid return value"; | 354 DCHECK(false) << "Invalid return value"; |
345 } | 355 } |
346 } | 356 } |
347 | 357 |
348 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { | 358 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { |
| 359 DCHECK(thread_checker_.CalledOnValidThread()); |
349 if (!use_multiple_video_buffers_) return; | 360 if (!use_multiple_video_buffers_) return; |
350 | 361 |
351 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); | 362 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); |
352 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 363 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
353 if (frame_id == used_buffers_frame_id_[i]) { | 364 if (frame_id == used_buffers_frame_id_[i]) { |
354 acked_frame_buffers_[i] = true; | 365 acked_frame_buffers_[i] = true; |
355 } | 366 } |
356 } | 367 } |
357 } | 368 } |
358 | 369 |
359 void Vp8Encoder::GenerateKeyFrame() { | 370 void Vp8Encoder::GenerateKeyFrame() { |
| 371 DCHECK(thread_checker_.CalledOnValidThread()); |
360 key_frame_requested_ = true; | 372 key_frame_requested_ = true; |
361 } | 373 } |
362 | 374 |
363 // Calculate the max size of the key frame relative to a normal delta frame. | 375 // Calculate the max size of the key frame relative to a normal delta frame. |
364 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { | 376 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { |
365 // Set max to the optimal buffer level (normalized by target BR), | 377 // Set max to the optimal buffer level (normalized by target BR), |
366 // and scaled by a scale_parameter. | 378 // and scaled by a scale_parameter. |
367 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. | 379 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. |
368 // This values is presented in percentage of perFrameBw: | 380 // This values is presented in percentage of perFrameBw: |
369 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. | 381 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. |
370 // The target in % is as follows: | 382 // The target in % is as follows: |
371 | 383 |
372 float scale_parameter = 0.5; | 384 float scale_parameter = 0.5; |
373 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * | 385 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * |
374 cast_config_.max_frame_rate / 10; | 386 cast_config_.max_frame_rate / 10; |
375 | 387 |
376 // Don't go below 3 times the per frame bandwidth. | 388 // Don't go below 3 times the per frame bandwidth. |
377 return std::max(target_pct, kMinIntra); | 389 return std::max(target_pct, kMinIntra); |
378 } | 390 } |
379 | 391 |
380 } // namespace cast | 392 } // namespace cast |
381 } // namespace media | 393 } // namespace media |
OLD | NEW |