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

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

Issue 1716173002: Histograms for H264EncoderImpl/H264DecoderImpl initialization and errors (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: 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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698