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

Side by Side Diff: media/video/ffmpeg_video_decode_engine.cc

Issue 8417019: Simplify VideoDecodeEngine interface by making everything synchronous. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: recycling: vanquished Created 9 years, 1 month 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 | Annotate | Revision Log
OLDNEW
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
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);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698