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

Unified 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 side-by-side diff with in-line comments
Download patch
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 364a4167bb2a9970f8252c71d10a45d857b29a07..11d284a2e0be40b095c5072f70cc956a35cc6844 100644
--- a/media/cast/sender/h264_vt_encoder.cc
+++ b/media/cast/sender/h264_vt_encoder.cc
@@ -13,6 +13,7 @@
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/synchronization/lock.h"
#include "media/base/mac/corevideo_glue.h"
#include "media/base/mac/video_frame_mac.h"
#include "media/cast/sender/video_frame_factory.h"
@@ -202,42 +203,74 @@ void CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
class VideoFrameFactoryCVPixelBufferPoolImpl : public VideoFrameFactory {
public:
VideoFrameFactoryCVPixelBufferPoolImpl(
- const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool)
- : pool_(pool) {}
+ const base::ScopedCFTypeRef<CVPixelBufferPoolRef>& pool,
+ const gfx::Size& frame_size)
+ : pool_(pool),
+ frame_size_(frame_size) {}
~VideoFrameFactoryCVPixelBufferPoolImpl() override {}
- scoped_refptr<VideoFrame> CreateFrame(base::TimeDelta timestamp) override {
+ 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.
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
+
base::ScopedCFTypeRef<CVPixelBufferRef> buffer;
- CHECK_EQ(kCVReturnSuccess,
- CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
- buffer.InitializeInto()));
+ if (CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
+ buffer.InitializeInto()) !=
+ kCVReturnSuccess ||
+ !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.
+ 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.
+
+ // On some Macs, the buffer pool seems to return pixel buffers without the
+ // required attachments set. Set them explicitly here, and they must be
+ // in-sync with those in H264VideoToolboxEncoder::ConfigureSession().
+ 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.
+ kCVImageBufferColorPrimaries_ITU_R_709_2,
+ kCVAttachmentMode_ShouldPropagate);
+ CVBufferSetAttachment(buffer, kCVImageBufferTransferFunctionKey,
+ kCVImageBufferTransferFunction_ITU_R_709_2,
+ kCVAttachmentMode_ShouldPropagate);
+ CVBufferSetAttachment(buffer, kCVImageBufferYCbCrMatrixKey,
+ kCVImageBufferYCbCrMatrix_ITU_R_709_2,
+ kCVAttachmentMode_ShouldPropagate);
+
return VideoFrame::WrapCVPixelBuffer(buffer, timestamp);
}
private:
- base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_;
+ const base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool_;
+ const gfx::Size frame_size_;
DISALLOW_COPY_AND_ASSIGN(VideoFrameFactoryCVPixelBufferPoolImpl);
};
} // namespace
+// static
+bool H264VideoToolboxEncoder::IsSupported(
+ const VideoSenderConfig& video_config) {
+ return video_config.codec == CODEC_VIDEO_H264 && VideoToolboxGlue::Get();
+}
+
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_id_(kStartFrameId),
+ frame_size_(frame_size),
+ status_change_cb_(status_change_cb),
+ next_frame_id_(first_frame_id),
encode_next_frame_as_keyframe_(false) {
- DCHECK(!frame_size.IsEmpty());
- DCHECK(!status_change_cb.is_null());
+ DCHECK(!frame_size_.IsEmpty());
+ DCHECK(!status_change_cb_.is_null());
OperationalStatus operational_status;
if (video_config.codec == CODEC_VIDEO_H264 && videotoolbox_glue_) {
- operational_status = Initialize(video_config, frame_size) ?
+ operational_status = Initialize(video_config) ?
STATUS_INITIALIZED : STATUS_INVALID_CONFIGURATION;
} else {
operational_status = STATUS_UNSUPPORTED_CODEC;
@@ -245,7 +278,7 @@ H264VideoToolboxEncoder::H264VideoToolboxEncoder(
cast_environment_->PostTask(
CastEnvironment::MAIN,
FROM_HERE,
- base::Bind(status_change_cb, operational_status));
+ base::Bind(status_change_cb_, operational_status));
}
H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
@@ -253,8 +286,7 @@ H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
}
bool H264VideoToolboxEncoder::Initialize(
- const VideoSenderConfig& video_config,
- const gfx::Size& frame_size) {
+ const VideoSenderConfig& video_config) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!compression_session_);
@@ -289,7 +321,7 @@ bool H264VideoToolboxEncoder::Initialize(
VTCompressionSessionRef session;
OSStatus status = videotoolbox_glue_->VTCompressionSessionCreate(
- kCFAllocatorDefault, frame_size.width(), frame_size.height(),
+ kCFAllocatorDefault, frame_size_.width(), frame_size_.height(),
CoreMediaGlue::kCMVideoCodecType_H264, encoder_spec, buffer_attributes,
nullptr /* compressedDataAllocator */,
&H264VideoToolboxEncoder::CompressionCallback,
@@ -329,6 +361,8 @@ void H264VideoToolboxEncoder::ConfigureSession(
SetSessionProperty(
videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(),
video_config.max_frame_rate);
+ // Keep these attachment settings in-sync with those in
+ // 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.
SetSessionProperty(
videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(),
kCVImageBufferColorPrimaries_ITU_R_709_2);
@@ -357,16 +391,11 @@ void H264VideoToolboxEncoder::Teardown() {
}
}
-bool H264VideoToolboxEncoder::CanEncodeVariedFrameSizes() const {
- return false;
-}
-
bool H264VideoToolboxEncoder::EncodeVideoFrame(
const scoped_refptr<media::VideoFrame>& video_frame,
const base::TimeTicks& reference_time,
const FrameEncodedCallback& frame_encoded_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!video_frame->visible_rect().IsEmpty());
DCHECK(!frame_encoded_callback.is_null());
if (!compression_session_) {
@@ -374,6 +403,9 @@ bool H264VideoToolboxEncoder::EncodeVideoFrame(
return false;
}
+ if (video_frame->visible_rect().size() != frame_size_)
+ 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.
+
// 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
// factory, then the returned CVPixelBuffer will have been obtained from the
@@ -435,12 +467,14 @@ void H264VideoToolboxEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) {
scoped_ptr<VideoFrameFactory>
H264VideoToolboxEncoder::CreateVideoFrameFactory() {
+ if (!videotoolbox_glue_ || !compression_session_)
+ 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.
base::ScopedCFTypeRef<CVPixelBufferPoolRef> pool(
videotoolbox_glue_->VTCompressionSessionGetPixelBufferPool(
compression_session_),
base::scoped_policy::RETAIN);
return scoped_ptr<VideoFrameFactory>(
- new VideoFrameFactoryCVPixelBufferPoolImpl(pool));
+ new VideoFrameFactoryCVPixelBufferPoolImpl(pool, frame_size_));
}
void H264VideoToolboxEncoder::EmitFrames() {
@@ -483,31 +517,38 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
OSStatus status,
VTEncodeInfoFlags info,
CMSampleBufferRef sbuf) {
- if (status != noErr) {
- DLOG(ERROR) << " encoding failed: " << status;
- return;
- }
- if ((info & VideoToolboxGlue::kVTEncodeInfo_FrameDropped)) {
- DVLOG(2) << " frame dropped";
- return;
- }
-
auto encoder = reinterpret_cast<H264VideoToolboxEncoder*>(encoder_opaque);
const scoped_ptr<InProgressFrameEncode> request(
reinterpret_cast<InProgressFrameEncode*>(request_opaque));
- auto sample_attachments = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(
- CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(sbuf, true), 0));
+ bool keyframe = false;
+ bool has_frame_data = false;
- // If the NotSync key is not present, it implies Sync, which indicates a
- // keyframe (at least I think, VT documentation is, erm, sparse). Could
- // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false.
- bool keyframe =
- !CFDictionaryContainsKey(sample_attachments,
- CoreMediaGlue::kCMSampleAttachmentKey_NotSync());
+ if (status != noErr) {
+ DLOG(ERROR) << " encoding failed: " << status;
+ encoder->cast_environment_->PostTask(
+ 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(
+ CoreMediaGlue::CMSampleBufferGetSampleAttachmentsArray(sbuf, true),
+ 0));
+
+ // If the NotSync key is not present, it implies Sync, which indicates a
+ // keyframe (at least I think, VT documentation is, erm, sparse). Could
+ // alternatively use kCMSampleAttachmentKey_DependsOnOthers == false.
+ keyframe = !CFDictionaryContainsKey(
+ 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.
- uint32 frame_id = ++encoder->frame_id_;
+ const uint32 frame_id = encoder->next_frame_id_++;
scoped_ptr<EncodedFrame> encoded_frame(new EncodedFrame());
encoded_frame->frame_id = frame_id;
@@ -530,7 +571,8 @@ void H264VideoToolboxEncoder::CompressionCallback(void* encoder_opaque,
encoded_frame->referenced_frame_id = frame_id - 1;
}
- CopySampleBufferToAnnexBBuffer(sbuf, &encoded_frame->data, keyframe);
+ if (has_frame_data)
+ 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.
encoder->cast_environment_->PostTask(
CastEnvironment::MAIN, FROM_HERE,
@@ -538,5 +580,87 @@ 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() {}
+
+scoped_ptr<VideoFrameFactory>
+ SizeAdaptableH264VideoToolboxVideoEncoder::CreateVideoFrameFactory() {
+ // A proxy allowing SizeAdaptableH264VideoToolboxVideoEncoder to swap out the
+ // VideoFrameFactory instance to match one appropriate for the current encoder
+ // instance.
+ 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.
+ 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);
+ };
+
+ return scoped_ptr<VideoFrameFactory>(new VideoFrameFactoryProxy(holder_));
+}
+
+scoped_ptr<VideoEncoder>
+ SizeAdaptableH264VideoToolboxVideoEncoder::CreateReplacementEncoder() {
+ return scoped_ptr<VideoEncoder>(new H264VideoToolboxEncoder(
+ cast_environment(),
+ video_config(),
+ 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
+ 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();
hubbe 2015/02/11 00:47:54 Call baseclass OnEncoderReplaced()
miu 2015/02/11 02:14:23 Done.
+}
+
+void SizeAdaptableH264VideoToolboxVideoEncoder::DestroyCurrentEncoder() {
+ {
+ base::AutoLock auto_lock(holder_->lock);
+ holder_->factory.reset();
+ }
+ SizeAdaptableVideoEncoderBase::DestroyCurrentEncoder();
+}
+
} // namespace cast
} // namespace media

Powered by Google App Engine
This is Rietveld 408576698