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..a1a08ac348383acb1b73ec1cbf63497c1dafbede 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 |
@@ -67,6 +67,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 +109,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 +170,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 +178,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 +189,9 @@ void VTCompressionOutputCallback(void* encoder, |
namespace webrtc { |
H264VideoToolboxEncoder::H264VideoToolboxEncoder() |
- : callback_(nullptr), compression_session_(nullptr) {} |
+ : callback_(nullptr), |
+ compression_session_(nullptr), |
+ bitrate_adjuster_(this) {} |
H264VideoToolboxEncoder::~H264VideoToolboxEncoder() { |
DestroyCompressionSession(); |
@@ -224,7 +207,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; |
+ bitrate_kbit_ = codec_settings->startBitrate; |
+ bitrate_adjuster_.SetTargetBitrate(bitrate_kbit_); |
// TODO(tkchin): Try setting payload size via |
// kVTCompressionPropertyKey_MaxH264SliceBytes. |
@@ -287,8 +271,8 @@ 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())); |
VTCompressionSessionEncodeFrame( |
compression_session_, pixel_buffer, presentation_time_stamp, |
kCMTimeInvalid, frame_properties, encode_params.release(), nullptr); |
@@ -315,20 +299,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_); |
- } |
+ bitrate_kbit_ = new_bitrate_kbit; |
+ bitrate_adjuster_.SetTargetBitrate(new_bitrate_kbit); |
+ SetBitrate(bitrate_kbit_); |
+ |
return WEBRTC_VIDEO_CODEC_OK; |
} |
int H264VideoToolboxEncoder::Release() { |
- callback_ = nullptr; |
// Need to reset to that the session is invalidated and won't use the |
sprang
2016/02/09 10:46:26
s/to that/so that
tkchin_webrtc
2016/02/11 00:03:21
Done.
|
- // callback anymore. |
- return ResetCompressionSession(); |
+ // callback anymore. Frames may continue to vend until it is torn down |
sprang
2016/02/09 10:46:26
s/vend/send
tkchin_webrtc
2016/02/11 00:03:21
Done.
|
+ // so we remove the callback after. |
+ int ret = ResetCompressionSession(); |
+ callback_ = nullptr; |
+ return ret; |
} |
int H264VideoToolboxEncoder::ResetCompressionSession() { |
@@ -389,11 +373,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); |
+ SetBitrate(bitrate_kbit_); |
// 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 +406,71 @@ const char* H264VideoToolboxEncoder::ImplementationName() const { |
return "VideoToolbox"; |
} |
+void H264VideoToolboxEncoder::OnBitrateAdjusted( |
+ uint32_t adjusted_bitrate_kbit) { |
+ SetBitrate(adjusted_bitrate_kbit); |
+} |
+ |
+void H264VideoToolboxEncoder::SetBitrate(uint32_t bitrate_kbit) { |
+ if (compression_session_) { |
+ internal::SetVTSessionProperty(compression_session_, |
+ kVTCompressionPropertyKey_AverageBitRate, |
+ bitrate_kbit * 1000); |
+ } |
+} |
+ |
+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) |