Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 "media/video/ffmpeg_video_decode_engine.h" | 5 #include "media/video/ffmpeg_video_decode_engine.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/logging.h" | |
| 8 #include "base/string_number_conversions.h" | 9 #include "base/string_number_conversions.h" |
| 9 #include "base/task.h" | |
| 10 #include "media/base/buffers.h" | 10 #include "media/base/buffers.h" |
| 11 #include "media/base/limits.h" | |
| 12 #include "media/base/media_switches.h" | 11 #include "media/base/media_switches.h" |
| 13 #include "media/base/pipeline.h" | 12 #include "media/base/video_decoder_config.h" |
| 14 #include "media/base/video_util.h" | 13 #include "media/base/video_util.h" |
| 15 #include "media/ffmpeg/ffmpeg_common.h" | 14 #include "media/ffmpeg/ffmpeg_common.h" |
| 16 | 15 |
| 17 namespace media { | 16 namespace media { |
| 18 | 17 |
| 19 FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() | 18 FFmpegVideoDecodeEngine::FFmpegVideoDecodeEngine() |
| 20 : codec_context_(NULL), | 19 : codec_context_(NULL), |
| 21 event_handler_(NULL), | 20 av_frame_(NULL), |
| 22 frame_rate_numerator_(0), | 21 frame_rate_numerator_(0), |
| 23 frame_rate_denominator_(0), | 22 frame_rate_denominator_(0) { |
| 24 pending_input_buffers_(0), | |
| 25 pending_output_buffers_(0), | |
| 26 output_eos_reached_(false), | |
| 27 flush_pending_(false) { | |
| 28 } | 23 } |
| 29 | 24 |
| 30 FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { | 25 FFmpegVideoDecodeEngine::~FFmpegVideoDecodeEngine() { |
| 31 if (codec_context_) { | 26 Uninitialize(); |
| 32 av_free(codec_context_->extradata); | |
| 33 avcodec_close(codec_context_); | |
| 34 av_free(codec_context_); | |
| 35 } | |
| 36 } | 27 } |
| 37 | 28 |
| 38 void FFmpegVideoDecodeEngine::Initialize( | 29 bool FFmpegVideoDecodeEngine::Initialize(const VideoDecoderConfig& config) { |
| 39 VideoDecodeEngine::EventHandler* event_handler, | |
| 40 const VideoDecoderConfig& config) { | |
| 41 frame_rate_numerator_ = config.frame_rate_numerator(); | 30 frame_rate_numerator_ = config.frame_rate_numerator(); |
| 42 frame_rate_denominator_ = config.frame_rate_denominator(); | 31 frame_rate_denominator_ = config.frame_rate_denominator(); |
| 43 | 32 |
| 44 // Always try to use three threads for video decoding. There is little reason | 33 // Always try to use three threads for video decoding. There is little reason |
| 45 // not to since current day CPUs tend to be multi-core and we measured | 34 // not to since current day CPUs tend to be multi-core and we measured |
| 46 // performance benefits on older machines such as P4s with hyperthreading. | 35 // performance benefits on older machines such as P4s with hyperthreading. |
| 47 // | 36 // |
| 48 // Handling decoding on separate threads also frees up the pipeline thread to | 37 // Handling decoding on separate threads also frees up the pipeline thread to |
| 49 // continue processing. Although it'd be nice to have the option of a single | 38 // continue processing. Although it'd be nice to have the option of a single |
| 50 // decoding thread, FFmpeg treats having one thread the same as having zero | 39 // decoding thread, FFmpeg treats having one thread the same as having zero |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 76 !base::StringToInt(threads, &decode_threads)) || | 65 !base::StringToInt(threads, &decode_threads)) || |
| 77 decode_threads < 0 || decode_threads > kMaxDecodeThreads) { | 66 decode_threads < 0 || decode_threads > kMaxDecodeThreads) { |
| 78 decode_threads = kDecodeThreads; | 67 decode_threads = kDecodeThreads; |
| 79 } | 68 } |
| 80 | 69 |
| 81 codec_context_->thread_count = decode_threads; | 70 codec_context_->thread_count = decode_threads; |
| 82 | 71 |
| 83 // We don't allocate AVFrame on the stack since different versions of FFmpeg | 72 // We don't allocate AVFrame on the stack since different versions of FFmpeg |
| 84 // may change the size of AVFrame, causing stack corruption. The solution is | 73 // may change the size of AVFrame, causing stack corruption. The solution is |
| 85 // to let FFmpeg allocate the structure via avcodec_alloc_frame(). | 74 // to let FFmpeg allocate the structure via avcodec_alloc_frame(). |
| 86 av_frame_.reset(avcodec_alloc_frame()); | 75 av_frame_ = avcodec_alloc_frame(); |
| 87 | |
| 88 // If we do not have enough buffers, we will report error too. | |
| 89 frame_queue_available_.clear(); | |
| 90 | |
| 91 // Create output buffer pool when direct rendering is not used. | |
| 92 for (size_t i = 0; i < Limits::kMaxVideoFrames; ++i) { | |
| 93 VideoFrame::Format format = | |
| 94 PixelFormatToVideoFormat(codec_context_->pix_fmt); | |
| 95 | |
| 96 scoped_refptr<VideoFrame> video_frame = | |
| 97 VideoFrame::CreateFrame(format, | |
| 98 config.visible_rect().width(), | |
| 99 config.visible_rect().height(), | |
| 100 kNoTimestamp, | |
| 101 kNoTimestamp); | |
| 102 frame_queue_available_.push_back(video_frame); | |
| 103 } | |
| 104 | 76 |
| 105 // Open the codec! | 77 // Open the codec! |
| 106 bool success = codec && avcodec_open(codec_context_, codec) >= 0; | 78 return codec && avcodec_open(codec_context_, codec) >= 0; |
| 107 event_handler_ = event_handler; | |
| 108 event_handler_->OnInitializeComplete(success); | |
| 109 } | 79 } |
| 110 | 80 |
| 111 void FFmpegVideoDecodeEngine::ConsumeVideoSample( | 81 void FFmpegVideoDecodeEngine::Uninitialize() { |
| 112 scoped_refptr<Buffer> buffer) { | 82 if (codec_context_) { |
| 113 pending_input_buffers_--; | 83 av_free(codec_context_->extradata); |
| 114 if (flush_pending_) { | 84 avcodec_close(codec_context_); |
| 115 TryToFinishPendingFlush(); | 85 av_free(codec_context_); |
| 116 } else { | 86 codec_context_ = NULL; |
| 117 // Otherwise try to decode this buffer. | |
| 118 DecodeFrame(buffer); | |
| 119 } | 87 } |
| 88 if (av_frame_) { | |
| 89 av_free(av_frame_); | |
| 90 av_frame_ = NULL; | |
| 91 } | |
| 92 frame_rate_numerator_ = 0; | |
| 93 frame_rate_denominator_ = 0; | |
| 120 } | 94 } |
| 121 | 95 |
| 122 void FFmpegVideoDecodeEngine::ProduceVideoFrame( | 96 bool FFmpegVideoDecodeEngine::Decode(scoped_refptr<Buffer> buffer, |
| 123 scoped_refptr<VideoFrame> frame) { | 97 scoped_refptr<VideoFrame>* video_frame) { |
| 124 // We should never receive NULL frame or EOS frame. | 98 DCHECK(video_frame); |
| 125 DCHECK(frame.get() && !frame->IsEndOfStream()); | |
| 126 | |
| 127 // Increment pending output buffer count. | |
| 128 pending_output_buffers_++; | |
| 129 | |
| 130 // Return this frame to available pool after display. | |
| 131 frame_queue_available_.push_back(frame); | |
| 132 | |
| 133 if (flush_pending_) { | |
| 134 TryToFinishPendingFlush(); | |
| 135 } else if (!output_eos_reached_) { | |
| 136 // If we already deliver EOS to renderer, we stop reading new input. | |
| 137 ReadInput(); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // Try to decode frame when both input and output are ready. | |
| 142 void FFmpegVideoDecodeEngine::DecodeFrame(scoped_refptr<Buffer> buffer) { | |
| 143 scoped_refptr<VideoFrame> video_frame; | |
| 144 | 99 |
| 145 // Create a packet for input data. | 100 // Create a packet for input data. |
| 146 // Due to FFmpeg API changes we no longer have const read-only pointers. | 101 // Due to FFmpeg API changes we no longer have const read-only pointers. |
| 147 AVPacket packet; | 102 AVPacket packet; |
| 148 av_init_packet(&packet); | 103 av_init_packet(&packet); |
| 149 packet.data = const_cast<uint8*>(buffer->GetData()); | 104 packet.data = const_cast<uint8*>(buffer->GetData()); |
| 150 packet.size = buffer->GetDataSize(); | 105 packet.size = buffer->GetDataSize(); |
| 151 | 106 |
| 152 PipelineStatistics statistics; | |
| 153 statistics.video_bytes_decoded = buffer->GetDataSize(); | |
| 154 | |
| 155 // Let FFmpeg handle presentation timestamp reordering. | 107 // Let FFmpeg handle presentation timestamp reordering. |
| 156 codec_context_->reordered_opaque = buffer->GetTimestamp().InMicroseconds(); | 108 codec_context_->reordered_opaque = buffer->GetTimestamp().InMicroseconds(); |
| 157 | 109 |
| 158 // This is for codecs not using get_buffer to initialize | 110 // This is for codecs not using get_buffer to initialize |
| 159 // |av_frame_->reordered_opaque| | 111 // |av_frame_->reordered_opaque| |
| 160 av_frame_->reordered_opaque = codec_context_->reordered_opaque; | 112 av_frame_->reordered_opaque = codec_context_->reordered_opaque; |
| 161 | 113 |
| 162 int frame_decoded = 0; | 114 int frame_decoded = 0; |
| 163 int result = avcodec_decode_video2(codec_context_, | 115 int result = avcodec_decode_video2(codec_context_, |
| 164 av_frame_.get(), | 116 av_frame_, |
| 165 &frame_decoded, | 117 &frame_decoded, |
| 166 &packet); | 118 &packet); |
| 167 // Log the problem if we can't decode a video frame and exit early. | 119 // Log the problem if we can't decode a video frame and exit early. |
| 168 if (result < 0) { | 120 if (result < 0) { |
| 169 LOG(ERROR) << "Error decoding a video frame with timestamp: " | 121 LOG(ERROR) << "Error decoding a video frame with timestamp: " |
| 170 << buffer->GetTimestamp().InMicroseconds() << " us, duration: " | 122 << buffer->GetTimestamp().InMicroseconds() << " us, duration: " |
| 171 << buffer->GetDuration().InMicroseconds() << " us, packet size: " | 123 << buffer->GetDuration().InMicroseconds() << " us, packet size: " |
| 172 << buffer->GetDataSize() << " bytes"; | 124 << buffer->GetDataSize() << " bytes"; |
| 173 event_handler_->OnError(); | 125 *video_frame = NULL; |
| 174 return; | 126 return false; |
| 175 } | 127 } |
| 176 | 128 |
| 177 // If frame_decoded == 0, then no frame was produced. | 129 // If no frame was produced then signal that more data is required to |
| 178 // In this case, if we already begin to flush codec with empty | 130 // produce more frames. This can happen under two circumstances: |
| 179 // input packet at the end of input stream, the first time we | 131 // 1) Decoder was recently initialized/flushed |
| 180 // encounter frame_decoded == 0 signal output frame had been | 132 // 2) End of stream was reached and all internal frames have been output |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
I worry that these are things a client needs to kn
scherkus (not reviewing)
2011/11/03 04:55:59
I thought I had that covered with the current docu
Ami GONE FROM CHROMIUM
2011/11/03 16:40:00
Before I was thinking you'd move this comment to t
| |
| 181 // drained, we mark the flag. Otherwise we read from demuxer again. | |
| 182 if (frame_decoded == 0) { | 133 if (frame_decoded == 0) { |
| 183 if (buffer->IsEndOfStream()) { // We had started flushing. | 134 *video_frame = NULL; |
| 184 event_handler_->ConsumeVideoFrame(video_frame, statistics); | 135 return true; |
| 185 output_eos_reached_ = true; | |
| 186 } else { | |
| 187 ReadInput(); | |
| 188 } | |
| 189 return; | |
| 190 } | 136 } |
| 191 | 137 |
| 192 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 | 138 // TODO(fbarchard): Work around for FFmpeg http://crbug.com/27675 |
| 193 // The decoder is in a bad state and not decoding correctly. | 139 // The decoder is in a bad state and not decoding correctly. |
| 194 // Checking for NULL avoids a crash in CopyPlane(). | 140 // Checking for NULL avoids a crash in CopyPlane(). |
| 195 if (!av_frame_->data[VideoFrame::kYPlane] || | 141 if (!av_frame_->data[VideoFrame::kYPlane] || |
| 196 !av_frame_->data[VideoFrame::kUPlane] || | 142 !av_frame_->data[VideoFrame::kUPlane] || |
| 197 !av_frame_->data[VideoFrame::kVPlane]) { | 143 !av_frame_->data[VideoFrame::kVPlane]) { |
| 198 event_handler_->OnError(); | 144 LOG(ERROR) << "Video frame was produced yet has invalid frame data."; |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
*video_frame=NULL;
?
| |
| 199 return; | 145 return false; |
| 200 } | 146 } |
| 201 | 147 |
| 148 // We've got a frame! Make sure we have a place to store it. | |
| 149 ReallocateVideoFrameIfNecessary(video_frame); | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
If you take my suggestion about INVALID below, mak
| |
| 150 | |
| 202 // Determine timestamp and calculate the duration based on the repeat picture | 151 // Determine timestamp and calculate the duration based on the repeat picture |
| 203 // count. According to FFmpeg docs, the total duration can be calculated as | 152 // count. According to FFmpeg docs, the total duration can be calculated as |
| 204 // follows: | 153 // follows: |
| 205 // fps = 1 / time_base | 154 // fps = 1 / time_base |
| 206 // | 155 // |
| 207 // duration = (1 / fps) + (repeat_pict) / (2 * fps) | 156 // duration = (1 / fps) + (repeat_pict) / (2 * fps) |
| 208 // = (2 + repeat_pict) / (2 * fps) | 157 // = (2 + repeat_pict) / (2 * fps) |
| 209 // = (2 + repeat_pict) / (2 * (1 / time_base)) | 158 // = (2 + repeat_pict) / (2 * (1 / time_base)) |
| 210 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check. | 159 DCHECK_LE(av_frame_->repeat_pict, 2); // Sanity check. |
| 211 AVRational doubled_time_base; | 160 AVRational doubled_time_base; |
| 212 doubled_time_base.num = frame_rate_denominator_; | 161 doubled_time_base.num = frame_rate_denominator_; |
| 213 doubled_time_base.den = frame_rate_numerator_ * 2; | 162 doubled_time_base.den = frame_rate_numerator_ * 2; |
| 214 | 163 |
| 215 base::TimeDelta timestamp = | 164 (*video_frame)->SetTimestamp( |
| 216 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque); | 165 base::TimeDelta::FromMicroseconds(av_frame_->reordered_opaque)); |
| 217 base::TimeDelta duration = | 166 (*video_frame)->SetDuration( |
| 218 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict); | 167 ConvertFromTimeBase(doubled_time_base, 2 + av_frame_->repeat_pict)); |
| 219 | |
| 220 // Available frame is guaranteed, because we issue as much reads as | |
| 221 // available frame, except the case of |frame_decoded| == 0, which | |
| 222 // implies decoder order delay, and force us to read more inputs. | |
| 223 DCHECK(frame_queue_available_.size()); | |
| 224 video_frame = frame_queue_available_.front(); | |
| 225 frame_queue_available_.pop_front(); | |
| 226 | 168 |
| 227 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame | 169 // Copy the frame data since FFmpeg reuses internal buffers for AVFrame |
| 228 // output, meaning the data is only valid until the next | 170 // output, meaning the data is only valid until the next |
| 229 // avcodec_decode_video() call. | 171 // avcodec_decode_video() call. |
| 230 // | |
| 231 // TODO(scherkus): use VideoFrame dimensions instead and re-allocate | |
| 232 // VideoFrame if dimensions changes, but for now adjust size locally. | |
| 233 int y_rows = codec_context_->height; | 172 int y_rows = codec_context_->height; |
| 234 int uv_rows = codec_context_->height; | 173 int uv_rows = codec_context_->height; |
| 235 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { | 174 if (codec_context_->pix_fmt == PIX_FMT_YUV420P) { |
| 236 uv_rows /= 2; | 175 uv_rows /= 2; |
| 237 } | 176 } |
| 238 | 177 |
| 239 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, video_frame); | 178 CopyYPlane(av_frame_->data[0], av_frame_->linesize[0], y_rows, *video_frame); |
| 240 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, video_frame); | 179 CopyUPlane(av_frame_->data[1], av_frame_->linesize[1], uv_rows, *video_frame); |
| 241 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, video_frame); | 180 CopyVPlane(av_frame_->data[2], av_frame_->linesize[2], uv_rows, *video_frame); |
| 242 | 181 |
| 243 video_frame->SetTimestamp(timestamp); | 182 return true; |
| 244 video_frame->SetDuration(duration); | |
| 245 | |
| 246 pending_output_buffers_--; | |
| 247 event_handler_->ConsumeVideoFrame(video_frame, statistics); | |
| 248 } | |
| 249 | |
| 250 void FFmpegVideoDecodeEngine::Uninitialize() { | |
| 251 event_handler_->OnUninitializeComplete(); | |
| 252 } | 183 } |
| 253 | 184 |
| 254 void FFmpegVideoDecodeEngine::Flush() { | 185 void FFmpegVideoDecodeEngine::Flush() { |
| 255 avcodec_flush_buffers(codec_context_); | 186 avcodec_flush_buffers(codec_context_); |
| 256 flush_pending_ = true; | |
| 257 TryToFinishPendingFlush(); | |
| 258 } | 187 } |
| 259 | 188 |
| 260 void FFmpegVideoDecodeEngine::TryToFinishPendingFlush() { | 189 void FFmpegVideoDecodeEngine::ReallocateVideoFrameIfNecessary( |
| 261 DCHECK(flush_pending_); | 190 scoped_refptr<VideoFrame>* video_frame) { |
| 191 VideoFrame::Format format = PixelFormatToVideoFormat(codec_context_->pix_fmt); | |
|
Ami GONE FROM CHROMIUM
2011/11/01 22:17:40
If you checked for VideoFrame::INVALID here and re
| |
| 192 size_t width = codec_context_->width; | |
| 193 size_t height = codec_context_->height; | |
| 262 | 194 |
| 263 // We consider ourself flushed when there is no pending input buffers | 195 // Check for format and visible dimension match. |
| 264 // and output buffers, which implies that all buffers had been returned | 196 if (video_frame && (*video_frame) && |
| 265 // to its owner. | 197 (*video_frame)->format() == format && |
| 266 if (!pending_input_buffers_ && !pending_output_buffers_) { | 198 (*video_frame)->width() == width && |
| 267 // Try to finish flushing and notify pipeline. | 199 (*video_frame)->height() == height) { |
| 268 flush_pending_ = false; | 200 return; |
| 269 event_handler_->OnFlushComplete(); | |
| 270 } | 201 } |
| 271 } | |
| 272 | 202 |
| 273 void FFmpegVideoDecodeEngine::Seek() { | 203 *video_frame = VideoFrame::CreateFrame(format, width, height, |
| 274 // After a seek, output stream no longer considered as EOS. | 204 kNoTimestamp, kNoTimestamp); |
| 275 output_eos_reached_ = false; | |
| 276 | |
| 277 // The buffer provider is assumed to perform pre-roll operation. | |
| 278 for (unsigned int i = 0; i < Limits::kMaxVideoFrames; ++i) | |
| 279 ReadInput(); | |
| 280 | |
| 281 event_handler_->OnSeekComplete(); | |
| 282 } | |
| 283 | |
| 284 void FFmpegVideoDecodeEngine::ReadInput() { | |
| 285 DCHECK_EQ(output_eos_reached_, false); | |
| 286 pending_input_buffers_++; | |
| 287 event_handler_->ProduceVideoSample(NULL); | |
| 288 } | 205 } |
| 289 | 206 |
| 290 } // namespace media | 207 } // namespace media |
| 291 | |
| 292 // Disable refcounting for this object because this object only lives | |
| 293 // on the video decoder thread and there's no need to refcount it. | |
| 294 DISABLE_RUNNABLE_METHOD_REFCOUNT(media::FFmpegVideoDecodeEngine); | |
| OLD | NEW |