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