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/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_); | |
41 DCHECK(!demuxer_stream_); | 37 DCHECK(!demuxer_stream_); |
42 DCHECK(initialize_callback_.is_null()); | |
43 | 38 |
44 if (!demuxer_stream) { | 39 if (!demuxer_stream) { |
45 host()->SetError(PIPELINE_ERROR_DECODE); | 40 host()->SetError(PIPELINE_ERROR_DECODE); |
46 callback.Run(); | 41 callback.Run(); |
47 return; | 42 return; |
48 } | 43 } |
49 | 44 |
50 demuxer_stream_ = demuxer_stream; | 45 demuxer_stream_ = demuxer_stream; |
51 initialize_callback_ = callback; | |
52 statistics_callback_ = stats_callback; | 46 statistics_callback_ = stats_callback; |
53 | 47 |
54 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); | 48 const VideoDecoderConfig& config = demuxer_stream->video_decoder_config(); |
55 | 49 |
56 pts_stream_.Initialize(GetFrameDuration(config)); | 50 // TODO(scherkus): this check should go in PipelineImpl prior to creating |
51 // decoder objects. | |
52 if (!config.IsValidConfig()) { | |
53 DLOG(ERROR) << "Invalid video stream -" | |
54 << " codec: " << config.codec() | |
55 << " format: " << config.format() | |
56 << " coded size: [" << config.coded_size().width() | |
57 << "," << config.coded_size().height() << "]" | |
58 << " visible rect: [" << config.visible_rect().x() | |
59 << "," << config.visible_rect().y() | |
60 << "," << config.visible_rect().width() | |
61 << "," << config.visible_rect().height() << "]" | |
62 << " natural size: [" << config.natural_size().width() | |
63 << "," << config.natural_size().height() << "]" | |
64 << " frame rate: " << config.frame_rate_numerator() | |
65 << "/" << config.frame_rate_denominator() | |
66 << " aspect ratio: " << config.aspect_ratio_numerator() | |
67 << "/" << config.aspect_ratio_denominator(); | |
57 | 68 |
58 natural_size_ = config.natural_size(); | 69 host()->SetError(PIPELINE_ERROR_DECODE); |
59 if (natural_size_.width() > Limits::kMaxDimension || | 70 callback.Run(); |
60 natural_size_.height() > Limits::kMaxDimension || | |
61 natural_size_.GetArea() > Limits::kMaxCanvas) { | |
62 OnInitializeComplete(false); | |
63 return; | 71 return; |
64 } | 72 } |
65 | 73 |
66 state_ = kInitializing; | 74 pts_stream_.Initialize(GetFrameDuration(config)); |
67 decode_engine_->Initialize(this, config); | 75 natural_size_ = config.natural_size(); |
68 } | |
69 | 76 |
70 void FFmpegVideoDecoder::OnInitializeComplete(bool success) { | 77 if (!decode_engine_->Initialize(config)) { |
71 DCHECK_EQ(MessageLoop::current(), message_loop_); | 78 host()->SetError(PIPELINE_ERROR_DECODE); |
72 DCHECK(!initialize_callback_.is_null()); | 79 callback.Run(); |
80 return; | |
81 } | |
73 | 82 |
74 if (success) { | 83 state_ = kNormal; |
75 state_ = kNormal; | 84 callback.Run(); |
76 } else { | |
77 host()->SetError(PIPELINE_ERROR_DECODE); | |
78 } | |
79 ResetAndRunCB(&initialize_callback_); | |
80 } | 85 } |
81 | 86 |
82 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { | 87 void FFmpegVideoDecoder::Stop(const base::Closure& callback) { |
83 if (MessageLoop::current() != message_loop_) { | 88 if (MessageLoop::current() != message_loop_) { |
84 message_loop_->PostTask(FROM_HERE, base::Bind( | 89 message_loop_->PostTask(FROM_HERE, base::Bind( |
85 &FFmpegVideoDecoder::Stop, this, callback)); | 90 &FFmpegVideoDecoder::Stop, this, callback)); |
86 return; | 91 return; |
87 } | 92 } |
88 | 93 |
89 DCHECK_EQ(MessageLoop::current(), message_loop_); | 94 decode_engine_->Uninitialize(); |
90 DCHECK(uninitialize_callback_.is_null()); | 95 state_ = kUninitialized; |
91 | 96 callback.Run(); |
92 uninitialize_callback_ = callback; | |
93 if (state_ != kUnInitialized) | |
94 decode_engine_->Uninitialize(); | |
95 else | |
96 OnUninitializeComplete(); | |
97 } | 97 } |
98 | 98 |
99 void FFmpegVideoDecoder::OnUninitializeComplete() { | 99 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
100 DCHECK_EQ(MessageLoop::current(), message_loop_); | 100 if (MessageLoop::current() != message_loop_) { |
101 DCHECK(!uninitialize_callback_.is_null()); | 101 message_loop_->PostTask(FROM_HERE, base::Bind( |
102 &FFmpegVideoDecoder::Seek, this, time, cb)); | |
103 return; | |
104 } | |
102 | 105 |
103 state_ = kStopped; | 106 pts_stream_.Seek(time); |
104 | 107 cb.Run(PIPELINE_OK); |
105 ResetAndRunCB(&uninitialize_callback_); | |
106 } | 108 } |
107 | 109 |
108 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { | 110 void FFmpegVideoDecoder::Pause(const base::Closure& callback) { |
109 if (MessageLoop::current() != message_loop_) { | 111 if (MessageLoop::current() != message_loop_) { |
110 message_loop_->PostTask(FROM_HERE, base::Bind( | 112 message_loop_->PostTask(FROM_HERE, base::Bind( |
111 &FFmpegVideoDecoder::Pause, this, callback)); | 113 &FFmpegVideoDecoder::Pause, this, callback)); |
112 return; | 114 return; |
113 } | 115 } |
114 | 116 |
115 state_ = kPausing; | |
116 callback.Run(); | 117 callback.Run(); |
117 } | 118 } |
118 | 119 |
119 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { | 120 void FFmpegVideoDecoder::Flush(const base::Closure& callback) { |
120 if (MessageLoop::current() != message_loop_) { | 121 if (MessageLoop::current() != message_loop_) { |
121 message_loop_->PostTask(FROM_HERE, base::Bind( | 122 message_loop_->PostTask(FROM_HERE, base::Bind( |
122 &FFmpegVideoDecoder::Flush, this, callback)); | 123 &FFmpegVideoDecoder::Flush, this, callback)); |
123 return; | 124 return; |
124 } | 125 } |
125 | 126 |
126 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 | |
135 decode_engine_->Flush(); | 127 decode_engine_->Flush(); |
128 pts_stream_.Flush(); | |
129 state_ = kNormal; | |
130 callback.Run(); | |
136 } | 131 } |
137 | 132 |
138 void FFmpegVideoDecoder::OnFlushComplete() { | 133 void FFmpegVideoDecoder::Read(const ReadCB& callback) { |
139 DCHECK_EQ(MessageLoop::current(), message_loop_); | 134 // Method must immediately return. |
140 DCHECK(!flush_callback_.is_null()); | 135 message_loop_->PostTask(FROM_HERE, base::Bind( |
scherkus (not reviewing)
2011/11/03 04:55:59
FYI forced post task since VideoRendererBase::Fram
Ami GONE FROM CHROMIUM
2011/11/03 16:40:00
This is a better comment than what you have in the
scherkus (not reviewing)
2011/11/03 20:34:38
Done.
| |
141 | 136 &FFmpegVideoDecoder::DoRead, this, callback)); |
142 // Everything in the presentation time queue is invalid, clear the queue. | |
143 pts_stream_.Flush(); | |
144 | |
145 // Mark flush operation had been done. | |
146 state_ = kNormal; | |
147 | |
148 ResetAndRunCB(&flush_callback_); | |
149 } | 137 } |
150 | 138 |
151 void FFmpegVideoDecoder::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 139 gfx::Size FFmpegVideoDecoder::natural_size() { |
Ami GONE FROM CHROMIUM
2011/11/03 16:40:00
return const&?
scherkus (not reviewing)
2011/11/03 20:34:38
make ALL the return types const&!
| |
152 if (MessageLoop::current() != message_loop_) { | 140 return natural_size_; |
153 message_loop_->PostTask(FROM_HERE, | 141 } |
154 base::Bind(&FFmpegVideoDecoder::Seek, this, | 142 |
155 time, cb)); | 143 void FFmpegVideoDecoder::DoRead(const ReadCB& callback) { |
156 return; | 144 DCHECK_EQ(MessageLoop::current(), message_loop_); |
145 CHECK(!callback.is_null()); | |
146 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
147 | |
148 // This can happen during shutdown after Stop() has been called. | |
149 if (state_ == kUninitialized) { | |
150 return; | |
157 } | 151 } |
158 | 152 |
159 DCHECK_EQ(MessageLoop::current(), message_loop_); | 153 // Return empty frames if decoding has finished. |
160 DCHECK(seek_cb_.is_null()); | 154 if (state_ == kDecodeFinished) { |
155 callback.Run(VideoFrame::CreateEmptyFrame()); | |
156 return; | |
157 } | |
161 | 158 |
162 pts_stream_.Seek(time); | 159 read_cb_ = callback; |
163 seek_cb_ = cb; | 160 ReadFromDemuxerStream(); |
164 decode_engine_->Seek(); | |
165 } | 161 } |
166 | 162 |
167 void FFmpegVideoDecoder::OnSeekComplete() { | |
168 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
169 DCHECK(!seek_cb_.is_null()); | |
170 | 163 |
171 ResetAndRunCB(&seek_cb_, PIPELINE_OK); | 164 void FFmpegVideoDecoder::ReadFromDemuxerStream() { |
165 DCHECK_NE(state_, kUninitialized); | |
166 DCHECK_NE(state_, kDecodeFinished); | |
167 DCHECK(!read_cb_.is_null()); | |
168 | |
169 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::DecodeBuffer, this)); | |
172 } | 170 } |
173 | 171 |
174 void FFmpegVideoDecoder::OnError() { | 172 void FFmpegVideoDecoder::DecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
175 VideoFrameReady(NULL); | 173 message_loop_->PostTask(FROM_HERE, base::Bind( |
scherkus (not reviewing)
2011/11/03 04:55:59
FYI forced post task since FFmpegDemuxerStream::Re
scherkus (not reviewing)
2011/11/03 20:34:38
Comment added.
| |
174 &FFmpegVideoDecoder::DoDecodeBuffer, this, buffer)); | |
176 } | 175 } |
177 | 176 |
178 void FFmpegVideoDecoder::OnReadComplete(const scoped_refptr<Buffer>& buffer) { | 177 void FFmpegVideoDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
179 message_loop_->PostTask(FROM_HERE, base::Bind( | |
180 &FFmpegVideoDecoder::OnReadCompleteTask, this, buffer)); | |
181 } | |
182 | |
183 void FFmpegVideoDecoder::OnReadCompleteTask( | |
184 const scoped_refptr<Buffer>& buffer) { | |
185 DCHECK_EQ(MessageLoop::current(), message_loop_); | 178 DCHECK_EQ(MessageLoop::current(), message_loop_); |
186 DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). | 179 DCHECK_NE(state_, kUninitialized); |
180 DCHECK_NE(state_, kDecodeFinished); | |
181 DCHECK(!read_cb_.is_null()); | |
187 | 182 |
188 // During decode, because reads are issued asynchronously, it is possible to | 183 // 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 | 184 // 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 | 185 // 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 | 186 // 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 | 187 // giving sensible data. After that, the decoder should output empty |
193 // frames. There are three states the decoder can be in: | 188 // frames. There are three states the decoder can be in: |
194 // | 189 // |
195 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 190 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
196 // are discarded. | 191 // are discarded. |
197 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 | 192 // kFlushCodec: There isn't any more input data. Call avcodec_decode_video2 |
198 // until no more data is returned to flush out remaining | 193 // until no more data is returned to flush out remaining |
199 // frames. The input buffer is ignored at this point. | 194 // frames. The input buffer is ignored at this point. |
200 // kDecodeFinished: All calls return empty frames. | 195 // kDecodeFinished: All calls return empty frames. |
201 // | 196 // |
202 // These are the possible state transitions. | 197 // These are the possible state transitions. |
203 // | 198 // |
204 // kNormal -> kFlushCodec: | 199 // kNormal -> kFlushCodec: |
205 // When buffer->IsEndOfStream() is first true. | 200 // When buffer->IsEndOfStream() is first true. |
206 // kNormal -> kDecodeFinished: | 201 // kNormal -> kDecodeFinished: |
207 // A catastrophic failure occurs, and decoding needs to stop. | 202 // A decoding error occurs and decoding needs to stop. |
208 // kFlushCodec -> kDecodeFinished: | 203 // kFlushCodec -> kDecodeFinished: |
209 // When avcodec_decode_video2() returns 0 data or errors out. | 204 // When avcodec_decode_video2() returns 0 data or errors out. |
210 // (any state) -> kNormal: | 205 // (any state) -> kNormal: |
211 // Any time buffer->IsDiscontinuous() is true. | 206 // Any time Flush() is called. |
212 | 207 |
213 // Transition to kFlushCodec on the first end of stream buffer. | 208 // Transition to kFlushCodec on the first end of stream buffer. |
214 if (state_ == kNormal && buffer->IsEndOfStream()) { | 209 if (state_ == kNormal && buffer->IsEndOfStream()) { |
215 state_ = kFlushCodec; | 210 state_ = kFlushCodec; |
216 } | 211 } |
217 | 212 |
218 // Push all incoming timestamps into the priority queue as long as we have | 213 // 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 | 214 // not yet received an end of stream buffer. It is important that this line |
220 // stay below the state transition into kFlushCodec done above. | 215 // stay below the state transition into kFlushCodec done above. |
221 if (state_ == kNormal) { | 216 if (state_ == kNormal) { |
222 pts_stream_.EnqueuePts(buffer.get()); | 217 pts_stream_.EnqueuePts(buffer.get()); |
223 } | 218 } |
224 | 219 |
225 // Otherwise, attempt to decode a single frame. | 220 scoped_refptr<VideoFrame> video_frame; |
226 decode_engine_->ConsumeVideoSample(buffer); | 221 if (!decode_engine_->Decode(buffer, &video_frame)) { |
227 } | 222 state_ = kDecodeFinished; |
228 | 223 DeliverFrame(VideoFrame::CreateEmptyFrame()); |
229 void FFmpegVideoDecoder::ProduceVideoFrame( | 224 host()->SetError(PIPELINE_ERROR_DECODE); |
230 scoped_refptr<VideoFrame> video_frame) { | |
231 if (MessageLoop::current() != message_loop_) { | |
232 if (state_ != kStopped) { | |
233 message_loop_->PostTask(FROM_HERE, base::Bind( | |
234 &FFmpegVideoDecoder::ProduceVideoFrame, this, video_frame)); | |
235 } | |
236 return; | 225 return; |
237 } | 226 } |
238 | 227 |
239 DCHECK_EQ(MessageLoop::current(), message_loop_); | 228 // Any successful decode counts! |
240 | 229 if (buffer->GetDataSize()) { |
241 // Synchronized flushing before stop should prevent this. | 230 PipelineStatistics statistics; |
242 DCHECK_NE(state_, kStopped); | 231 statistics.video_bytes_decoded = buffer->GetDataSize(); |
243 | 232 statistics_callback_.Run(statistics); |
244 // If the decoding is finished, we just always return empty frames. | |
245 if (state_ == kDecodeFinished) { | |
246 // Signal VideoRenderer the end of the stream event. | |
247 VideoFrameReady(VideoFrame::CreateEmptyFrame()); | |
248 | |
249 // Fall through, because we still need to keep record of this frame. | |
250 } | 233 } |
251 | 234 |
252 // Notify decode engine the available of new frame. | 235 // If we didn't get a frame then we've either completely finished decoding or |
253 decode_engine_->ProduceVideoFrame(video_frame); | 236 // we need more data. |
254 } | 237 if (!video_frame) { |
255 | 238 if (state_ == kFlushCodec) { |
256 void FFmpegVideoDecoder::ConsumeVideoFrame( | 239 state_ = kDecodeFinished; |
257 scoped_refptr<VideoFrame> video_frame, | 240 DeliverFrame(VideoFrame::CreateEmptyFrame()); |
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; | 241 return; |
270 } | 242 } |
271 | 243 |
272 // If we actually got data back, enqueue a frame. | 244 ReadFromDemuxerStream(); |
273 pts_stream_.UpdatePtsAndDuration(video_frame.get()); | 245 return; |
246 } | |
274 | 247 |
275 video_frame->SetTimestamp(pts_stream_.current_pts()); | 248 // If we got a frame make sure its timestamp is correct before sending it off. |
276 video_frame->SetDuration(pts_stream_.current_duration()); | 249 pts_stream_.UpdatePtsAndDuration(video_frame.get()); |
250 video_frame->SetTimestamp(pts_stream_.current_pts()); | |
251 video_frame->SetDuration(pts_stream_.current_duration()); | |
277 | 252 |
278 VideoFrameReady(video_frame); | 253 DeliverFrame(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 } | 254 } |
290 | 255 |
291 void FFmpegVideoDecoder::ProduceVideoSample( | 256 void FFmpegVideoDecoder::DeliverFrame( |
292 scoped_refptr<Buffer> buffer) { | 257 const scoped_refptr<VideoFrame>& video_frame) { |
293 DCHECK_EQ(MessageLoop::current(), message_loop_); | 258 // Reset the callback before running to protect against reentrancy. |
294 DCHECK_NE(state_, kStopped); | 259 ReadCB read_cb = read_cb_; |
295 | 260 read_cb_.Reset(); |
296 demuxer_stream_->Read(base::Bind(&FFmpegVideoDecoder::OnReadComplete, this)); | 261 read_cb.Run(video_frame); |
297 } | |
298 | |
299 gfx::Size FFmpegVideoDecoder::natural_size() { | |
300 return natural_size_; | |
301 } | |
302 | |
303 void FFmpegVideoDecoder::FlushBuffers() { | |
304 while (!frame_queue_flushed_.empty()) { | |
305 scoped_refptr<VideoFrame> video_frame; | |
306 video_frame = frame_queue_flushed_.front(); | |
307 frame_queue_flushed_.pop_front(); | |
308 | |
309 // Return frames back to the decode engine. | |
310 decode_engine_->ProduceVideoFrame(video_frame); | |
311 } | |
312 } | |
313 | |
314 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( | |
315 VideoDecodeEngine* engine) { | |
316 decode_engine_.reset(engine); | |
317 } | 262 } |
318 | 263 |
319 } // namespace media | 264 } // namespace media |
OLD | NEW |