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

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: Addressed hubbe's comments. 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
« no previous file with comments | « media/cast/sender/h264_vt_encoder.h ('k') | media/cast/sender/h264_vt_encoder_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..c8ae82bb37a3f5433b757c9cc7bedb7ff6d88d6f 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"
@@ -36,25 +37,35 @@ struct InProgressFrameEncode {
frame_encoded_callback(callback) {}
};
+base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeysAndValues(
+ CFTypeRef* keys,
+ CFTypeRef* values,
+ size_t size) {
+ return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate(
+ kCFAllocatorDefault,
+ keys,
+ values,
+ size,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+}
+
base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key,
CFTypeRef value) {
CFTypeRef keys[1] = {key};
CFTypeRef values[1] = {value};
- return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate(
- kCFAllocatorDefault, keys, values, 1, &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks));
+ return DictionaryWithKeysAndValues(keys, values, 1);
}
-base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const std::vector<int>& v) {
+base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) {
std::vector<CFNumberRef> numbers;
- numbers.reserve(v.size());
- for (const int i : v) {
- numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, &i));
- }
+ numbers.reserve(size);
+ for (const int* end = v + size; v < end; ++v)
+ numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v));
base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate(
kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]),
numbers.size(), &kCFTypeArrayCallBacks));
- for (CFNumberRef number : numbers) {
+ for (auto& number : numbers) {
CFRelease(number);
}
return array;
@@ -202,42 +213,61 @@ 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.
+
base::ScopedCFTypeRef<CVPixelBufferRef> buffer;
- CHECK_EQ(kCVReturnSuccess,
- CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
- buffer.InitializeInto()));
+ if (CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool_,
+ buffer.InitializeInto()) !=
+ kCVReturnSuccess)
+ return nullptr; // Buffer pool has run out of pixel buffers.
+ DCHECK(buffer);
+
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 +275,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 +283,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_);
@@ -280,16 +309,40 @@ 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};
- base::ScopedCFTypeRef<CFArrayRef> formats_array = ArrayWithIntegers(
- std::vector<int>(formats, formats + arraysize(formats)));
- base::ScopedCFTypeRef<CFDictionaryRef> buffer_attributes =
- DictionaryWithKeyValue(kCVPixelBufferPixelFormatTypeKey, formats_array);
+ 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 buffer_attributes_values[] = {
+ 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,
+ arraysize(buffer_attributes_keys));
+ for (auto& v : buffer_attributes_values)
+ CFRelease(v);
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 +382,7 @@ void H264VideoToolboxEncoder::ConfigureSession(
SetSessionProperty(
videotoolbox_glue_->kVTCompressionPropertyKey_ExpectedFrameRate(),
video_config.max_frame_rate);
+ // Keep these attachment settings in-sync with those in Initialize().
SetSessionProperty(
videotoolbox_glue_->kVTCompressionPropertyKey_ColorPrimaries(),
kCVImageBufferColorPrimaries_ITU_R_709_2);
@@ -357,16 +411,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 +423,9 @@ bool H264VideoToolboxEncoder::EncodeVideoFrame(
return false;
}
+ if (video_frame->visible_rect().size() != frame_size_)
+ 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
// factory, then the returned CVPixelBuffer will have been obtained from the
@@ -435,12 +487,14 @@ 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);
return scoped_ptr<VideoFrameFactory>(
- new VideoFrameFactoryCVPixelBufferPoolImpl(pool));
+ new VideoFrameFactoryCVPixelBufferPoolImpl(pool, frame_size_));
}
void H264VideoToolboxEncoder::EmitFrames() {
@@ -483,31 +537,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 +591,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);
encoder->cast_environment_->PostTask(
CastEnvironment::MAIN, FROM_HERE,
@@ -538,5 +600,89 @@ 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
« no previous file with comments | « media/cast/sender/h264_vt_encoder.h ('k') | media/cast/sender/h264_vt_encoder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698