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

Unified Diff: webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc

Issue 1660963002: Bitrate controller for VideoToolbox encoder. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Fix UT Created 4 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: webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
diff --git a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
index 7df4ec74ba1971236fecc8b5a92f5b690b869f36..ba40ec919dda5a551c6e9e6e5679850befcaf9d4 100644
--- a/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
+++ b/webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_encoder.cc
@@ -21,6 +21,7 @@
#include "webrtc/base/logging.h"
#include "webrtc/base/scoped_ptr.h"
#include "webrtc/modules/video_coding/codecs/h264/h264_video_toolbox_nalu.h"
+#include "webrtc/system_wrappers/include/clock.h"
namespace internal {
@@ -67,6 +68,22 @@ void SetVTSessionProperty(VTSessionRef session,
}
// Convenience function for setting a VT property.
+void SetVTSessionProperty(VTSessionRef session,
+ CFStringRef key,
+ uint32_t value) {
+ int64_t value_64 = value;
+ CFNumberRef cfNum =
+ CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt64Type, &value_64);
+ OSStatus status = VTSessionSetProperty(session, key, cfNum);
+ CFRelease(cfNum);
+ if (status != noErr) {
+ std::string key_string = CFStringToString(key);
+ LOG(LS_ERROR) << "VTSessionSetProperty failed to set: " << key_string
+ << " to " << value << ": " << status;
+ }
+}
+
+// Convenience function for setting a VT property.
void SetVTSessionProperty(VTSessionRef session, CFStringRef key, bool value) {
CFBooleanRef cf_bool = (value) ? kCFBooleanTrue : kCFBooleanFalse;
OSStatus status = VTSessionSetProperty(session, key, cf_bool);
@@ -93,20 +110,21 @@ void SetVTSessionProperty(VTSessionRef session,
// Struct that we pass to the encoder per frame to encode. We receive it again
// in the encoder callback.
struct FrameEncodeParams {
- FrameEncodeParams(webrtc::EncodedImageCallback* cb,
+ FrameEncodeParams(webrtc::H264VideoToolboxEncoder* e,
const webrtc::CodecSpecificInfo* csi,
int32_t w,
int32_t h,
int64_t rtms,
uint32_t ts)
- : callback(cb), width(w), height(h), render_time_ms(rtms), timestamp(ts) {
+ : encoder(e), width(w), height(h), render_time_ms(rtms), timestamp(ts) {
if (csi) {
codec_specific_info = *csi;
} else {
codec_specific_info.codecType = webrtc::kVideoCodecH264;
}
}
- webrtc::EncodedImageCallback* callback;
+
+ webrtc::H264VideoToolboxEncoder* encoder;
webrtc::CodecSpecificInfo codec_specific_info;
int32_t width;
int32_t height;
@@ -153,7 +171,7 @@ bool CopyVideoFrameToPixelBuffer(const webrtc::VideoFrame& frame,
}
// This is the callback function that VideoToolbox calls when encode is
-// complete.
+// complete. From inspection this happens on its own queue.
void VTCompressionOutputCallback(void* encoder,
void* params,
OSStatus status,
@@ -161,46 +179,10 @@ void VTCompressionOutputCallback(void* encoder,
CMSampleBufferRef sample_buffer) {
rtc::scoped_ptr<FrameEncodeParams> encode_params(
reinterpret_cast<FrameEncodeParams*>(params));
- if (status != noErr) {
- LOG(LS_ERROR) << "H264 encoding failed.";
- return;
- }
- if (info_flags & kVTEncodeInfo_FrameDropped) {
- LOG(LS_INFO) << "H264 encode dropped frame.";
- }
-
- bool is_keyframe = false;
- CFArrayRef attachments =
- CMSampleBufferGetSampleAttachmentsArray(sample_buffer, 0);
- if (attachments != nullptr && CFArrayGetCount(attachments)) {
- CFDictionaryRef attachment =
- static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachments, 0));
- is_keyframe =
- !CFDictionaryContainsKey(attachment, kCMSampleAttachmentKey_NotSync);
- }
-
- // Convert the sample buffer into a buffer suitable for RTP packetization.
- // TODO(tkchin): Allocate buffers through a pool.
- rtc::scoped_ptr<rtc::Buffer> buffer(new rtc::Buffer());
- rtc::scoped_ptr<webrtc::RTPFragmentationHeader> header;
- if (!H264CMSampleBufferToAnnexBBuffer(sample_buffer, is_keyframe,
- buffer.get(), header.accept())) {
- return;
- }
- webrtc::EncodedImage frame(buffer->data(), buffer->size(), buffer->size());
- frame._encodedWidth = encode_params->width;
- frame._encodedHeight = encode_params->height;
- frame._completeFrame = true;
- frame._frameType =
- is_keyframe ? webrtc::kVideoFrameKey : webrtc::kVideoFrameDelta;
- frame.capture_time_ms_ = encode_params->render_time_ms;
- frame._timeStamp = encode_params->timestamp;
-
- int result = encode_params->callback->Encoded(
- frame, &(encode_params->codec_specific_info), header.get());
- if (result != 0) {
- LOG(LS_ERROR) << "Encoded callback failed: " << result;
- }
+ encode_params->encoder->OnEncodedFrame(
+ status, info_flags, sample_buffer, encode_params->codec_specific_info,
+ encode_params->width, encode_params->height,
+ encode_params->render_time_ms, encode_params->timestamp);
}
} // namespace internal
@@ -208,7 +190,9 @@ void VTCompressionOutputCallback(void* encoder,
namespace webrtc {
H264VideoToolboxEncoder::H264VideoToolboxEncoder()
- : callback_(nullptr), compression_session_(nullptr) {}
+ : callback_(nullptr),
+ compression_session_(nullptr),
+ bitrate_adjuster_(Clock::GetRealTimeClock()) {}
H264VideoToolboxEncoder::~H264VideoToolboxEncoder() {
DestroyCompressionSession();
@@ -224,7 +208,8 @@ int H264VideoToolboxEncoder::InitEncode(const VideoCodec* codec_settings,
width_ = codec_settings->width;
height_ = codec_settings->height;
// We can only set average bitrate on the HW encoder.
- bitrate_ = codec_settings->startBitrate * 1000;
+ target_bitrate_bps_ = codec_settings->startBitrate;
+ bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_);
// TODO(tkchin): Try setting payload size via
// kVTCompressionPropertyKey_MaxH264SliceBytes.
@@ -287,8 +272,12 @@ int H264VideoToolboxEncoder::Encode(
}
rtc::scoped_ptr<internal::FrameEncodeParams> encode_params;
encode_params.reset(new internal::FrameEncodeParams(
- callback_, codec_specific_info, width_, height_,
- input_image.render_time_ms(), input_image.timestamp()));
+ this, codec_specific_info, width_, height_, input_image.render_time_ms(),
+ input_image.timestamp()));
+
+ // Update the bitrate if needed.
+ SetBitrateBps(bitrate_adjuster_.GetAdjustedBitrateBps());
+
VTCompressionSessionEncodeFrame(
compression_session_, pixel_buffer, presentation_time_stamp,
kCMTimeInvalid, frame_properties, encode_params.release(), nullptr);
@@ -315,20 +304,20 @@ int H264VideoToolboxEncoder::SetChannelParameters(uint32_t packet_loss,
int H264VideoToolboxEncoder::SetRates(uint32_t new_bitrate_kbit,
uint32_t frame_rate) {
- bitrate_ = new_bitrate_kbit * 1000;
- if (compression_session_) {
- internal::SetVTSessionProperty(compression_session_,
- kVTCompressionPropertyKey_AverageBitRate,
- bitrate_);
- }
+ target_bitrate_bps_ = 1000 * new_bitrate_kbit;
+ bitrate_adjuster_.SetTargetBitrateBps(target_bitrate_bps_);
+ SetBitrateBps(bitrate_adjuster_.GetAdjustedBitrateBps());
+
return WEBRTC_VIDEO_CODEC_OK;
}
int H264VideoToolboxEncoder::Release() {
+ // Need to reset so that the session is invalidated and won't use the
+ // callback anymore. Do not remove callback until the session is invalidated
+ // since async encoder callbacks can occur until invalidation.
+ int ret = ResetCompressionSession();
callback_ = nullptr;
- // Need to reset to that the session is invalidated and won't use the
- // callback anymore.
- return ResetCompressionSession();
+ return ret;
}
int H264VideoToolboxEncoder::ResetCompressionSession() {
@@ -389,11 +378,10 @@ void H264VideoToolboxEncoder::ConfigureCompressionSession() {
internal::SetVTSessionProperty(compression_session_,
kVTCompressionPropertyKey_ProfileLevel,
kVTProfileLevel_H264_Baseline_AutoLevel);
- internal::SetVTSessionProperty(
- compression_session_, kVTCompressionPropertyKey_AverageBitRate, bitrate_);
internal::SetVTSessionProperty(compression_session_,
kVTCompressionPropertyKey_AllowFrameReordering,
false);
+ SetEncoderBitrateBps(target_bitrate_bps_);
// TODO(tkchin): Look at entropy mode and colorspace matrices.
// TODO(tkchin): Investigate to see if there's any way to make this work.
// May need it to interop with Android. Currently this call just fails.
@@ -423,6 +411,73 @@ const char* H264VideoToolboxEncoder::ImplementationName() const {
return "VideoToolbox";
}
+void H264VideoToolboxEncoder::SetBitrateBps(uint32_t bitrate_bps) {
+ if (encoder_bitrate_bps_ != bitrate_bps) {
+ SetEncoderBitrateBps(bitrate_bps);
+ }
+}
+
+void H264VideoToolboxEncoder::SetEncoderBitrateBps(uint32_t bitrate_bps) {
+ if (compression_session_) {
+ internal::SetVTSessionProperty(compression_session_,
+ kVTCompressionPropertyKey_AverageBitRate,
+ bitrate_bps);
+ encoder_bitrate_bps_ = bitrate_bps;
+ }
+}
+
+void H264VideoToolboxEncoder::OnEncodedFrame(
+ OSStatus status,
+ VTEncodeInfoFlags info_flags,
+ CMSampleBufferRef sample_buffer,
+ CodecSpecificInfo codec_specific_info,
+ int32_t width,
+ int32_t height,
+ int64_t render_time_ms,
+ uint32_t timestamp) {
+ if (status != noErr) {
+ LOG(LS_ERROR) << "H264 encode failed.";
+ return;
+ }
+ if (info_flags & kVTEncodeInfo_FrameDropped) {
+ LOG(LS_INFO) << "H264 encode dropped frame.";
+ }
+
+ bool is_keyframe = false;
+ CFArrayRef attachments =
+ CMSampleBufferGetSampleAttachmentsArray(sample_buffer, 0);
+ if (attachments != nullptr && CFArrayGetCount(attachments)) {
+ CFDictionaryRef attachment =
+ static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(attachments, 0));
+ is_keyframe =
+ !CFDictionaryContainsKey(attachment, kCMSampleAttachmentKey_NotSync);
+ }
+
+ // Convert the sample buffer into a buffer suitable for RTP packetization.
+ // TODO(tkchin): Allocate buffers through a pool.
+ rtc::scoped_ptr<rtc::Buffer> buffer(new rtc::Buffer());
+ rtc::scoped_ptr<webrtc::RTPFragmentationHeader> header;
+ if (!H264CMSampleBufferToAnnexBBuffer(sample_buffer, is_keyframe,
+ buffer.get(), header.accept())) {
+ return;
+ }
+ webrtc::EncodedImage frame(buffer->data(), buffer->size(), buffer->size());
+ frame._encodedWidth = width;
+ frame._encodedHeight = height;
+ frame._completeFrame = true;
+ frame._frameType =
+ is_keyframe ? webrtc::kVideoFrameKey : webrtc::kVideoFrameDelta;
+ frame.capture_time_ms_ = render_time_ms;
+ frame._timeStamp = timestamp;
+
+ int result = callback_->Encoded(frame, &codec_specific_info, header.get());
+ if (result != 0) {
+ LOG(LS_ERROR) << "Encode callback failed: " << result;
+ return;
+ }
+ bitrate_adjuster_.Update(frame._size);
+}
+
} // namespace webrtc
#endif // defined(WEBRTC_VIDEO_TOOLBOX_SUPPORTED)

Powered by Google App Engine
This is Rietveld 408576698