Chromium Code Reviews| 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 |