| Index: media/cast/sender/h264_vt_encoder.cc
|
| diff --git a/media/cast/sender/h264_vt_encoder.cc b/media/cast/sender/h264_vt_encoder.cc
|
| index c8ae82bb37a3f5433b757c9cc7bedb7ff6d88d6f..e109bcfca57f930c57ef0edaf07b61a25b58de0a 100644
|
| --- a/media/cast/sender/h264_vt_encoder.cc
|
| +++ b/media/cast/sender/h264_vt_encoder.cc
|
| @@ -37,16 +37,10 @@ struct InProgressFrameEncode {
|
| frame_encoded_callback(callback) {}
|
| };
|
|
|
| -base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeysAndValues(
|
| - CFTypeRef* keys,
|
| - CFTypeRef* values,
|
| - size_t size) {
|
| +base::ScopedCFTypeRef<CFDictionaryRef>
|
| +DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) {
|
| return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate(
|
| - kCFAllocatorDefault,
|
| - keys,
|
| - values,
|
| - size,
|
| - &kCFTypeDictionaryKeyCallBacks,
|
| + kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks,
|
| &kCFTypeDictionaryValueCallBacks));
|
| }
|
|
|
| @@ -209,40 +203,107 @@ void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
|
| }
|
| }
|
|
|
| -// Implementation of the VideoFrameFactory interface using |CVPixelBufferPool|.
|
| -class VideoFrameFactoryCVPixelBufferPoolImpl : public VideoFrameFactory {
|
| +} // namespace
|
| +
|
| +class H264VideoToolboxEncoder::VideoFrameFactoryImpl
|
| + : public base::RefCountedThreadSafe<VideoFrameFactoryImpl>,
|
| + public VideoFrameFactory {
|
| public:
|
| - VideoFrameFactoryCVPixelBufferPoolImpl(
|
| - const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool,
|
| - const gfx::Size& frame_size)
|
| - : pool_(pool),
|
| - frame_size_(frame_size) {}
|
| + // Type that proxies the VideoFrameFactory interface to this class.
|
| + class Proxy;
|
|
|
| - ~VideoFrameFactoryCVPixelBufferPoolImpl() override {}
|
| + VideoFrameFactoryImpl(const base::WeakPtr<H264VideoToolboxEncoder>& encoder,
|
| + const scoped_refptr<CastEnvironment>& cast_environment)
|
| + : encoder_(encoder), cast_environment_(cast_environment) {}
|
|
|
| scoped_refptr<VideoFrame> MaybeCreateFrame(
|
| - const gfx::Size& frame_size, base::TimeDelta timestamp) override {
|
| - if (frame_size != frame_size_)
|
| - return nullptr; // Buffer pool is not a match for requested frame size.
|
| + const gfx::Size& frame_size,
|
| + base::TimeDelta timestamp) override {
|
| + if (frame_size.IsEmpty()) {
|
| + DVLOG(1) << "Rejecting empty video frame.";
|
| + return nullptr;
|
| + }
|
| +
|
| + base::AutoLock auto_lock(lock_);
|
| +
|
| + // If the pool size does not match, speculatively reset the encoder to use
|
| + // the new size and return null. Cache the new frame size right away and
|
| + // toss away the pixel buffer pool to avoid spurious tasks until the encoder
|
| + // is done resetting.
|
| + if (frame_size != pool_frame_size_) {
|
| + DVLOG(1) << "MaybeCreateFrame: Detected frame size change.";
|
| + cast_environment_->PostTask(
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| + base::Bind(&H264VideoToolboxEncoder::UpdateFrameSize, encoder_,
|
| + frame_size));
|
| + pool_frame_size_ = frame_size;
|
| + pool_.reset();
|
| + return nullptr;
|
| + }
|
| +
|
| + if (!pool_) {
|
| + DVLOG(1) << "MaybeCreateFrame: No pixel buffer pool.";
|
| + return nullptr;
|
| + }
|
|
|
| + // Allocate a pixel buffer from the pool and return a wrapper VideoFrame.
|
| base::ScopedCFTypeRef<CVPixelBufferRef> buffer;
|
| - if (CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
|
| - buffer.InitializeInto()) !=
|
| - kCVReturnSuccess)
|
| - return nullptr; // Buffer pool has run out of pixel buffers.
|
| - DCHECK(buffer);
|
| + auto status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
|
| + buffer.InitializeInto());
|
| + if (status != kCVReturnSuccess) {
|
| + DLOG(ERROR) << "CVPixelBufferPoolCreatePixelBuffer failed: " << status;
|
| + return nullptr;
|
| + }
|
|
|
| + DCHECK(buffer);
|
| return VideoFrame::WrapCVPixelBuffer(buffer, timestamp);
|
| }
|
|
|
| + void Update(const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool,
|
| + const gfx::Size& frame_size) {
|
| + base::AutoLock auto_lock(lock_);
|
| + pool_ = pool;
|
| + pool_frame_size_ = frame_size;
|
| + }
|
| +
|
| private:
|
| - const base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_;
|
| - const gfx::Size frame_size_;
|
| + friend class base::RefCountedThreadSafe<VideoFrameFactoryImpl>;
|
| + ~VideoFrameFactoryImpl() override {}
|
| +
|
| + base::Lock lock_;
|
| + base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_;
|
| + gfx::Size pool_frame_size_;
|
| +
|
| + // Weak back reference to the encoder and the cast envrionment so we can
|
| + // message the encoder when the frame size changes.
|
| + const base::WeakPtr<H264VideoToolboxEncoder> encoder_;
|
| + const scoped_refptr<CastEnvironment> cast_environment_;
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryCVPixelBufferPoolImpl);
|
| + DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryImpl);
|
| };
|
|
|
| -} // namespace
|
| +class H264VideoToolboxEncoder::VideoFrameFactoryImpl::Proxy
|
| + : public VideoFrameFactory {
|
| + public:
|
| + explicit Proxy(
|
| + const scoped_refptr<VideoFrameFactoryImpl>& video_frame_factory)
|
| + : video_frame_factory_(video_frame_factory) {
|
| + DCHECK(video_frame_factory_);
|
| + }
|
| +
|
| + scoped_refptr<VideoFrame> MaybeCreateFrame(
|
| + const gfx::Size& frame_size,
|
| + base::TimeDelta timestamp) override {
|
| + return video_frame_factory_->MaybeCreateFrame(frame_size, timestamp);
|
| + }
|
| +
|
| + private:
|
| + ~Proxy() override {}
|
| +
|
| + const scoped_refptr<VideoFrameFactoryImpl> video_frame_factory_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Proxy);
|
| +};
|
|
|
| // static
|
| bool H264VideoToolboxEncoder::IsSupported(
|
| @@ -253,48 +314,46 @@ bool H264VideoToolboxEncoder::IsSupported(
|
| H264VideoToolboxEncoder::H264VideoToolboxEncoder(
|
| const scoped_refptr<CastEnvironment>& cast_environment,
|
| const VideoSenderConfig& video_config,
|
| - const gfx::Size& frame_size,
|
| - uint32 first_frame_id,
|
| const StatusChangeCallback& status_change_cb)
|
| : cast_environment_(cast_environment),
|
| videotoolbox_glue_(VideoToolboxGlue::Get()),
|
| - frame_size_(frame_size),
|
| + video_config_(video_config),
|
| status_change_cb_(status_change_cb),
|
| - next_frame_id_(first_frame_id),
|
| - encode_next_frame_as_keyframe_(false) {
|
| - DCHECK(!frame_size_.IsEmpty());
|
| + last_frame_id_(kStartFrameId),
|
| + encode_next_frame_as_keyframe_(false),
|
| + weak_factory_(this) {
|
| + DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
|
| DCHECK(!status_change_cb_.is_null());
|
|
|
| - OperationalStatus operational_status;
|
| - if (video_config.codec == CODEC_VIDEO_H264 && videotoolbox_glue_) {
|
| - operational_status = Initialize(video_config) ?
|
| - STATUS_INITIALIZED : STATUS_INVALID_CONFIGURATION;
|
| - } else {
|
| - operational_status = STATUS_UNSUPPORTED_CODEC;
|
| - }
|
| + OperationalStatus operational_status =
|
| + H264VideoToolboxEncoder::IsSupported(video_config)
|
| + ? STATUS_INITIALIZED
|
| + : STATUS_UNSUPPORTED_CODEC;
|
| cast_environment_->PostTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| base::Bind(status_change_cb_, operational_status));
|
| +
|
| + if (operational_status == STATUS_INITIALIZED) {
|
| + video_frame_factory_ =
|
| + scoped_refptr<VideoFrameFactoryImpl>(new VideoFrameFactoryImpl(
|
| + weak_factory_.GetWeakPtr(), cast_environment_));
|
| + }
|
| }
|
|
|
| H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
|
| - Teardown();
|
| + DestroyCompressionSession();
|
| }
|
|
|
| -bool H264VideoToolboxEncoder::Initialize(
|
| - const VideoSenderConfig& video_config) {
|
| +void H264VideoToolboxEncoder::ResetCompressionSession() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| - DCHECK(!compression_session_);
|
|
|
| - // Note that the encoder object is given to the compression session as the
|
| - // callback context using a raw pointer. The C API does not allow us to use
|
| - // a smart pointer, nor is this encoder ref counted. However, this is still
|
| - // safe, because we 1) we own the compression session and 2) we tear it down
|
| - // safely. When destructing the encoder, the compression session is flushed
|
| - // and invalidated. Internally, VideoToolbox will join all of its threads
|
| - // before returning to the client. Therefore, when control returns to us, we
|
| - // are guaranteed that the output callback will not execute again.
|
| + // Notify that we're resetting the encoder.
|
| + cast_environment_->PostTask(
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| + base::Bind(status_change_cb_, STATUS_CODEC_REINIT_PENDING));
|
| +
|
| + // Destroy the current session, if any.
|
| + DestroyCompressionSession();
|
|
|
| // On OS X, allow the hardware encoder. Don't require it, it does not support
|
| // all configurations (some of which are used for testing).
|
| @@ -309,30 +368,21 @@ bool H264VideoToolboxEncoder::Initialize(
|
| // Certain encoders prefer kCVPixelFormatType_422YpCbCr8, which is not
|
| // supported through VideoFrame. We can force 420 formats to be used instead.
|
| const int formats[] = {
|
| - kCVPixelFormatType_420YpCbCr8Planar,
|
| - CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
|
| - };
|
| + kCVPixelFormatType_420YpCbCr8Planar,
|
| + CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange};
|
| // Keep these attachment settings in-sync with those in ConfigureSession().
|
| - CFTypeRef attachments_keys[] = {
|
| - kCVImageBufferColorPrimariesKey,
|
| - kCVImageBufferTransferFunctionKey,
|
| - kCVImageBufferYCbCrMatrixKey
|
| - };
|
| - CFTypeRef attachments_values[] = {
|
| - kCVImageBufferColorPrimaries_ITU_R_709_2,
|
| - kCVImageBufferTransferFunction_ITU_R_709_2,
|
| - kCVImageBufferYCbCrMatrix_ITU_R_709_2
|
| - };
|
| - CFTypeRef buffer_attributes_keys[] = {
|
| - kCVPixelBufferPixelFormatTypeKey,
|
| - kCVBufferPropagatedAttachmentsKey
|
| - };
|
| + CFTypeRef attachments_keys[] = {kCVImageBufferColorPrimariesKey,
|
| + kCVImageBufferTransferFunctionKey,
|
| + kCVImageBufferYCbCrMatrixKey};
|
| + CFTypeRef attachments_values[] = {kCVImageBufferColorPrimaries_ITU_R_709_2,
|
| + kCVImageBufferTransferFunction_ITU_R_709_2,
|
| + kCVImageBufferYCbCrMatrix_ITU_R_709_2};
|
| + CFTypeRef buffer_attributes_keys[] = {kCVPixelBufferPixelFormatTypeKey,
|
| + kCVBufferPropagatedAttachmentsKey};
|
| CFTypeRef buffer_attributes_values[] = {
|
| - ArrayWithIntegers(formats, arraysize(formats)).release(),
|
| - DictionaryWithKeysAndValues(attachments_keys,
|
| - attachments_values,
|
| - arraysize(attachments_keys)).release()
|
| - };
|
| + ArrayWithIntegers(formats, arraysize(formats)).release(),
|
| + DictionaryWithKeysAndValues(attachments_keys, attachments_values,
|
| + arraysize(attachments_keys)).release()};
|
| const base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes =
|
| DictionaryWithKeysAndValues(buffer_attributes_keys,
|
| buffer_attributes_values,
|
| @@ -340,26 +390,49 @@ bool H264VideoToolboxEncoder::Initialize(
|
| for (auto& v : buffer_attributes_values)
|
| CFRelease(v);
|
|
|
| - VTCompressionSessionRef session;
|
| + // Create the compression session.
|
| +
|
| + // Note that the encoder object is given to the compression session as the
|
| + // callback context using a raw pointer. The C API does not allow us to use a
|
| + // smart pointer, nor is this encoder ref counted. However, this is still
|
| + // safe, because we 1) we own the compression session and 2) we tear it down
|
| + // safely. When destructing the encoder, the compression session is flushed
|
| + // and invalidated. Internally, VideoToolbox will join all of its threads
|
| + // before returning to the client. Therefore, when control returns to us, we
|
| + // are guaranteed that the output callback will not execute again.
|
| OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate(
|
| kCFAllocatorDefault, frame_size_.width(), frame_size_.height(),
|
| CoreMediaGlue::kCMVideoCodecType_H264, encoder_spec, buffer_attributes,
|
| nullptr /* compressedDataAllocator */,
|
| &H264VideoToolboxEncoder::CompressionCallback,
|
| - reinterpret_cast<void*>(this), &session);
|
| + reinterpret_cast<void*>(this), compression_session_.InitializeInto());
|
| if (status != noErr) {
|
| DLOG(ERROR) << " VTCompressionSessionCreate failed: " << status;
|
| - return false;
|
| + // Notify that reinitialization has failed.
|
| + cast_environment_->PostTask(
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| + base::Bind(status_change_cb_, STATUS_CODEC_INIT_FAILED));
|
| + return;
|
| }
|
| - compression_session_.reset(session);
|
|
|
| - ConfigureSession(video_config);
|
| + // Configure the session (apply session properties based on the current state
|
| + // of the encoder, experimental tuning and requirements).
|
| + ConfigureCompressionSession();
|
|
|
| - return true;
|
| + // Update the video frame factory.
|
| + base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool(
|
| + videotoolbox_glue_->VTCompressionSessionGetPixelBufferPool(
|
| + compression_session_),
|
| + base::scoped_policy::RETAIN);
|
| + video_frame_factory_->Update(pool, frame_size_);
|
| +
|
| + // Notify that reinitialization is done.
|
| + cast_environment_->PostTask(
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| + base::Bind(status_change_cb_, STATUS_INITIALIZED));
|
| }
|
|
|
| -void H264VideoToolboxEncoder::ConfigureSession(
|
| - const VideoSenderConfig& video_config) {
|
| +void H264VideoToolboxEncoder::ConfigureCompressionSession() {
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_ProfileLevel(),
|
| videotoolbox_glue_->kVTProfileLevel_H264_Main_AutoLevel());
|
| @@ -378,10 +451,10 @@ void H264VideoToolboxEncoder::ConfigureSession(
|
| // https://crbug.com/425352
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_AverageBitRate(),
|
| - (video_config.min_bitrate + video_config.max_bitrate) / 2);
|
| + (video_config_.min_bitrate + video_config_.max_bitrate) / 2);
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(),
|
| - video_config.max_frame_rate);
|
| + video_config_.max_frame_rate);
|
| // Keep these attachment settings in-sync with those in Initialize().
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(),
|
| @@ -392,20 +465,31 @@ void H264VideoToolboxEncoder::ConfigureSession(
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_YCbCrMatrix(),
|
| kCVImageBufferYCbCrMatrix_ITU_R_709_2);
|
| - if (video_config.max_number_of_video_buffers_used > 0) {
|
| + if (video_config_.max_number_of_video_buffers_used > 0) {
|
| SetSessionProperty(
|
| videotoolbox_glue_->kVTCompressionPropertyKey_MaxFrameDelayCount(),
|
| - video_config.max_number_of_video_buffers_used);
|
| + video_config_.max_number_of_video_buffers_used);
|
| }
|
| }
|
|
|
| -void H264VideoToolboxEncoder::Teardown() {
|
| +void H264VideoToolboxEncoder::DestroyCompressionSession() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
| // If the compression session exists, invalidate it. This blocks until all
|
| // pending output callbacks have returned and any internal threads have
|
| // joined, ensuring no output callback ever sees a dangling encoder pointer.
|
| + //
|
| + // Before destroying the compression session, the video frame factory's pool
|
| + // is updated to null so that no thread will produce new video frames via the
|
| + // factory until a new compression session is created. The current frame size
|
| + // is passed to prevent the video frame factory from posting |UpdateFrameSize|
|
| + // tasks. Indeed, |DestroyCompressionSession| is either called from
|
| + // |ResetCompressionSession|, in which case a new pool and frame size will be
|
| + // set, or from callsites that require that there be no compression session
|
| + // (ex: the dtor).
|
| if (compression_session_) {
|
| + video_frame_factory_->Update(
|
| + base::ScopedCFTypeRef<CVPixelBufferPoolRef>(nullptr), frame_size_);
|
| videotoolbox_glue_->VTCompressionSessionInvalidate(compression_session_);
|
| compression_session_.reset();
|
| }
|
| @@ -418,13 +502,24 @@ bool H264VideoToolboxEncoder::EncodeVideoFrame(
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| DCHECK(!frame_encoded_callback.is_null());
|
|
|
| - if (!compression_session_) {
|
| - DLOG(ERROR) << " compression session is null";
|
| + // Reject empty video frames.
|
| + const gfx::Size frame_size = video_frame->visible_rect().size();
|
| + if (frame_size.IsEmpty()) {
|
| + DVLOG(1) << "Rejecting empty video frame.";
|
| return false;
|
| }
|
|
|
| - if (video_frame->visible_rect().size() != frame_size_)
|
| + // Handle frame size changes. This will reset the compression session.
|
| + if (frame_size != frame_size_) {
|
| + DVLOG(1) << "EncodeVideoFrame: Detected frame size change.";
|
| + UpdateFrameSize(frame_size);
|
| + }
|
| +
|
| + // Need a compression session to continue.
|
| + if (!compression_session_) {
|
| + DLOG(ERROR) << "No compression session.";
|
| return false;
|
| + }
|
|
|
| // Wrap the VideoFrame in a CVPixelBuffer. In all cases, no data will be
|
| // copied. If the VideoFrame was created by this encoder's video frame
|
| @@ -434,16 +529,21 @@ bool H264VideoToolboxEncoder::EncodeVideoFrame(
|
| // lifetime is extended for the lifetime of the returned CVPixelBuffer.
|
| auto pixel_buffer = media::WrapVideoFrameInCVPixelBuffer(*video_frame);
|
| if (!pixel_buffer) {
|
| + DLOG(ERROR) << "WrapVideoFrameInCVPixelBuffer failed.";
|
| return false;
|
| }
|
|
|
| + // Convert the frame timestamp to CMTime.
|
| auto timestamp_cm = CoreMediaGlue::CMTimeMake(
|
| (reference_time - base::TimeTicks()).InMicroseconds(), USEC_PER_SEC);
|
|
|
| + // Wrap information we'll need after the frame is encoded in a heap object.
|
| + // We'll get the pointer back from the VideoToolbox completion callback.
|
| scoped_ptr<InProgressFrameEncode> request(new InProgressFrameEncode(
|
| TimeDeltaToRtpDelta(video_frame->timestamp(), kVideoFrequency),
|
| reference_time, frame_encoded_callback));
|
|
|
| + // Build a suitable frame properties dictionary for keyframes.
|
| base::ScopedCFTypeRef<CFDictionaryRef> frame_props;
|
| if (encode_next_frame_as_keyframe_) {
|
| frame_props = DictionaryWithKeyValue(
|
| @@ -452,32 +552,53 @@ bool H264VideoToolboxEncoder::EncodeVideoFrame(
|
| encode_next_frame_as_keyframe_ = false;
|
| }
|
|
|
| - VTEncodeInfoFlags info;
|
| + // Submit the frame to the compression session. The function returns as soon
|
| + // as the frame has been enqueued.
|
| OSStatus status = videotoolbox_glue_->VTCompressionSessionEncodeFrame(
|
| compression_session_, pixel_buffer, timestamp_cm,
|
| CoreMediaGlue::CMTime{0, 0, 0, 0}, frame_props,
|
| - reinterpret_cast<void*>(request.release()), &info);
|
| + reinterpret_cast<void*>(request.release()), nullptr);
|
| if (status != noErr) {
|
| DLOG(ERROR) << " VTCompressionSessionEncodeFrame failed: " << status;
|
| return false;
|
| }
|
| - if ((info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped)) {
|
| - DLOG(ERROR) << " frame dropped";
|
| - return false;
|
| - }
|
|
|
| return true;
|
| }
|
|
|
| -void H264VideoToolboxEncoder::SetBitRate(int new_bit_rate) {
|
| +void H264VideoToolboxEncoder::UpdateFrameSize(const gfx::Size& size_needed) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| +
|
| + // Our video frame factory posts a task to update the frame size when its
|
| + // cache of the frame size differs from what the client requested. To avoid
|
| + // spurious encoder resets, check again here.
|
| + if (size_needed == frame_size_) {
|
| + DCHECK(compression_session_);
|
| + return;
|
| + }
|
| +
|
| + VLOG(1) << "Resetting compression session (for frame size change from "
|
| + << frame_size_.ToString() << " to " << size_needed.ToString() << ").";
|
| +
|
| + // If there is an existing session, finish every pending frame.
|
| + if (compression_session_) {
|
| + EmitFrames();
|
| + }
|
| +
|
| + // Store the new frame size.
|
| + frame_size_ = size_needed;
|
| +
|
| + // Reset the compression session.
|
| + ResetCompressionSession();
|
| +}
|
| +
|
| +void H264VideoToolboxEncoder::SetBitRate(int /*new_bit_rate*/) {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| // VideoToolbox does not seem to support bitrate reconfiguration.
|
| }
|
|
|
| void H264VideoToolboxEncoder::GenerateKeyFrame() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| - DCHECK(compression_session_);
|
| -
|
| encode_next_frame_as_keyframe_ = true;
|
| }
|
|
|
| @@ -487,23 +608,15 @@ void H264VideoToolboxEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
|
|
|
| scoped_ptr<VideoFrameFactory>
|
| H264VideoToolboxEncoder::CreateVideoFrameFactory() {
|
| - if (!videotoolbox_glue_ || !compression_session_)
|
| - return nullptr;
|
| - base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool(
|
| - videotoolbox_glue_->VTCompressionSessionGetPixelBufferPool(
|
| - compression_session_),
|
| - base::scoped_policy::RETAIN);
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| return scoped_ptr<VideoFrameFactory>(
|
| - new VideoFrameFactoryCVPixelBufferPoolImpl(pool, frame_size_));
|
| + new VideoFrameFactoryImpl::Proxy(video_frame_factory_));
|
| }
|
|
|
| void H264VideoToolboxEncoder::EmitFrames() {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| -
|
| - if (!compression_session_) {
|
| - DLOG(ERROR) << " compression session is null";
|
| + if (!compression_session_)
|
| return;
|
| - }
|
|
|
| OSStatus status = videotoolbox_glue_->VTCompressionSessionCompleteFrames(
|
| compression_session_, CoreMediaGlue::CMTime{0, 0, 0, 0});
|
| @@ -546,14 +659,13 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
|
| if (status != noErr) {
|
| DLOG(ERROR) << " encoding failed: " << status;
|
| encoder->cast_environment_->PostTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| + CastEnvironment::MAIN, FROM_HERE,
|
| base::Bind(encoder->status_change_cb_, STATUS_CODEC_RUNTIME_ERROR));
|
| } else if ((info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped)) {
|
| DVLOG(2) << " frame dropped";
|
| } else {
|
| - auto sample_attachments = static_cast<CFDictionaryRef>(
|
| - CFArrayGetValueAtIndex(
|
| + auto sample_attachments =
|
| + static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(
|
| CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(sbuf, true),
|
| 0));
|
|
|
| @@ -561,14 +673,14 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
|
| // keyframe (at least I think, VT documentation is, erm, sparse). Could
|
| // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false.
|
| keyframe = !CFDictionaryContainsKey(
|
| - sample_attachments,
|
| - CoreMediaGlue::kCMSampleAttachmentKey_NotSync());
|
| + sample_attachments,
|
| + CoreMediaGlue::kCMSampleAttachmentKey_NotSync());
|
| has_frame_data = true;
|
| }
|
|
|
| // Increment the encoder-scoped frame id and assign the new value to this
|
| // frame. VideoToolbox calls the output callback serially, so this is safe.
|
| - const uint32 frame_id = encoder->next_frame_id_++;
|
| + const uint32 frame_id = ++encoder->last_frame_id_;
|
|
|
| scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame());
|
| encoded_frame->frame_id = frame_id;
|
| @@ -600,89 +712,5 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
|
| base::Passed(&encoded_frame)));
|
| }
|
|
|
| -// A ref-counted structure that is shared to provide concurrent access to the
|
| -// VideoFrameFactory instance for the current encoder. OnEncoderReplaced() can
|
| -// change |factory| whenever an encoder instance has been replaced, while users
|
| -// of CreateVideoFrameFactory() may attempt to read/use |factory| by any thread
|
| -// at any time.
|
| -struct SizeAdaptableH264VideoToolboxVideoEncoder::FactoryHolder
|
| - : public base::RefCountedThreadSafe<FactoryHolder> {
|
| - base::Lock lock;
|
| - scoped_ptr<VideoFrameFactory> factory;
|
| -
|
| - private:
|
| - friend class base::RefCountedThreadSafe<FactoryHolder>;
|
| - ~FactoryHolder() {}
|
| -};
|
| -
|
| -SizeAdaptableH264VideoToolboxVideoEncoder::
|
| - SizeAdaptableH264VideoToolboxVideoEncoder(
|
| - const scoped_refptr<CastEnvironment>& cast_environment,
|
| - const VideoSenderConfig& video_config,
|
| - const StatusChangeCallback& status_change_cb)
|
| - : SizeAdaptableVideoEncoderBase(cast_environment,
|
| - video_config,
|
| - status_change_cb),
|
| - holder_(new FactoryHolder()) {}
|
| -
|
| -SizeAdaptableH264VideoToolboxVideoEncoder::
|
| - ~SizeAdaptableH264VideoToolboxVideoEncoder() {}
|
| -
|
| -// A proxy allowing SizeAdaptableH264VideoToolboxVideoEncoder to swap out the
|
| -// VideoFrameFactory instance to match one appropriate for the current encoder
|
| -// instance.
|
| -class SizeAdaptableH264VideoToolboxVideoEncoder::VideoFrameFactoryProxy
|
| - : public VideoFrameFactory {
|
| - public:
|
| - explicit VideoFrameFactoryProxy(const scoped_refptr<FactoryHolder>& holder)
|
| - : holder_(holder) {}
|
| -
|
| - ~VideoFrameFactoryProxy() override {}
|
| -
|
| - scoped_refptr<VideoFrame> MaybeCreateFrame(
|
| - const gfx::Size& frame_size, base::TimeDelta timestamp) override {
|
| - base::AutoLock auto_lock(holder_->lock);
|
| - return holder_->factory ?
|
| - holder_->factory->MaybeCreateFrame(frame_size, timestamp) : nullptr;
|
| - }
|
| -
|
| - private:
|
| - const scoped_refptr<FactoryHolder> holder_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryProxy);
|
| -};
|
| -
|
| -scoped_ptr<VideoFrameFactory>
|
| - SizeAdaptableH264VideoToolboxVideoEncoder::CreateVideoFrameFactory() {
|
| - return scoped_ptr<VideoFrameFactory>(new VideoFrameFactoryProxy(holder_));
|
| -}
|
| -
|
| -scoped_ptr<VideoEncoder>
|
| - SizeAdaptableH264VideoToolboxVideoEncoder::CreateEncoder() {
|
| - return scoped_ptr<VideoEncoder>(new H264VideoToolboxEncoder(
|
| - cast_environment(),
|
| - video_config(),
|
| - frame_size(),
|
| - last_frame_id() + 1,
|
| - CreateEncoderStatusChangeCallback()));
|
| -}
|
| -
|
| -void SizeAdaptableH264VideoToolboxVideoEncoder::OnEncoderReplaced(
|
| - VideoEncoder* replacement_encoder) {
|
| - scoped_ptr<VideoFrameFactory> current_factory(
|
| - replacement_encoder->CreateVideoFrameFactory());
|
| - base::AutoLock auto_lock(holder_->lock);
|
| - holder_->factory = current_factory.Pass();
|
| - SizeAdaptableVideoEncoderBase::OnEncoderReplaced(replacement_encoder);
|
| -}
|
| -
|
| -void SizeAdaptableH264VideoToolboxVideoEncoder::DestroyEncoder() {
|
| - {
|
| - base::AutoLock auto_lock(holder_->lock);
|
| - holder_->factory.reset();
|
| - }
|
| - SizeAdaptableVideoEncoderBase::DestroyEncoder();
|
| -}
|
| -
|
| } // namespace cast
|
| } // namespace media
|
|
|