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

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

Issue 339743002: Cast: Make vp8 3-buffer mode work (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: minor test fix Created 6 years, 3 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/end2end_unittest.cc » ('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 <vector> 7 #include <vector>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "media/base/video_frame.h" 10 #include "media/base/video_frame.h"
11 #include "media/cast/cast_defines.h" 11 #include "media/cast/cast_defines.h"
12 #include "media/cast/net/cast_transport_config.h" 12 #include "media/cast/net/cast_transport_config.h"
13 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" 13 #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
14 14
15 namespace media { 15 namespace media {
16 namespace cast { 16 namespace cast {
17 17
18 static const uint32 kMinIntra = 300; 18 static const uint32 kMinIntra = 300;
19 19
20 static int ComputeMaxNumOfRepeatedBuffers(int max_unacked_frames) {
21 if (max_unacked_frames > kNumberOfVp8VideoBuffers)
22 return (max_unacked_frames - 1) / kNumberOfVp8VideoBuffers;
23
24 return 0;
25 }
26
27 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config, 20 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config,
28 int max_unacked_frames) 21 int max_unacked_frames)
29 : cast_config_(video_config), 22 : cast_config_(video_config),
30 use_multiple_video_buffers_( 23 use_multiple_video_buffers_(
31 cast_config_.max_number_of_video_buffers_used == 24 cast_config_.max_number_of_video_buffers_used ==
32 kNumberOfVp8VideoBuffers), 25 kNumberOfVp8VideoBuffers),
33 max_number_of_repeated_buffers_in_a_row_(
34 ComputeMaxNumOfRepeatedBuffers(max_unacked_frames)),
35 key_frame_requested_(true), 26 key_frame_requested_(true),
36 first_frame_received_(false), 27 first_frame_received_(false),
37 last_encoded_frame_id_(kStartFrameId), 28 last_encoded_frame_id_(kStartFrameId),
38 number_of_repeated_buffers_(0) { 29 last_acked_frame_id_(kStartFrameId),
39 // TODO(pwestin): we need to figure out how to synchronize the acking with the 30 frame_id_to_reference_(kStartFrameId - 1),
40 // internal state of the encoder, ideally the encoder will tell if we can 31 undroppable_frames_(0) {
41 // send another frame.
42 DCHECK(!use_multiple_video_buffers_ ||
43 max_number_of_repeated_buffers_in_a_row_ == 0)
44 << "Invalid config";
45
46 // VP8 have 3 buffers available for prediction, with 32 // VP8 have 3 buffers available for prediction, with
47 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency 33 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency
48 // however in this mode we can not skip frames in the receiver to catch up 34 // however in this mode we can not skip frames in the receiver to catch up
49 // after a temporary network outage; with max_number_of_video_buffers_used 35 // after a temporary network outage; with max_number_of_video_buffers_used
50 // set to 3 we allow 2 frames to be skipped by the receiver without error 36 // set to 3 we allow 2 frames to be skipped by the receiver without error
51 // propagation. 37 // propagation.
52 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || 38 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 ||
53 cast_config_.max_number_of_video_buffers_used == 39 cast_config_.max_number_of_video_buffers_used ==
54 kNumberOfVp8VideoBuffers) 40 kNumberOfVp8VideoBuffers)
55 << "Invalid argument"; 41 << "Invalid argument";
(...skipping 11 matching lines...) Expand all
67 config_.reset(new vpx_codec_enc_cfg_t()); 53 config_.reset(new vpx_codec_enc_cfg_t());
68 encoder_.reset(new vpx_codec_ctx_t()); 54 encoder_.reset(new vpx_codec_ctx_t());
69 55
70 // Creating a wrapper to the image - setting image data to NULL. Actual 56 // Creating a wrapper to the image - setting image data to NULL. Actual
71 // pointer will be set during encode. Setting align to 1, as it is 57 // pointer will be set during encode. Setting align to 1, as it is
72 // meaningless (actual memory is not allocated). 58 // meaningless (actual memory is not allocated).
73 raw_image_ = vpx_img_wrap( 59 raw_image_ = vpx_img_wrap(
74 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); 60 NULL, IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL);
75 61
76 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { 62 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
77 acked_frame_buffers_[i] = true; 63 buffer_state_[i].frame_id = kStartFrameId;
78 used_buffers_frame_id_[i] = kStartFrameId; 64 buffer_state_[i].state = kBufferStartState;
79 } 65 }
80 InitEncode(cast_config_.number_of_encode_threads); 66 InitEncode(cast_config_.number_of_encode_threads);
81 } 67 }
82 68
83 void Vp8Encoder::InitEncode(int number_of_encode_threads) { 69 void Vp8Encoder::InitEncode(int number_of_encode_threads) {
84 DCHECK(thread_checker_.CalledOnValidThread()); 70 DCHECK(thread_checker_.CalledOnValidThread());
85 // Populate encoder configuration with default values. 71 // Populate encoder configuration with default values.
86 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)) {
87 DCHECK(false) << "Invalid return value"; 73 DCHECK(false) << "Invalid return value";
88 } 74 }
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 vpx_codec_flags_t flags = 0; 139 vpx_codec_flags_t flags = 0;
154 if (key_frame_requested_) { 140 if (key_frame_requested_) {
155 flags = VPX_EFLAG_FORCE_KF; 141 flags = VPX_EFLAG_FORCE_KF;
156 // Self reference. 142 // Self reference.
157 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; 143 latest_frame_id_to_reference = last_encoded_frame_id_ + 1;
158 // We can pick any buffer as buffer_to_update since we update 144 // We can pick any buffer as buffer_to_update since we update
159 // them all. 145 // them all.
160 buffer_to_update = kLastBuffer; 146 buffer_to_update = kLastBuffer;
161 } else { 147 } else {
162 // Reference all acked frames (buffers). 148 // Reference all acked frames (buffers).
163 latest_frame_id_to_reference = GetLatestFrameIdToReference(); 149 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags);
164 GetCodecReferenceFlags(&flags);
165 buffer_to_update = GetNextBufferToUpdate(); 150 buffer_to_update = GetNextBufferToUpdate();
166 GetCodecUpdateFlags(buffer_to_update, &flags); 151 GetCodecUpdateFlags(buffer_to_update, &flags);
167 } 152 }
168 153
169 // Note: The duration does not reflect the real time between frames. This is 154 // Note: The duration does not reflect the real time between frames. This is
170 // done to keep the encoder happy. 155 // done to keep the encoder happy.
171 // 156 //
172 // TODO(miu): This is a semi-hack. We should consider using 157 // TODO(miu): This is a semi-hack. We should consider using
173 // |video_frame->timestamp()| instead. 158 // |video_frame->timestamp()| instead.
174 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate; 159 uint32 duration = kVideoFrequency / cast_config_.max_frame_rate;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY); 192 is_key_frame = !!(pkt->data.frame.flags & VPX_FRAME_IS_KEY);
208 break; // Done, since all data is provided in one CX_FRAME_PKT packet. 193 break; // Done, since all data is provided in one CX_FRAME_PKT packet.
209 } 194 }
210 // Don't update frame_id for zero size frames. 195 // Don't update frame_id for zero size frames.
211 if (encoded_image->data.empty()) 196 if (encoded_image->data.empty())
212 return true; 197 return true;
213 198
214 // Populate the encoded frame. 199 // Populate the encoded frame.
215 encoded_image->frame_id = ++last_encoded_frame_id_; 200 encoded_image->frame_id = ++last_encoded_frame_id_;
216 if (is_key_frame) { 201 if (is_key_frame) {
202 // TODO(Hubbe): Replace "dependency" with a "bool is_key_frame".
217 encoded_image->dependency = EncodedFrame::KEY; 203 encoded_image->dependency = EncodedFrame::KEY;
218 encoded_image->referenced_frame_id = encoded_image->frame_id; 204 encoded_image->referenced_frame_id = encoded_image->frame_id;
219 } else { 205 } else {
220 encoded_image->dependency = EncodedFrame::DEPENDENT; 206 encoded_image->dependency = EncodedFrame::DEPENDENT;
221 encoded_image->referenced_frame_id = latest_frame_id_to_reference; 207 encoded_image->referenced_frame_id = latest_frame_id_to_reference;
222 } 208 }
223 209
224 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id 210 DVLOG(1) << "VP8 encoded frame_id " << encoded_image->frame_id
225 << ", sized:" << encoded_image->data.size(); 211 << ", sized:" << encoded_image->data.size();
226 212
227 if (is_key_frame) { 213 if (is_key_frame) {
228 key_frame_requested_ = false; 214 key_frame_requested_ = false;
229 215
230 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { 216 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
231 used_buffers_frame_id_[i] = encoded_image->frame_id; 217 buffer_state_[i].state = kBufferSent;
218 buffer_state_[i].frame_id = encoded_image->frame_id;
232 } 219 }
233 // We can pick any buffer as last_used_vp8_buffer_ since we update
234 // them all.
235 last_used_vp8_buffer_ = buffer_to_update;
236 } else { 220 } else {
237 if (buffer_to_update != kNoBuffer) { 221 if (buffer_to_update != kNoBuffer) {
238 acked_frame_buffers_[buffer_to_update] = false; 222 buffer_state_[buffer_to_update].state = kBufferSent;
239 used_buffers_frame_id_[buffer_to_update] = encoded_image->frame_id; 223 buffer_state_[buffer_to_update].frame_id = encoded_image->frame_id;
240 last_used_vp8_buffer_ = buffer_to_update;
241 } 224 }
242 } 225 }
243 return true; 226 return true;
244 } 227 }
245 228
246 void Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) { 229 uint32 Vp8Encoder::GetCodecReferenceFlags(vpx_codec_flags_t* flags) {
247 if (!use_multiple_video_buffers_) 230 if (!use_multiple_video_buffers_)
248 return; 231 return last_encoded_frame_id_ + 1;
249 232
250 // We need to reference something. 233 const uint32 kMagicFrameOffset = 512;
251 DCHECK(acked_frame_buffers_[kAltRefBuffer] || 234 // We set latest_frame_to_reference to an old frame so that
252 acked_frame_buffers_[kGoldenBuffer] || 235 // IsNewerFrameId will work correctly.
253 acked_frame_buffers_[kLastBuffer]) 236 uint32 latest_frame_to_reference =
254 << "Invalid state"; 237 last_encoded_frame_id_ - kMagicFrameOffset;
255 238
256 if (!acked_frame_buffers_[kAltRefBuffer]) { 239 // Reference all acked frames.
257 *flags |= VP8_EFLAG_NO_REF_ARF; 240 // TODO(hubbe): We may also want to allow references to the
258 } 241 // last encoded frame, if that frame was assigned to a buffer.
259 if (!acked_frame_buffers_[kGoldenBuffer]) { 242 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
260 *flags |= VP8_EFLAG_NO_REF_GF; 243 if (buffer_state_[i].state == kBufferAcked) {
261 } 244 if (IsNewerFrameId(buffer_state_[i].frame_id,
262 if (!acked_frame_buffers_[kLastBuffer]) { 245 latest_frame_to_reference)) {
263 *flags |= VP8_EFLAG_NO_REF_LAST; 246 latest_frame_to_reference = buffer_state_[i].frame_id;
264 } 247 }
265 }
266
267 uint32 Vp8Encoder::GetLatestFrameIdToReference() {
268 if (!use_multiple_video_buffers_)
269 return last_encoded_frame_id_;
270
271 int latest_frame_id_to_reference = -1;
272 if (acked_frame_buffers_[kAltRefBuffer]) {
273 latest_frame_id_to_reference = used_buffers_frame_id_[kAltRefBuffer];
274 }
275 if (acked_frame_buffers_[kGoldenBuffer]) {
276 if (latest_frame_id_to_reference == -1) {
277 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer];
278 } else { 248 } else {
279 if (IsNewerFrameId(used_buffers_frame_id_[kGoldenBuffer], 249 switch (i) {
280 latest_frame_id_to_reference)) { 250 case kAltRefBuffer:
281 latest_frame_id_to_reference = used_buffers_frame_id_[kGoldenBuffer]; 251 *flags |= VP8_EFLAG_NO_REF_ARF;
252 break;
253 case kGoldenBuffer:
254 *flags |= VP8_EFLAG_NO_REF_GF;
255 break;
256 case kLastBuffer:
257 *flags |= VP8_EFLAG_NO_REF_LAST;
258 break;
282 } 259 }
283 } 260 }
284 } 261 }
285 if (acked_frame_buffers_[kLastBuffer]) { 262
286 if (latest_frame_id_to_reference == -1) { 263 if (latest_frame_to_reference ==
287 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer]; 264 last_encoded_frame_id_ - kMagicFrameOffset) {
288 } else { 265 // We have nothing to reference, it's kind of like a key frame,
289 if (IsNewerFrameId(used_buffers_frame_id_[kLastBuffer], 266 // but doesn't reset buffers.
290 latest_frame_id_to_reference)) { 267 latest_frame_to_reference = last_encoded_frame_id_ + 1;
291 latest_frame_id_to_reference = used_buffers_frame_id_[kLastBuffer];
292 }
293 }
294 } 268 }
295 DCHECK(latest_frame_id_to_reference != -1) << "Invalid state"; 269
296 return static_cast<uint32>(latest_frame_id_to_reference); 270 return latest_frame_to_reference;
297 } 271 }
298 272
299 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() { 273 Vp8Encoder::Vp8Buffers Vp8Encoder::GetNextBufferToUpdate() {
300 if (!use_multiple_video_buffers_) 274 if (!use_multiple_video_buffers_)
301 return kNoBuffer; 275 return kNoBuffer;
302 276
303 // Update at most one buffer, except for key-frames. 277 // The goal here is to make sure that we always keep one ACKed
278 // buffer while trying to get an ACK for a newer buffer as we go.
279 // Here are the rules for which buffer to select for update:
280 // 1. If there is a buffer in state kStartState, use it.
281 // 2. If there is a buffer other than the oldest buffer
282 // which is Acked, use the oldest buffer.
283 // 3. If there are Sent buffers which are older than
284 // latest_acked_frame_, use the oldest one.
285 // 4. If all else fails, just overwrite the newest buffer,
286 // but no more than 3 times in a row.
287 // TODO(hubbe): Figure out if 3 is optimal.
288 // Note, rule 1-3 describe cases where there is a "free" buffer
289 // that we can use. Rule 4 describes what happens when there is
290 // no free buffer available.
304 291
305 Vp8Buffers buffer_to_update = kNoBuffer; 292 // Buffers, sorted from oldest frame to newest.
306 if (number_of_repeated_buffers_ < max_number_of_repeated_buffers_in_a_row_) { 293 Vp8Encoder::Vp8Buffers buffers[kNumberOfVp8VideoBuffers];
307 // TODO(pwestin): experiment with this. The issue with only this change is 294
308 // that we can end up with only 4 frames in flight when we expect 6. 295 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
309 // buffer_to_update = last_used_vp8_buffer_; 296 Vp8Encoder::Vp8Buffers buffer = static_cast<Vp8Encoder::Vp8Buffers>(i);
310 buffer_to_update = kNoBuffer; 297
311 ++number_of_repeated_buffers_; 298 // Rule 1
312 } else { 299 if (buffer_state_[buffer].state == kBufferStartState) {
313 number_of_repeated_buffers_ = 0; 300 undroppable_frames_ = 0;
314 switch (last_used_vp8_buffer_) { 301 return buffer;
315 case kAltRefBuffer: 302 }
316 buffer_to_update = kLastBuffer; 303 buffers[buffer] = buffer;
317 VLOG(1) << "VP8 update last buffer"; 304 }
318 break; 305
319 case kLastBuffer: 306 // Sorting three elements with selection sort.
320 buffer_to_update = kGoldenBuffer; 307 for (int i = 0; i < kNumberOfVp8VideoBuffers - 1; i++) {
321 VLOG(1) << "VP8 update golden buffer"; 308 for (int j = i + 1; j < kNumberOfVp8VideoBuffers; j++) {
322 break; 309 if (IsOlderFrameId(buffer_state_[buffers[j]].frame_id,
323 case kGoldenBuffer: 310 buffer_state_[buffers[i]].frame_id)) {
324 buffer_to_update = kAltRefBuffer; 311 std::swap(buffers[i], buffers[j]);
325 VLOG(1) << "VP8 update alt-ref buffer"; 312 }
326 break;
327 case kNoBuffer:
328 DCHECK(false) << "Invalid state";
329 break;
330 } 313 }
331 } 314 }
332 return buffer_to_update; 315
316 // Rule 2
317 if (buffer_state_[buffers[1]].state == kBufferAcked ||
318 buffer_state_[buffers[2]].state == kBufferAcked) {
319 undroppable_frames_ = 0;
320 return buffers[0];
321 }
322
323 // Rule 3
324 for (int i = 0; i < kNumberOfVp8VideoBuffers; i++) {
325 if (buffer_state_[buffers[i]].state == kBufferSent &&
326 IsOlderFrameId(buffer_state_[buffers[i]].frame_id,
327 last_acked_frame_id_)) {
328 undroppable_frames_ = 0;
329 return buffers[i];
330 }
331 }
332
333 // Rule 4
334 if (undroppable_frames_ >= 3) {
335 undroppable_frames_ = 0;
336 return kNoBuffer;
337 } else {
338 undroppable_frames_++;
339 return buffers[kNumberOfVp8VideoBuffers - 1];
340 }
333 } 341 }
334 342
335 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update, 343 void Vp8Encoder::GetCodecUpdateFlags(Vp8Buffers buffer_to_update,
336 vpx_codec_flags_t* flags) { 344 vpx_codec_flags_t* flags) {
337 if (!use_multiple_video_buffers_) 345 if (!use_multiple_video_buffers_)
338 return; 346 return;
339 347
340 // Update at most one buffer, except for key-frames. 348 // Update at most one buffer, except for key-frames.
341 switch (buffer_to_update) { 349 switch (buffer_to_update) {
342 case kAltRefBuffer: 350 case kAltRefBuffer:
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 } 382 }
375 } 383 }
376 384
377 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { 385 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) {
378 DCHECK(thread_checker_.CalledOnValidThread()); 386 DCHECK(thread_checker_.CalledOnValidThread());
379 if (!use_multiple_video_buffers_) 387 if (!use_multiple_video_buffers_)
380 return; 388 return;
381 389
382 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id); 390 VLOG(1) << "VP8 ok to reference frame:" << static_cast<int>(frame_id);
383 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { 391 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) {
384 if (frame_id == used_buffers_frame_id_[i]) { 392 if (frame_id == buffer_state_[i].frame_id) {
385 acked_frame_buffers_[i] = true; 393 buffer_state_[i].state = kBufferAcked;
394 break;
386 } 395 }
387 } 396 }
397 if (IsOlderFrameId(last_acked_frame_id_, frame_id)) {
398 last_acked_frame_id_ = frame_id;
399 }
388 } 400 }
389 401
390 void Vp8Encoder::GenerateKeyFrame() { 402 void Vp8Encoder::GenerateKeyFrame() {
391 DCHECK(thread_checker_.CalledOnValidThread()); 403 DCHECK(thread_checker_.CalledOnValidThread());
392 key_frame_requested_ = true; 404 key_frame_requested_ = true;
393 } 405 }
394 406
395 // Calculate the max size of the key frame relative to a normal delta frame. 407 // Calculate the max size of the key frame relative to a normal delta frame.
396 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const { 408 uint32 Vp8Encoder::MaxIntraTarget(uint32 optimal_buffer_size_ms) const {
397 // Set max to the optimal buffer level (normalized by target BR), 409 // Set max to the optimal buffer level (normalized by target BR),
398 // and scaled by a scale_parameter. 410 // and scaled by a scale_parameter.
399 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps]. 411 // Max target size = scalePar * optimalBufferSize * targetBR[Kbps].
400 // This values is presented in percentage of perFrameBw: 412 // This values is presented in percentage of perFrameBw:
401 // perFrameBw = targetBR[Kbps] * 1000 / frameRate. 413 // perFrameBw = targetBR[Kbps] * 1000 / frameRate.
402 // The target in % is as follows: 414 // The target in % is as follows:
403 415
404 float scale_parameter = 0.5; 416 float scale_parameter = 0.5;
405 uint32 target_pct = optimal_buffer_size_ms * scale_parameter * 417 uint32 target_pct = optimal_buffer_size_ms * scale_parameter *
406 cast_config_.max_frame_rate / 10; 418 cast_config_.max_frame_rate / 10;
407 419
408 // Don't go below 3 times the per frame bandwidth. 420 // Don't go below 3 times the per frame bandwidth.
409 return std::max(target_pct, kMinIntra); 421 return std::max(target_pct, kMinIntra);
410 } 422 }
411 423
412 } // namespace cast 424 } // namespace cast
413 } // namespace media 425 } // namespace media
OLDNEW
« no previous file with comments | « media/cast/sender/vp8_encoder.h ('k') | media/cast/test/end2end_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698