OLD | NEW |
| (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 | |
OLD | NEW |