Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.h" | 5 #include "webkit/media/crypto/ppapi/ffmpeg_cdm_video_decoder.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
| 9 #include "media/base/buffers.h" | 9 #include "media/base/buffers.h" |
| 10 #include "media/base/limits.h" | 10 #include "media/base/limits.h" |
| 11 #include "webkit/media/crypto/ppapi/content_decryption_module.h" | 11 #include "webkit/media/crypto/ppapi/content_decryption_module.h" |
| 12 | 12 |
| 13 // Include FFmpeg header files. | 13 // Include FFmpeg header files. |
| 14 extern "C" { | 14 extern "C" { |
| 15 // Temporarily disable possible loss of data warning. | 15 // Temporarily disable possible loss of data warning. |
| 16 MSVC_PUSH_DISABLE_WARNING(4244); | 16 MSVC_PUSH_DISABLE_WARNING(4244); |
| 17 #include <libavcodec/avcodec.h> | 17 #include <libavcodec/avcodec.h> |
| 18 MSVC_POP_WARNING(); | 18 MSVC_POP_WARNING(); |
| 19 } // extern "C" | 19 } // extern "C" |
| 20 | 20 |
| 21 // TODO(tomfinegan): Move libvpx decode support somewhere else. Another ifdef | |
| 22 // is a crime against humanity etc etc... | |
| 23 #define USE_LIBVPX 1 | |
| 24 | |
| 25 #if defined (USE_LIBVPX) | |
| 26 // Include libvpx header files. | |
| 27 extern "C" { | |
| 28 #define VPX_CODEC_DISABLE_COMPAT 1 | |
| 29 #include "third_party/libvpx/libvpx.h" | |
| 30 } | |
| 31 #endif | |
| 32 | |
| 33 // TODO(tomfinegan): I'm seeing bad video output, so I put in code w/memcpy's | |
| 34 // instead of using |CopyPlane()| to try to figure out what was going on. Turns | |
| 35 // out video output is bad with or without libvpx in the mix. I've left this | |
| 36 // mess here because full plane copies are what the stuff we're comparing | |
| 37 // against does, so maybe it's of some use... | |
|
xhwang
2012/11/16 17:33:02
Is this what you are seeing? http://code.google.co
Tom Finegan
2012/11/16 19:36:51
Yeah, that's the problem. Things look fine with --
| |
| 38 // #define USE_COPYPLANE_WITH_LIBVPX 1 | |
| 39 | |
| 21 namespace webkit_media { | 40 namespace webkit_media { |
| 22 | 41 |
| 23 static const int kDecodeThreads = 1; | 42 static const int kDecodeThreads = 1; |
| 24 | 43 |
| 25 static cdm::VideoFormat PixelFormatToCdmVideoFormat(PixelFormat pixel_format) { | 44 static cdm::VideoFormat PixelFormatToCdmVideoFormat(PixelFormat pixel_format) { |
| 26 switch (pixel_format) { | 45 switch (pixel_format) { |
| 27 case PIX_FMT_YUV420P: | 46 case PIX_FMT_YUV420P: |
| 28 return cdm::kYv12; | 47 return cdm::kYv12; |
| 29 default: | 48 default: |
| 30 DVLOG(1) << "Unsupported PixelFormat: " << pixel_format; | 49 DVLOG(1) << "Unsupported PixelFormat: " << pixel_format; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 126 memcpy(target + target_offset, | 145 memcpy(target + target_offset, |
| 127 source + source_offset, | 146 source + source_offset, |
| 128 copy_bytes_per_row); | 147 copy_bytes_per_row); |
| 129 } | 148 } |
| 130 } | 149 } |
| 131 | 150 |
| 132 FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(cdm::Allocator* allocator) | 151 FFmpegCdmVideoDecoder::FFmpegCdmVideoDecoder(cdm::Allocator* allocator) |
| 133 : codec_context_(NULL), | 152 : codec_context_(NULL), |
| 134 av_frame_(NULL), | 153 av_frame_(NULL), |
| 135 is_initialized_(false), | 154 is_initialized_(false), |
| 136 allocator_(allocator) { | 155 allocator_(allocator), |
| 156 vpx_codec_(NULL), | |
| 157 vpx_image_(NULL) { | |
| 137 } | 158 } |
| 138 | 159 |
| 139 FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() { | 160 FFmpegCdmVideoDecoder::~FFmpegCdmVideoDecoder() { |
| 140 ReleaseFFmpegResources(); | 161 ReleaseFFmpegResources(); |
| 141 } | 162 } |
| 142 | 163 |
| 143 bool FFmpegCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) { | 164 bool FFmpegCdmVideoDecoder::Initialize(const cdm::VideoDecoderConfig& config) { |
| 144 DVLOG(1) << "Initialize()"; | 165 DVLOG(1) << "Initialize()"; |
| 145 | 166 |
| 146 if (!IsValidOutputConfig(config.format, config.coded_size)) { | 167 if (!IsValidOutputConfig(config.format, config.coded_size)) { |
| 147 LOG(ERROR) << "Initialize(): invalid video decoder configuration."; | 168 LOG(ERROR) << "Initialize(): invalid video decoder configuration."; |
| 148 return false; | 169 return false; |
| 149 } | 170 } |
| 150 | 171 |
| 151 if (is_initialized_) { | 172 if (is_initialized_) { |
| 152 LOG(ERROR) << "Initialize(): Already initialized."; | 173 LOG(ERROR) << "Initialize(): Already initialized."; |
| 153 return false; | 174 return false; |
| 154 } | 175 } |
| 155 | 176 |
| 156 // Initialize AVCodecContext structure. | 177 // TODO(tomfinegan): Move libvpx decode support somewhere else. |
| 157 codec_context_ = avcodec_alloc_context3(NULL); | 178 #if defined(USE_LIBVPX) |
| 158 CdmVideoDecoderConfigToAVCodecContext(config, codec_context_); | 179 if (config.codec == cdm::VideoDecoderConfig::kCodecVp8) { |
| 180 return InitializeLibvpx(config); | |
| 181 } | |
| 182 #endif | |
| 159 | 183 |
| 160 // Enable motion vector search (potentially slow), strong deblocking filter | 184 return InitializeFFmpeg(config); |
| 161 // for damaged macroblocks, and set our error detection sensitivity. | |
| 162 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
| 163 codec_context_->err_recognition = AV_EF_CAREFUL; | |
| 164 codec_context_->thread_count = kDecodeThreads; | |
| 165 codec_context_->opaque = this; | |
| 166 codec_context_->flags |= CODEC_FLAG_EMU_EDGE; | |
| 167 | |
| 168 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
| 169 if (!codec) { | |
| 170 LOG(ERROR) << "Initialize(): avcodec_find_decoder failed."; | |
| 171 return false; | |
| 172 } | |
| 173 | |
| 174 int status; | |
| 175 if ((status = avcodec_open2(codec_context_, codec, NULL)) < 0) { | |
| 176 LOG(ERROR) << "Initialize(): avcodec_open2 failed: " << status; | |
| 177 return false; | |
| 178 } | |
| 179 | |
| 180 av_frame_ = avcodec_alloc_frame(); | |
| 181 is_initialized_ = true; | |
| 182 | |
| 183 return true; | |
| 184 } | 185 } |
| 185 | 186 |
| 186 void FFmpegCdmVideoDecoder::Deinitialize() { | 187 void FFmpegCdmVideoDecoder::Deinitialize() { |
| 187 DVLOG(1) << "Deinitialize()"; | 188 DVLOG(1) << "Deinitialize()"; |
| 188 ReleaseFFmpegResources(); | 189 ReleaseFFmpegResources(); |
| 190 | |
| 191 if (vpx_codec_) { | |
| 192 vpx_codec_destroy(vpx_codec_); | |
| 193 vpx_codec_ = NULL; | |
| 194 } | |
| 195 | |
| 189 is_initialized_ = false; | 196 is_initialized_ = false; |
| 190 } | 197 } |
| 191 | 198 |
| 192 void FFmpegCdmVideoDecoder::Reset() { | 199 void FFmpegCdmVideoDecoder::Reset() { |
| 193 DVLOG(1) << "Reset()"; | 200 DVLOG(1) << "Reset()"; |
| 194 avcodec_flush_buffers(codec_context_); | 201 |
| 202 if (codec_context_) | |
| 203 avcodec_flush_buffers(codec_context_); | |
| 195 } | 204 } |
| 196 | 205 |
| 197 // static | 206 // static |
| 198 bool FFmpegCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format, | 207 bool FFmpegCdmVideoDecoder::IsValidOutputConfig(cdm::VideoFormat format, |
| 199 const cdm::Size& data_size) { | 208 const cdm::Size& data_size) { |
| 200 return ((format == cdm::kYv12 || format == cdm::kI420) && | 209 return ((format == cdm::kYv12 || format == cdm::kI420) && |
| 201 (data_size.width % 2) == 0 && (data_size.height % 2) == 0 && | 210 (data_size.width % 2) == 0 && (data_size.height % 2) == 0 && |
| 202 data_size.width > 0 && data_size.height > 0 && | 211 data_size.width > 0 && data_size.height > 0 && |
| 203 data_size.width <= media::limits::kMaxDimension && | 212 data_size.width <= media::limits::kMaxDimension && |
| 204 data_size.height <= media::limits::kMaxDimension && | 213 data_size.height <= media::limits::kMaxDimension && |
| 205 data_size.width * data_size.height <= media::limits::kMaxCanvas); | 214 data_size.width * data_size.height <= media::limits::kMaxCanvas); |
| 206 } | 215 } |
| 207 | 216 |
| 208 cdm::Status FFmpegCdmVideoDecoder::DecodeFrame( | 217 cdm::Status FFmpegCdmVideoDecoder::DecodeFrame( |
| 209 const uint8_t* compressed_frame, | 218 const uint8_t* compressed_frame, |
| 210 int32_t compressed_frame_size, | 219 int32_t compressed_frame_size, |
| 211 int64_t timestamp, | 220 int64_t timestamp, |
| 212 cdm::VideoFrame* decoded_frame) { | 221 cdm::VideoFrame* decoded_frame) { |
| 213 DVLOG(1) << "DecodeFrame()"; | |
| 214 DCHECK(decoded_frame); | 222 DCHECK(decoded_frame); |
| 215 | 223 |
| 216 // Create a packet for input data. | 224 // TODO(tomfinegan): Move libvpx decode support somewhere else. |
| 217 AVPacket packet; | 225 #if defined(USE_LIBVPX) |
| 218 av_init_packet(&packet); | 226 if (vpx_codec_) { |
| 227 return DecodeFrameLibvpx(compressed_frame, compressed_frame_size, | |
| 228 timestamp, | |
| 229 decoded_frame); | |
| 230 } | |
| 231 #endif | |
| 219 | 232 |
| 220 // The FFmpeg API does not allow us to have const read-only pointers. | 233 return DecodeFrameFFmpeg(compressed_frame, compressed_frame_size, timestamp, |
| 221 packet.data = const_cast<uint8_t*>(compressed_frame); | 234 decoded_frame); |
| 222 packet.size = compressed_frame_size; | |
| 223 | |
| 224 // Let FFmpeg handle presentation timestamp reordering. | |
| 225 codec_context_->reordered_opaque = timestamp; | |
| 226 | |
| 227 // Reset frame to default values. | |
| 228 avcodec_get_frame_defaults(av_frame_); | |
| 229 | |
| 230 // This is for codecs not using get_buffer to initialize | |
| 231 // |av_frame_->reordered_opaque| | |
| 232 av_frame_->reordered_opaque = codec_context_->reordered_opaque; | |
| 233 | |
| 234 int frame_decoded = 0; | |
| 235 int result = avcodec_decode_video2(codec_context_, | |
| 236 av_frame_, | |
| 237 &frame_decoded, | |
| 238 &packet); | |
| 239 // Log the problem when we can't decode a video frame and exit early. | |
| 240 if (result < 0) { | |
| 241 LOG(ERROR) << "DecodeFrame(): Error decoding video frame with timestamp: " | |
| 242 << timestamp << " us, packet size: " << packet.size << " bytes"; | |
| 243 return cdm::kDecodeError; | |
| 244 } | |
| 245 | |
| 246 // If no frame was produced then signal that more data is required to produce | |
| 247 // more frames. | |
| 248 if (frame_decoded == 0) | |
| 249 return cdm::kNeedMoreData; | |
| 250 | |
| 251 // The decoder is in a bad state and not decoding correctly. | |
| 252 // Checking for NULL avoids a crash. | |
| 253 if (!av_frame_->data[cdm::VideoFrame::kYPlane] || | |
| 254 !av_frame_->data[cdm::VideoFrame::kUPlane] || | |
| 255 !av_frame_->data[cdm::VideoFrame::kVPlane]) { | |
| 256 LOG(ERROR) << "DecodeFrame(): Video frame has invalid frame data."; | |
| 257 return cdm::kDecodeError; | |
| 258 } | |
| 259 | |
| 260 if (!CopyAvFrameTo(decoded_frame)) { | |
| 261 LOG(ERROR) << "DecodeFrame() could not copy video frame to output buffer."; | |
| 262 return cdm::kDecodeError; | |
| 263 } | |
| 264 | |
| 265 return cdm::kSuccess; | |
| 266 } | 235 } |
| 267 | 236 |
| 268 bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame* cdm_video_frame) { | 237 bool FFmpegCdmVideoDecoder::CopyAvFrameTo(cdm::VideoFrame* cdm_video_frame) { |
| 269 DCHECK(cdm_video_frame); | 238 DCHECK(cdm_video_frame); |
| 270 DCHECK_EQ(av_frame_->format, PIX_FMT_YUV420P); | 239 DCHECK_EQ(av_frame_->format, PIX_FMT_YUV420P); |
| 271 DCHECK_EQ(av_frame_->width % 2, 0); | 240 DCHECK_EQ(av_frame_->width % 2, 0); |
| 272 DCHECK_EQ(av_frame_->height % 2, 0); | 241 DCHECK_EQ(av_frame_->height % 2, 0); |
| 273 | 242 |
| 274 const int y_size = av_frame_->width * av_frame_->height; | 243 const int y_size = av_frame_->width * av_frame_->height; |
| 275 const int uv_size = y_size / 2; | 244 const int uv_size = y_size / 2; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 320 | 289 |
| 321 cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, av_frame_->width); | 290 cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, av_frame_->width); |
| 322 cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, uv_stride); | 291 cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, uv_stride); |
| 323 cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, uv_stride); | 292 cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, uv_stride); |
| 324 | 293 |
| 325 cdm_video_frame->set_timestamp(av_frame_->reordered_opaque); | 294 cdm_video_frame->set_timestamp(av_frame_->reordered_opaque); |
| 326 | 295 |
| 327 return true; | 296 return true; |
| 328 } | 297 } |
| 329 | 298 |
| 299 bool FFmpegCdmVideoDecoder::CopyVpxImageTo(cdm::VideoFrame* cdm_video_frame) { | |
| 300 DCHECK(cdm_video_frame); | |
| 301 DCHECK_EQ(vpx_image_->fmt, VPX_IMG_FMT_I420); | |
| 302 DCHECK_EQ(vpx_image_->d_w % 2, 0U); | |
| 303 DCHECK_EQ(vpx_image_->d_h % 2, 0U); | |
| 304 | |
| 305 #if defined(USE_COPYPLANE_WITH_LIBVPX) | |
| 306 const int y_size = vpx_image_->d_w * vpx_image_->d_h; | |
| 307 const int uv_size = y_size / 2; | |
| 308 const int space_required = y_size + (uv_size * 2); | |
| 309 | |
| 310 DCHECK(!cdm_video_frame->frame_buffer()); | |
| 311 cdm_video_frame->set_frame_buffer(allocator_->Allocate(space_required)); | |
| 312 if (!cdm_video_frame->frame_buffer()) { | |
| 313 LOG(ERROR) << "CopyVpxImageTo() cdm::Allocator::Allocate failed."; | |
| 314 return false; | |
| 315 } | |
| 316 | |
| 317 CopyPlane(vpx_image_->planes[VPX_PLANE_Y], | |
| 318 vpx_image_->stride[VPX_PLANE_Y], | |
| 319 vpx_image_->d_w, | |
| 320 vpx_image_->d_h, | |
| 321 vpx_image_->d_w, | |
| 322 cdm_video_frame->frame_buffer()->data()); | |
| 323 | |
| 324 const int uv_stride = vpx_image_->d_w / 2; | |
| 325 const int uv_rows = vpx_image_->d_h / 2; | |
| 326 CopyPlane(vpx_image_->planes[VPX_PLANE_U], | |
| 327 vpx_image_->stride[VPX_PLANE_U], | |
| 328 uv_stride, | |
| 329 uv_rows, | |
| 330 uv_stride, | |
| 331 cdm_video_frame->frame_buffer()->data() + y_size); | |
| 332 | |
| 333 CopyPlane(vpx_image_->planes[VPX_PLANE_V], | |
| 334 vpx_image_->stride[VPX_PLANE_V], | |
| 335 uv_stride, | |
| 336 uv_rows, | |
| 337 uv_stride, | |
| 338 cdm_video_frame->frame_buffer()->data() + y_size + uv_size); | |
| 339 | |
| 340 cdm_video_frame->set_format(cdm::kYv12); | |
| 341 | |
| 342 cdm::Size video_frame_size; | |
| 343 video_frame_size.width = vpx_image_->d_w; | |
| 344 video_frame_size.height = vpx_image_->d_h; | |
| 345 cdm_video_frame->set_size(video_frame_size); | |
| 346 | |
| 347 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kYPlane, 0); | |
| 348 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kUPlane, y_size); | |
| 349 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kVPlane, | |
| 350 y_size + uv_size); | |
| 351 | |
| 352 cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, vpx_image_->d_w); | |
| 353 cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, uv_stride); | |
| 354 cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, uv_stride); | |
| 355 #else | |
| 356 const int y_size = vpx_image_->stride[VPX_PLANE_Y] * vpx_image_->d_h; | |
| 357 const int uv_rows = vpx_image_->d_h / 2; | |
| 358 const int u_size = vpx_image_->stride[VPX_PLANE_U] * uv_rows; | |
| 359 const int v_size = vpx_image_->stride[VPX_PLANE_V] * uv_rows; | |
| 360 const int space_required = y_size + u_size + v_size; | |
| 361 | |
| 362 DCHECK(!cdm_video_frame->frame_buffer()); | |
| 363 cdm_video_frame->set_frame_buffer(allocator_->Allocate(space_required)); | |
| 364 if (!cdm_video_frame->frame_buffer()) { | |
| 365 LOG(ERROR) << "CopyVpxImageTo() cdm::Allocator::Allocate failed."; | |
| 366 return false; | |
| 367 } | |
| 368 | |
| 369 memcpy(cdm_video_frame->frame_buffer()->data(), | |
| 370 vpx_image_->planes[VPX_PLANE_Y], | |
| 371 y_size); | |
| 372 memcpy(cdm_video_frame->frame_buffer()->data() + y_size, | |
| 373 vpx_image_->planes[VPX_PLANE_U], | |
| 374 u_size); | |
| 375 memcpy(cdm_video_frame->frame_buffer()->data() + y_size + u_size, | |
| 376 vpx_image_->planes[VPX_PLANE_V], | |
| 377 v_size); | |
| 378 | |
| 379 cdm_video_frame->set_format(cdm::kYv12); | |
| 380 | |
| 381 cdm::Size video_frame_size; | |
| 382 video_frame_size.width = vpx_image_->d_w; | |
| 383 video_frame_size.height = vpx_image_->d_h; | |
| 384 cdm_video_frame->set_size(video_frame_size); | |
| 385 | |
| 386 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kYPlane, 0); | |
| 387 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kUPlane, y_size); | |
| 388 cdm_video_frame->set_plane_offset(cdm::VideoFrame::kVPlane, | |
| 389 y_size + u_size); | |
| 390 | |
| 391 cdm_video_frame->set_stride(cdm::VideoFrame::kYPlane, | |
| 392 vpx_image_->stride[VPX_PLANE_Y]); | |
| 393 cdm_video_frame->set_stride(cdm::VideoFrame::kUPlane, | |
| 394 vpx_image_->stride[VPX_PLANE_U]); | |
| 395 cdm_video_frame->set_stride(cdm::VideoFrame::kVPlane, | |
| 396 vpx_image_->stride[VPX_PLANE_V]); | |
| 397 #endif // USE_COPYPLANE_WITH_LIBVPX | |
| 398 | |
| 399 return true; | |
| 400 } | |
| 401 | |
| 330 void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() { | 402 void FFmpegCdmVideoDecoder::ReleaseFFmpegResources() { |
| 331 DVLOG(1) << "ReleaseFFmpegResources()"; | 403 DVLOG(1) << "ReleaseFFmpegResources()"; |
| 332 | 404 |
| 333 if (codec_context_) { | 405 if (codec_context_) { |
| 334 av_free(codec_context_->extradata); | 406 av_free(codec_context_->extradata); |
| 335 avcodec_close(codec_context_); | 407 avcodec_close(codec_context_); |
| 336 av_free(codec_context_); | 408 av_free(codec_context_); |
| 337 codec_context_ = NULL; | 409 codec_context_ = NULL; |
| 338 } | 410 } |
| 339 if (av_frame_) { | 411 if (av_frame_) { |
| 340 av_free(av_frame_); | 412 av_free(av_frame_); |
| 341 av_frame_ = NULL; | 413 av_frame_ = NULL; |
| 342 } | 414 } |
| 343 } | 415 } |
| 344 | 416 |
| 417 bool FFmpegCdmVideoDecoder::InitializeFFmpeg( | |
| 418 const cdm::VideoDecoderConfig& config) { | |
| 419 // Initialize AVCodecContext structure. | |
| 420 codec_context_ = avcodec_alloc_context3(NULL); | |
| 421 CdmVideoDecoderConfigToAVCodecContext(config, codec_context_); | |
| 422 | |
| 423 // Enable motion vector search (potentially slow), strong deblocking filter | |
| 424 // for damaged macroblocks, and set our error detection sensitivity. | |
| 425 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
| 426 codec_context_->err_recognition = AV_EF_CAREFUL; | |
| 427 codec_context_->thread_count = kDecodeThreads; | |
| 428 codec_context_->opaque = this; | |
| 429 codec_context_->flags |= CODEC_FLAG_EMU_EDGE; | |
| 430 | |
| 431 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
| 432 if (!codec) { | |
| 433 LOG(ERROR) << "InitializeFFmpeg(): avcodec_find_decoder failed."; | |
| 434 return false; | |
| 435 } | |
| 436 | |
| 437 int status; | |
| 438 if ((status = avcodec_open2(codec_context_, codec, NULL)) < 0) { | |
| 439 LOG(ERROR) << "InitializeFFmpeg(): avcodec_open2 failed: " << status; | |
| 440 return false; | |
| 441 } | |
| 442 | |
| 443 av_frame_ = avcodec_alloc_frame(); | |
| 444 is_initialized_ = true; | |
| 445 | |
| 446 return true; | |
| 447 } | |
| 448 | |
| 449 bool FFmpegCdmVideoDecoder::InitializeLibvpx( | |
| 450 const cdm::VideoDecoderConfig& config) { | |
| 451 vpx_codec_ = new vpx_codec_ctx_t(); | |
| 452 vpx_codec_dec_cfg_t vpx_config = {0}; | |
| 453 vpx_config.w = config.coded_size.width; | |
| 454 vpx_config.h = config.coded_size.height; | |
| 455 vpx_config.threads = kDecodeThreads; | |
| 456 | |
| 457 vpx_codec_err_t status = vpx_codec_dec_init(vpx_codec_, | |
| 458 vpx_codec_vp8_dx(), | |
| 459 &vpx_config, | |
| 460 0); | |
| 461 if (status != VPX_CODEC_OK) { | |
| 462 LOG(ERROR) << "InitializeLibvpx(): vpx_codec_dec_init failed, ret=" | |
| 463 << status; | |
| 464 delete vpx_codec_; | |
| 465 vpx_codec_ = NULL; | |
| 466 } | |
| 467 | |
| 468 is_initialized_ = true; | |
| 469 return true; | |
| 470 } | |
| 471 | |
| 472 cdm::Status FFmpegCdmVideoDecoder::DecodeFrameFFmpeg( | |
| 473 const uint8_t* compressed_frame, | |
| 474 int32_t compressed_frame_size, | |
| 475 int64_t timestamp, | |
| 476 cdm::VideoFrame* decoded_frame) { | |
| 477 DVLOG(1) << "DecodeFrameFFmpeg()"; | |
| 478 | |
| 479 // Create a packet for input data. | |
| 480 AVPacket packet; | |
| 481 av_init_packet(&packet); | |
| 482 | |
| 483 // The FFmpeg API does not allow us to have const read-only pointers. | |
| 484 packet.data = const_cast<uint8_t*>(compressed_frame); | |
| 485 packet.size = compressed_frame_size; | |
| 486 | |
| 487 // Let FFmpeg handle presentation timestamp reordering. | |
| 488 codec_context_->reordered_opaque = timestamp; | |
| 489 | |
| 490 // Reset frame to default values. | |
| 491 avcodec_get_frame_defaults(av_frame_); | |
| 492 | |
| 493 // This is for codecs not using get_buffer to initialize | |
| 494 // |av_frame_->reordered_opaque| | |
| 495 av_frame_->reordered_opaque = codec_context_->reordered_opaque; | |
| 496 | |
| 497 int frame_decoded = 0; | |
| 498 int result = avcodec_decode_video2(codec_context_, | |
| 499 av_frame_, | |
| 500 &frame_decoded, | |
| 501 &packet); | |
| 502 // Log the problem when we can't decode a video frame and exit early. | |
| 503 if (result < 0) { | |
| 504 LOG(ERROR) << "DecodeFrameFFmpeg(): Error decoding video frame with " | |
| 505 << "timestamp: " << timestamp << " us, packet size: " | |
| 506 << packet.size << " bytes"; | |
| 507 return cdm::kDecodeError; | |
| 508 } | |
| 509 | |
| 510 // If no frame was produced then signal that more data is required to produce | |
| 511 // more frames. | |
| 512 if (frame_decoded == 0) | |
| 513 return cdm::kNeedMoreData; | |
| 514 | |
| 515 // The decoder is in a bad state and not decoding correctly. | |
| 516 // Checking for NULL avoids a crash. | |
| 517 if (!av_frame_->data[cdm::VideoFrame::kYPlane] || | |
| 518 !av_frame_->data[cdm::VideoFrame::kUPlane] || | |
| 519 !av_frame_->data[cdm::VideoFrame::kVPlane]) { | |
| 520 LOG(ERROR) << "DecodeFrameFFmpeg(): Video frame has invalid frame data."; | |
| 521 return cdm::kDecodeError; | |
| 522 } | |
| 523 | |
| 524 if (!CopyAvFrameTo(decoded_frame)) { | |
| 525 LOG(ERROR) << "DecodeFrameFFmpeg() could not copy video frame to output " | |
| 526 << "buffer."; | |
| 527 return cdm::kDecodeError; | |
| 528 } | |
| 529 | |
| 530 return cdm::kSuccess; | |
| 531 } | |
| 532 | |
| 533 cdm::Status FFmpegCdmVideoDecoder::DecodeFrameLibvpx( | |
| 534 const uint8_t* compressed_frame, | |
| 535 int32_t compressed_frame_size, | |
| 536 int64_t timestamp, | |
| 537 cdm::VideoFrame* decoded_frame) { | |
| 538 DVLOG(1) << "DecodeFrameLibvpx()"; | |
| 539 | |
| 540 // Pass |compressed_frame| to libvpx. | |
| 541 void* user_priv = reinterpret_cast<void*>(×tamp); | |
| 542 vpx_codec_err_t status = vpx_codec_decode(vpx_codec_, | |
| 543 compressed_frame, | |
| 544 compressed_frame_size, | |
| 545 user_priv, | |
| 546 0); | |
| 547 if (status != VPX_CODEC_OK) { | |
| 548 LOG(ERROR) << "DecodeFrameLibvpx(): vpx_codec_decode failed, status=" | |
| 549 << status; | |
| 550 return cdm::kDecodeError; | |
| 551 } | |
| 552 | |
| 553 // Gets pointer to decoded data. | |
| 554 vpx_codec_iter_t iter = NULL; | |
| 555 vpx_image_ = vpx_codec_get_frame(vpx_codec_, &iter); | |
| 556 if (!vpx_image_) | |
| 557 return cdm::kNeedMoreData; | |
| 558 | |
| 559 if (vpx_image_->user_priv != reinterpret_cast<void*>(×tamp)) { | |
| 560 LOG(ERROR) << "DecodeFrameLibvpx() invalid output timestamp."; | |
| 561 return cdm::kDecodeError; | |
| 562 } | |
| 563 decoded_frame->set_timestamp(timestamp); | |
| 564 | |
| 565 if (!CopyVpxImageTo(decoded_frame)) { | |
| 566 LOG(ERROR) << "DecodeFrameLibvpx() could not copy vpx image to output " | |
| 567 << "buffer."; | |
| 568 return cdm::kDecodeError; | |
| 569 } | |
| 570 | |
|
Tom Finegan
2012/11/16 10:29:54
Oops... I'll fix this whitespace.
| |
| 571 | |
| 572 | |
| 573 | |
| 574 | |
| 575 return cdm::kSuccess; | |
| 576 } | |
| 577 | |
| 345 } // namespace webkit_media | 578 } // namespace webkit_media |
| OLD | NEW |