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

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

Issue 906403006: [Cast] Size-Adaptable platform video encoders. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix CastStreamingApiTestWithPixelOutput.RtpStreamError test. 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
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/h264_vt_encoder.h" 5 #include "media/cast/sender/h264_vt_encoder.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/big_endian.h" 10 #include "base/big_endian.h"
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/bind_helpers.h" 12 #include "base/bind_helpers.h"
13 #include "base/location.h" 13 #include "base/location.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/macros.h" 15 #include "base/macros.h"
16 #include "base/synchronization/lock.h"
16 #include "media/base/mac/corevideo_glue.h" 17 #include "media/base/mac/corevideo_glue.h"
17 #include "media/base/mac/video_frame_mac.h" 18 #include "media/base/mac/video_frame_mac.h"
18 #include "media/cast/sender/video_frame_factory.h" 19 #include "media/cast/sender/video_frame_factory.h"
19 20
20 namespace media { 21 namespace media {
21 namespace cast { 22 namespace cast {
22 23
23 namespace { 24 namespace {
24 25
25 // Container for the associated data of a video frame being processed. 26 // Container for the associated data of a video frame being processed.
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer); 196 CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer);
196 } else { 197 } else {
197 NOTREACHED(); 198 NOTREACHED();
198 } 199 }
199 } 200 }
200 201
201 // Implementation of the VideoFrameFactory interface using |CVPixelBufferPool|. 202 // Implementation of the VideoFrameFactory interface using |CVPixelBufferPool|.
202 class VideoFrameFactoryCVPixelBufferPoolImpl : public VideoFrameFactory { 203 class VideoFrameFactoryCVPixelBufferPoolImpl : public VideoFrameFactory {
203 public: 204 public:
204 VideoFrameFactoryCVPixelBufferPoolImpl( 205 VideoFrameFactoryCVPixelBufferPoolImpl(
205 const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool) 206 const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool,
206 : pool_(pool) {} 207 const gfx::Size& frame_size)
208 : pool_(pool),
209 frame_size_(frame_size) {}
207 210
208 ~VideoFrameFactoryCVPixelBufferPoolImpl() override {} 211 ~VideoFrameFactoryCVPixelBufferPoolImpl() override {}
209 212
210 scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) override { 213 scoped_refptr<VideoFrame> MaybeCreateFrame(
214 const gfx::Size& frame_size, base::TimeDelta timestamp) override {
215 if (frame_size != frame_size_)
216 return nullptr; // Buffer pool is not a match for requested frame size.
jfroy 2015/02/10 22:51:37 Not sure about chromium style, but maybe wrap in {
miu 2015/02/11 00:31:24 Unfortunately, in Chromium the style is to NOT wra
217
211 base::ScopedCFTypeRef<CVPixelBufferRef> buffer; 218 base::ScopedCFTypeRef<CVPixelBufferRef> buffer;
212 CHECK_EQ(kCVReturnSuccess, 219 if (CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
213 CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_, 220 buffer.InitializeInto()) !=
214 buffer.InitializeInto())); 221 kCVReturnSuccess ||
222 !buffer)
jfroy 2015/02/10 22:51:37 There should never be a case where buffer == nullp
miu 2015/02/11 00:31:24 Done.
223 return nullptr; // Buffer pool has run out of pixel buffers.
jfroy 2015/02/10 22:51:37 Not sure about chromium style, but maybe wrap in {
miu 2015/02/11 00:31:25 Acknowledged.
224
225 // On some Macs, the buffer pool seems to return pixel buffers without the
226 // required attachments set. Set them explicitly here, and they must be
227 // in-sync with those in H264VideoToolboxEncoder::ConfigureSession().
228 CVBufferSetAttachment(buffer, kCVImageBufferColorPrimariesKey,
jfroy 2015/02/10 22:51:37 As discussed, instead of setting these attributes
miu 2015/02/11 00:31:24 Done.
229 kCVImageBufferColorPrimaries_ITU_R_709_2,
230 kCVAttachmentMode_ShouldPropagate);
231 CVBufferSetAttachment(buffer, kCVImageBufferTransferFunctionKey,
232 kCVImageBufferTransferFunction_ITU_R_709_2,
233 kCVAttachmentMode_ShouldPropagate);
234 CVBufferSetAttachment(buffer, kCVImageBufferYCbCrMatrixKey,
235 kCVImageBufferYCbCrMatrix_ITU_R_709_2,
236 kCVAttachmentMode_ShouldPropagate);
237
215 return VideoFrame::WrapCVPixelBuffer(buffer, timestamp); 238 return VideoFrame::WrapCVPixelBuffer(buffer, timestamp);
216 } 239 }
217 240
218 private: 241 private:
219 base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_; 242 const base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_;
243 const gfx::Size frame_size_;
220 244
221 DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryCVPixelBufferPoolImpl); 245 DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryCVPixelBufferPoolImpl);
222 }; 246 };
223 247
224 } // namespace 248 } // namespace
225 249
250 // static
251 bool H264VideoToolboxEncoder::IsSupported(
252 const VideoSenderConfig& video_config) {
253 return video_config.codec == CODEC_VIDEO_H264 && VideoToolboxGlue::Get();
254 }
255
226 H264VideoToolboxEncoder::H264VideoToolboxEncoder( 256 H264VideoToolboxEncoder::H264VideoToolboxEncoder(
227 const scoped_refptr<CastEnvironment>& cast_environment, 257 const scoped_refptr<CastEnvironment>& cast_environment,
228 const VideoSenderConfig& video_config, 258 const VideoSenderConfig& video_config,
229 const gfx::Size& frame_size, 259 const gfx::Size& frame_size,
260 uint32 first_frame_id,
230 const StatusChangeCallback& status_change_cb) 261 const StatusChangeCallback& status_change_cb)
231 : cast_environment_(cast_environment), 262 : cast_environment_(cast_environment),
232 videotoolbox_glue_(VideoToolboxGlue::Get()), 263 videotoolbox_glue_(VideoToolboxGlue::Get()),
233 frame_id_(kStartFrameId), 264 frame_size_(frame_size),
265 status_change_cb_(status_change_cb),
266 next_frame_id_(first_frame_id),
234 encode_next_frame_as_keyframe_(false) { 267 encode_next_frame_as_keyframe_(false) {
235 DCHECK(!frame_size.IsEmpty()); 268 DCHECK(!frame_size_.IsEmpty());
236 DCHECK(!status_change_cb.is_null()); 269 DCHECK(!status_change_cb_.is_null());
237 270
238 OperationalStatus operational_status; 271 OperationalStatus operational_status;
239 if (video_config.codec == CODEC_VIDEO_H264 && videotoolbox_glue_) { 272 if (video_config.codec == CODEC_VIDEO_H264 && videotoolbox_glue_) {
240 operational_status = Initialize(video_config, frame_size) ? 273 operational_status = Initialize(video_config) ?
241 STATUS_INITIALIZED : STATUS_INVALID_CONFIGURATION; 274 STATUS_INITIALIZED : STATUS_INVALID_CONFIGURATION;
242 } else { 275 } else {
243 operational_status = STATUS_UNSUPPORTED_CODEC; 276 operational_status = STATUS_UNSUPPORTED_CODEC;
244 } 277 }
245 cast_environment_->PostTask( 278 cast_environment_->PostTask(
246 CastEnvironment::MAIN, 279 CastEnvironment::MAIN,
247 FROM_HERE, 280 FROM_HERE,
248 base::Bind(status_change_cb, operational_status)); 281 base::Bind(status_change_cb_, operational_status));
249 } 282 }
250 283
251 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() { 284 H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
252 Teardown(); 285 Teardown();
253 } 286 }
254 287
255 bool H264VideoToolboxEncoder::Initialize( 288 bool H264VideoToolboxEncoder::Initialize(
256 const VideoSenderConfig& video_config, 289 const VideoSenderConfig& video_config) {
257 const gfx::Size& frame_size) {
258 DCHECK(thread_checker_.CalledOnValidThread()); 290 DCHECK(thread_checker_.CalledOnValidThread());
259 DCHECK(!compression_session_); 291 DCHECK(!compression_session_);
260 292
261 // Note that the encoder object is given to the compression session as the 293 // Note that the encoder object is given to the compression session as the
262 // callback context using a raw pointer. The C API does not allow us to use 294 // callback context using a raw pointer. The C API does not allow us to use
263 // a smart pointer, nor is this encoder ref counted. However, this is still 295 // a smart pointer, nor is this encoder ref counted. However, this is still
264 // safe, because we 1) we own the compression session and 2) we tear it down 296 // safe, because we 1) we own the compression session and 2) we tear it down
265 // safely. When destructing the encoder, the compression session is flushed 297 // safely. When destructing the encoder, the compression session is flushed
266 // and invalidated. Internally, VideoToolbox will join all of its threads 298 // and invalidated. Internally, VideoToolbox will join all of its threads
267 // before returning to the client. Therefore, when control returns to us, we 299 // before returning to the client. Therefore, when control returns to us, we
(...skipping 14 matching lines...) Expand all
282 const int formats[] = { 314 const int formats[] = {
283 kCVPixelFormatType_420YpCbCr8Planar, 315 kCVPixelFormatType_420YpCbCr8Planar,
284 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange}; 316 CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange};
285 base::ScopedCFTypeRef<CFArrayRef> formats_array = ArrayWithIntegers( 317 base::ScopedCFTypeRef<CFArrayRef> formats_array = ArrayWithIntegers(
286 std::vector<int>(formats, formats + arraysize(formats))); 318 std::vector<int>(formats, formats + arraysize(formats)));
287 base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes = 319 base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes =
288 DictionaryWithKeyValue(kCVPixelBufferPixelFormatTypeKey, formats_array); 320 DictionaryWithKeyValue(kCVPixelBufferPixelFormatTypeKey, formats_array);
289 321
290 VTCompressionSessionRef session; 322 VTCompressionSessionRef session;
291 OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate( 323 OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate(
292 kCFAllocatorDefault, frame_size.width(), frame_size.height(), 324 kCFAllocatorDefault, frame_size_.width(), frame_size_.height(),
293 CoreMediaGlue::kCMVideoCodecType_H264, encoder_spec, buffer_attributes, 325 CoreMediaGlue::kCMVideoCodecType_H264, encoder_spec, buffer_attributes,
294 nullptr /* compressedDataAllocator */, 326 nullptr /* compressedDataAllocator */,
295 &H264VideoToolboxEncoder::CompressionCallback, 327 &H264VideoToolboxEncoder::CompressionCallback,
296 reinterpret_cast<void*>(this), &session); 328 reinterpret_cast<void*>(this), &session);
297 if (status != noErr) { 329 if (status != noErr) {
298 DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status; 330 DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status;
299 return false; 331 return false;
300 } 332 }
301 compression_session_.reset(session); 333 compression_session_.reset(session);
302 334
(...skipping 19 matching lines...) Expand all
322 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(), 354 ->kVTCompressionPropertyKey_MaxKeyFrameIntervalDuration(),
323 240); 355 240);
324 // TODO(jfroy): implement better bitrate control 356 // TODO(jfroy): implement better bitrate control
325 // https://crbug.com/425352 357 // https://crbug.com/425352
326 SetSessionProperty( 358 SetSessionProperty(
327 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(), 359 videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(),
328 (video_config.min_bitrate + video_config.max_bitrate) / 2); 360 (video_config.min_bitrate + video_config.max_bitrate) / 2);
329 SetSessionProperty( 361 SetSessionProperty(
330 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(), 362 videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(),
331 video_config.max_frame_rate); 363 video_config.max_frame_rate);
364 // Keep these attachment settings in-sync with those in
365 // VideoFrameFactoryCVPixelBufferPoolImpl::MaybeCreateFrame().
jfroy 2015/02/10 22:51:37 Update this comment if the colorimetric keys are m
miu 2015/02/11 00:31:25 Done.
332 SetSessionProperty( 366 SetSessionProperty(
333 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(), 367 videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(),
334 kCVImageBufferColorPrimaries_ITU_R_709_2); 368 kCVImageBufferColorPrimaries_ITU_R_709_2);
335 SetSessionProperty( 369 SetSessionProperty(
336 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(), 370 videotoolbox_glue_->kVTCompressionPropertyKey_TransferFunction(),
337 kCVImageBufferTransferFunction_ITU_R_709_2); 371 kCVImageBufferTransferFunction_ITU_R_709_2);
338 SetSessionProperty( 372 SetSessionProperty(
339 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(), 373 videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(),
340 kCVImageBufferYCbCrMatrix_ITU_R_709_2); 374 kCVImageBufferYCbCrMatrix_ITU_R_709_2);
341 if (video_config.max_number_of_video_buffers_used > 0) { 375 if (video_config.max_number_of_video_buffers_used > 0) {
342 SetSessionProperty( 376 SetSessionProperty(
343 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(), 377 videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(),
344 video_config.max_number_of_video_buffers_used); 378 video_config.max_number_of_video_buffers_used);
345 } 379 }
346 } 380 }
347 381
348 void H264VideoToolboxEncoder::Teardown() { 382 void H264VideoToolboxEncoder::Teardown() {
349 DCHECK(thread_checker_.CalledOnValidThread()); 383 DCHECK(thread_checker_.CalledOnValidThread());
350 384
351 // If the compression session exists, invalidate it. This blocks until all 385 // If the compression session exists, invalidate it. This blocks until all
352 // pending output callbacks have returned and any internal threads have 386 // pending output callbacks have returned and any internal threads have
353 // joined, ensuring no output callback ever sees a dangling encoder pointer. 387 // joined, ensuring no output callback ever sees a dangling encoder pointer.
354 if (compression_session_) { 388 if (compression_session_) {
355 videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_); 389 videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_);
356 compression_session_.reset(); 390 compression_session_.reset();
357 } 391 }
358 } 392 }
359 393
360 bool H264VideoToolboxEncoder::CanEncodeVariedFrameSizes() const {
361 return false;
362 }
363
364 bool H264VideoToolboxEncoder::EncodeVideoFrame( 394 bool H264VideoToolboxEncoder::EncodeVideoFrame(
365 const scoped_refptr<media::VideoFrame>& video_frame, 395 const scoped_refptr<media::VideoFrame>& video_frame,
366 const base::TimeTicks& reference_time, 396 const base::TimeTicks& reference_time,
367 const FrameEncodedCallback& frame_encoded_callback) { 397 const FrameEncodedCallback& frame_encoded_callback) {
368 DCHECK(thread_checker_.CalledOnValidThread()); 398 DCHECK(thread_checker_.CalledOnValidThread());
369 DCHECK(!video_frame->visible_rect().IsEmpty());
370 DCHECK(!frame_encoded_callback.is_null()); 399 DCHECK(!frame_encoded_callback.is_null());
371 400
372 if (!compression_session_) { 401 if (!compression_session_) {
373 DLOG(ERROR) << " compression session is null"; 402 DLOG(ERROR) << " compression session is null";
374 return false; 403 return false;
375 } 404 }
376 405
406 if (video_frame->visible_rect().size() != frame_size_)
407 return false;
jfroy 2015/02/10 22:51:37 Not sure about chromium style, but maybe wrap in {
miu 2015/02/11 00:31:24 Acknowledged.
408
377 // Wrap the VideoFrame in a CVPixelBuffer. In all cases, no data will be 409 // Wrap the VideoFrame in a CVPixelBuffer. In all cases, no data will be
378 // copied. If the VideoFrame was created by this encoder's video frame 410 // copied. If the VideoFrame was created by this encoder's video frame
379 // factory, then the returned CVPixelBuffer will have been obtained from the 411 // factory, then the returned CVPixelBuffer will have been obtained from the
380 // compression session's pixel buffer pool. This will eliminate a copy of the 412 // compression session's pixel buffer pool. This will eliminate a copy of the
381 // frame into memory visible by the hardware encoder. The VideoFrame's 413 // frame into memory visible by the hardware encoder. The VideoFrame's
382 // lifetime is extended for the lifetime of the returned CVPixelBuffer. 414 // lifetime is extended for the lifetime of the returned CVPixelBuffer.
383 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame); 415 auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame);
384 if (!pixel_buffer) { 416 if (!pixel_buffer) {
385 return false; 417 return false;
386 } 418 }
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
428 460
429 encode_next_frame_as_keyframe_ = true; 461 encode_next_frame_as_keyframe_ = true;
430 } 462 }
431 463
432 void H264VideoToolboxEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) { 464 void H264VideoToolboxEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
433 // Not supported by VideoToolbox in any meaningful manner. 465 // Not supported by VideoToolbox in any meaningful manner.
434 } 466 }
435 467
436 scoped_ptr<VideoFrameFactory> 468 scoped_ptr<VideoFrameFactory>
437 H264VideoToolboxEncoder::CreateVideoFrameFactory() { 469 H264VideoToolboxEncoder::CreateVideoFrameFactory() {
470 if (!videotoolbox_glue_ || !compression_session_)
471 return nullptr;
jfroy 2015/02/10 22:51:37 Not sure about chromium style, but maybe wrap in {
miu 2015/02/11 00:31:25 Acknowledged.
438 base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool( 472 base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool(
439 videotoolbox_glue_->VTCompressionSessionGetPixelBufferPool( 473 videotoolbox_glue_->VTCompressionSessionGetPixelBufferPool(
440 compression_session_), 474 compression_session_),
441 base::scoped_policy::RETAIN); 475 base::scoped_policy::RETAIN);
442 return scoped_ptr<VideoFrameFactory>( 476 return scoped_ptr<VideoFrameFactory>(
443 new VideoFrameFactoryCVPixelBufferPoolImpl(pool)); 477 new VideoFrameFactoryCVPixelBufferPoolImpl(pool, frame_size_));
444 } 478 }
445 479
446 void H264VideoToolboxEncoder::EmitFrames() { 480 void H264VideoToolboxEncoder::EmitFrames() {
447 DCHECK(thread_checker_.CalledOnValidThread()); 481 DCHECK(thread_checker_.CalledOnValidThread());
448 482
449 if (!compression_session_) { 483 if (!compression_session_) {
450 DLOG(ERROR) << " compression session is null"; 484 DLOG(ERROR) << " compression session is null";
451 return; 485 return;
452 } 486 }
453 487
(...skipping 22 matching lines...) Expand all
476 CFStringRef value) { 510 CFStringRef value) {
477 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key, 511 return videotoolbox_glue_->VTSessionSetProperty(compression_session_, key,
478 value) == noErr; 512 value) == noErr;
479 } 513 }
480 514
481 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque, 515 void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
482 void* request_opaque, 516 void* request_opaque,
483 OSStatus status, 517 OSStatus status,
484 VTEncodeInfoFlags info, 518 VTEncodeInfoFlags info,
485 CMSampleBufferRef sbuf) { 519 CMSampleBufferRef sbuf) {
486 if (status != noErr) {
487 DLOG(ERROR) << " encoding failed: " << status;
488 return;
489 }
490 if ((info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped)) {
491 DVLOG(2) << " frame dropped";
492 return;
493 }
494
495 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque); 520 auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque);
496 const scoped_ptr<InProgressFrameEncode> request( 521 const scoped_ptr<InProgressFrameEncode> request(
497 reinterpret_cast<InProgressFrameEncode*>(request_opaque)); 522 reinterpret_cast<InProgressFrameEncode*>(request_opaque));
498 auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex( 523 bool keyframe = false;
499 CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(sbuf, true), 0)); 524 bool has_frame_data = false;
500 525
501 // If the NotSync key is not present, it implies Sync, which indicates a 526 if (status != noErr) {
502 // keyframe (at least I think, VT documentation is, erm, sparse). Could 527 DLOG(ERROR) << " encoding failed: " << status;
503 // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false. 528 encoder->cast_environment_->PostTask(
504 bool keyframe = 529 CastEnvironment::MAIN,
505 !CFDictionaryContainsKey(sample_attachments, 530 FROM_HERE,
506 CoreMediaGlue::kCMSampleAttachmentKey_NotSync()); 531 base::Bind(encoder->status_change_cb_, STATUS_CODEC_RUNTIME_ERROR));
532 } else if ((info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped)) {
533 DVLOG(2) << " frame dropped";
534 } else {
535 auto sample_attachments = static_cast<CFDictionaryRef>(
536 CFArrayGetValueAtIndex(
537 CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(sbuf, true),
538 0));
539
540 // If the NotSync key is not present, it implies Sync, which indicates a
541 // keyframe (at least I think, VT documentation is, erm, sparse). Could
542 // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false.
543 keyframe = !CFDictionaryContainsKey(
544 sample_attachments,
545 CoreMediaGlue::kCMSampleAttachmentKey_NotSync());
546 has_frame_data = true;
547 }
507 548
508 // Increment the encoder-scoped frame id and assign the new value to this 549 // Increment the encoder-scoped frame id and assign the new value to this
509 // frame. VideoToolbox calls the output callback serially, so this is safe. 550 // frame. VideoToolbox calls the output callback serially, so this is safe.
510 uint32 frame_id = ++encoder->frame_id_; 551 const uint32 frame_id = encoder->next_frame_id_++;
511 552
512 scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); 553 scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame());
513 encoded_frame->frame_id = frame_id; 554 encoded_frame->frame_id = frame_id;
514 encoded_frame->reference_time = request->reference_time; 555 encoded_frame->reference_time = request->reference_time;
515 encoded_frame->rtp_timestamp = request->rtp_timestamp; 556 encoded_frame->rtp_timestamp = request->rtp_timestamp;
516 if (keyframe) { 557 if (keyframe) {
517 encoded_frame->dependency = EncodedFrame::KEY; 558 encoded_frame->dependency = EncodedFrame::KEY;
518 encoded_frame->referenced_frame_id = frame_id; 559 encoded_frame->referenced_frame_id = frame_id;
519 } else { 560 } else {
520 encoded_frame->dependency = EncodedFrame::DEPENDENT; 561 encoded_frame->dependency = EncodedFrame::DEPENDENT;
521 // H.264 supports complex frame reference schemes (multiple reference 562 // H.264 supports complex frame reference schemes (multiple reference
522 // frames, slice references, backward and forward references, etc). Cast 563 // frames, slice references, backward and forward references, etc). Cast
523 // doesn't support the concept of forward-referencing frame dependencies or 564 // doesn't support the concept of forward-referencing frame dependencies or
524 // multiple frame dependencies; so pretend that all frames are only 565 // multiple frame dependencies; so pretend that all frames are only
525 // decodable after their immediately preceding frame is decoded. This will 566 // decodable after their immediately preceding frame is decoded. This will
526 // ensure a Cast receiver only attempts to decode the frames sequentially 567 // ensure a Cast receiver only attempts to decode the frames sequentially
527 // and in order. Furthermore, the encoder is configured to never use forward 568 // and in order. Furthermore, the encoder is configured to never use forward
528 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There 569 // references (see |kVTCompressionPropertyKey_AllowFrameReordering|). There
529 // is no way to prevent multiple reference frames. 570 // is no way to prevent multiple reference frames.
530 encoded_frame->referenced_frame_id = frame_id - 1; 571 encoded_frame->referenced_frame_id = frame_id - 1;
531 } 572 }
532 573
533 CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe); 574 if (has_frame_data)
575 CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe);
jfroy 2015/02/10 22:51:37 Not sure about chromium style, but maybe wrap in {
miu 2015/02/11 00:31:25 Acknowledged.
534 576
535 encoder->cast_environment_->PostTask( 577 encoder->cast_environment_->PostTask(
536 CastEnvironment::MAIN, FROM_HERE, 578 CastEnvironment::MAIN, FROM_HERE,
537 base::Bind(request->frame_encoded_callback, 579 base::Bind(request->frame_encoded_callback,
538 base::Passed(&encoded_frame))); 580 base::Passed(&encoded_frame)));
539 } 581 }
540 582
583 // A ref-counted structure that is shared to provide concurrent access to the
584 // VideoFrameFactory instance for the current encoder. OnEncoderReplaced() can
585 // change |factory| whenever an encoder instance has been replaced, while users
586 // of CreateVideoFrameFactory() may attempt to read/use |factory| by any thread
587 // at any time.
588 struct SizeAdaptableH264VideoToolboxVideoEncoder::FactoryHolder
589 : public base::RefCountedThreadSafe<FactoryHolder> {
590 base::Lock lock;
591 scoped_ptr<VideoFrameFactory> factory;
592
593 private:
594 friend class base::RefCountedThreadSafe<FactoryHolder>;
595 ~FactoryHolder() {}
596 };
597
598 SizeAdaptableH264VideoToolboxVideoEncoder::
599 SizeAdaptableH264VideoToolboxVideoEncoder(
600 const scoped_refptr<CastEnvironment>& cast_environment,
601 const VideoSenderConfig& video_config,
602 const StatusChangeCallback& status_change_cb)
603 : SizeAdaptableVideoEncoderBase(cast_environment,
604 video_config,
605 status_change_cb),
606 holder_(new FactoryHolder()) {}
607
608 SizeAdaptableH264VideoToolboxVideoEncoder::
609 ~SizeAdaptableH264VideoToolboxVideoEncoder() {}
610
611 scoped_ptr<VideoFrameFactory>
612 SizeAdaptableH264VideoToolboxVideoEncoder::CreateVideoFrameFactory() {
613 // A proxy allowing SizeAdaptableH264VideoToolboxVideoEncoder to swap out the
614 // VideoFrameFactory instance to match one appropriate for the current encoder
615 // instance.
616 class VideoFrameFactoryProxy : public VideoFrameFactory {
hubbe 2015/02/11 00:47:54 Personally I think local classes are harder to rea
miu 2015/02/11 02:14:23 Done.
617 public:
618 explicit VideoFrameFactoryProxy(const scoped_refptr<FactoryHolder>& holder)
619 : holder_(holder) {}
620
621 ~VideoFrameFactoryProxy() override {}
622
623 scoped_refptr<VideoFrame> MaybeCreateFrame(
624 const gfx::Size& frame_size, base::TimeDelta timestamp) override {
625 base::AutoLock auto_lock(holder_->lock);
626 return holder_->factory ?
627 holder_->factory->MaybeCreateFrame(frame_size, timestamp) : nullptr;
628 }
629
630 private:
631 const scoped_refptr<FactoryHolder> holder_;
632
633 DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryProxy);
634 };
635
636 return scoped_ptr<VideoFrameFactory>(new VideoFrameFactoryProxy(holder_));
637 }
638
639 scoped_ptr<VideoEncoder>
640 SizeAdaptableH264VideoToolboxVideoEncoder::CreateReplacementEncoder() {
641 return scoped_ptr<VideoEncoder>(new H264VideoToolboxEncoder(
642 cast_environment(),
643 video_config(),
644 next_encoder_frame_size(),
hubbe 2015/02/11 00:47:54 It feels a little awkward to use "next_encoder_fra
miu 2015/02/11 02:14:23 Eliminated "next_encoder" part of things, as discu
645 last_frame_id() + 1,
646 CreateEncoderStatusChangeCallback()));
647 }
648
649 void SizeAdaptableH264VideoToolboxVideoEncoder::OnEncoderReplaced(
650 VideoEncoder* replacement_encoder) {
651 scoped_ptr<VideoFrameFactory> current_factory(
652 replacement_encoder->CreateVideoFrameFactory());
653 base::AutoLock auto_lock(holder_->lock);
654 holder_->factory = current_factory.Pass();
hubbe 2015/02/11 00:47:54 Call baseclass OnEncoderReplaced()
miu 2015/02/11 02:14:23 Done.
655 }
656
657 void SizeAdaptableH264VideoToolboxVideoEncoder::DestroyCurrentEncoder() {
658 {
659 base::AutoLock auto_lock(holder_->lock);
660 holder_->factory.reset();
661 }
662 SizeAdaptableVideoEncoderBase::DestroyCurrentEncoder();
663 }
664
541 } // namespace cast 665 } // namespace cast
542 } // namespace media 666 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698