| Index: webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm
|
| diff --git a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/decoder.mm b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm
|
| similarity index 38%
|
| rename from webrtc/sdk/objc/Framework/Classes/VideoToolbox/decoder.mm
|
| rename to webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm
|
| index a8da8091d9f9a67cd90852c4df077870e0d51781..f657f9c2f12b68b1d1e95adf6370f418fb2655de 100644
|
| --- a/webrtc/sdk/objc/Framework/Classes/VideoToolbox/decoder.mm
|
| +++ b/webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm
|
| @@ -9,93 +9,80 @@
|
| *
|
| */
|
|
|
| -#include "webrtc/sdk/objc/Framework/Classes/VideoToolbox/decoder.h"
|
| +#import "WebRTC/RTCVideoCodecH264.h"
|
|
|
| -#include <memory>
|
| +#import <VideoToolbox/VideoToolbox.h>
|
|
|
| -#include "libyuv/convert.h"
|
| -#include "webrtc/api/video/video_frame.h"
|
| -#include "webrtc/common_video/include/video_frame.h"
|
| +#include "webrtc/modules/video_coding/include/video_error_codes.h"
|
| #include "webrtc/rtc_base/checks.h"
|
| #include "webrtc/rtc_base/logging.h"
|
| -#include "webrtc/sdk/objc/Framework/Classes/Video/objc_frame_buffer.h"
|
| +#include "webrtc/rtc_base/timeutils.h"
|
| #include "webrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h"
|
|
|
| +#import "WebRTC/RTCVideoFrame.h"
|
| #import "WebRTC/RTCVideoFrameBuffer.h"
|
| +#import "helpers.h"
|
|
|
| #if defined(WEBRTC_IOS)
|
| #import "Common/RTCUIApplicationStatusObserver.h"
|
| #endif
|
|
|
| -namespace webrtc {
|
| -namespace {
|
| -
|
| -static const int64_t kMsPerSec = 1000;
|
| -
|
| -// Convenience function for creating a dictionary.
|
| -inline CFDictionaryRef CreateCFDictionary(CFTypeRef* keys,
|
| - CFTypeRef* values,
|
| - size_t size) {
|
| - return CFDictionaryCreate(nullptr, keys, values, size,
|
| - &kCFTypeDictionaryKeyCallBacks,
|
| - &kCFTypeDictionaryValueCallBacks);
|
| -}
|
| -
|
| // Struct that we pass to the decoder per frame to decode. We receive it again
|
| // in the decoder callback.
|
| -struct FrameDecodeParams {
|
| - FrameDecodeParams(DecodedImageCallback* cb, int64_t ts)
|
| - : callback(cb), timestamp(ts) {}
|
| - DecodedImageCallback* callback;
|
| +struct RTCFrameDecodeParams {
|
| + RTCFrameDecodeParams(RTCVideoDecoderCallback cb, int64_t ts) : callback(cb), timestamp(ts) {}
|
| + RTCVideoDecoderCallback callback;
|
| int64_t timestamp;
|
| };
|
|
|
| // This is the callback function that VideoToolbox calls when decode is
|
| // complete.
|
| -void VTDecompressionOutputCallback(void* decoder,
|
| - void* params,
|
| - OSStatus status,
|
| - VTDecodeInfoFlags info_flags,
|
| - CVImageBufferRef image_buffer,
|
| - CMTime timestamp,
|
| - CMTime duration) {
|
| - std::unique_ptr<FrameDecodeParams> decode_params(
|
| - reinterpret_cast<FrameDecodeParams*>(params));
|
| +void decompressionOutputCallback(void *decoder,
|
| + void *params,
|
| + OSStatus status,
|
| + VTDecodeInfoFlags infoFlags,
|
| + CVImageBufferRef imageBuffer,
|
| + CMTime timestamp,
|
| + CMTime duration) {
|
| + std::unique_ptr<RTCFrameDecodeParams> decodeParams(
|
| + reinterpret_cast<RTCFrameDecodeParams *>(params));
|
| if (status != noErr) {
|
| LOG(LS_ERROR) << "Failed to decode frame. Status: " << status;
|
| return;
|
| }
|
| // TODO(tkchin): Handle CVO properly.
|
| - rtc::scoped_refptr<VideoFrameBuffer> buffer = new rtc::RefCountedObject<ObjCFrameBuffer>(
|
| - [[RTCCVPixelBuffer alloc] initWithPixelBuffer:image_buffer]);
|
| - VideoFrame decoded_frame(buffer, decode_params->timestamp,
|
| - CMTimeGetSeconds(timestamp) * kMsPerSec,
|
| - kVideoRotation_0);
|
| - decode_params->callback->Decoded(decoded_frame);
|
| + RTCCVPixelBuffer *frameBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer:imageBuffer];
|
| + RTCVideoFrame *decodedFrame =
|
| + [[RTCVideoFrame alloc] initWithBuffer:frameBuffer
|
| + rotation:RTCVideoRotation_0
|
| + timeStampNs:CMTimeGetSeconds(timestamp) * rtc::kNumNanosecsPerSec];
|
| + decodedFrame.timeStamp = decodeParams->timestamp;
|
| + decodeParams->callback(decodedFrame);
|
| }
|
|
|
| -} // namespace
|
| -
|
| -H264VideoToolboxDecoder::H264VideoToolboxDecoder()
|
| - : callback_(nullptr), video_format_(nullptr), decompression_session_(nullptr) {}
|
| +// Decoder.
|
| +@implementation RTCVideoDecoderH264 {
|
| + CMVideoFormatDescriptionRef _videoFormat;
|
| + VTDecompressionSessionRef _decompressionSession;
|
| + RTCVideoDecoderCallback _callback;
|
| +}
|
|
|
| -H264VideoToolboxDecoder::~H264VideoToolboxDecoder() {
|
| - DestroyDecompressionSession();
|
| - SetVideoFormat(nullptr);
|
| +- (void)dealloc {
|
| + [self destroyDecompressionSession];
|
| + [self setVideoFormat:nullptr];
|
| }
|
|
|
| -int H264VideoToolboxDecoder::InitDecode(const VideoCodec* video_codec,
|
| - int number_of_cores) {
|
| +- (NSInteger)startDecodeWithSettings:(RTCVideoEncoderSettings *)settings
|
| + numberOfCores:(int)numberOfCores {
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| -int H264VideoToolboxDecoder::Decode(
|
| - const EncodedImage& input_image,
|
| - bool missing_frames,
|
| - const RTPFragmentationHeader* fragmentation,
|
| - const CodecSpecificInfo* codec_specific_info,
|
| - int64_t render_time_ms) {
|
| - RTC_DCHECK(input_image._buffer);
|
| +- (NSInteger)decode:(RTCEncodedImage *)inputImage
|
| + missingFrames:(BOOL)missingFrames
|
| + fragmentationHeader:(RTCRtpFragmentationHeader *)fragmentationHeader
|
| + codecSpecificInfo:(__nullable id<RTCCodecSpecificInfo>)info
|
| + renderTimeMs:(int64_t)renderTimeMs {
|
| + RTC_DCHECK(inputImage.buffer);
|
|
|
| #if defined(WEBRTC_IOS)
|
| if (![[RTCUIApplicationStatusObserver sharedInstance] isApplicationActive]) {
|
| @@ -103,26 +90,26 @@ int H264VideoToolboxDecoder::Decode(
|
| // hardware decoder has been invalidated by the OS.
|
| // Reset video format so that we won't process frames until the next
|
| // keyframe.
|
| - SetVideoFormat(nullptr);
|
| + [self setVideoFormat:nullptr];
|
| return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
|
| }
|
| #endif
|
| - CMVideoFormatDescriptionRef input_format = nullptr;
|
| - if (H264AnnexBBufferHasVideoFormatDescription(input_image._buffer,
|
| - input_image._length)) {
|
| - input_format = CreateVideoFormatDescription(input_image._buffer,
|
| - input_image._length);
|
| - if (input_format) {
|
| + CMVideoFormatDescriptionRef inputFormat = nullptr;
|
| + if (webrtc::H264AnnexBBufferHasVideoFormatDescription((uint8_t *)inputImage.buffer.bytes,
|
| + inputImage.buffer.length)) {
|
| + inputFormat = webrtc::CreateVideoFormatDescription((uint8_t *)inputImage.buffer.bytes,
|
| + inputImage.buffer.length);
|
| + if (inputFormat) {
|
| // Check if the video format has changed, and reinitialize decoder if
|
| // needed.
|
| - if (!CMFormatDescriptionEqual(input_format, video_format_)) {
|
| - SetVideoFormat(input_format);
|
| - ResetDecompressionSession();
|
| + if (!CMFormatDescriptionEqual(inputFormat, _videoFormat)) {
|
| + [self setVideoFormat:inputFormat];
|
| + [self resetDecompressionSession];
|
| }
|
| - CFRelease(input_format);
|
| + CFRelease(inputFormat);
|
| }
|
| }
|
| - if (!video_format_) {
|
| + if (!_videoFormat) {
|
| // We received a frame but we don't have format information so we can't
|
| // decode it.
|
| // This can happen after backgrounding. We need to wait for the next
|
| @@ -131,34 +118,29 @@ int H264VideoToolboxDecoder::Decode(
|
| LOG(LS_WARNING) << "Missing video format. Frame with sps/pps required.";
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| - CMSampleBufferRef sample_buffer = nullptr;
|
| - if (!H264AnnexBBufferToCMSampleBuffer(input_image._buffer,
|
| - input_image._length, video_format_,
|
| - &sample_buffer)) {
|
| + CMSampleBufferRef sampleBuffer = nullptr;
|
| + if (!webrtc::H264AnnexBBufferToCMSampleBuffer((uint8_t *)inputImage.buffer.bytes,
|
| + inputImage.buffer.length,
|
| + _videoFormat,
|
| + &sampleBuffer)) {
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| - RTC_DCHECK(sample_buffer);
|
| - VTDecodeFrameFlags decode_flags =
|
| - kVTDecodeFrame_EnableAsynchronousDecompression;
|
| - std::unique_ptr<FrameDecodeParams> frame_decode_params;
|
| - frame_decode_params.reset(
|
| - new FrameDecodeParams(callback_, input_image._timeStamp));
|
| + RTC_DCHECK(sampleBuffer);
|
| + VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompression;
|
| + std::unique_ptr<RTCFrameDecodeParams> frameDecodeParams;
|
| + frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeStamp));
|
| OSStatus status = VTDecompressionSessionDecodeFrame(
|
| - decompression_session_, sample_buffer, decode_flags,
|
| - frame_decode_params.release(), nullptr);
|
| + _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.release(), nullptr);
|
| #if defined(WEBRTC_IOS)
|
| // Re-initialize the decoder if we have an invalid session while the app is
|
| // active and retry the decode request.
|
| - if (status == kVTInvalidSessionErr &&
|
| - ResetDecompressionSession() == WEBRTC_VIDEO_CODEC_OK) {
|
| - frame_decode_params.reset(
|
| - new FrameDecodeParams(callback_, input_image._timeStamp));
|
| + if (status == kVTInvalidSessionErr && [self resetDecompressionSession] == WEBRTC_VIDEO_CODEC_OK) {
|
| + frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeStamp));
|
| status = VTDecompressionSessionDecodeFrame(
|
| - decompression_session_, sample_buffer, decode_flags,
|
| - frame_decode_params.release(), nullptr);
|
| + _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.release(), nullptr);
|
| }
|
| #endif
|
| - CFRelease(sample_buffer);
|
| + CFRelease(sampleBuffer);
|
| if (status != noErr) {
|
| LOG(LS_ERROR) << "Failed to decode frame with code: " << status;
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| @@ -166,27 +148,26 @@ int H264VideoToolboxDecoder::Decode(
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| -int H264VideoToolboxDecoder::RegisterDecodeCompleteCallback(
|
| - DecodedImageCallback* callback) {
|
| - RTC_DCHECK(!callback_);
|
| - callback_ = callback;
|
| - return WEBRTC_VIDEO_CODEC_OK;
|
| +- (void)setCallback:(RTCVideoDecoderCallback)callback {
|
| + _callback = callback;
|
| }
|
|
|
| -int H264VideoToolboxDecoder::Release() {
|
| +- (NSInteger)releaseDecoder {
|
| // Need to invalidate the session so that callbacks no longer occur and it
|
| // is safe to null out the callback.
|
| - DestroyDecompressionSession();
|
| - SetVideoFormat(nullptr);
|
| - callback_ = nullptr;
|
| + [self destroyDecompressionSession];
|
| + [self setVideoFormat:nullptr];
|
| + _callback = nullptr;
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| -int H264VideoToolboxDecoder::ResetDecompressionSession() {
|
| - DestroyDecompressionSession();
|
| +#pragma mark - Private
|
| +
|
| +- (int)resetDecompressionSession {
|
| + [self destroyDecompressionSession];
|
|
|
| // Need to wait for the first SPS to initialize decoder.
|
| - if (!video_format_) {
|
| + if (!_videoFormat) {
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| @@ -198,8 +179,8 @@ int H264VideoToolboxDecoder::ResetDecompressionSession() {
|
| // CVPixelBuffers directly to the renderer.
|
| // TODO(tkchin): Maybe only set OpenGL/IOSurface keys if we know that that
|
| // we can pass CVPixelBuffers as native handles in decoder output.
|
| - static size_t const attributes_size = 3;
|
| - CFTypeRef keys[attributes_size] = {
|
| + static size_t const attributesSize = 3;
|
| + CFTypeRef keys[attributesSize] = {
|
| #if defined(WEBRTC_IOS)
|
| kCVPixelBufferOpenGLESCompatibilityKey,
|
| #elif defined(WEBRTC_MAC)
|
| @@ -208,70 +189,64 @@ int H264VideoToolboxDecoder::ResetDecompressionSession() {
|
| kCVPixelBufferIOSurfacePropertiesKey,
|
| kCVPixelBufferPixelFormatTypeKey
|
| };
|
| - CFDictionaryRef io_surface_value = CreateCFDictionary(nullptr, nullptr, 0);
|
| + CFDictionaryRef ioSurfaceValue = CreateCFTypeDictionary(nullptr, nullptr, 0);
|
| int64_t nv12type = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
|
| - CFNumberRef pixel_format =
|
| - CFNumberCreate(nullptr, kCFNumberLongType, &nv12type);
|
| - CFTypeRef values[attributes_size] = {kCFBooleanTrue, io_surface_value,
|
| - pixel_format};
|
| - CFDictionaryRef attributes =
|
| - CreateCFDictionary(keys, values, attributes_size);
|
| - if (io_surface_value) {
|
| - CFRelease(io_surface_value);
|
| - io_surface_value = nullptr;
|
| + CFNumberRef pixelFormat = CFNumberCreate(nullptr, kCFNumberLongType, &nv12type);
|
| + CFTypeRef values[attributesSize] = {kCFBooleanTrue, ioSurfaceValue, pixelFormat};
|
| + CFDictionaryRef attributes = CreateCFTypeDictionary(keys, values, attributesSize);
|
| + if (ioSurfaceValue) {
|
| + CFRelease(ioSurfaceValue);
|
| + ioSurfaceValue = nullptr;
|
| }
|
| - if (pixel_format) {
|
| - CFRelease(pixel_format);
|
| - pixel_format = nullptr;
|
| + if (pixelFormat) {
|
| + CFRelease(pixelFormat);
|
| + pixelFormat = nullptr;
|
| }
|
| VTDecompressionOutputCallbackRecord record = {
|
| - VTDecompressionOutputCallback, this,
|
| + decompressionOutputCallback, nullptr,
|
| };
|
| - OSStatus status =
|
| - VTDecompressionSessionCreate(nullptr, video_format_, nullptr, attributes,
|
| - &record, &decompression_session_);
|
| + OSStatus status = VTDecompressionSessionCreate(
|
| + nullptr, _videoFormat, nullptr, attributes, &record, &_decompressionSession);
|
| CFRelease(attributes);
|
| if (status != noErr) {
|
| - DestroyDecompressionSession();
|
| + [self destroyDecompressionSession];
|
| return WEBRTC_VIDEO_CODEC_ERROR;
|
| }
|
| - ConfigureDecompressionSession();
|
| + [self configureDecompressionSession];
|
|
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| -void H264VideoToolboxDecoder::ConfigureDecompressionSession() {
|
| - RTC_DCHECK(decompression_session_);
|
| +- (void)configureDecompressionSession {
|
| + RTC_DCHECK(_decompressionSession);
|
| #if defined(WEBRTC_IOS)
|
| - VTSessionSetProperty(decompression_session_,
|
| - kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
|
| + VTSessionSetProperty(_decompressionSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
|
| #endif
|
| }
|
|
|
| -void H264VideoToolboxDecoder::DestroyDecompressionSession() {
|
| - if (decompression_session_) {
|
| - VTDecompressionSessionInvalidate(decompression_session_);
|
| - CFRelease(decompression_session_);
|
| - decompression_session_ = nullptr;
|
| +- (void)destroyDecompressionSession {
|
| + if (_decompressionSession) {
|
| + VTDecompressionSessionInvalidate(_decompressionSession);
|
| + CFRelease(_decompressionSession);
|
| + _decompressionSession = nullptr;
|
| }
|
| }
|
|
|
| -void H264VideoToolboxDecoder::SetVideoFormat(
|
| - CMVideoFormatDescriptionRef video_format) {
|
| - if (video_format_ == video_format) {
|
| +- (void)setVideoFormat:(CMVideoFormatDescriptionRef)videoFormat {
|
| + if (_videoFormat == videoFormat) {
|
| return;
|
| }
|
| - if (video_format_) {
|
| - CFRelease(video_format_);
|
| + if (_videoFormat) {
|
| + CFRelease(_videoFormat);
|
| }
|
| - video_format_ = video_format;
|
| - if (video_format_) {
|
| - CFRetain(video_format_);
|
| + _videoFormat = videoFormat;
|
| + if (_videoFormat) {
|
| + CFRetain(_videoFormat);
|
| }
|
| }
|
|
|
| -const char* H264VideoToolboxDecoder::ImplementationName() const {
|
| - return "VideoToolbox";
|
| +- (NSString *)implementationName {
|
| + return @"VideoToolbox";
|
| }
|
|
|
| -} // namespace webrtc
|
| +@end
|
|
|