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 |
(...skipping 65 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 | 103 // (top-left corner) after decoding to avoid visible borders to the right and |
103 // bottom of the actual image. | 104 // bottom of the actual image. |
104 avcodec_align_dimensions(context, &width, &height); | 105 avcodec_align_dimensions(context, &width, &height); |
105 | 106 |
106 RTC_CHECK_GE(width, 0); | 107 RTC_CHECK_GE(width, 0); |
107 RTC_CHECK_GE(height, 0); | 108 RTC_CHECK_GE(height, 0); |
108 int ret = av_image_check_size(static_cast<unsigned int>(width), | 109 int ret = av_image_check_size(static_cast<unsigned int>(width), |
109 static_cast<unsigned int>(height), 0, nullptr); | 110 static_cast<unsigned int>(height), 0, nullptr); |
110 if (ret < 0) { | 111 if (ret < 0) { |
111 LOG(LS_ERROR) << "Invalid picture size " << width << "x" << height; | 112 LOG(LS_ERROR) << "Invalid picture size " << width << "x" << height; |
113 decoder->ReportError(); | |
112 return ret; | 114 return ret; |
113 } | 115 } |
114 | 116 |
115 // The video frame is stored in |video_frame|. |av_frame| is FFmpeg's version | 117 // 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. | 118 // of a video frame and will be set up to reference |video_frame|'s buffers. |
117 VideoFrame* video_frame = new VideoFrame(); | 119 VideoFrame* video_frame = new VideoFrame(); |
118 // FFmpeg expects the initial allocation to be zero-initialized according to | 120 // 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. | 121 // http://crbug.com/390941. Our pool is set up to zero-initialize new buffers. |
120 video_frame->set_video_frame_buffer( | 122 video_frame->set_video_frame_buffer( |
121 decoder->pool_.CreateBuffer(width, height)); | 123 decoder->pool_.CreateBuffer(width, height)); |
(...skipping 28 matching lines...) Expand all Loading... | |
150 } | 152 } |
151 | 153 |
152 void H264DecoderImpl::AVFreeBuffer2(void* opaque, uint8_t* data) { | 154 void H264DecoderImpl::AVFreeBuffer2(void* opaque, uint8_t* data) { |
153 // The buffer pool recycles the buffer used by |video_frame| when there are no | 155 // 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 | 156 // more references to it. |video_frame| is a thin buffer holder and is not |
155 // recycled. | 157 // recycled. |
156 VideoFrame* video_frame = static_cast<VideoFrame*>(opaque); | 158 VideoFrame* video_frame = static_cast<VideoFrame*>(opaque); |
157 delete video_frame; | 159 delete video_frame; |
158 } | 160 } |
159 | 161 |
160 H264DecoderImpl::H264DecoderImpl() : pool_(true), | 162 H264DecoderImpl::H264DecoderImpl() : has_reported_init_(false), |
163 has_reported_error_(false), | |
164 pool_(true), | |
161 decoded_image_callback_(nullptr) { | 165 decoded_image_callback_(nullptr) { |
162 } | 166 } |
163 | 167 |
164 H264DecoderImpl::~H264DecoderImpl() { | 168 H264DecoderImpl::~H264DecoderImpl() { |
165 Release(); | 169 Release(); |
166 } | 170 } |
167 | 171 |
168 int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings, | 172 int32_t H264DecoderImpl::InitDecode(const VideoCodec* codec_settings, |
169 int32_t number_of_cores) { | 173 int32_t number_of_cores) { |
174 ReportInit(); | |
170 if (codec_settings && | 175 if (codec_settings && |
171 codec_settings->codecType != kVideoCodecH264) { | 176 codec_settings->codecType != kVideoCodecH264) { |
177 ReportError(); | |
172 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 178 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
173 } | 179 } |
174 | 180 |
175 // FFmpeg must have been initialized (with |av_lockmgr_register| and | 181 // FFmpeg must have been initialized (with |av_lockmgr_register| and |
176 // |av_register_all|) before we proceed. |InitializeFFmpeg| does this, which | 182 // |av_register_all|) before we proceed. |InitializeFFmpeg| does this, which |
177 // makes sense for WebRTC standalone. In other cases, such as Chromium, FFmpeg | 183 // makes sense for WebRTC standalone. In other cases, such as Chromium, FFmpeg |
178 // is initialized externally and calling |InitializeFFmpeg| would be | 184 // is initialized externally and calling |InitializeFFmpeg| would be |
179 // thread-unsafe and result in FFmpeg being initialized twice, which could | 185 // thread-unsafe and result in FFmpeg being initialized twice, which could |
180 // break other FFmpeg usage. See the |rtc_initialize_ffmpeg| flag. | 186 // break other FFmpeg usage. See the |rtc_initialize_ffmpeg| flag. |
181 #if defined(WEBRTC_INITIALIZE_FFMPEG) | 187 #if defined(WEBRTC_INITIALIZE_FFMPEG) |
182 // Make sure FFmpeg has been initialized. Subsequent |InitializeFFmpeg| calls | 188 // Make sure FFmpeg has been initialized. Subsequent |InitializeFFmpeg| calls |
183 // do nothing. | 189 // do nothing. |
184 InitializeFFmpeg(); | 190 InitializeFFmpeg(); |
185 #endif | 191 #endif |
186 | 192 |
187 // Release necessary in case of re-initializing. | 193 // Release necessary in case of re-initializing. |
188 int32_t ret = Release(); | 194 int32_t ret = Release(); |
189 if (ret != WEBRTC_VIDEO_CODEC_OK) | 195 if (ret != WEBRTC_VIDEO_CODEC_OK) { |
196 ReportError(); | |
190 return ret; | 197 return ret; |
198 } | |
191 RTC_DCHECK(!av_context_); | 199 RTC_DCHECK(!av_context_); |
192 | 200 |
193 // Initialize AVCodecContext. | 201 // Initialize AVCodecContext. |
194 av_context_.reset(avcodec_alloc_context3(nullptr)); | 202 av_context_.reset(avcodec_alloc_context3(nullptr)); |
195 | 203 |
196 av_context_->codec_type = AVMEDIA_TYPE_VIDEO; | 204 av_context_->codec_type = AVMEDIA_TYPE_VIDEO; |
197 av_context_->codec_id = AV_CODEC_ID_H264; | 205 av_context_->codec_id = AV_CODEC_ID_H264; |
198 if (codec_settings) { | 206 if (codec_settings) { |
199 av_context_->coded_width = codec_settings->width; | 207 av_context_->coded_width = codec_settings->width; |
200 av_context_->coded_height = codec_settings->height; | 208 av_context_->coded_height = codec_settings->height; |
(...skipping 14 matching lines...) Expand all Loading... | |
215 av_context_->opaque = this; | 223 av_context_->opaque = this; |
216 // Use ref counted frames (av_frame_unref). | 224 // Use ref counted frames (av_frame_unref). |
217 av_context_->refcounted_frames = 1; // true | 225 av_context_->refcounted_frames = 1; // true |
218 | 226 |
219 AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); | 227 AVCodec* codec = avcodec_find_decoder(av_context_->codec_id); |
220 if (!codec) { | 228 if (!codec) { |
221 // This is an indication that FFmpeg has not been initialized or it has not | 229 // This is an indication that FFmpeg has not been initialized or it has not |
222 // been compiled/initialized with the correct set of codecs. | 230 // been compiled/initialized with the correct set of codecs. |
223 LOG(LS_ERROR) << "FFmpeg H.264 decoder not found."; | 231 LOG(LS_ERROR) << "FFmpeg H.264 decoder not found."; |
224 Release(); | 232 Release(); |
233 ReportError(); | |
225 return WEBRTC_VIDEO_CODEC_ERROR; | 234 return WEBRTC_VIDEO_CODEC_ERROR; |
226 } | 235 } |
227 int res = avcodec_open2(av_context_.get(), codec, nullptr); | 236 int res = avcodec_open2(av_context_.get(), codec, nullptr); |
228 if (res < 0) { | 237 if (res < 0) { |
229 LOG(LS_ERROR) << "avcodec_open2 error: " << res; | 238 LOG(LS_ERROR) << "avcodec_open2 error: " << res; |
230 Release(); | 239 Release(); |
240 ReportError(); | |
231 return WEBRTC_VIDEO_CODEC_ERROR; | 241 return WEBRTC_VIDEO_CODEC_ERROR; |
232 } | 242 } |
233 | 243 |
234 av_frame_.reset(av_frame_alloc()); | 244 av_frame_.reset(av_frame_alloc()); |
235 return WEBRTC_VIDEO_CODEC_OK; | 245 return WEBRTC_VIDEO_CODEC_OK; |
236 } | 246 } |
237 | 247 |
238 int32_t H264DecoderImpl::Release() { | 248 int32_t H264DecoderImpl::Release() { |
239 av_context_.reset(); | 249 av_context_.reset(); |
240 av_frame_.reset(); | 250 av_frame_.reset(); |
241 return WEBRTC_VIDEO_CODEC_OK; | 251 return WEBRTC_VIDEO_CODEC_OK; |
242 } | 252 } |
243 | 253 |
244 int32_t H264DecoderImpl::RegisterDecodeCompleteCallback( | 254 int32_t H264DecoderImpl::RegisterDecodeCompleteCallback( |
245 DecodedImageCallback* callback) { | 255 DecodedImageCallback* callback) { |
246 decoded_image_callback_ = callback; | 256 decoded_image_callback_ = callback; |
247 return WEBRTC_VIDEO_CODEC_OK; | 257 return WEBRTC_VIDEO_CODEC_OK; |
248 } | 258 } |
249 | 259 |
250 int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, | 260 int32_t H264DecoderImpl::Decode(const EncodedImage& input_image, |
251 bool /*missing_frames*/, | 261 bool /*missing_frames*/, |
252 const RTPFragmentationHeader* /*fragmentation*/, | 262 const RTPFragmentationHeader* /*fragmentation*/, |
253 const CodecSpecificInfo* codec_specific_info, | 263 const CodecSpecificInfo* codec_specific_info, |
254 int64_t /*render_time_ms*/) { | 264 int64_t /*render_time_ms*/) { |
255 if (!IsInitialized()) | 265 if (!IsInitialized()) { |
266 ReportError(); | |
256 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 267 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
268 } | |
257 if (!decoded_image_callback_) { | 269 if (!decoded_image_callback_) { |
258 LOG(LS_WARNING) << "InitDecode() has been called, but a callback function " | 270 LOG(LS_WARNING) << "InitDecode() has been called, but a callback function " |
259 "has not been set with RegisterDecodeCompleteCallback()"; | 271 "has not been set with RegisterDecodeCompleteCallback()"; |
272 ReportError(); | |
260 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; | 273 return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
261 } | 274 } |
262 if (!input_image._buffer || !input_image._length) | 275 if (!input_image._buffer || !input_image._length) { |
276 ReportError(); | |
263 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 277 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
278 } | |
264 if (codec_specific_info && | 279 if (codec_specific_info && |
265 codec_specific_info->codecType != kVideoCodecH264) { | 280 codec_specific_info->codecType != kVideoCodecH264) { |
281 ReportError(); | |
266 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; | 282 return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
267 } | 283 } |
268 | 284 |
269 // FFmpeg requires padding due to some optimized bitstream readers reading 32 | 285 // 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. | 286 // 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 + | 287 RTC_CHECK_GE(input_image._size, input_image._length + |
272 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); | 288 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); |
273 // "If the first 23 bits of the additional bytes are not 0, then damaged MPEG | 289 // "If the first 23 bits of the additional bytes are not 0, then damaged MPEG |
274 // bitstreams could cause overread and segfault." See | 290 // bitstreams could cause overread and segfault." See |
275 // AV_INPUT_BUFFER_PADDING_SIZE. We'll zero the entire padding just in case. | 291 // AV_INPUT_BUFFER_PADDING_SIZE. We'll zero the entire padding just in case. |
276 memset(input_image._buffer + input_image._length, | 292 memset(input_image._buffer + input_image._length, |
277 0, | 293 0, |
278 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); | 294 EncodedImage::GetBufferPaddingBytes(kVideoCodecH264)); |
279 | 295 |
280 AVPacket packet; | 296 AVPacket packet; |
281 av_init_packet(&packet); | 297 av_init_packet(&packet); |
282 packet.data = input_image._buffer; | 298 packet.data = input_image._buffer; |
283 if (input_image._length > | 299 if (input_image._length > |
284 static_cast<size_t>(std::numeric_limits<int>::max())) { | 300 static_cast<size_t>(std::numeric_limits<int>::max())) { |
301 ReportError(); | |
285 return WEBRTC_VIDEO_CODEC_ERROR; | 302 return WEBRTC_VIDEO_CODEC_ERROR; |
286 } | 303 } |
287 packet.size = static_cast<int>(input_image._length); | 304 packet.size = static_cast<int>(input_image._length); |
288 av_context_->reordered_opaque = input_image.ntp_time_ms_ * 1000; // ms -> μs | 305 av_context_->reordered_opaque = input_image.ntp_time_ms_ * 1000; // ms -> μs |
289 | 306 |
290 int frame_decoded = 0; | 307 int frame_decoded = 0; |
291 int result = avcodec_decode_video2(av_context_.get(), | 308 int result = avcodec_decode_video2(av_context_.get(), |
292 av_frame_.get(), | 309 av_frame_.get(), |
293 &frame_decoded, | 310 &frame_decoded, |
294 &packet); | 311 &packet); |
295 if (result < 0) { | 312 if (result < 0) { |
296 LOG(LS_ERROR) << "avcodec_decode_video2 error: " << result; | 313 LOG(LS_ERROR) << "avcodec_decode_video2 error: " << result; |
314 ReportError(); | |
297 return WEBRTC_VIDEO_CODEC_ERROR; | 315 return WEBRTC_VIDEO_CODEC_ERROR; |
298 } | 316 } |
299 // |result| is number of bytes used, which should be all of them. | 317 // |result| is number of bytes used, which should be all of them. |
300 if (result != packet.size) { | 318 if (result != packet.size) { |
301 LOG(LS_ERROR) << "avcodec_decode_video2 consumed " << result << " bytes " | 319 LOG(LS_ERROR) << "avcodec_decode_video2 consumed " << result << " bytes " |
302 "when " << packet.size << " bytes were expected."; | 320 "when " << packet.size << " bytes were expected."; |
321 ReportError(); | |
303 return WEBRTC_VIDEO_CODEC_ERROR; | 322 return WEBRTC_VIDEO_CODEC_ERROR; |
304 } | 323 } |
305 | 324 |
306 if (!frame_decoded) { | 325 if (!frame_decoded) { |
307 LOG(LS_WARNING) << "avcodec_decode_video2 successful but no frame was " | 326 LOG(LS_WARNING) << "avcodec_decode_video2 successful but no frame was " |
308 "decoded."; | 327 "decoded."; |
309 return WEBRTC_VIDEO_CODEC_OK; | 328 return WEBRTC_VIDEO_CODEC_OK; |
310 } | 329 } |
311 | 330 |
312 // Obtain the |video_frame| containing the decoded image. | 331 // Obtain the |video_frame| containing the decoded image. |
(...skipping 29 matching lines...) Expand all Loading... | |
342 LOG(LS_WARNING) << "DecodedImageCallback::Decoded returned " << ret; | 361 LOG(LS_WARNING) << "DecodedImageCallback::Decoded returned " << ret; |
343 return ret; | 362 return ret; |
344 } | 363 } |
345 return WEBRTC_VIDEO_CODEC_OK; | 364 return WEBRTC_VIDEO_CODEC_OK; |
346 } | 365 } |
347 | 366 |
348 bool H264DecoderImpl::IsInitialized() const { | 367 bool H264DecoderImpl::IsInitialized() const { |
349 return av_context_ != nullptr; | 368 return av_context_ != nullptr; |
350 } | 369 } |
351 | 370 |
371 void H264DecoderImpl::ReportInit() { | |
372 if (has_reported_init_) | |
373 return; | |
374 RTC_HISTOGRAM_COUNTS("WebRTC.Video.H264DecoderImpl.Init", | |
hlundin-webrtc
2016/02/23 13:55:53
Did you consider using RTC_HISTOGRAM_ENUMERATION i
hbos
2016/02/23 14:43:26
Oh, that makes a lot of sense. Done.
| |
375 1 /* sample */, | |
376 1 /* min */, 1 /* max */, | |
377 1 /* bucket count */); | |
378 has_reported_init_ = true; | |
379 } | |
380 | |
381 void H264DecoderImpl::ReportError() { | |
382 if (has_reported_error_) | |
383 return; | |
384 RTC_HISTOGRAM_COUNTS("WebRTC.Video.H264DecoderImpl.Error", | |
385 1 /* sample */, | |
386 1 /* min */, 1 /* max */, | |
387 1 /* bucket count */); | |
388 has_reported_error_ = true; | |
389 } | |
390 | |
352 } // namespace webrtc | 391 } // namespace webrtc |
OLD | NEW |