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

Side by Side Diff: webrtc/sdk/objc/Framework/Classes/VideoToolbox/RTCVideoDecoderH264.mm

Issue 2978623002: Implement H264 codec in Objective-C classes. (Closed)
Patch Set: Fix test after rebase. Created 3 years, 5 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 unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #import "WebRTC/RTCVideoCodecH264.h"
12
13 #import <VideoToolbox/VideoToolbox.h>
14
15 #if defined(WEBRTC_IOS)
16 #import "Common/RTCUIApplicationStatusObserver.h"
17 #endif
18 #import "WebRTC/RTCVideoFrame.h"
19 #import "WebRTC/RTCVideoFrameBuffer.h"
20 #import "helpers.h"
21
22 #include "webrtc/modules/video_coding/include/video_error_codes.h"
23 #include "webrtc/rtc_base/checks.h"
24 #include "webrtc/rtc_base/logging.h"
25 #include "webrtc/rtc_base/timeutils.h"
26 #include "webrtc/sdk/objc/Framework/Classes/VideoToolbox/nalu_rewriter.h"
27
28 // Struct that we pass to the decoder per frame to decode. We receive it again
29 // in the decoder callback.
30 struct RTCFrameDecodeParams {
31 RTCFrameDecodeParams(RTCVideoDecoderCallback cb, int64_t ts) : callback(cb), t imestamp(ts) {}
32 RTCVideoDecoderCallback callback;
33 int64_t timestamp;
34 };
35
36 // This is the callback function that VideoToolbox calls when decode is
37 // complete.
38 void decompressionOutputCallback(void *decoder,
39 void *params,
40 OSStatus status,
41 VTDecodeInfoFlags infoFlags,
42 CVImageBufferRef imageBuffer,
43 CMTime timestamp,
44 CMTime duration) {
45 std::unique_ptr<RTCFrameDecodeParams> decodeParams(
46 reinterpret_cast<RTCFrameDecodeParams *>(params));
47 if (status != noErr) {
48 LOG(LS_ERROR) << "Failed to decode frame. Status: " << status;
49 return;
50 }
51 // TODO(tkchin): Handle CVO properly.
52 RTCCVPixelBuffer *frameBuffer = [[RTCCVPixelBuffer alloc] initWithPixelBuffer: imageBuffer];
53 RTCVideoFrame *decodedFrame =
54 [[RTCVideoFrame alloc] initWithBuffer:frameBuffer
55 rotation:RTCVideoRotation_0
56 timeStampNs:CMTimeGetSeconds(timestamp) * rtc::k NumNanosecsPerSec];
57 decodedFrame.timeStamp = decodeParams->timestamp;
58 decodeParams->callback(decodedFrame);
59 }
60
61 // Decoder.
62 @implementation RTCVideoDecoderH264 {
63 CMVideoFormatDescriptionRef _videoFormat;
64 VTDecompressionSessionRef _decompressionSession;
65 RTCVideoDecoderCallback _callback;
66 }
67
68 - (instancetype)init {
69 if (self = [super init]) {
70 }
71 return self;
72 }
73
74 - (int)initDecodeWithSettings:(RTCVideoEncoderSettings *)settings numberOfCores: (int)numberOfCores {
75 return WEBRTC_VIDEO_CODEC_OK;
76 }
77
78 - (void)dealloc {
79 [self destroyDecompressionSession];
80 [self setVideoFormat:nullptr];
81 }
82
83 - (void)setCallback:(RTCVideoDecoderCallback)callback {
84 _callback = callback;
85 }
86
87 - (int32_t)releaseDecode {
88 // Need to invalidate the session so that callbacks no longer occur and it
89 // is safe to null out the callback.
90 [self destroyDecompressionSession];
91 [self setVideoFormat:nullptr];
92 _callback = nullptr;
93 return WEBRTC_VIDEO_CODEC_OK;
94 }
95
96 - (int)decode:(RTCEncodedImage *)inputImage
97 missingFrames:(BOOL)missingFrames
98 fragmentationHeader:(RTCRtpFragmentationHeader *)fragmentationHeader
99 codecSpecificInfo:(__nullable id<RTCCodecSpecificInfo>)info
100 renderTimeMs:(int64_t)renderTimeMs {
101 RTC_DCHECK(inputImage.buffer);
102
103 #if defined(WEBRTC_IOS)
104 if (![[RTCUIApplicationStatusObserver sharedInstance] isApplicationActive]) {
105 // Ignore all decode requests when app isn't active. In this state, the
106 // hardware decoder has been invalidated by the OS.
107 // Reset video format so that we won't process frames until the next
108 // keyframe.
109 [self setVideoFormat:nullptr];
110 return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
111 }
112 #endif
113 CMVideoFormatDescriptionRef inputFormat = nullptr;
114 if (webrtc::H264AnnexBBufferHasVideoFormatDescription((uint8_t *)inputImage.bu ffer.bytes,
115 inputImage.buffer.length )) {
116 inputFormat = webrtc::CreateVideoFormatDescription((uint8_t *)inputImage.buf fer.bytes,
117 inputImage.buffer.length) ;
118 if (inputFormat) {
119 // Check if the video format has changed, and reinitialize decoder if
120 // needed.
121 if (!CMFormatDescriptionEqual(inputFormat, _videoFormat)) {
122 [self setVideoFormat:inputFormat];
123 [self resetDecompressionSession];
124 }
125 CFRelease(inputFormat);
126 }
127 }
128 if (!_videoFormat) {
129 // We received a frame but we don't have format information so we can't
130 // decode it.
131 // This can happen after backgrounding. We need to wait for the next
132 // sps/pps before we can resume so we request a keyframe by returning an
133 // error.
134 LOG(LS_WARNING) << "Missing video format. Frame with sps/pps required.";
135 return WEBRTC_VIDEO_CODEC_ERROR;
136 }
137 CMSampleBufferRef sampleBuffer = nullptr;
138 if (!webrtc::H264AnnexBBufferToCMSampleBuffer((uint8_t *)inputImage.buffer.byt es,
139 inputImage.buffer.length,
140 _videoFormat,
141 &sampleBuffer)) {
142 return WEBRTC_VIDEO_CODEC_ERROR;
143 }
144 RTC_DCHECK(sampleBuffer);
145 VTDecodeFrameFlags decodeFlags = kVTDecodeFrame_EnableAsynchronousDecompressio n;
146 std::unique_ptr<RTCFrameDecodeParams> frameDecodeParams;
147 frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeSta mp));
148 OSStatus status = VTDecompressionSessionDecodeFrame(
149 _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.releas e(), nullptr);
150 #if defined(WEBRTC_IOS)
151 // Re-initialize the decoder if we have an invalid session while the app is
152 // active and retry the decode request.
153 if (status == kVTInvalidSessionErr && [self resetDecompressionSession] == WEBR TC_VIDEO_CODEC_OK) {
154 frameDecodeParams.reset(new RTCFrameDecodeParams(_callback, inputImage.timeS tamp));
155 status = VTDecompressionSessionDecodeFrame(
156 _decompressionSession, sampleBuffer, decodeFlags, frameDecodeParams.rele ase(), nullptr);
157 }
158 #endif
159 CFRelease(sampleBuffer);
160 if (status != noErr) {
161 LOG(LS_ERROR) << "Failed to decode frame with code: " << status;
162 return WEBRTC_VIDEO_CODEC_ERROR;
163 }
164 return WEBRTC_VIDEO_CODEC_OK;
165 }
166
167 #pragma mark - Private
168
169 - (void)setVideoFormat:(CMVideoFormatDescriptionRef)videoFormat {
170 if (_videoFormat == videoFormat) {
171 return;
172 }
173 if (_videoFormat) {
174 CFRelease(_videoFormat);
175 }
176 _videoFormat = videoFormat;
177 if (_videoFormat) {
178 CFRetain(_videoFormat);
179 }
180 }
181
182 - (void)destroyDecompressionSession {
183 if (_decompressionSession) {
184 VTDecompressionSessionInvalidate(_decompressionSession);
185 CFRelease(_decompressionSession);
186 _decompressionSession = nullptr;
187 }
188 }
189
190 - (int)resetDecompressionSession {
191 [self destroyDecompressionSession];
192
193 // Need to wait for the first SPS to initialize decoder.
194 if (!_videoFormat) {
195 return WEBRTC_VIDEO_CODEC_OK;
196 }
197
198 // Set keys for OpenGL and IOSurface compatibilty, which makes the encoder
199 // create pixel buffers with GPU backed memory. The intent here is to pass
200 // the pixel buffers directly so we avoid a texture upload later during
201 // rendering. This currently is moot because we are converting back to an
202 // I420 frame after decode, but eventually we will be able to plumb
203 // CVPixelBuffers directly to the renderer.
204 // TODO(tkchin): Maybe only set OpenGL/IOSurface keys if we know that that
205 // we can pass CVPixelBuffers as native handles in decoder output.
206 static size_t const attributesSize = 3;
207 CFTypeRef keys[attributesSize] = {
208 #if defined(WEBRTC_IOS)
209 kCVPixelBufferOpenGLESCompatibilityKey,
210 #elif defined(WEBRTC_MAC)
211 kCVPixelBufferOpenGLCompatibilityKey,
212 #endif
213 kCVPixelBufferIOSurfacePropertiesKey,
214 kCVPixelBufferPixelFormatTypeKey
215 };
216 CFDictionaryRef ioSurfaceValue = CreateCFTypeDictionary(nullptr, nullptr, 0);
217 int64_t nv12type = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
218 CFNumberRef pixelFormat = CFNumberCreate(nullptr, kCFNumberLongType, &nv12type );
219 CFTypeRef values[attributesSize] = {kCFBooleanTrue, ioSurfaceValue, pixelForma t};
220 CFDictionaryRef attributes = CreateCFTypeDictionary(keys, values, attributesSi ze);
221 if (ioSurfaceValue) {
222 CFRelease(ioSurfaceValue);
223 ioSurfaceValue = nullptr;
224 }
225 if (pixelFormat) {
226 CFRelease(pixelFormat);
227 pixelFormat = nullptr;
228 }
229 VTDecompressionOutputCallbackRecord record = {
230 decompressionOutputCallback, nullptr,
231 };
232 OSStatus status = VTDecompressionSessionCreate(
233 nullptr, _videoFormat, nullptr, attributes, &record, &_decompressionSessio n);
234 CFRelease(attributes);
235 if (status != noErr) {
236 [self destroyDecompressionSession];
237 return WEBRTC_VIDEO_CODEC_ERROR;
238 }
239 [self configureDecompressionSession];
240
241 return WEBRTC_VIDEO_CODEC_OK;
242 }
243
244 - (void)configureDecompressionSession {
245 RTC_DCHECK(_decompressionSession);
246 #if defined(WEBRTC_IOS)
247 VTSessionSetProperty(_decompressionSession, kVTDecompressionPropertyKey_RealTi me, kCFBooleanTrue);
248 #endif
249 }
250
251 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698