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

Side by Side Diff: webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_decoder.cc

Issue 2890733003: Reland of Split iOS sdk in to separate targets (Closed)
Patch Set: Created 3 years, 7 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 (c) 2015 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
12 #include "webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_decoder.h"
13
14 #include <memory>
15
16 #if defined(WEBRTC_IOS)
17 #include "RTCUIApplication.h"
18 #endif
19 #include "libyuv/convert.h"
20 #include "webrtc/api/video/video_frame.h"
21 #include "webrtc/base/checks.h"
22 #include "webrtc/base/logging.h"
23 #include "webrtc/common_video/include/video_frame.h"
24 #include "webrtc/sdk/objc/Framework/Classes/corevideo_frame_buffer.h"
25 #include "webrtc/sdk/objc/Framework/Classes/h264_video_toolbox_nalu.h"
26
27 namespace webrtc {
28 namespace {
29
30 static const int64_t kMsPerSec = 1000;
31
32 // Convenience function for creating a dictionary.
33 inline CFDictionaryRef CreateCFDictionary(CFTypeRef* keys,
34 CFTypeRef* values,
35 size_t size) {
36 return CFDictionaryCreate(nullptr, keys, values, size,
37 &kCFTypeDictionaryKeyCallBacks,
38 &kCFTypeDictionaryValueCallBacks);
39 }
40
41 // Struct that we pass to the decoder per frame to decode. We receive it again
42 // in the decoder callback.
43 struct FrameDecodeParams {
44 FrameDecodeParams(DecodedImageCallback* cb, int64_t ts)
45 : callback(cb), timestamp(ts) {}
46 DecodedImageCallback* callback;
47 int64_t timestamp;
48 };
49
50 // This is the callback function that VideoToolbox calls when decode is
51 // complete.
52 void VTDecompressionOutputCallback(void* decoder,
53 void* params,
54 OSStatus status,
55 VTDecodeInfoFlags info_flags,
56 CVImageBufferRef image_buffer,
57 CMTime timestamp,
58 CMTime duration) {
59 std::unique_ptr<FrameDecodeParams> decode_params(
60 reinterpret_cast<FrameDecodeParams*>(params));
61 if (status != noErr) {
62 LOG(LS_ERROR) << "Failed to decode frame. Status: " << status;
63 return;
64 }
65 // TODO(tkchin): Handle CVO properly.
66 rtc::scoped_refptr<VideoFrameBuffer> buffer =
67 new rtc::RefCountedObject<CoreVideoFrameBuffer>(image_buffer);
68 VideoFrame decoded_frame(buffer, decode_params->timestamp,
69 CMTimeGetSeconds(timestamp) * kMsPerSec,
70 kVideoRotation_0);
71 decode_params->callback->Decoded(decoded_frame);
72 }
73
74 } // namespace
75
76 H264VideoToolboxDecoder::H264VideoToolboxDecoder()
77 : callback_(nullptr),
78 video_format_(nullptr),
79 decompression_session_(nullptr) {}
80
81 H264VideoToolboxDecoder::~H264VideoToolboxDecoder() {
82 DestroyDecompressionSession();
83 SetVideoFormat(nullptr);
84 }
85
86 int H264VideoToolboxDecoder::InitDecode(const VideoCodec* video_codec,
87 int number_of_cores) {
88 return WEBRTC_VIDEO_CODEC_OK;
89 }
90
91 int H264VideoToolboxDecoder::Decode(
92 const EncodedImage& input_image,
93 bool missing_frames,
94 const RTPFragmentationHeader* fragmentation,
95 const CodecSpecificInfo* codec_specific_info,
96 int64_t render_time_ms) {
97 RTC_DCHECK(input_image._buffer);
98
99 #if defined(WEBRTC_IOS)
100 if (!RTCIsUIApplicationActive()) {
101 // Ignore all decode requests when app isn't active. In this state, the
102 // hardware decoder has been invalidated by the OS.
103 // Reset video format so that we won't process frames until the next
104 // keyframe.
105 SetVideoFormat(nullptr);
106 return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
107 }
108 #endif
109 CMVideoFormatDescriptionRef input_format = nullptr;
110 if (H264AnnexBBufferHasVideoFormatDescription(input_image._buffer,
111 input_image._length)) {
112 input_format = CreateVideoFormatDescription(input_image._buffer,
113 input_image._length);
114 if (input_format) {
115 // Check if the video format has changed, and reinitialize decoder if
116 // needed.
117 if (!CMFormatDescriptionEqual(input_format, video_format_)) {
118 SetVideoFormat(input_format);
119 ResetDecompressionSession();
120 }
121 CFRelease(input_format);
122 }
123 }
124 if (!video_format_) {
125 // We received a frame but we don't have format information so we can't
126 // decode it.
127 // This can happen after backgrounding. We need to wait for the next
128 // sps/pps before we can resume so we request a keyframe by returning an
129 // error.
130 LOG(LS_WARNING) << "Missing video format. Frame with sps/pps required.";
131 return WEBRTC_VIDEO_CODEC_ERROR;
132 }
133 CMSampleBufferRef sample_buffer = nullptr;
134 if (!H264AnnexBBufferToCMSampleBuffer(input_image._buffer,
135 input_image._length, video_format_,
136 &sample_buffer)) {
137 return WEBRTC_VIDEO_CODEC_ERROR;
138 }
139 RTC_DCHECK(sample_buffer);
140 VTDecodeFrameFlags decode_flags =
141 kVTDecodeFrame_EnableAsynchronousDecompression;
142 std::unique_ptr<FrameDecodeParams> frame_decode_params;
143 frame_decode_params.reset(
144 new FrameDecodeParams(callback_, input_image._timeStamp));
145 OSStatus status = VTDecompressionSessionDecodeFrame(
146 decompression_session_, sample_buffer, decode_flags,
147 frame_decode_params.release(), nullptr);
148 #if defined(WEBRTC_IOS)
149 // Re-initialize the decoder if we have an invalid session while the app is
150 // active and retry the decode request.
151 if (status == kVTInvalidSessionErr &&
152 ResetDecompressionSession() == WEBRTC_VIDEO_CODEC_OK) {
153 frame_decode_params.reset(
154 new FrameDecodeParams(callback_, input_image._timeStamp));
155 status = VTDecompressionSessionDecodeFrame(
156 decompression_session_, sample_buffer, decode_flags,
157 frame_decode_params.release(), nullptr);
158 }
159 #endif
160 CFRelease(sample_buffer);
161 if (status != noErr) {
162 LOG(LS_ERROR) << "Failed to decode frame with code: " << status;
163 return WEBRTC_VIDEO_CODEC_ERROR;
164 }
165 return WEBRTC_VIDEO_CODEC_OK;
166 }
167
168 int H264VideoToolboxDecoder::RegisterDecodeCompleteCallback(
169 DecodedImageCallback* callback) {
170 RTC_DCHECK(!callback_);
171 callback_ = callback;
172 return WEBRTC_VIDEO_CODEC_OK;
173 }
174
175 int H264VideoToolboxDecoder::Release() {
176 // Need to invalidate the session so that callbacks no longer occur and it
177 // is safe to null out the callback.
178 DestroyDecompressionSession();
179 SetVideoFormat(nullptr);
180 callback_ = nullptr;
181 return WEBRTC_VIDEO_CODEC_OK;
182 }
183
184 int H264VideoToolboxDecoder::ResetDecompressionSession() {
185 DestroyDecompressionSession();
186
187 // Need to wait for the first SPS to initialize decoder.
188 if (!video_format_) {
189 return WEBRTC_VIDEO_CODEC_OK;
190 }
191
192 // Set keys for OpenGL and IOSurface compatibilty, which makes the encoder
193 // create pixel buffers with GPU backed memory. The intent here is to pass
194 // the pixel buffers directly so we avoid a texture upload later during
195 // rendering. This currently is moot because we are converting back to an
196 // I420 frame after decode, but eventually we will be able to plumb
197 // CVPixelBuffers directly to the renderer.
198 // TODO(tkchin): Maybe only set OpenGL/IOSurface keys if we know that that
199 // we can pass CVPixelBuffers as native handles in decoder output.
200 static size_t const attributes_size = 3;
201 CFTypeRef keys[attributes_size] = {
202 #if defined(WEBRTC_IOS)
203 kCVPixelBufferOpenGLESCompatibilityKey,
204 #elif defined(WEBRTC_MAC)
205 kCVPixelBufferOpenGLCompatibilityKey,
206 #endif
207 kCVPixelBufferIOSurfacePropertiesKey,
208 kCVPixelBufferPixelFormatTypeKey
209 };
210 CFDictionaryRef io_surface_value = CreateCFDictionary(nullptr, nullptr, 0);
211 int64_t nv12type = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
212 CFNumberRef pixel_format =
213 CFNumberCreate(nullptr, kCFNumberLongType, &nv12type);
214 CFTypeRef values[attributes_size] = {kCFBooleanTrue, io_surface_value,
215 pixel_format};
216 CFDictionaryRef attributes =
217 CreateCFDictionary(keys, values, attributes_size);
218 if (io_surface_value) {
219 CFRelease(io_surface_value);
220 io_surface_value = nullptr;
221 }
222 if (pixel_format) {
223 CFRelease(pixel_format);
224 pixel_format = nullptr;
225 }
226 VTDecompressionOutputCallbackRecord record = {
227 VTDecompressionOutputCallback, this,
228 };
229 OSStatus status =
230 VTDecompressionSessionCreate(nullptr, video_format_, nullptr, attributes,
231 &record, &decompression_session_);
232 CFRelease(attributes);
233 if (status != noErr) {
234 DestroyDecompressionSession();
235 return WEBRTC_VIDEO_CODEC_ERROR;
236 }
237 ConfigureDecompressionSession();
238
239 return WEBRTC_VIDEO_CODEC_OK;
240 }
241
242 void H264VideoToolboxDecoder::ConfigureDecompressionSession() {
243 RTC_DCHECK(decompression_session_);
244 #if defined(WEBRTC_IOS)
245 VTSessionSetProperty(decompression_session_,
246 kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
247 #endif
248 }
249
250 void H264VideoToolboxDecoder::DestroyDecompressionSession() {
251 if (decompression_session_) {
252 VTDecompressionSessionInvalidate(decompression_session_);
253 CFRelease(decompression_session_);
254 decompression_session_ = nullptr;
255 }
256 }
257
258 void H264VideoToolboxDecoder::SetVideoFormat(
259 CMVideoFormatDescriptionRef video_format) {
260 if (video_format_ == video_format) {
261 return;
262 }
263 if (video_format_) {
264 CFRelease(video_format_);
265 }
266 video_format_ = video_format;
267 if (video_format_) {
268 CFRetain(video_format_);
269 }
270 }
271
272 const char* H264VideoToolboxDecoder::ImplementationName() const {
273 return "VideoToolbox";
274 }
275
276 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698