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

Side by Side Diff: media/cast/sender/vp8_encoder.cc

Issue 892383002: RELAND: [Cast] Software encoder support for varying video frame sizes. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix for 'access to uninitialized memory' error. Created 5 years, 10 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
« no previous file with comments | « media/cast/sender/vp8_encoder.h ('k') | media/cast/test/fake_media_source.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "media/cast/sender/vp8_encoder.h" 5 #include "media/cast/sender/vp8_encoder.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "media/base/video_frame.h" 8 #include "media/base/video_frame.h"
9 #include "media/cast/cast_defines.h" 9 #include "media/cast/cast_defines.h"
10 #include "media/cast/net/cast_transport_config.h" 10 #include "media/cast/net/cast_transport_config.h"
(...skipping 10 matching lines...) Expand all
21 // pause in the video stream. 21 // pause in the video stream.
22 const int kRestartFramePeriods = 3; 22 const int kRestartFramePeriods = 3;
23 23
24 } // namespace 24 } // namespace
25 25
26 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config) 26 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config)
27 : cast_config_(video_config), 27 : cast_config_(video_config),
28 use_multiple_video_buffers_( 28 use_multiple_video_buffers_(
29 cast_config_.max_number_of_video_buffers_used == 29 cast_config_.max_number_of_video_buffers_used ==
30 kNumberOfVp8VideoBuffers), 30 kNumberOfVp8VideoBuffers),
31 raw_image_(nullptr),
32 key_frame_requested_(true), 31 key_frame_requested_(true),
32 bitrate_kbit_(cast_config_.start_bitrate / 1000),
33 last_encoded_frame_id_(kStartFrameId), 33 last_encoded_frame_id_(kStartFrameId),
34 last_acked_frame_id_(kStartFrameId), 34 last_acked_frame_id_(kStartFrameId),
35 undroppable_frames_(0) { 35 undroppable_frames_(0) {
36 config_.g_timebase.den = 0; // Not initialized. 36 config_.g_timebase.den = 0; // Not initialized.
37 37
38 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
39 buffer_state_[i].frame_id = last_encoded_frame_id_;
40 buffer_state_[i].state = kBufferStartState;
41 }
42
38 // VP8 have 3 buffers available for prediction, with 43 // VP8 have 3 buffers available for prediction, with
39 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency 44 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
40 // however in this mode we can not skip frames in the receiver to catch up 45 // however in this mode we can not skip frames in the receiver to catch up
41 // after a temporary network outage; with max_number_of_video_buffers_used 46 // after a temporary network outage; with max_number_of_video_buffers_used
42 // set to 3 we allow 2 frames to be skipped by the receiver without error 47 // set to 3 we allow 2 frames to be skipped by the receiver without error
43 // propagation. 48 // propagation.
44 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || 49 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
45 cast_config_.max_number_of_video_buffers_used == 50 cast_config_.max_number_of_video_buffers_used ==
46 kNumberOfVp8VideoBuffers) 51 kNumberOfVp8VideoBuffers)
47 << "Invalid argument"; 52 << "Invalid argument";
48 53
49 thread_checker_.DetachFromThread(); 54 thread_checker_.DetachFromThread();
50 } 55 }
51 56
52 Vp8Encoder::~Vp8Encoder() { 57 Vp8Encoder::~Vp8Encoder() {
53 DCHECK(thread_checker_.CalledOnValidThread()); 58 DCHECK(thread_checker_.CalledOnValidThread());
54 if (is_initialized()) 59 if (is_initialized())
55 vpx_codec_destroy(&encoder_); 60 vpx_codec_destroy(&encoder_);
56 vpx_img_free(raw_image_);
57 } 61 }
58 62
59 void Vp8Encoder::Initialize() { 63 void Vp8Encoder::Initialize() {
60 DCHECK(thread_checker_.CalledOnValidThread()); 64 DCHECK(thread_checker_.CalledOnValidThread());
61 DCHECK(!is_initialized()); 65 DCHECK(!is_initialized());
66 // The encoder will be created/configured when the first frame encode is
67 // requested.
68 }
62 69
63 // Creating a wrapper to the image - setting image data to NULL. Actual 70 void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) {
64 // pointer will be set during encode. Setting align to 1, as it is 71 if (is_initialized()) {
65 // meaningless (actual memory is not allocated). 72 // Workaround for VP8 bug: If the new size is strictly less-than-or-equal to
66 raw_image_ = vpx_img_wrap( 73 // the old size, in terms of area, the existing encoder instance can
67 NULL, VPX_IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); 74 // continue. Otherwise, completely tear-down and re-create a new encoder to
75 // avoid a shutdown crash.
76 if (frame_size.GetArea() <= gfx::Size(config_.g_w, config_.g_h).GetArea() &&
77 !use_multiple_video_buffers_) {
78 DVLOG(1) << "Continuing to use existing encoder at smaller frame size: "
79 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> "
80 << frame_size.ToString();
81 config_.g_w = frame_size.width();
82 config_.g_h = frame_size.height();
83 CHECK_EQ(vpx_codec_enc_config_set(&encoder_, &config_), VPX_CODEC_OK)
84 << "Failed to update frame size in encoder config.";
85 return;
86 }
68 87
88 DVLOG(1) << "Destroying/Re-Creating encoder for larger frame size: "
89 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> "
90 << frame_size.ToString();
91 vpx_codec_destroy(&encoder_);
92 } else {
93 DVLOG(1) << "Creating encoder for the first frame; size: "
94 << frame_size.ToString();
95 }
96
97 // Reset multi-buffer mode state.
98 last_acked_frame_id_ = last_encoded_frame_id_;
99 undroppable_frames_ = 0;
69 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { 100 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
70 buffer_state_[i].frame_id = kStartFrameId; 101 buffer_state_[i].frame_id = last_encoded_frame_id_;
71 buffer_state_[i].state = kBufferStartState; 102 buffer_state_[i].state = kBufferStartState;
72 } 103 }
73 104
74 // Populate encoder configuration with default values. 105 // Populate encoder configuration with default values.
75 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0)) { 106 CHECK_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0),
76 NOTREACHED() << "Invalid return value"; 107 VPX_CODEC_OK);
77 config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor.
78 return;
79 }
80 108
81 config_.g_threads = cast_config_.number_of_encode_threads; 109 config_.g_threads = cast_config_.number_of_encode_threads;
82 config_.g_w = cast_config_.width; 110 config_.g_w = frame_size.width();
83 config_.g_h = cast_config_.height; 111 config_.g_h = frame_size.height();
84 // Set the timebase to match that of base::TimeDelta. 112 // Set the timebase to match that of base::TimeDelta.
85 config_.g_timebase.num = 1; 113 config_.g_timebase.num = 1;
86 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond; 114 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond;
87 if (use_multiple_video_buffers_) { 115 if (use_multiple_video_buffers_) {
88 // We must enable error resilience when we use multiple buffers, due to 116 // We must enable error resilience when we use multiple buffers, due to
89 // codec requirements. 117 // codec requirements.
90 config_.g_error_resilient = 1; 118 config_.g_error_resilient = 1;
91 } 119 }
120 // |g_pass| and |g_lag_in_frames| must be "one pass" and zero, respectively,
121 // in order for VP8 to support changing frame sizes during encoding:
92 config_.g_pass = VPX_RC_ONE_PASS; 122 config_.g_pass = VPX_RC_ONE_PASS;
93 config_.g_lag_in_frames = 0; // Immediate data output for each frame. 123 config_.g_lag_in_frames = 0; // Immediate data output for each frame.
94 124
95 // Rate control settings. 125 // Rate control settings.
96 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames. 126 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames.
97 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this. 127 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this.
98 config_.rc_end_usage = VPX_CBR; 128 config_.rc_end_usage = VPX_CBR;
99 config_.rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s. 129 config_.rc_target_bitrate = bitrate_kbit_;
100 config_.rc_min_quantizer = cast_config_.min_qp; 130 config_.rc_min_quantizer = cast_config_.min_qp;
101 config_.rc_max_quantizer = cast_config_.max_qp; 131 config_.rc_max_quantizer = cast_config_.max_qp;
102 // TODO(miu): Revisit these now that the encoder is being successfully 132 // TODO(miu): Revisit these now that the encoder is being successfully
103 // micro-managed. 133 // micro-managed.
104 config_.rc_undershoot_pct = 100; 134 config_.rc_undershoot_pct = 100;
105 config_.rc_overshoot_pct = 15; 135 config_.rc_overshoot_pct = 15;
106 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or 136 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or
107 // research for better values. Should they be computed from the target 137 // research for better values. Should they be computed from the target
108 // playout delay? 138 // playout delay?
109 config_.rc_buf_initial_sz = 500; 139 config_.rc_buf_initial_sz = 500;
110 config_.rc_buf_optimal_sz = 600; 140 config_.rc_buf_optimal_sz = 600;
111 config_.rc_buf_sz = 1000; 141 config_.rc_buf_sz = 1000;
112 142
113 config_.kf_mode = VPX_KF_DISABLED; 143 config_.kf_mode = VPX_KF_DISABLED;
114 144
115 vpx_codec_flags_t flags = 0; 145 vpx_codec_flags_t flags = 0;
116 if (vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags)) { 146 CHECK_EQ(vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags),
117 NOTREACHED() << "vpx_codec_enc_init() failed."; 147 VPX_CODEC_OK);
118 config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor.
119 return;
120 }
121 148
122 // Raise the threshold for considering macroblocks as static. The default is 149 // Raise the threshold for considering macroblocks as static. The default is
123 // zero, so this setting makes the encoder less sensitive to motion. This 150 // zero, so this setting makes the encoder less sensitive to motion. This
124 // lowers the probability of needing to utilize more CPU to search for motion 151 // lowers the probability of needing to utilize more CPU to search for motion
125 // vectors. 152 // vectors.
126 vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1); 153 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1),
154 VPX_CODEC_OK);
127 155
128 // Improve quality by enabling sets of codec features that utilize more CPU. 156 // Improve quality by enabling sets of codec features that utilize more CPU.
129 // The default is zero, with increasingly more CPU to be used as the value is 157 // The default is zero, with increasingly more CPU to be used as the value is
130 // more negative. 158 // more negative.
131 // TODO(miu): Document why this value was chosen and expected behaviors. 159 // TODO(miu): Document why this value was chosen and expected behaviors.
132 // Should this be dynamic w.r.t. hardware performance? 160 // Should this be dynamic w.r.t. hardware performance?
133 vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6); 161 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6), VPX_CODEC_OK);
134 } 162 }
135 163
136 void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, 164 void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
137 const base::TimeTicks& reference_time, 165 const base::TimeTicks& reference_time,
138 EncodedFrame* encoded_frame) { 166 EncodedFrame* encoded_frame) {
139 DCHECK(thread_checker_.CalledOnValidThread()); 167 DCHECK(thread_checker_.CalledOnValidThread());
140 DCHECK(encoded_frame); 168 DCHECK(encoded_frame);
141 169
142 CHECK(is_initialized()); // No illegal reference to |config_| or |encoder_|. 170 // Initialize on-demand. Later, if the video frame size has changed, update
143 171 // the encoder configuration.
144 // Image in vpx_image_t format. 172 const gfx::Size frame_size = video_frame->visible_rect().size();
145 // Input image is const. VP8's raw image is not defined as const. 173 if (!is_initialized() || gfx::Size(config_.g_w, config_.g_h) != frame_size)
146 raw_image_->planes[VPX_PLANE_Y] = 174 ConfigureForNewFrameSize(frame_size);
147 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane));
148 raw_image_->planes[VPX_PLANE_U] =
149 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane));
150 raw_image_->planes[VPX_PLANE_V] =
151 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane));
152
153 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
154 raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
155 raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
156 175
157 uint32 latest_frame_id_to_reference; 176 uint32 latest_frame_id_to_reference;
158 Vp8Buffers buffer_to_update; 177 Vp8Buffers buffer_to_update;
159 vpx_codec_flags_t flags = 0; 178 vpx_codec_flags_t flags = 0;
160 if (key_frame_requested_) { 179 if (key_frame_requested_) {
161 flags = VPX_EFLAG_FORCE_KF; 180 flags = VPX_EFLAG_FORCE_KF;
162 // Self reference. 181 // Self reference.
163 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; 182 latest_frame_id_to_reference = last_encoded_frame_id_ + 1;
164 // We can pick any buffer as buffer_to_update since we update 183 // We can pick any buffer as buffer_to_update since we update
165 // them all. 184 // them all.
166 buffer_to_update = kLastBuffer; 185 buffer_to_update = kLastBuffer;
167 } else { 186 } else {
168 // Reference all acked frames (buffers). 187 // Reference all acked frames (buffers).
169 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags); 188 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags);
170 buffer_to_update = GetNextBufferToUpdate(); 189 buffer_to_update = GetNextBufferToUpdate();
171 GetCodecUpdateFlags(buffer_to_update, &flags); 190 GetCodecUpdateFlags(buffer_to_update, &flags);
172 } 191 }
173 192
193 // Wrapper for vpx_codec_encode() to access the YUV data in the |video_frame|.
194 // Only the VISIBLE rectangle within |video_frame| is exposed to the codec.
195 vpx_image_t vpx_image;
196 vpx_image_t* const result = vpx_img_wrap(
197 &vpx_image,
198 VPX_IMG_FMT_I420,
199 frame_size.width(),
200 frame_size.height(),
201 1,
202 video_frame->data(VideoFrame::kYPlane));
203 DCHECK_EQ(result, &vpx_image);
204 vpx_image.planes[VPX_PLANE_Y] =
205 video_frame->visible_data(VideoFrame::kYPlane);
206 vpx_image.planes[VPX_PLANE_U] =
207 video_frame->visible_data(VideoFrame::kUPlane);
208 vpx_image.planes[VPX_PLANE_V] =
209 video_frame->visible_data(VideoFrame::kVPlane);
210 vpx_image.stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
211 vpx_image.stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
212 vpx_image.stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
213
174 // The frame duration given to the VP8 codec affects a number of important 214 // The frame duration given to the VP8 codec affects a number of important
175 // behaviors, including: per-frame bandwidth, CPU time spent encoding, 215 // behaviors, including: per-frame bandwidth, CPU time spent encoding,
176 // temporal quality trade-offs, and key/golden/alt-ref frame generation 216 // temporal quality trade-offs, and key/golden/alt-ref frame generation
177 // intervals. Use the actual amount of time between the current and previous 217 // intervals. Use the actual amount of time between the current and previous
178 // frames as a prediction for the next frame's duration, but bound the 218 // frames as a prediction for the next frame's duration, but bound the
179 // prediction to account for the fact that the frame rate can be highly 219 // prediction to account for the fact that the frame rate can be highly
180 // variable, including long pauses in the video stream. 220 // variable, including long pauses in the video stream.
181 const base::TimeDelta minimum_frame_duration = 221 const base::TimeDelta minimum_frame_duration =
182 base::TimeDelta::FromSecondsD(1.0 / cast_config_.max_frame_rate); 222 base::TimeDelta::FromSecondsD(1.0 / cast_config_.max_frame_rate);
183 const base::TimeDelta maximum_frame_duration = 223 const base::TimeDelta maximum_frame_duration =
184 base::TimeDelta::FromSecondsD(static_cast<double>(kRestartFramePeriods) / 224 base::TimeDelta::FromSecondsD(static_cast<double>(kRestartFramePeriods) /
185 cast_config_.max_frame_rate); 225 cast_config_.max_frame_rate);
186 const base::TimeDelta last_frame_duration = 226 const base::TimeDelta last_frame_duration =
187 video_frame->timestamp() - last_frame_timestamp_; 227 video_frame->timestamp() - last_frame_timestamp_;
188 const base::TimeDelta predicted_frame_duration = 228 const base::TimeDelta predicted_frame_duration =
189 std::max(minimum_frame_duration, 229 std::max(minimum_frame_duration,
190 std::min(maximum_frame_duration, last_frame_duration)); 230 std::min(maximum_frame_duration, last_frame_duration));
191 last_frame_timestamp_ = video_frame->timestamp(); 231 last_frame_timestamp_ = video_frame->timestamp();
192 232
193 // Encode the frame. The presentation time stamp argument here is fixed to 233 // Encode the frame. The presentation time stamp argument here is fixed to
194 // zero to force the encoder to base its single-frame bandwidth calculations 234 // zero to force the encoder to base its single-frame bandwidth calculations
195 // entirely on |predicted_frame_duration| and the target bitrate setting being 235 // entirely on |predicted_frame_duration| and the target bitrate setting being
196 // micro-managed via calls to UpdateRates(). 236 // micro-managed via calls to UpdateRates().
197 CHECK_EQ(vpx_codec_encode(&encoder_, 237 CHECK_EQ(vpx_codec_encode(&encoder_,
198 raw_image_, 238 &vpx_image,
199 0, 239 0,
200 predicted_frame_duration.InMicroseconds(), 240 predicted_frame_duration.InMicroseconds(),
201 flags, 241 flags,
202 VPX_DL_REALTIME), 242 VPX_DL_REALTIME),
203 VPX_CODEC_OK) 243 VPX_CODEC_OK)
204 << "BUG: Invalid arguments passed to vpx_codec_encode()."; 244 << "BUG: Invalid arguments passed to vpx_codec_encode().";
205 245
206 // Pull data from the encoder, populating a new EncodedFrame. 246 // Pull data from the encoder, populating a new EncodedFrame.
207 encoded_frame->frame_id = ++last_encoded_frame_id_; 247 encoded_frame->frame_id = ++last_encoded_frame_id_;
208 const vpx_codec_cx_pkt_t* pkt = NULL; 248 const vpx_codec_cx_pkt_t* pkt = NULL;
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { 435 void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
396 DCHECK(thread_checker_.CalledOnValidThread()); 436 DCHECK(thread_checker_.CalledOnValidThread());
397 437
398 if (!is_initialized()) 438 if (!is_initialized())
399 return; 439 return;
400 440
401 uint32 new_bitrate_kbit = new_bitrate / 1000; 441 uint32 new_bitrate_kbit = new_bitrate / 1000;
402 if (config_.rc_target_bitrate == new_bitrate_kbit) 442 if (config_.rc_target_bitrate == new_bitrate_kbit)
403 return; 443 return;
404 444
405 config_.rc_target_bitrate = new_bitrate_kbit; 445 config_.rc_target_bitrate = bitrate_kbit_ = new_bitrate_kbit;
406 446
407 // Update encoder context. 447 // Update encoder context.
408 if (vpx_codec_enc_config_set(&encoder_, &config_)) { 448 if (vpx_codec_enc_config_set(&encoder_, &config_)) {
409 NOTREACHED() << "Invalid return value"; 449 NOTREACHED() << "Invalid return value";
410 } 450 }
411 451
412 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps"; 452 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps";
413 } 453 }
414 454
415 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { 455 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
(...skipping 13 matching lines...) Expand all
429 } 469 }
430 } 470 }
431 471
432 void Vp8Encoder::GenerateKeyFrame() { 472 void Vp8Encoder::GenerateKeyFrame() {
433 DCHECK(thread_checker_.CalledOnValidThread()); 473 DCHECK(thread_checker_.CalledOnValidThread());
434 key_frame_requested_ = true; 474 key_frame_requested_ = true;
435 } 475 }
436 476
437 } // namespace cast 477 } // namespace cast
438 } // namespace media 478 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/sender/vp8_encoder.h ('k') | media/cast/test/fake_media_source.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698