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

Side by Side Diff: media/filters/ffmpeg_video_decoder.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/filters/ffmpeg_video_decoder.h" 5 #include "media/filters/ffmpeg_video_decoder.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/message_loop.h" 8 #include "base/message_loop.h"
10 #include "base/task.h"
11 #include "media/base/demuxer_stream.h" 9 #include "media/base/demuxer_stream.h"
12 #include "media/base/filter_host.h" 10 #include "media/base/filter_host.h"
13 #include "media/base/limits.h" 11 #include "media/base/limits.h"
12 #include "media/base/video_decoder_config.h"
14 #include "media/base/video_frame.h" 13 #include "media/base/video_frame.h"
15 #include "media/ffmpeg/ffmpeg_common.h" 14 #include "media/ffmpeg/ffmpeg_common.h"
16 #include "media/video/ffmpeg_video_decode_engine.h" 15 #include "media/video/ffmpeg_video_decode_engine.h"
17 16
18 namespace media { 17 namespace media {
19 18
20 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop) 19 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop)
21 : message_loop_(message_loop), 20 : message_loop_(message_loop),
22 state_(kUnInitialized), 21 state_(kUninitialized),
23 decode_engine_(new FFmpegVideoDecodeEngine()) { 22 decode_engine_(new FFmpegVideoDecodeEngine()) {
24 } 23 }
25 24
26 FFmpegVideoDecoder::~FFmpegVideoDecoder() {} 25 FFmpegVideoDecoder::~FFmpegVideoDecoder() {}
27 26
28 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, 27 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream,
29 const base::Closure& callback, 28 const base::Closure& callback,
30 const StatisticsCallback& stats_callback) { 29 const StatisticsCallback& stats_callback) {
31 if (MessageLoop::current() != message_loop_) { 30 if (MessageLoop::current() != message_loop_) {
32 message_loop_->PostTask( 31 message_loop_->PostTask(FROM_HERE, base::Bind(
33 FROM_HERE, 32 &FFmpegVideoDecoder::Initialize, this,
34 base::Bind(&FFmpegVideoDecoder::Initialize, this, 33 make_scoped_refptr(demuxer_stream), callback, stats_callback));
35 make_scoped_refptr(demuxer_stream),
36 callback, stats_callback));
37 return; 34 return;
38 } 35 }
39 36
40 DCHECK_EQ(MessageLoop::current(), message_loop_); 37 DCHECK_EQ(MessageLoop::current(), message_loop_);
41 DCHECK(!demuxer_stream_); 38 DCHECK(!demuxer_stream_);
42 DCHECK(initialize_callback_.is_null());
43 39
44 if (!demuxer_stream) { 40 if (!demuxer_stream) {
45 host()->SetError(PIPELINE_ERROR_DECODE); 41 host()->SetError(PIPELINE_ERROR_DECODE);
46 callback.Run(); 42 callback.Run();
47 return; 43 return;
48 } 44 }
49 45
50 demuxer_stream_ = demuxer_stream; 46 demuxer_stream_ = demuxer_stream;
51 initialize_callback_ = callback;
52 statistics_callback_ = stats_callback; 47 statistics_callback_ = stats_callback;
53 48
54 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); 49 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config();
55 50
56 pts_stream_.Initialize(GetFrameDuration(config)); 51 pts_stream_.Initialize(GetFrameDuration(config));
57 52
58 natural_size_ = config.natural_size(); 53 natural_size_ = config.natural_size();
59 if (natural_size_.width() > Limits::kMaxDimension || 54 if (natural_size_.width() > Limits::kMaxDimension ||
60 natural_size_.height() > Limits::kMaxDimension || 55 natural_size_.height() > Limits::kMaxDimension ||
61 natural_size_.GetArea() > Limits::kMaxCanvas) { 56 natural_size_.GetArea() > Limits::kMaxCanvas) {
62 OnInitializeComplete(false); 57 host()->SetError(PIPELINE_ERROR_DECODE);
58 callback.Run();
63 return; 59 return;
64 } 60 }
65 61
66 state_ = kInitializing; 62 if (!decode_engine_->Initialize(config)) {
67 decode_engine_->Initialize(this, config); 63 host()->SetError(PIPELINE_ERROR_DECODE);
68 } 64 callback.Run();
65 return;
66 }
69 67
70 void FFmpegVideoDecoder::OnInitializeComplete(bool success) { 68 state_ = kNormal;
71 DCHECK_EQ(MessageLoop::current(), message_loop_); 69 callback.Run();
72 DCHECK(!initialize_callback_.is_null());
73
74 if (success) {
75 state_ = kNormal;
76 } else {
77 host()->SetError(PIPELINE_ERROR_DECODE);
78 }
79 ResetAndRunCB(&initialize_callback_);
80 } 70 }
81 71
82 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { 72 void FFmpegVideoDecoder::Stop(const base::Closure& callback) {
83 if (MessageLoop::current() != message_loop_) { 73 if (MessageLoop::current() != message_loop_) {
84 message_loop_->PostTask(FROM_HERE, base::Bind( 74 message_loop_->PostTask(FROM_HERE, base::Bind(
85 &FFmpegVideoDecoder::Stop, this, callback)); 75 &FFmpegVideoDecoder::Stop, this, callback));
86 return; 76 return;
87 } 77 }
88 78
89 DCHECK_EQ(MessageLoop::current(), message_loop_); 79 DCHECK_EQ(MessageLoop::current(), message_loop_);
90 DCHECK(uninitialize_callback_.is_null());
91 80
92 uninitialize_callback_ = callback; 81 decode_engine_->Uninitialize();
93 if (state_ != kUnInitialized) 82 state_ = kUninitialized;
94 decode_engine_->Uninitialize(); 83 callback.Run();
95 else
96 OnUninitializeComplete();
97 } 84 }
98 85
99 void FFmpegVideoDecoder::OnUninitializeComplete() { 86 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
87 if (MessageLoop::current() != message_loop_) {
88 message_loop_->PostTask(FROM_HERE, base::Bind(
89 &FFmpegVideoDecoder::Seek, this, time, cb));
90 return;
91 }
92
100 DCHECK_EQ(MessageLoop::current(), message_loop_); 93 DCHECK_EQ(MessageLoop::current(), message_loop_);
101 DCHECK(!uninitialize_callback_.is_null());
102 94
103 state_ = kStopped; 95 pts_stream_.Seek(time);
104 96 cb.Run(PIPELINE_OK);
105 ResetAndRunCB(&uninitialize_callback_);
106 } 97 }
107 98
108 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { 99 void FFmpegVideoDecoder::Pause(const base::Closure& callback) {
109 if (MessageLoop::current() != message_loop_) { 100 if (MessageLoop::current() != message_loop_) {
110 message_loop_->PostTask(FROM_HERE, base::Bind( 101 message_loop_->PostTask(FROM_HERE, base::Bind(
111 &FFmpegVideoDecoder::Pause, this, callback)); 102 &FFmpegVideoDecoder::Pause, this, callback));
112 return; 103 return;
113 } 104 }
114 105
115 state_ = kPausing;
116 callback.Run(); 106 callback.Run();
117 } 107 }
118 108
119 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { 109 void FFmpegVideoDecoder::Flush(const base::Closure& callback) {
120 if (MessageLoop::current() != message_loop_) { 110 if (MessageLoop::current() != message_loop_) {
121 message_loop_->PostTask(FROM_HERE, base::Bind( 111 message_loop_->PostTask(FROM_HERE, base::Bind(
122 &FFmpegVideoDecoder::Flush, this, callback)); 112 &FFmpegVideoDecoder::Flush, this, callback));
123 return; 113 return;
124 } 114 }
125 115
126 DCHECK_EQ(MessageLoop::current(), message_loop_); 116 DCHECK_EQ(MessageLoop::current(), message_loop_);
127 DCHECK(flush_callback_.is_null());
128
129 state_ = kFlushing;
130
131 FlushBuffers();
132
133 flush_callback_ = callback;
134 117
135 decode_engine_->Flush(); 118 decode_engine_->Flush();
119 pts_stream_.Flush();
120 state_ = kNormal;
121 callback.Run();
136 } 122 }
137 123
138 void FFmpegVideoDecoder::OnFlushComplete() { 124 void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) {
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 Why have this be a separate function? It makes De
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
139 DCHECK_EQ(MessageLoop::current(), message_loop_); 125 DCHECK_EQ(MessageLoop::current(), message_loop_);
140 DCHECK(!flush_callback_.is_null()); 126 DCHECK_NE(state_, kUninitialized);
141 127 DCHECK_NE(state_, kDecodeFinished);
142 // Everything in the presentation time queue is invalid, clear the queue. 128 DCHECK(!frame_ready_cb_.is_null());
143 pts_stream_.Flush();
144
145 // Mark flush operation had been done.
146 state_ = kNormal;
147
148 ResetAndRunCB(&flush_callback_);
149 }
150
151 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) {
152 if (MessageLoop::current() != message_loop_) {
153 message_loop_->PostTask(FROM_HERE,
154 base::Bind(&FFmpegVideoDecoder::Seek, this,
155 time, cb));
156 return;
157 }
158
159 DCHECK_EQ(MessageLoop::current(), message_loop_);
160 DCHECK(seek_cb_.is_null());
161
162 pts_stream_.Seek(time);
163 seek_cb_ = cb;
164 decode_engine_->Seek();
165 }
166
167 void FFmpegVideoDecoder::OnSeekComplete() {
168 DCHECK_EQ(MessageLoop::current(), message_loop_);
169 DCHECK(!seek_cb_.is_null());
170
171 ResetAndRunCB(&seek_cb_, PIPELINE_OK);
172 }
173
174 void FFmpegVideoDecoder::OnError() {
175 VideoFrameReady(NULL);
176 }
177
178 void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) {
179 scoped_refptr<Buffer> buffer(buffer_in);
180 message_loop_->PostTask(FROM_HERE, base::Bind(
181 &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer));
182 }
183
184 void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) {
185 DCHECK_EQ(MessageLoop::current(), message_loop_);
186 DCHECK_NE(state_, kStopped); // because of Flush() before Stop().
187 129
188 // During decode, because reads are issued asynchronously, it is possible to 130 // During decode, because reads are issued asynchronously, it is possible to
189 // receive multiple end of stream buffers since each read is acked. When the 131 // receive multiple end of stream buffers since each read is acked. When the
190 // first end of stream buffer is read, FFmpeg may still have frames queued 132 // first end of stream buffer is read, FFmpeg may still have frames queued
191 // up in the decoder so we need to go through the decode loop until it stops 133 // up in the decoder so we need to go through the decode loop until it stops
192 // giving sensible data. After that, the decoder should output empty 134 // giving sensible data. After that, the decoder should output empty
193 // frames. There are three states the decoder can be in: 135 // frames. There are three states the decoder can be in:
194 // 136 //
195 // kNormal: This is the starting state. Buffers are decoded. Decode errors 137 // kNormal: This is the starting state. Buffers are decoded. Decode errors
196 // are discarded. 138 // are discarded.
197 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 139 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2
198 // until no more data is returned to flush out remaining 140 // until no more data is returned to flush out remaining
199 // frames. The input buffer is ignored at this point. 141 // frames. The input buffer is ignored at this point.
200 // kDecodeFinished: All calls return empty frames. 142 // kDecodeFinished: All calls return empty frames.
201 // 143 //
202 // These are the possible state transitions. 144 // These are the possible state transitions.
203 // 145 //
204 // kNormal -> kFlushCodec: 146 // kNormal -> kFlushCodec:
205 // When buffer->IsEndOfStream() is first true. 147 // When buffer->IsEndOfStream() is first true.
206 // kNormal -> kDecodeFinished: 148 // kNormal -> kDecodeFinished:
207 // A catastrophic failure occurs, and decoding needs to stop. 149 // A catastrophic failure occurs, and decoding needs to stop.
208 // kFlushCodec -> kDecodeFinished: 150 // kFlushCodec -> kDecodeFinished:
209 // When avcodec_decode_video2() returns 0 data or errors out. 151 // When avcodec_decode_video2() returns 0 data or errors out.
210 // (any state) -> kNormal: 152 // (any state) -> kNormal:
211 // Any time buffer->IsDiscontinuous() is true. 153 // Any time Flush() is called.
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 I'm surprised a Flush() can recover from a "catast
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
212 154
213 // Transition to kFlushCodec on the first end of stream buffer. 155 // Transition to kFlushCodec on the first end of stream buffer.
214 if (state_ == kNormal && buffer->IsEndOfStream()) { 156 if (state_ == kNormal && buffer->IsEndOfStream()) {
215 state_ = kFlushCodec; 157 state_ = kFlushCodec;
216 } 158 }
217 159
218 // Push all incoming timestamps into the priority queue as long as we have 160 // Push all incoming timestamps into the priority queue as long as we have
219 // not yet received an end of stream buffer. It is important that this line 161 // not yet received an end of stream buffer. It is important that this line
220 // stay below the state transition into kFlushCodec done above. 162 // stay below the state transition into kFlushCodec done above.
221 if (state_ == kNormal) { 163 if (state_ == kNormal) {
222 pts_stream_.EnqueuePts(buffer.get()); 164 pts_stream_.EnqueuePts(buffer.get());
223 } 165 }
224 166
225 // Otherwise, attempt to decode a single frame. 167 scoped_refptr<VideoFrame> video_frame;
226 decode_engine_->ConsumeVideoSample(buffer); 168 if (!decode_engine_->Decode(buffer, &video_frame)) {
169 state_ = kDecodeFinished;
170 host()->SetError(PIPELINE_ERROR_DECODE);
171 return;
172 }
173
174 // Any successful decode counts!
175 if (buffer->GetDataSize()) {
176 PipelineStatistics statistics;
177 statistics.video_bytes_decoded = buffer->GetDataSize();
178 statistics_callback_.Run(statistics);
179 }
180
181 // If we didn't get a frame then we've either completely finished decoding or
182 // we need more data.
183 if (!video_frame) {
184 if (state_ == kFlushCodec) {
185 state_ = kDecodeFinished;
186 DeliverFrame(VideoFrame::CreateEmptyFrame());
187 return;
188 }
189
190 ReadFromDemuxerStream();
191 return;
192 }
193
194 // If we got a frame make sure its timestamp is correct before sending it off.
195 pts_stream_.UpdatePtsAndDuration(video_frame.get());
196 video_frame->SetTimestamp(pts_stream_.current_pts());
197 video_frame->SetDuration(pts_stream_.current_duration());
198
199 DeliverFrame(video_frame);
227 } 200 }
228 201
229 void FFmpegVideoDecoder::ProduceVideoFrame( 202 void FFmpegVideoDecoder::Read(const FrameReadyCB& callback) {
230 scoped_refptr<VideoFrame> video_frame) {
231 if (MessageLoop::current() != message_loop_) { 203 if (MessageLoop::current() != message_loop_) {
232 if (state_ != kStopped) { 204 message_loop_->PostTask(FROM_HERE, base::Bind(
233 message_loop_->PostTask(FROM_HERE, base::Bind( 205 &FFmpegVideoDecoder::Read, this, callback));
234 &FFmpegVideoDecoder::ProduceVideoFrame, this, video_frame));
235 }
236 return; 206 return;
237 } 207 }
238 208
239 DCHECK_EQ(MessageLoop::current(), message_loop_); 209 DCHECK_EQ(MessageLoop::current(), message_loop_);
210 CHECK(!callback.is_null());
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 Here and below s/CHECK/DCHECK/?
scherkus (not reviewing) 2011/11/03 04:55:59 maybe... I feel like this is programmer error and
211 CHECK(frame_ready_cb_.is_null()) << "Overlapping decodes are not supported.";
240 212
241 // Synchronized flushing before stop should prevent this. 213 // This can happen during shutdown after Stop() has been called.
242 DCHECK_NE(state_, kStopped); 214 if (state_ == kUninitialized) {
243 215 // XXXXXXX
244 // If the decoding is finished, we just always return empty frames. 216 // just drop the callback? do we want to honour every callback no matter
245 if (state_ == kDecodeFinished) { 217 // what?
scherkus (not reviewing) 2011/11/01 04:18:26 FYI
acolwell GONE FROM CHROMIUM 2011/11/01 18:31:03 Will the caller ever block progress if it doesn't
Ami GONE FROM CHROMIUM 2011/11/01 22:17:40 I say drop the callback.
scherkus (not reviewing) 2011/11/03 04:55:59 Done.
246 // Signal VideoRenderer the end of the stream event. 218 return;
247 VideoFrameReady(VideoFrame::CreateEmptyFrame());
248
249 // Fall through, because we still need to keep record of this frame.
250 } 219 }
251 220
252 // Notify decode engine the available of new frame. 221 // Return empty frames if decoding has finished.
253 decode_engine_->ProduceVideoFrame(video_frame); 222 if (state_ == kDecodeFinished) {
254 } 223 callback.Run(VideoFrame::CreateEmptyFrame());
224 return;
225 }
255 226
256 void FFmpegVideoDecoder::ConsumeVideoFrame( 227 frame_ready_cb_ = callback;
257 scoped_refptr<VideoFrame> video_frame, 228 ReadFromDemuxerStream();
258 const PipelineStatistics& statistics) {
259 DCHECK_EQ(MessageLoop::current(), message_loop_);
260 DCHECK_NE(state_, kStopped);
261
262 statistics_callback_.Run(statistics);
263
264 if (video_frame.get()) {
265 if (kPausing == state_ || kFlushing == state_) {
266 frame_queue_flushed_.push_back(video_frame);
267 if (kFlushing == state_)
268 FlushBuffers();
269 return;
270 }
271
272 // If we actually got data back, enqueue a frame.
273 pts_stream_.UpdatePtsAndDuration(video_frame.get());
274
275 video_frame->SetTimestamp(pts_stream_.current_pts());
276 video_frame->SetDuration(pts_stream_.current_duration());
277
278 VideoFrameReady(video_frame);
279 } else {
280 // When in kFlushCodec, any errored decode, or a 0-lengthed frame,
281 // is taken as a signal to stop decoding.
282 if (state_ == kFlushCodec) {
283 state_ = kDecodeFinished;
284
285 // Signal VideoRenderer the end of the stream event.
286 VideoFrameReady(VideoFrame::CreateEmptyFrame());
287 }
288 }
289 }
290
291 void FFmpegVideoDecoder::ProduceVideoSample(
292 scoped_refptr<Buffer> buffer) {
293 DCHECK_EQ(MessageLoop::current(), message_loop_);
294 DCHECK_NE(state_, kStopped);
295
296 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete,
297 this));
298 } 229 }
299 230
300 gfx::Size FFmpegVideoDecoder::natural_size() { 231 gfx::Size FFmpegVideoDecoder::natural_size() {
301 return natural_size_; 232 return natural_size_;
302 } 233 }
303 234
304 void FFmpegVideoDecoder::FlushBuffers() { 235 void FFmpegVideoDecoder::ReadFromDemuxerStream() {
305 while (!frame_queue_flushed_.empty()) { 236 DCHECK_NE(state_, kUninitialized);
306 scoped_refptr<VideoFrame> video_frame; 237 DCHECK_NE(state_, kDecodeFinished);
307 video_frame = frame_queue_flushed_.front(); 238 DCHECK(!frame_ready_cb_.is_null());
308 frame_queue_flushed_.pop_front();
309 239
310 // Return frames back to the decode engine. 240 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this));
311 decode_engine_->ProduceVideoFrame(video_frame);
312 }
313 } 241 }
314 242
315 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( 243 void FFmpegVideoDecoder::DecodeBuffer(Buffer* buffer) {
316 VideoDecodeEngine* engine) { 244 // TODO(scherkus): change DemuxerStream::Read() to use scoped_refptr<> for
acolwell GONE FROM CHROMIUM 2011/11/01 18:31:03 This TODO should be in demuxer_stream.h
scherkus (not reviewing) 2011/11/03 04:55:59 TODO was fixed in r108178
317 decode_engine_.reset(engine); 245 // callback.
246 message_loop_->PostTask(FROM_HERE, base::Bind(
247 &FFmpegVideoDecoder::DoDecodeBuffer, this, make_scoped_refptr(buffer)));
248 }
249
250 void FFmpegVideoDecoder::DeliverFrame(
251 const scoped_refptr<VideoFrame>& video_frame) {
252 // Reset the callback before running to protect against reentrancy.
253 FrameReadyCB frame_ready_cb = frame_ready_cb_;
254 frame_ready_cb_.Reset();
255 frame_ready_cb.Run(video_frame);
318 } 256 }
319 257
320 } // namespace media 258 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698