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

Side by Side Diff: webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.cc

Issue 1306813009: H.264 video codec support using OpenH264/FFmpeg (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: (Alphabetical sorting in common_video.gyp deps) Created 4 years, 10 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/modules/video_coding/codecs/h264/h264_decoder_impl.h"
13
14 #include <algorithm>
15 #include <limits>
16
17 extern "C" {
18 #include "third_party/ffmpeg/libavcodec/avcodec.h"
19 #include "third_party/ffmpeg/libavformat/avformat.h"
20 #include "third_party/ffmpeg/libavutil/imgutils.h"
21 } // extern "C"
22
23 #include "webrtc/base/checks.h"
24 #include "webrtc/base/criticalsection.h"
25 #include "webrtc/base/keep_ref_until_done.h"
26 #include "webrtc/base/logging.h"
27
28 namespace webrtc {
29
30 namespace {
31
32 const AVPixelFormat kPixelFormat = AV_PIX_FMT_YUV420P;
33 const size_t kYPlaneIndex = 0;
34 const size_t kUPlaneIndex = 1;
35 const size_t kVPlaneIndex = 2;
36
37 #if !defined(WEBRTC_CHROMIUM_BUILD)
38
39 bool ffmpeg_initialized = false;
40
41 // Called by FFmpeg to do mutex operations if initialized using
42 // |InitializeFFmpeg|.
43 int LockManagerOperation(void** lock, AVLockOp op)
44 EXCLUSIVE_LOCK_FUNCTION() UNLOCK_FUNCTION() {
45 switch (op) {
46 case AV_LOCK_CREATE:
47 *lock = new rtc::CriticalSection();
48 return 0;
49 case AV_LOCK_OBTAIN:
50 static_cast<rtc::CriticalSection*>(*lock)->Enter();
51 return 0;
52 case AV_LOCK_RELEASE:
53 static_cast<rtc::CriticalSection*>(*lock)->Leave();
54 return 0;
55 case AV_LOCK_DESTROY:
56 delete static_cast<rtc::CriticalSection*>(*lock);
57 *lock = nullptr;
58 return 0;
59 }
60 RTC_NOTREACHED() << "Unrecognized AVLockOp.";
61 return -1;
62 }
63
64 // TODO(hbos): Assumed to be called on a single thread. Should DCHECK that
65 // InitializeFFmpeg is only called on one thread or make it thread safe.
66 // See https://bugs.chromium.org/p/webrtc/issues/detail?id=5427.
67 void InitializeFFmpeg() {
68 if (!ffmpeg_initialized) {
69 if (av_lockmgr_register(LockManagerOperation) < 0) {
70 RTC_NOTREACHED() << "av_lockmgr_register failed.";
71 return;
72 }
73 av_register_all();
74 ffmpeg_initialized = true;
75 }
76 }
77
78 #endif // !defined(WEBRTC_CHROMIUM_BUILD)
79
80 // Called by FFmpeg when it is done with a frame buffer, see AVGetBuffer2.
81 void AVFreeBuffer2(void* opaque, uint8_t* data) {
82 VideoFrame* video_frame = static_cast<VideoFrame*>(opaque);
83 delete video_frame;
84 }
85
86 // Called by FFmpeg when it needs a frame buffer to store decoded frames in.
87 // The VideoFrames returned by FFmpeg at |Decode| originate from here. They are
88 // reference counted and freed by FFmpeg using |AVFreeBuffer2|.
89 // TODO(hbos): Use a frame pool for better performance instead of create/free.
90 // Could be owned by decoder, |static_cast<H264DecoderImpl*>(context->opaque)|.
91 // Consider verifying that the buffer was allocated by us to avoid unsafe type
92 // cast. See https://bugs.chromium.org/p/webrtc/issues/detail?id=5428.
93 int AVGetBuffer2(AVCodecContext* context, AVFrame* av_frame, int flags) {
94 RTC_CHECK_EQ(context->pix_fmt, kPixelFormat); // Same as in InitDecode.
95 // Necessary capability to be allowed to provide our own buffers.
96 RTC_CHECK(context->codec->capabilities | AV_CODEC_CAP_DR1);
97 // |av_frame->width| and |av_frame->height| are set by FFmpeg. These are the
98 // actual image's dimensions and may be different from |context->width| and
99 // |context->coded_width| due to reordering.
100 int width = av_frame->width;
101 int height = av_frame->height;
102 // See |lowres|, if used the decoder scales the image by 1/2^(lowres). This
103 // has implications on which resolutions are valid, but we don't use it.
104 RTC_CHECK_EQ(context->lowres, 0);
105 // Adjust the |width| and |height| to values acceptable by the decoder.
106 // Without this, FFmpeg may overflow the buffer. If modified, |width| and/or
107 // |height| are larger than the actual image and the image has to be cropped
108 // (top-left corner) after decoding to avoid visible borders to the right and
109 // bottom of the actual image.
110 avcodec_align_dimensions(context, &width, &height);
111
112 RTC_CHECK_GE(width, 0);
113 RTC_CHECK_GE(height, 0);
114 int ret = av_image_check_size(static_cast<unsigned int>(width),
115 static_cast<unsigned int>(height), 0, nullptr);
116 if (ret < 0) {
117 LOG(LS_ERROR) << "Invalid picture size " << width << "x" << height;
118 return ret;
119 }
120
121 // The video frame is stored in |video_frame|. |av_frame| is FFmpeg's version
122 // of a video frame and will be set up to reference |video_frame|'s buffers.
123 VideoFrame* video_frame = new VideoFrame();
124 int stride_y = width;
125 int stride_uv = (width + 1) / 2;
126 RTC_CHECK_EQ(0, video_frame->CreateEmptyFrame(
127 width, height, stride_y, stride_uv, stride_uv));
128 int total_size = video_frame->allocated_size(kYPlane) +
129 video_frame->allocated_size(kUPlane) +
130 video_frame->allocated_size(kVPlane);
131 RTC_DCHECK_EQ(total_size, stride_y * height +
132 (stride_uv + stride_uv) * ((height + 1) / 2));
133
134 // FFmpeg expects the initial allocation to be zero-initialized according to
135 // http://crbug.com/390941.
136 // Using a single |av_frame->buf| - YUV is required to be a continuous blob of
137 // memory. We can zero-initialize with one memset operation for all planes.
138 RTC_DCHECK_EQ(video_frame->buffer(kUPlane),
139 video_frame->buffer(kYPlane) + video_frame->allocated_size(kYPlane));
140 RTC_DCHECK_EQ(video_frame->buffer(kVPlane),
141 video_frame->buffer(kUPlane) + video_frame->allocated_size(kUPlane));
142 memset(video_frame->buffer(kYPlane), 0, total_size);
143
144 av_frame->format = context->pix_fmt;
145 av_frame->reordered_opaque = context->reordered_opaque;
146
147 // Set |av_frame| members as required by FFmpeg.
148 av_frame->data[kYPlaneIndex] = video_frame->buffer(kYPlane);
149 av_frame->linesize[kYPlaneIndex] = video_frame->stride(kYPlane);
150 av_frame->data[kUPlaneIndex] = video_frame->buffer(kUPlane);
151 av_frame->linesize[kUPlaneIndex] = video_frame->stride(kUPlane);
152 av_frame->data[kVPlaneIndex] = video_frame->buffer(kVPlane);
153 av_frame->linesize[kVPlaneIndex] = video_frame->stride(kVPlane);
154 RTC_DCHECK_EQ(av_frame->extended_data, av_frame->data);
155
156 av_frame->buf[0] = av_buffer_create(av_frame->data[kYPlaneIndex],
157 total_size,
158 AVFreeBuffer2,
159 static_cast<void*>(video_frame),
160 0);
161 RTC_CHECK(av_frame->buf[0]);
162 return 0;
163 }
164
165 } // namespace
166
167 H264DecoderImpl::H264DecoderImpl()
168 : decoded_image_callback_(nullptr) {
169 }
170
171 H264DecoderImpl::~H264DecoderImpl() {
172 Release();
173 }
174
175 int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings,
176 int32_t number_of_cores) {
177 if (codec_settings &&
178 codec_settings->codecType != kVideoCodecH264) {
179 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
180 }
181
182 // In Chromium FFmpeg will be initialized outside of WebRTC and we should not
183 // attempt to do so ourselves or it will be initialized twice.
184 // TODO(hbos): Put behind a different flag in case non-chromium project wants
185 // to initialize externally.
186 // See https://bugs.chromium.org/p/webrtc/issues/detail?id=5427.
187 #if !defined(WEBRTC_CHROMIUM_BUILD)
188 // Make sure FFmpeg has been initialized.
189 InitializeFFmpeg();
190 #endif
191
192 // Release necessary in case of re-initializing.
193 int32_t ret = Release();
194 if (ret != WEBRTC_VIDEO_CODEC_OK)
195 return ret;
196 RTC_DCHECK(!av_context_);
197
198 // Initialize AVCodecContext.
199 av_context_.reset(avcodec_alloc_context3(nullptr));
200
201 av_context_->codec_type = AVMEDIA_TYPE_VIDEO;
202 av_context_->codec_id = AV_CODEC_ID_H264;
203 if (codec_settings) {
204 av_context_->coded_width = codec_settings->width;
205 av_context_->coded_height = codec_settings->height;
206 }
207 av_context_->pix_fmt = kPixelFormat;
208 av_context_->extradata = nullptr;
209 av_context_->extradata_size = 0;
210
211 av_context_->thread_count = 1;
212 av_context_->thread_type = FF_THREAD_SLICE;
213
214 // FFmpeg will get video buffers from our AVGetBuffer2, memory managed by us.
215 av_context_->get_buffer2 = AVGetBuffer2;
216 // get_buffer2 is called with the context, there |opaque| can be used to get a
217 // pointer |this|.
218 av_context_->opaque = this;
219 // Use ref counted frames (av_frame_unref).
220 av_context_->refcounted_frames = 1; // true
221
222 AVCodec* codec = avcodec_find_decoder(av_context_->codec_id);
223 if (!codec) {
224 // This is an indication that FFmpeg has not been initialized or it has not
225 // been compiled/initialized with the correct set of codecs.
226 LOG(LS_ERROR) << "FFmpeg H.264 decoder not found.";
227 Release();
228 return WEBRTC_VIDEO_CODEC_ERROR;
229 }
230 int res = avcodec_open2(av_context_.get(), codec, nullptr);
231 if (res < 0) {
232 LOG(LS_ERROR) << "avcodec_open2 error: " << res;
233 Release();
234 return WEBRTC_VIDEO_CODEC_ERROR;
235 }
236
237 av_frame_.reset(av_frame_alloc());
238 return WEBRTC_VIDEO_CODEC_OK;
239 }
240
241 int32_t H264DecoderImpl::Release() {
242 av_context_.reset();
243 av_frame_.reset();
244 return WEBRTC_VIDEO_CODEC_OK;
245 }
246
247 int32_t H264DecoderImpl::Reset() {
248 if (!IsInitialized())
249 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
250 InitDecode(nullptr, 1);
251 return WEBRTC_VIDEO_CODEC_OK;
252 }
253
254 int32_t H264DecoderImpl::RegisterDecodeCompleteCallback(
255 DecodedImageCallback* callback) {
256 decoded_image_callback_ = callback;
257 return WEBRTC_VIDEO_CODEC_OK;
258 }
259
260 int32_t H264DecoderImpl::Decode(const EncodedImage& input_image,
261 bool /*missing_frames*/,
262 const RTPFragmentationHeader* /*fragmentation*/,
263 const CodecSpecificInfo* codec_specific_info,
264 int64_t /*render_time_ms*/) {
265 if (!IsInitialized())
266 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
267 if (!decoded_image_callback_) {
268 LOG(LS_WARNING) << "InitDecode() has been called, but a callback function "
269 "has not been set with RegisterDecodeCompleteCallback()";
270 return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
271 }
272 if (!input_image._buffer || !input_image._length)
273 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
274 if (codec_specific_info &&
275 codec_specific_info->codecType != kVideoCodecH264) {
276 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
277 }
278
279 // FFmpeg requires padding due to some optimized bitstream readers reading 32
280 // or 64 bits at once and could read over the end. See avcodec_decode_video2.
281 RTC_CHECK_GE(input_image._size, input_image._length +
282 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264));
283 // "If the first 23 bits of the additional bytes are not 0, then damaged MPEG
284 // bitstreams could cause overread and segfault." See
285 // AV_INPUT_BUFFER_PADDING_SIZE. We'll zero the entire padding just in case.
286 memset(input_image._buffer + input_image._length,
287 0,
288 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264));
289
290 AVPacket packet;
291 av_init_packet(&packet);
292 packet.data = input_image._buffer;
293 if (input_image._length >
294 static_cast<size_t>(std::numeric_limits<int>::max())) {
295 return WEBRTC_VIDEO_CODEC_ERROR;
296 }
297 packet.size = static_cast<int>(input_image._length);
298 av_context_->reordered_opaque = input_image.ntp_time_ms_ * 1000; // ms -> μs
299
300 int frame_decoded = 0;
301 int result = avcodec_decode_video2(av_context_.get(),
302 av_frame_.get(),
303 &frame_decoded,
304 &packet);
305 if (result < 0) {
306 LOG(LS_ERROR) << "avcodec_decode_video2 error: " << result;
307 return WEBRTC_VIDEO_CODEC_ERROR;
308 }
309 // |result| is number of bytes used, which should be all of them.
310 if (result != packet.size) {
311 LOG(LS_ERROR) << "avcodec_decode_video2 consumed " << result << " bytes "
312 "when " << packet.size << " bytes were expected.";
313 return WEBRTC_VIDEO_CODEC_ERROR;
314 }
315
316 if (!frame_decoded) {
317 LOG(LS_WARNING) << "avcodec_decode_video2 successful but no frame was "
318 "decoded.";
319 return WEBRTC_VIDEO_CODEC_OK;
320 }
321
322 // Obtain the |video_frame| containing the decoded image.
323 VideoFrame* video_frame = static_cast<VideoFrame*>(
324 av_buffer_get_opaque(av_frame_->buf[0]));
325 RTC_DCHECK(video_frame);
326 RTC_CHECK_EQ(av_frame_->data[kYPlane], video_frame->buffer(kYPlane));
327 RTC_CHECK_EQ(av_frame_->data[kUPlane], video_frame->buffer(kUPlane));
328 RTC_CHECK_EQ(av_frame_->data[kVPlane], video_frame->buffer(kVPlane));
329 video_frame->set_timestamp(input_image._timeStamp);
330
331 // The decoded image may be larger than what is supposed to be visible, see
332 // |AVGetBuffer2|'s use of |avcodec_align_dimensions|. This crops the image
333 // without copying the underlying buffer.
334 rtc::scoped_refptr<VideoFrameBuffer> buf = video_frame->video_frame_buffer();
335 if (av_frame_->width != buf->width() || av_frame_->height != buf->height()) {
336 video_frame->set_video_frame_buffer(
337 new rtc::RefCountedObject<WrappedI420Buffer>(
338 av_frame_->width, av_frame_->height,
339 buf->data(kYPlane), buf->stride(kYPlane),
340 buf->data(kUPlane), buf->stride(kUPlane),
341 buf->data(kVPlane), buf->stride(kVPlane),
342 rtc::KeepRefUntilDone(buf)));
343 }
344
345 // Return decoded frame.
346 int32_t ret = decoded_image_callback_->Decoded(*video_frame);
347 // Stop referencing it, possibly freeing |video_frame|.
348 av_frame_unref(av_frame_.get());
349 video_frame = nullptr;
350
351 if (ret) {
352 LOG(LS_WARNING) << "DecodedImageCallback::Decoded returned " << ret;
353 return ret;
354 }
355 return WEBRTC_VIDEO_CODEC_OK;
356 }
357
358 bool H264DecoderImpl::IsInitialized() const {
359 return av_context_ != nullptr;
360 }
361
362 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698