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

Side by Side Diff: media/cast/video_sender/codecs/vp8/vp8_encoder.cc

Issue 388663003: Cast: Reshuffle files under media/cast (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: missing includes Created 6 years, 5 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4 //
5 // TODO (pwestin): add a link to the design document describing the generic
6 // protocol and the VP8 specific details.
7 #include "media/cast/video_sender/codecs/vp8/vp8_encoder.h"
8
9 #include <vector>
10
11 #include "base/logging.h"
12 #include "media/base/video_frame.h"
13 #include "media/cast/cast_defines.h"
14 #include "media/cast/transport/cast_transport_config.h"
15 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
16
17 namespace media {
18 namespace cast {
19
20 static const uint32 kMinIntra = 300;
21
22 static int ComputeMaxNumOfRepeatedBuffes(int max_unacked_frames) {
23 if (max_unacked_frames > kNumberOfVp8VideoBuffers)
24 return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers;
25
26 return 0;
27 }
28
29 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
30 int max_unacked_frames)
31 : cast_config_(video_config),
32 use_multiple_video_buffers_(
33 cast_config_.max_number_of_video_buffers_used ==
34 kNumberOfVp8VideoBuffers),
35 max_number_of_repeated_buffers_in_a_row_(
36 ComputeMaxNumOfRepeatedBuffes(max_unacked_frames)),
37 key_frame_requested_(true),
38 first_frame_received_(false),
39 last_encoded_frame_id_(kStartFrameId),
40 number_of_repeated_buffers_(0) {
41 // TODO(pwestin): we need to figure out how to synchronize the acking with the
42 // internal state of the encoder, ideally the encoder will tell if we can
43 // send another frame.
44 DCHECK(!use_multiple_video_buffers_ ||
45 max_number_of_repeated_buffers_in_a_row_ == 0)
46 << "Invalid config";
47
48 // VP8 have 3 buffers available for prediction, with
49 // 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
51 // 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
53 // propagation.
54 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
55 cast_config_.max_number_of_video_buffers_used ==
56 kNumberOfVp8VideoBuffers)
57 << "Invalid argument";
58
59 thread_checker_.DetachFromThread();
60 }
61
62 Vp8Encoder::~Vp8Encoder() {
63 vpx_codec_destroy(encoder_.get());
64 vpx_img_free(raw_image_);
65 }
66
67 void Vp8Encoder::Initialize() {
68 DCHECK(thread_checker_.CalledOnValidThread());
69 config_.reset(new vpx_codec_enc_cfg_t());
70 encoder_.reset(new vpx_codec_ctx_t());
71
72 // Creating a wrapper to the image - setting image data to NULL. Actual
73 // pointer will be set during encode. Setting align to 1, as it is
74 // meaningless (actual memory is not allocated).
75 raw_image_ = vpx_img_wrap(
76 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
77
78 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
79 acked_frame_buffers_[i] = true;
80 used_buffers_frame_id_[i] = kStartFrameId;
81 }
82 InitEncode(cast_config_.number_of_encode_threads);
83 }
84
85 void Vp8Encoder::InitEncode(int number_of_encode_threads) {
86 DCHECK(thread_checker_.CalledOnValidThread());
87 // Populate encoder configuration with default values.
88 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), config_.get(), 0)) {
89 DCHECK(false) << "Invalid return value";
90 }
91 config_->g_w = cast_config_.width;
92 config_->g_h = cast_config_.height;
93 config_->rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s.
94
95 // Setting the codec time base.
96 config_->g_timebase.num = 1;
97 config_->g_timebase.den = kVideoFrequency;
98 config_->g_lag_in_frames = 0;
99 config_->kf_mode = VPX_KF_DISABLED;
100 if (use_multiple_video_buffers_) {
101 // We must enable error resilience when we use multiple buffers, due to
102 // codec requirements.
103 config_->g_error_resilient = 1;
104 }
105 config_->g_threads = number_of_encode_threads;
106
107 // Rate control settings.
108 // Never allow the encoder to drop frame internally.
109 config_->rc_dropframe_thresh = 0;
110 config_->rc_end_usage = VPX_CBR;
111 config_->g_pass = VPX_RC_ONE_PASS;
112 config_->rc_resize_allowed = 0;
113 config_->rc_min_quantizer = cast_config_.min_qp;
114 config_->rc_max_quantizer = cast_config_.max_qp;
115 config_->rc_undershoot_pct = 100;
116 config_->rc_overshoot_pct = 15;
117 config_->rc_buf_initial_sz = 500;
118 config_->rc_buf_optimal_sz = 600;
119 config_->rc_buf_sz = 1000;
120
121 // set the maximum target size of any key-frame.
122 uint32 rc_max_intra_target = MaxIntraTarget(config_->rc_buf_optimal_sz);
123 vpx_codec_flags_t flags = 0;
124 if (vpx_codec_enc_init(
125 encoder_.get(), vpx_codec_vp8_cx(), config_.get(), flags)) {
126 DCHECK(false) << "vpx_codec_enc_init() failed.";
127 encoder_.reset();
128 return;
129 }
130 vpx_codec_control(encoder_.get(), VP8E_SET_STATIC_THRESHOLD, 1);
131 vpx_codec_control(encoder_.get(), VP8E_SET_NOISE_SENSITIVITY, 0);
132 vpx_codec_control(encoder_.get(), VP8E_SET_CPUUSED, -6);
133 vpx_codec_control(
134 encoder_.get(), VP8E_SET_MAX_INTRA_BITRATE_PCT, rc_max_intra_target);
135 }
136
137 bool Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame,
138 transport::EncodedFrame* encoded_image) {
139 DCHECK(thread_checker_.CalledOnValidThread());
140 // Image in vpx_image_t format.
141 // Input image is const. VP8's raw image is not defined as const.
142 raw_image_->planes[PLANE_Y] =
143 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane));
144 raw_image_->planes[PLANE_U] =
145 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane));
146 raw_image_->planes[PLANE_V] =
147 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane));
148
149 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane);
150 raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane);
151 raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane);
152
153 uint8 latest_frame_id_to_reference;
154 Vp8Buffers buffer_to_update;
155 vpx_codec_flags_t flags = 0;
156 if (key_frame_requested_) {
157 flags = VPX_EFLAG_FORCE_KF;
158 // Self reference.
159 latest_frame_id_to_reference = last_encoded_frame_id_ + 1;
160 // We can pick any buffer as buffer_to_update since we update
161 // them all.
162 buffer_to_update = kLastBuffer;
163 } else {
164 // Reference all acked frames (buffers).
165 latest_frame_id_to_reference = GetLatestFrameIdToReference();
166 GetCodecReferenceFlags(&flags);
167 buffer_to_update = GetNextBufferToUpdate();
168 GetCodecUpdateFlags(buffer_to_update, &flags);
169 }
170
171 // Note: The duration does not reflect the real time between frames. This is
172 // done to keep the encoder happy.
173 //
174 // TODO(miu): This is a semi-hack. We should consider using
175 // |video_frame->timestamp()| instead.
176 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate;
177
178 // Note: Timestamp here is used for bitrate calculation. The absolute value
179 // is not important.
180 if (!first_frame_received_) {
181 first_frame_received_ = true;
182 first_frame_timestamp_ = video_frame->timestamp();
183 }
184
185 vpx_codec_pts_t timestamp =
186 (video_frame->timestamp() - first_frame_timestamp_).InMicroseconds() *
187 kVideoFrequency / base::Time::kMicrosecondsPerSecond;
188
189 if (vpx_codec_encode(encoder_.get(),
190 raw_image_,
191 timestamp,
192 duration,
193 flags,
194 VPX_DL_REALTIME) != VPX_CODEC_OK) {
195 LOG(ERROR) << "Failed to encode for once.";
196 return false;
197 }
198
199 // Get encoded frame.
200 const vpx_codec_cx_pkt_t* pkt = NULL;
201 vpx_codec_iter_t iter = NULL;
202 bool is_key_frame = false;
203 while ((pkt = vpx_codec_get_cx_data(encoder_.get(), &iter)) != NULL) {
204 if (pkt->kind != VPX_CODEC_CX_FRAME_PKT)
205 continue;
206 encoded_image->data.assign(
207 static_cast<const uint8*>(pkt->data.frame.buf),
208 static_cast<const uint8*>(pkt->data.frame.buf) + pkt->data.frame.sz);
209 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY);
210 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
211 }
212 // Don't update frame_id for zero size frames.
213 if (encoded_image->data.empty())
214 return true;
215
216 // Populate the encoded frame.
217 encoded_image->frame_id = ++last_encoded_frame_id_;
218 if (is_key_frame) {
219 encoded_image->dependency = transport::EncodedFrame::KEY;
220 encoded_image->referenced_frame_id = encoded_image->frame_id;
221 } else {
222 encoded_image->dependency = transport::EncodedFrame::DEPENDENT;
223 encoded_image->referenced_frame_id = latest_frame_id_to_reference;
224 }
225
226 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id
227 << ", sized:" << encoded_image->data.size();
228
229 if (is_key_frame) {
230 key_frame_requested_ = false;
231
232 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
233 used_buffers_frame_id_[i] = encoded_image->frame_id;
234 }
235 // We can pick any buffer as last_used_vp8_buffer_ since we update
236 // them all.
237 last_used_vp8_buffer_ = buffer_to_update;
238 } else {
239 if (buffer_to_update != kNoBuffer) {
240 acked_frame_buffers_[buffer_to_update] = false;
241 used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id;
242 last_used_vp8_buffer_ = buffer_to_update;
243 }
244 }
245 return true;
246 }
247
248 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
249 if (!use_multiple_video_buffers_)
250 return;
251
252 // We need to reference something.
253 DCHECK(acked_frame_buffers_[kAltRefBuffer] ||
254 acked_frame_buffers_[kGoldenBuffer] ||
255 acked_frame_buffers_[kLastBuffer])
256 << "Invalid state";
257
258 if (!acked_frame_buffers_[kAltRefBuffer]) {
259 *flags |= VP8_EFLAG_NO_REF_ARF;
260 }
261 if (!acked_frame_buffers_[kGoldenBuffer]) {
262 *flags |= VP8_EFLAG_NO_REF_GF;
263 }
264 if (!acked_frame_buffers_[kLastBuffer]) {
265 *flags |= VP8_EFLAG_NO_REF_LAST;
266 }
267 }
268
269 uint32 Vp8Encoder::GetLatestFrameIdToReference() {
270 if (!use_multiple_video_buffers_)
271 return last_encoded_frame_id_;
272
273 int latest_frame_id_to_reference = -1;
274 if (acked_frame_buffers_[kAltRefBuffer]) {
275 latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer];
276 }
277 if (acked_frame_buffers_[kGoldenBuffer]) {
278 if (latest_frame_id_to_reference == -1) {
279 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
280 } else {
281 if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer],
282 latest_frame_id_to_reference)) {
283 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
284 }
285 }
286 }
287 if (acked_frame_buffers_[kLastBuffer]) {
288 if (latest_frame_id_to_reference == -1) {
289 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
290 } else {
291 if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer],
292 latest_frame_id_to_reference)) {
293 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
294 }
295 }
296 }
297 DCHECK(latest_frame_id_to_reference != -1) << "Invalid state";
298 return static_cast<uint32>(latest_frame_id_to_reference);
299 }
300
301 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
302 if (!use_multiple_video_buffers_)
303 return kNoBuffer;
304
305 // Update at most one buffer, except for key-frames.
306
307 Vp8Buffers buffer_to_update = kNoBuffer;
308 if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) {
309 // TODO(pwestin): experiment with this. The issue with only this change is
310 // that we can end up with only 4 frames in flight when we expect 6.
311 // buffer_to_update = last_used_vp8_buffer_;
312 buffer_to_update = kNoBuffer;
313 ++number_of_repeated_buffers_;
314 } else {
315 number_of_repeated_buffers_ = 0;
316 switch (last_used_vp8_buffer_) {
317 case kAltRefBuffer:
318 buffer_to_update = kLastBuffer;
319 VLOG(1) << "VP8 update last buffer";
320 break;
321 case kLastBuffer:
322 buffer_to_update = kGoldenBuffer;
323 VLOG(1) << "VP8 update golden buffer";
324 break;
325 case kGoldenBuffer:
326 buffer_to_update = kAltRefBuffer;
327 VLOG(1) << "VP8 update alt-ref buffer";
328 break;
329 case kNoBuffer:
330 DCHECK(false) << "Invalid state";
331 break;
332 }
333 }
334 return buffer_to_update;
335 }
336
337 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
338 vpx_codec_flags_t* flags) {
339 if (!use_multiple_video_buffers_)
340 return;
341
342 // Update at most one buffer, except for key-frames.
343 switch (buffer_to_update) {
344 case kAltRefBuffer:
345 *flags |= VP8_EFLAG_NO_UPD_GF;
346 *flags |= VP8_EFLAG_NO_UPD_LAST;
347 break;
348 case kLastBuffer:
349 *flags |= VP8_EFLAG_NO_UPD_GF;
350 *flags |= VP8_EFLAG_NO_UPD_ARF;
351 break;
352 case kGoldenBuffer:
353 *flags |= VP8_EFLAG_NO_UPD_ARF;
354 *flags |= VP8_EFLAG_NO_UPD_LAST;
355 break;
356 case kNoBuffer:
357 *flags |= VP8_EFLAG_NO_UPD_ARF;
358 *flags |= VP8_EFLAG_NO_UPD_GF;
359 *flags |= VP8_EFLAG_NO_UPD_LAST;
360 *flags |= VP8_EFLAG_NO_UPD_ENTROPY;
361 break;
362 }
363 }
364
365 void Vp8Encoder::UpdateRates(uint32 new_bitrate) {
366 DCHECK(thread_checker_.CalledOnValidThread());
367 uint32 new_bitrate_kbit = new_bitrate / 1000;
368 if (config_->rc_target_bitrate == new_bitrate_kbit)
369 return;
370
371 config_->rc_target_bitrate = new_bitrate_kbit;
372
373 // Update encoder context.
374 if (vpx_codec_enc_config_set(encoder_.get(), config_.get())) {
375 DCHECK(false) << "Invalid return value";
376 }
377 }
378
379 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
380 DCHECK(thread_checker_.CalledOnValidThread());
381 if (!use_multiple_video_buffers_)
382 return;
383
384 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
385 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
386 if (frame_id == used_buffers_frame_id_[i]) {
387 acked_frame_buffers_[i] = true;
388 }
389 }
390 }
391
392 void Vp8Encoder::GenerateKeyFrame() {
393 DCHECK(thread_checker_.CalledOnValidThread());
394 key_frame_requested_ = true;
395 }
396
397 // Calculate the max size of the key frame relative to a normal delta frame.
398 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const {
399 // Set max to the optimal buffer level (normalized by target BR),
400 // and scaled by a scale_parameter.
401 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
402 // This values is presented in percentage of perFrameBw:
403 // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
404 // The target in % is as follows:
405
406 float scale_parameter = 0.5;
407 uint32 target_pct = optimal_buffer_size_ms * scale_parameter *
408 cast_config_.max_frame_rate / 10;
409
410 // Don't go below 3 times the per frame bandwidth.
411 return std::max(target_pct, kMinIntra);
412 }
413
414 } // namespace cast
415 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/video_sender/codecs/vp8/vp8_encoder.h ('k') | media/cast/video_sender/external_video_encoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698