H264VideoToolboxEncoder: Add support for native handles

Support encoding from CVPixelBuffers directly in H264VideoToolboxEncoder.
If the frame needs to be scaled, it will fall back to the previous slow
path:
CVPixelBuffer -> NV12 -> I420 -> (scale) I420 -> NV12 -> CVPixelBuffer.

BUG=webrtc:4081

Review-Url: https://codereview.webrtc.org/2140573002
Cr-Commit-Position: refs/heads/master@{#13439}
This commit is contained in:
magjed
2016-07-12 01:26:42 -07:00
committed by Commit bot
parent b89257a51b
commit 11dd52bcc6
2 changed files with 53 additions and 33 deletions

View File

@ -250,7 +250,16 @@ H264VideoToolboxEncoder::GetScaledBufferOnEncode(
const rtc::scoped_refptr<VideoFrameBuffer>& frame) { const rtc::scoped_refptr<VideoFrameBuffer>& frame) {
rtc::CritScope lock(&quality_scaler_crit_); rtc::CritScope lock(&quality_scaler_crit_);
quality_scaler_.OnEncodeFrame(frame->width(), frame->height()); quality_scaler_.OnEncodeFrame(frame->width(), frame->height());
return quality_scaler_.GetScaledBuffer(frame); if (!frame->native_handle())
return quality_scaler_.GetScaledBuffer(frame);
// Handle native (CVImageRef) scaling.
const QualityScaler::Resolution res = quality_scaler_.GetScaledResolution();
if (res.width == frame->width() && res.height == frame->height())
return frame;
// TODO(magjed): Implement efficient CVImageRef -> CVImageRef scaling instead
// of doing it via I420.
return quality_scaler_.GetScaledBuffer(frame->NativeToI420Buffer());
} }
int H264VideoToolboxEncoder::Encode( int H264VideoToolboxEncoder::Encode(
@ -280,40 +289,45 @@ int H264VideoToolboxEncoder::Encode(
return ret; return ret;
} }
// Get a pixel buffer from the pool and copy frame data over. CVPixelBufferRef pixel_buffer =
CVPixelBufferPoolRef pixel_buffer_pool = static_cast<CVPixelBufferRef>(input_image->native_handle());
VTCompressionSessionGetPixelBufferPool(compression_session_); if (pixel_buffer) {
#if defined(WEBRTC_IOS) CVBufferRetain(pixel_buffer);
if (!pixel_buffer_pool) { } else {
// Kind of a hack. On backgrounding, the compression session seems to get // Get a pixel buffer from the pool and copy frame data over.
// invalidated, which causes this pool call to fail when the application CVPixelBufferPoolRef pixel_buffer_pool =
// is foregrounded and frames are being sent for encoding again.
// Resetting the session when this happens fixes the issue.
// In addition we request a keyframe so video can recover quickly.
ResetCompressionSession();
pixel_buffer_pool =
VTCompressionSessionGetPixelBufferPool(compression_session_); VTCompressionSessionGetPixelBufferPool(compression_session_);
is_keyframe_required = true; #if defined(WEBRTC_IOS)
} if (!pixel_buffer_pool) {
// Kind of a hack. On backgrounding, the compression session seems to get
// invalidated, which causes this pool call to fail when the application
// is foregrounded and frames are being sent for encoding again.
// Resetting the session when this happens fixes the issue.
// In addition we request a keyframe so video can recover quickly.
ResetCompressionSession();
pixel_buffer_pool =
VTCompressionSessionGetPixelBufferPool(compression_session_);
is_keyframe_required = true;
}
#endif #endif
if (!pixel_buffer_pool) { if (!pixel_buffer_pool) {
LOG(LS_ERROR) << "Failed to get pixel buffer pool."; LOG(LS_ERROR) << "Failed to get pixel buffer pool.";
return WEBRTC_VIDEO_CODEC_ERROR; return WEBRTC_VIDEO_CODEC_ERROR;
} }
CVPixelBufferRef pixel_buffer = nullptr; CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(
CVReturn ret = CVPixelBufferPoolCreatePixelBuffer(nullptr, pixel_buffer_pool, nullptr, pixel_buffer_pool, &pixel_buffer);
&pixel_buffer); if (ret != kCVReturnSuccess) {
if (ret != kCVReturnSuccess) { LOG(LS_ERROR) << "Failed to create pixel buffer: " << ret;
LOG(LS_ERROR) << "Failed to create pixel buffer: " << ret; // We probably want to drop frames here, since failure probably means
// We probably want to drop frames here, since failure probably means // that the pool is empty.
// that the pool is empty. return WEBRTC_VIDEO_CODEC_ERROR;
return WEBRTC_VIDEO_CODEC_ERROR; }
} RTC_DCHECK(pixel_buffer);
RTC_DCHECK(pixel_buffer); if (!internal::CopyVideoFrameToPixelBuffer(input_image, pixel_buffer)) {
if (!internal::CopyVideoFrameToPixelBuffer(input_image, pixel_buffer)) { LOG(LS_ERROR) << "Failed to copy frame data.";
LOG(LS_ERROR) << "Failed to copy frame data."; CVBufferRelease(pixel_buffer);
CVBufferRelease(pixel_buffer); return WEBRTC_VIDEO_CODEC_ERROR;
return WEBRTC_VIDEO_CODEC_ERROR; }
} }
// Check if we need a keyframe. // Check if we need a keyframe.
@ -488,6 +502,10 @@ const char* H264VideoToolboxEncoder::ImplementationName() const {
return "VideoToolbox"; return "VideoToolbox";
} }
bool H264VideoToolboxEncoder::SupportsNativeHandle() const {
return true;
}
void H264VideoToolboxEncoder::SetBitrateBps(uint32_t bitrate_bps) { void H264VideoToolboxEncoder::SetBitrateBps(uint32_t bitrate_bps) {
if (encoder_bitrate_bps_ != bitrate_bps) { if (encoder_bitrate_bps_ != bitrate_bps) {
SetEncoderBitrateBps(bitrate_bps); SetEncoderBitrateBps(bitrate_bps);

View File

@ -56,6 +56,8 @@ class H264VideoToolboxEncoder : public H264Encoder {
const char* ImplementationName() const override; const char* ImplementationName() const override;
bool SupportsNativeHandle() const override;
void OnEncodedFrame(OSStatus status, void OnEncodedFrame(OSStatus status,
VTEncodeInfoFlags info_flags, VTEncodeInfoFlags info_flags,
CMSampleBufferRef sample_buffer, CMSampleBufferRef sample_buffer,