OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 * | 9 * |
10 */ | 10 */ |
11 | 11 |
12 #include "webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.h" | 12 #include "webrtc/modules/video_coding/codecs/h264/h264_decoder_impl.h" |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <limits> | 15 #include <limits> |
16 | 16 |
17 extern "C" { | 17 extern "C" { |
18 #include "third_party/ffmpeg/libavcodec/avcodec.h" | 18 #include "third_party/ffmpeg/libavcodec/avcodec.h" |
19 #include "third_party/ffmpeg/libavformat/avformat.h" | 19 #include "third_party/ffmpeg/libavformat/avformat.h" |
20 #include "third_party/ffmpeg/libavutil/imgutils.h" | 20 #include "third_party/ffmpeg/libavutil/imgutils.h" |
21 } // extern "C" | 21 } // extern "C" |
22 | 22 |
23 #include "webrtc/base/checks.h" | 23 #include "webrtc/base/checks.h" |
24 #include "webrtc/base/criticalsection.h" | 24 #include "webrtc/base/criticalsection.h" |
25 #include "webrtc/base/keep_ref_until_done.h" | 25 #include "webrtc/base/keep_ref_until_done.h" |
26 #include "webrtc/base/logging.h" | 26 #include "webrtc/base/logging.h" |
27 #include "webrtc/system_wrappers/include/metrics.h" | |
27 | 28 |
28 namespace webrtc { | 29 namespace webrtc { |
29 | 30 |
30 namespace { | 31 namespace { |
31 | 32 |
32 const AVPixelFormat kPixelFormat = AV_PIX_FMT_YUV420P; | 33 const AVPixelFormat kPixelFormat = AV_PIX_FMT_YUV420P; |
33 const size_t kYPlaneIndex = 0; | 34 const size_t kYPlaneIndex = 0; |
34 const size_t kUPlaneIndex = 1; | 35 const size_t kUPlaneIndex = 1; |
35 const size_t kVPlaneIndex = 2; | 36 const size_t kVPlaneIndex = 2; |
36 | 37 |
38 // Used by histograms. Values of entries should not be changed. | |
39 enum H264DecoderImplEvent { | |
40 kH264DecoderEventInit = 0, | |
41 kH264DecoderEventError = 1, | |
42 kH264DecoderEventMax = 16, | |
hbos
2016/02/23 15:01:22
The example I looked at was WebRTC.Video.Encoder.C
hlundin-webrtc
2016/02/24 09:17:57
I don't know the history behind CodecType. The des
hbos
2016/02/24 11:11:25
Ok. Landed as-is. rkaplow if you know I could adju
rkaplow
2016/02/24 16:10:08
usually people don't even set the numbers here man
| |
43 }; | |
44 | |
37 #if defined(WEBRTC_INITIALIZE_FFMPEG) | 45 #if defined(WEBRTC_INITIALIZE_FFMPEG) |
38 | 46 |
39 rtc::CriticalSection ffmpeg_init_lock; | 47 rtc::CriticalSection ffmpeg_init_lock; |
40 bool ffmpeg_initialized = false; | 48 bool ffmpeg_initialized = false; |
41 | 49 |
42 // Called by FFmpeg to do mutex operations if initialized using | 50 // Called by FFmpeg to do mutex operations if initialized using |
43 // |InitializeFFmpeg|. | 51 // |InitializeFFmpeg|. |
44 int LockManagerOperation(void** lock, AVLockOp op) | 52 int LockManagerOperation(void** lock, AVLockOp op) |
45 EXCLUSIVE_LOCK_FUNCTION() UNLOCK_FUNCTION() { | 53 EXCLUSIVE_LOCK_FUNCTION() UNLOCK_FUNCTION() { |
46 switch (op) { | 54 switch (op) { |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
102 // (top-left corner) after decoding to avoid visible borders to the right and | 110 // (top-left corner) after decoding to avoid visible borders to the right and |
103 // bottom of the actual image. | 111 // bottom of the actual image. |
104 avcodec_align_dimensions(context, &width, &height); | 112 avcodec_align_dimensions(context, &width, &height); |
105 | 113 |
106 RTC_CHECK_GE(width, 0); | 114 RTC_CHECK_GE(width, 0); |
107 RTC_CHECK_GE(height, 0); | 115 RTC_CHECK_GE(height, 0); |
108 int ret = av_image_check_size(static_cast<unsigned int>(width), | 116 int ret = av_image_check_size(static_cast<unsigned int>(width), |
109 static_cast<unsigned int>(height), 0, nullptr); | 117 static_cast<unsigned int>(height), 0, nullptr); |
110 if (ret < 0) { | 118 if (ret < 0) { |
111 LOG(LS_ERROR) << "Invalid picture size " << width << "x" << height; | 119 LOG(LS_ERROR) << "Invalid picture size " << width << "x" << height; |
120 decoder->ReportError(); | |
112 return ret; | 121 return ret; |
113 } | 122 } |
114 | 123 |
115 // The video frame is stored in |video_frame|. |av_frame| is FFmpeg's version | 124 // The video frame is stored in |video_frame|. |av_frame| is FFmpeg's version |
116 // of a video frame and will be set up to reference |video_frame|'s buffers. | 125 // of a video frame and will be set up to reference |video_frame|'s buffers. |
117 VideoFrame* video_frame = new VideoFrame(); | 126 VideoFrame* video_frame = new VideoFrame(); |
118 // FFmpeg expects the initial allocation to be zero-initialized according to | 127 // FFmpeg expects the initial allocation to be zero-initialized according to |
119 // http://crbug.com/390941. Our pool is set up to zero-initialize new buffers. | 128 // http://crbug.com/390941. Our pool is set up to zero-initialize new buffers. |
120 video_frame->set_video_frame_buffer( | 129 video_frame->set_video_frame_buffer( |
121 decoder->pool_.CreateBuffer(width, height)); | 130 decoder->pool_.CreateBuffer(width, height)); |
(...skipping 29 matching lines...) Expand all Loading... | |
151 | 160 |
152 void H264DecoderImpl::AVFreeBuffer2(void* opaque, uint8_t* data) { | 161 void H264DecoderImpl::AVFreeBuffer2(void* opaque, uint8_t* data) { |
153 // The buffer pool recycles the buffer used by |video_frame| when there are no | 162 // The buffer pool recycles the buffer used by |video_frame| when there are no |
154 // more references to it. |video_frame| is a thin buffer holder and is not | 163 // more references to it. |video_frame| is a thin buffer holder and is not |
155 // recycled. | 164 // recycled. |
156 VideoFrame* video_frame = static_cast<VideoFrame*>(opaque); | 165 VideoFrame* video_frame = static_cast<VideoFrame*>(opaque); |
157 delete video_frame; | 166 delete video_frame; |
158 } | 167 } |
159 | 168 |
160 H264DecoderImpl::H264DecoderImpl() : pool_(true), | 169 H264DecoderImpl::H264DecoderImpl() : pool_(true), |
161 decoded_image_callback_(nullptr) { | 170 decoded_image_callback_(nullptr), |
171 has_reported_init_(false), | |
172 has_reported_error_(false) { | |
162 } | 173 } |
163 | 174 |
164 H264DecoderImpl::~H264DecoderImpl() { | 175 H264DecoderImpl::~H264DecoderImpl() { |
165 Release(); | 176 Release(); |
166 } | 177 } |
167 | 178 |
168 int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings, | 179 int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings, |
169 int32_t number_of_cores) { | 180 int32_t number_of_cores) { |
181 ReportInit(); | |
170 if (codec_settings && | 182 if (codec_settings && |
171 codec_settings->codecType != kVideoCodecH264) { | 183 codec_settings->codecType != kVideoCodecH264) { |
184 ReportError(); | |
172 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 185 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
173 } | 186 } |
174 | 187 |
175 // FFmpeg must have been initialized (with |av_lockmgr_register| and | 188 // FFmpeg must have been initialized (with |av_lockmgr_register| and |
176 // |av_register_all|) before we proceed. |InitializeFFmpeg| does this, which | 189 // |av_register_all|) before we proceed. |InitializeFFmpeg| does this, which |
177 // makes sense for WebRTC standalone. In other cases, such as Chromium, FFmpeg | 190 // makes sense for WebRTC standalone. In other cases, such as Chromium, FFmpeg |
178 // is initialized externally and calling |InitializeFFmpeg| would be | 191 // is initialized externally and calling |InitializeFFmpeg| would be |
179 // thread-unsafe and result in FFmpeg being initialized twice, which could | 192 // thread-unsafe and result in FFmpeg being initialized twice, which could |
180 // break other FFmpeg usage. See the |rtc_initialize_ffmpeg| flag. | 193 // break other FFmpeg usage. See the |rtc_initialize_ffmpeg| flag. |
181 #if defined(WEBRTC_INITIALIZE_FFMPEG) | 194 #if defined(WEBRTC_INITIALIZE_FFMPEG) |
182 // Make sure FFmpeg has been initialized. Subsequent |InitializeFFmpeg| calls | 195 // Make sure FFmpeg has been initialized. Subsequent |InitializeFFmpeg| calls |
183 // do nothing. | 196 // do nothing. |
184 InitializeFFmpeg(); | 197 InitializeFFmpeg(); |
185 #endif | 198 #endif |
186 | 199 |
187 // Release necessary in case of re-initializing. | 200 // Release necessary in case of re-initializing. |
188 int32_t ret = Release(); | 201 int32_t ret = Release(); |
189 if (ret != WEBRTC_VIDEO_CODEC_OK) | 202 if (ret != WEBRTC_VIDEO_CODEC_OK) { |
203 ReportError(); | |
190 return ret; | 204 return ret; |
205 } | |
191 RTC_DCHECK(!av_context_); | 206 RTC_DCHECK(!av_context_); |
192 | 207 |
193 // Initialize AVCodecContext. | 208 // Initialize AVCodecContext. |
194 av_context_.reset(avcodec_alloc_context3(nullptr)); | 209 av_context_.reset(avcodec_alloc_context3(nullptr)); |
195 | 210 |
196 av_context_->codec_type = AVMEDIA_TYPE_VIDEO; | 211 av_context_->codec_type = AVMEDIA_TYPE_VIDEO; |
197 av_context_->codec_id = AV_CODEC_ID_H264; | 212 av_context_->codec_id = AV_CODEC_ID_H264; |
198 if (codec_settings) { | 213 if (codec_settings) { |
199 av_context_->coded_width = codec_settings->width; | 214 av_context_->coded_width = codec_settings->width; |
200 av_context_->coded_height = codec_settings->height; | 215 av_context_->coded_height = codec_settings->height; |
(...skipping 14 matching lines...) Expand all Loading... | |
215 av_context_->opaque = this; | 230 av_context_->opaque = this; |
216 // Use ref counted frames (av_frame_unref). | 231 // Use ref counted frames (av_frame_unref). |
217 av_context_->refcounted_frames = 1; // true | 232 av_context_->refcounted_frames = 1; // true |
218 | 233 |
219 AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); | 234 AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); |
220 if (!codec) { | 235 if (!codec) { |
221 // This is an indication that FFmpeg has not been initialized or it has not | 236 // This is an indication that FFmpeg has not been initialized or it has not |
222 // been compiled/initialized with the correct set of codecs. | 237 // been compiled/initialized with the correct set of codecs. |
223 LOG(LS_ERROR) << "FFmpeg H.264 decoder not found."; | 238 LOG(LS_ERROR) << "FFmpeg H.264 decoder not found."; |
224 Release(); | 239 Release(); |
240 ReportError(); | |
225 return WEBRTC_VIDEO_CODEC_ERROR; | 241 return WEBRTC_VIDEO_CODEC_ERROR; |
226 } | 242 } |
227 int res = avcodec_open2(av_context_.get(), codec, nullptr); | 243 int res = avcodec_open2(av_context_.get(), codec, nullptr); |
228 if (res < 0) { | 244 if (res < 0) { |
229 LOG(LS_ERROR) << "avcodec_open2 error: " << res; | 245 LOG(LS_ERROR) << "avcodec_open2 error: " << res; |
230 Release(); | 246 Release(); |
247 ReportError(); | |
231 return WEBRTC_VIDEO_CODEC_ERROR; | 248 return WEBRTC_VIDEO_CODEC_ERROR; |
232 } | 249 } |
233 | 250 |
234 av_frame_.reset(av_frame_alloc()); | 251 av_frame_.reset(av_frame_alloc()); |
235 return WEBRTC_VIDEO_CODEC_OK; | 252 return WEBRTC_VIDEO_CODEC_OK; |
236 } | 253 } |
237 | 254 |
238 int32_t H264DecoderImpl::Release() { | 255 int32_t H264DecoderImpl::Release() { |
239 av_context_.reset(); | 256 av_context_.reset(); |
240 av_frame_.reset(); | 257 av_frame_.reset(); |
241 return WEBRTC_VIDEO_CODEC_OK; | 258 return WEBRTC_VIDEO_CODEC_OK; |
242 } | 259 } |
243 | 260 |
244 int32_t H264DecoderImpl::RegisterDecodeCompleteCallback( | 261 int32_t H264DecoderImpl::RegisterDecodeCompleteCallback( |
245 DecodedImageCallback* callback) { | 262 DecodedImageCallback* callback) { |
246 decoded_image_callback_ = callback; | 263 decoded_image_callback_ = callback; |
247 return WEBRTC_VIDEO_CODEC_OK; | 264 return WEBRTC_VIDEO_CODEC_OK; |
248 } | 265 } |
249 | 266 |
250 int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, | 267 int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, |
251 bool /*missing_frames*/, | 268 bool /*missing_frames*/, |
252 const RTPFragmentationHeader* /*fragmentation*/, | 269 const RTPFragmentationHeader* /*fragmentation*/, |
253 const CodecSpecificInfo* codec_specific_info, | 270 const CodecSpecificInfo* codec_specific_info, |
254 int64_t /*render_time_ms*/) { | 271 int64_t /*render_time_ms*/) { |
255 if (!IsInitialized()) | 272 if (!IsInitialized()) { |
273 ReportError(); | |
256 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 274 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
275 } | |
257 if (!decoded_image_callback_) { | 276 if (!decoded_image_callback_) { |
258 LOG(LS_WARNING) << "InitDecode() has been called, but a callback function " | 277 LOG(LS_WARNING) << "InitDecode() has been called, but a callback function " |
259 "has not been set with RegisterDecodeCompleteCallback()"; | 278 "has not been set with RegisterDecodeCompleteCallback()"; |
279 ReportError(); | |
260 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 280 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
261 } | 281 } |
262 if (!input_image._buffer || !input_image._length) | 282 if (!input_image._buffer || !input_image._length) { |
283 ReportError(); | |
263 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 284 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
285 } | |
264 if (codec_specific_info && | 286 if (codec_specific_info && |
265 codec_specific_info->codecType != kVideoCodecH264) { | 287 codec_specific_info->codecType != kVideoCodecH264) { |
288 ReportError(); | |
266 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 289 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
267 } | 290 } |
268 | 291 |
269 // FFmpeg requires padding due to some optimized bitstream readers reading 32 | 292 // FFmpeg requires padding due to some optimized bitstream readers reading 32 |
270 // or 64 bits at once and could read over the end. See avcodec_decode_video2. | 293 // or 64 bits at once and could read over the end. See avcodec_decode_video2. |
271 RTC_CHECK_GE(input_image._size, input_image._length + | 294 RTC_CHECK_GE(input_image._size, input_image._length + |
272 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); | 295 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); |
273 // "If the first 23 bits of the additional bytes are not 0, then damaged MPEG | 296 // "If the first 23 bits of the additional bytes are not 0, then damaged MPEG |
274 // bitstreams could cause overread and segfault." See | 297 // bitstreams could cause overread and segfault." See |
275 // AV_INPUT_BUFFER_PADDING_SIZE. We'll zero the entire padding just in case. | 298 // AV_INPUT_BUFFER_PADDING_SIZE. We'll zero the entire padding just in case. |
276 memset(input_image._buffer + input_image._length, | 299 memset(input_image._buffer + input_image._length, |
277 0, | 300 0, |
278 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); | 301 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); |
279 | 302 |
280 AVPacket packet; | 303 AVPacket packet; |
281 av_init_packet(&packet); | 304 av_init_packet(&packet); |
282 packet.data = input_image._buffer; | 305 packet.data = input_image._buffer; |
283 if (input_image._length > | 306 if (input_image._length > |
284 static_cast<size_t>(std::numeric_limits<int>::max())) { | 307 static_cast<size_t>(std::numeric_limits<int>::max())) { |
308 ReportError(); | |
285 return WEBRTC_VIDEO_CODEC_ERROR; | 309 return WEBRTC_VIDEO_CODEC_ERROR; |
286 } | 310 } |
287 packet.size = static_cast<int>(input_image._length); | 311 packet.size = static_cast<int>(input_image._length); |
288 av_context_->reordered_opaque = input_image.ntp_time_ms_ * 1000; // ms -> μs | 312 av_context_->reordered_opaque = input_image.ntp_time_ms_ * 1000; // ms -> μs |
289 | 313 |
290 int frame_decoded = 0; | 314 int frame_decoded = 0; |
291 int result = avcodec_decode_video2(av_context_.get(), | 315 int result = avcodec_decode_video2(av_context_.get(), |
292 av_frame_.get(), | 316 av_frame_.get(), |
293 &frame_decoded, | 317 &frame_decoded, |
294 &packet); | 318 &packet); |
295 if (result < 0) { | 319 if (result < 0) { |
296 LOG(LS_ERROR) << "avcodec_decode_video2 error: " << result; | 320 LOG(LS_ERROR) << "avcodec_decode_video2 error: " << result; |
321 ReportError(); | |
297 return WEBRTC_VIDEO_CODEC_ERROR; | 322 return WEBRTC_VIDEO_CODEC_ERROR; |
298 } | 323 } |
299 // |result| is number of bytes used, which should be all of them. | 324 // |result| is number of bytes used, which should be all of them. |
300 if (result != packet.size) { | 325 if (result != packet.size) { |
301 LOG(LS_ERROR) << "avcodec_decode_video2 consumed " << result << " bytes " | 326 LOG(LS_ERROR) << "avcodec_decode_video2 consumed " << result << " bytes " |
302 "when " << packet.size << " bytes were expected."; | 327 "when " << packet.size << " bytes were expected."; |
328 ReportError(); | |
303 return WEBRTC_VIDEO_CODEC_ERROR; | 329 return WEBRTC_VIDEO_CODEC_ERROR; |
304 } | 330 } |
305 | 331 |
306 if (!frame_decoded) { | 332 if (!frame_decoded) { |
307 LOG(LS_WARNING) << "avcodec_decode_video2 successful but no frame was " | 333 LOG(LS_WARNING) << "avcodec_decode_video2 successful but no frame was " |
308 "decoded."; | 334 "decoded."; |
309 return WEBRTC_VIDEO_CODEC_OK; | 335 return WEBRTC_VIDEO_CODEC_OK; |
310 } | 336 } |
311 | 337 |
312 // Obtain the |video_frame| containing the decoded image. | 338 // Obtain the |video_frame| containing the decoded image. |
(...skipping 29 matching lines...) Expand all Loading... | |
342 LOG(LS_WARNING) << "DecodedImageCallback::Decoded returned " << ret; | 368 LOG(LS_WARNING) << "DecodedImageCallback::Decoded returned " << ret; |
343 return ret; | 369 return ret; |
344 } | 370 } |
345 return WEBRTC_VIDEO_CODEC_OK; | 371 return WEBRTC_VIDEO_CODEC_OK; |
346 } | 372 } |
347 | 373 |
348 bool H264DecoderImpl::IsInitialized() const { | 374 bool H264DecoderImpl::IsInitialized() const { |
349 return av_context_ != nullptr; | 375 return av_context_ != nullptr; |
350 } | 376 } |
351 | 377 |
378 void H264DecoderImpl::ReportInit() { | |
379 if (has_reported_init_) | |
380 return; | |
381 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264DecoderImpl.Event", | |
382 kH264DecoderEventInit, | |
383 kH264DecoderEventMax); | |
384 has_reported_init_ = true; | |
385 } | |
386 | |
387 void H264DecoderImpl::ReportError() { | |
388 if (has_reported_error_) | |
389 return; | |
390 RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264DecoderImpl.Event", | |
391 kH264DecoderEventError, | |
392 kH264DecoderEventMax); | |
393 has_reported_error_ = true; | |
394 } | |
395 | |
352 } // namespace webrtc | 396 } // namespace webrtc |
OLD | NEW |