| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/video/ffmpeg_video_decode_engine.h" | |
| 6 | |
| 7 #include "base/command_line.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/string_number_conversions.h" | |
| 10 #include "media/base/buffers.h" | |
| 11 #include "media/base/media_switches.h" | |
| 12 #include "media/base/video_decoder_config.h" | |
| 13 #include "media/base/video_util.h" | |
| 14 #include "media/ffmpeg/ffmpeg_common.h" | |
| 15 | |
| 16 namespace media { | |
| 17 | |
| 18 FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() | |
| 19 : codec_context_(NULL), | |
| 20 av_frame_(NULL), | |
| 21 frame_rate_numerator_(0), | |
| 22 frame_rate_denominator_(0) { | |
| 23 } | |
| 24 | |
| 25 FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { | |
| 26 Uninitialize(); | |
| 27 } | |
| 28 | |
| 29 bool FFmpegVideoDecodeEngine::Initialize(const VideoDecoderConfig& config) { | |
| 30 frame_rate_numerator_ = config.frame_rate_numerator(); | |
| 31 frame_rate_denominator_ = config.frame_rate_denominator(); | |
| 32 | |
| 33 // Always try to use three threads for video decoding. There is little reason | |
| 34 // not to since current day CPUs tend to be multi-core and we measured | |
| 35 // performance benefits on older machines such as P4s with hyperthreading. | |
| 36 // | |
| 37 // Handling decoding on separate threads also frees up the pipeline thread to | |
| 38 // continue processing. Although it'd be nice to have the option of a single | |
| 39 // decoding thread, FFmpeg treats having one thread the same as having zero | |
| 40 // threads (i.e., avcodec_decode_video() will execute on the calling thread). | |
| 41 // Yet another reason for having two threads :) | |
| 42 static const int kDecodeThreads = 2; | |
| 43 static const int kMaxDecodeThreads = 16; | |
| 44 | |
| 45 // Initialize AVCodecContext structure. | |
| 46 codec_context_ = avcodec_alloc_context(); | |
| 47 VideoDecoderConfigToAVCodecContext(config, codec_context_); | |
| 48 | |
| 49 // Enable motion vector search (potentially slow), strong deblocking filter | |
| 50 // for damaged macroblocks, and set our error detection sensitivity. | |
| 51 codec_context_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK; | |
| 52 codec_context_->error_recognition = FF_ER_CAREFUL; | |
| 53 | |
| 54 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | |
| 55 | |
| 56 // TODO(fbarchard): Improve thread logic based on size / codec. | |
| 57 // TODO(fbarchard): Fix bug affecting video-cookie.html | |
| 58 // 07/21/11(ihf): Still about 20 failures when enabling. | |
| 59 int decode_threads = (codec_context_->codec_id == CODEC_ID_THEORA) ? | |
| 60 1 : kDecodeThreads; | |
| 61 | |
| 62 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | |
| 63 std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads)); | |
| 64 if ((!threads.empty() && | |
| 65 !base::StringToInt(threads, &decode_threads)) || | |
| 66 decode_threads < 0 || decode_threads > kMaxDecodeThreads) { | |
| 67 decode_threads = kDecodeThreads; | |
| 68 } | |
| 69 | |
| 70 codec_context_->thread_count = decode_threads; | |
| 71 | |
| 72 av_frame_ = avcodec_alloc_frame(); | |
| 73 | |
| 74 // Open the codec! | |
| 75 return codec && avcodec_open(codec_context_, codec) >= 0; | |
| 76 } | |
| 77 | |
| 78 void FFmpegVideoDecodeEngine::Uninitialize() { | |
| 79 if (codec_context_) { | |
| 80 av_free(codec_context_->extradata); | |
| 81 avcodec_close(codec_context_); | |
| 82 av_free(codec_context_); | |
| 83 codec_context_ = NULL; | |
| 84 } | |
| 85 if (av_frame_) { | |
| 86 av_free(av_frame_); | |
| 87 av_frame_ = NULL; | |
| 88 } | |
| 89 frame_rate_numerator_ = 0; | |
| 90 frame_rate_denominator_ = 0; | |
| 91 } | |
| 92 | |
| 93 bool FFmpegVideoDecodeEngine::Decode(const scoped_refptr<Buffer>& buffer, | |
| 94 scoped_refptr<VideoFrame>* video_frame) { | |
| 95 DCHECK(video_frame); | |
| 96 | |
| 97 // Create a packet for input data. | |
| 98 // Due to FFmpeg API changes we no longer have const read-only pointers. | |
| 99 AVPacket packet; | |
| 100 av_init_packet(&packet); | |
| 101 packet.data = const_cast<uint8*>(buffer->GetData()); | |
| 102 packet.size = buffer->GetDataSize(); | |
| 103 | |
| 104 // Let FFmpeg handle presentation timestamp reordering. | |
| 105 codec_context_->reordered_opaque = buffer->GetTimestamp().InMicroseconds(); | |
| 106 | |
| 107 // This is for codecs not using get_buffer to initialize | |
| 108 // |av_frame_->reordered_opaque| | |
| 109 av_frame_->reordered_opaque = codec_context_->reordered_opaque; | |
| 110 | |
| 111 int frame_decoded = 0; | |
| 112 int result = avcodec_decode_video2(codec_context_, | |
| 113 av_frame_, | |
| 114 &frame_decoded, | |
| 115 &packet); | |
| 116 // Log the problem if we can't decode a video frame and exit early. | |
| 117 if (result < 0) { | |
| 118 LOG(ERROR) << "Error decoding a video frame with timestamp: " | |
| 119 << buffer->GetTimestamp().InMicroseconds() << " us, duration: " | |
| 120 << buffer->GetDuration().InMicroseconds() << " us, packet size: " | |
| 121 << buffer->GetDataSize() << " bytes"; | |
| 122 *video_frame = NULL; | |
| 123 return false; | |
| 124 } | |
| 125 | |
| 126 // If no frame was produced then signal that more data is required to | |
| 127 // produce more frames. This can happen under two circumstances: | |
| 128 // 1) Decoder was recently initialized/flushed | |
| 129 // 2) End of stream was reached and all internal frames have been output | |
| 130 if (frame_decoded == 0) { | |
| 131 *video_frame = NULL; | |
| 132 return true; | |
| 133 } | |
| 134 | |
| 135 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 | |
| 136 // The decoder is in a bad state and not decoding correctly. | |
| 137 // Checking for NULL avoids a crash in CopyPlane(). | |
| 138 if (!av_frame_->data[VideoFrame::kYPlane] || | |
| 139 !av_frame_->data[VideoFrame::kUPlane] || | |
| 140 !av_frame_->data[VideoFrame::kVPlane]) { | |
| 141 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; | |
| 142 *video_frame = NULL; | |
| 143 return false; | |
| 144 } | |
| 145 | |
| 146 // We've got a frame! Make sure we have a place to store it. | |
| 147 *video_frame = AllocateVideoFrame(); | |
| 148 if (!(*video_frame)) { | |
| 149 LOG(ERROR) << "Failed to allocate video frame"; | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 // Determine timestamp and calculate the duration based on the repeat picture | |
| 154 // count. According to FFmpeg docs, the total duration can be calculated as | |
| 155 // follows: | |
| 156 // fps = 1 / time_base | |
| 157 // | |
| 158 // duration = (1 / fps) + (repeat_pict) / (2 * fps) | |
| 159 // = (2 + repeat_pict) / (2 * fps) | |
| 160 // = (2 + repeat_pict) / (2 * (1 / time_base)) | |
| 161 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check. | |
| 162 AVRational doubled_time_base; | |
| 163 doubled_time_base.num = frame_rate_denominator_; | |
| 164 doubled_time_base.den = frame_rate_numerator_ * 2; | |
| 165 | |
| 166 (*video_frame)->SetTimestamp( | |
| 167 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); | |
| 168 (*video_frame)->SetDuration( | |
| 169 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict)); | |
| 170 | |
| 171 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame | |
| 172 // output, meaning the data is only valid until the next | |
| 173 // avcodec_decode_video() call. | |
| 174 int y_rows = codec_context_->height; | |
| 175 int uv_rows = codec_context_->height; | |
| 176 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { | |
| 177 uv_rows /= 2; | |
| 178 } | |
| 179 | |
| 180 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame); | |
| 181 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame); | |
| 182 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame); | |
| 183 | |
| 184 return true; | |
| 185 } | |
| 186 | |
| 187 void FFmpegVideoDecodeEngine::Flush() { | |
| 188 avcodec_flush_buffers(codec_context_); | |
| 189 } | |
| 190 | |
| 191 scoped_refptr<VideoFrame> FFmpegVideoDecodeEngine::AllocateVideoFrame() { | |
| 192 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt); | |
| 193 size_t width = codec_context_->width; | |
| 194 size_t height = codec_context_->height; | |
| 195 | |
| 196 return VideoFrame::CreateFrame(format, width, height, | |
| 197 kNoTimestamp, kNoTimestamp); | |
| 198 } | |
| 199 | |
| 200 } // namespace media | |
| OLD | NEW |