OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 <deque> | 7 #include <deque> |
8 | 8 |
9 #include "base/task.h" | 9 #include "base/task.h" |
10 #include "media/base/callback.h" | 10 #include "media/base/callback.h" |
11 #include "media/base/filters.h" | 11 #include "media/base/filters.h" |
12 #include "media/base/filter_host.h" | 12 #include "media/base/filter_host.h" |
13 #include "media/base/limits.h" | 13 #include "media/base/limits.h" |
14 #include "media/base/media_format.h" | 14 #include "media/base/media_format.h" |
15 #include "media/base/video_frame.h" | 15 #include "media/base/video_frame.h" |
16 #include "media/ffmpeg/ffmpeg_common.h" | 16 #include "media/ffmpeg/ffmpeg_common.h" |
17 #include "media/ffmpeg/ffmpeg_util.h" | 17 #include "media/ffmpeg/ffmpeg_util.h" |
18 #include "media/filters/ffmpeg_interfaces.h" | 18 #include "media/filters/ffmpeg_interfaces.h" |
19 #include "media/video/ffmpeg_video_decode_engine.h" | 19 #include "media/video/ffmpeg_video_decode_engine.h" |
20 #include "media/video/video_decode_context.h" | 20 #include "media/video/video_decode_context.h" |
21 | 21 |
22 namespace media { | 22 namespace media { |
23 | 23 |
24 FFmpegVideoDecoder::FFmpegVideoDecoder(VideoDecodeContext* decode_context) | 24 FFmpegVideoDecoder::FFmpegVideoDecoder(MessageLoop* message_loop, |
25 : width_(0), | 25 VideoDecodeContext* decode_context) |
| 26 : message_loop_(message_loop), |
| 27 width_(0), |
26 height_(0), | 28 height_(0), |
27 time_base_(new AVRational()), | 29 time_base_(new AVRational()), |
28 state_(kUnInitialized), | 30 state_(kUnInitialized), |
29 decode_engine_(new FFmpegVideoDecodeEngine()), | 31 decode_engine_(new FFmpegVideoDecodeEngine()), |
30 decode_context_(decode_context) { | 32 decode_context_(decode_context) { |
31 memset(&info_, 0, sizeof(info_)); | 33 memset(&info_, 0, sizeof(info_)); |
32 } | 34 } |
33 | 35 |
34 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | 36 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
35 } | 37 } |
36 | 38 |
37 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, | 39 void FFmpegVideoDecoder::Initialize(DemuxerStream* demuxer_stream, |
38 FilterCallback* callback) { | 40 FilterCallback* callback) { |
39 if (MessageLoop::current() != message_loop()) { | 41 if (MessageLoop::current() != message_loop_) { |
40 message_loop()->PostTask( | 42 message_loop_->PostTask( |
41 FROM_HERE, | 43 FROM_HERE, |
42 NewRunnableMethod(this, | 44 NewRunnableMethod(this, |
43 &FFmpegVideoDecoder::Initialize, | 45 &FFmpegVideoDecoder::Initialize, |
44 make_scoped_refptr(demuxer_stream), | 46 make_scoped_refptr(demuxer_stream), |
45 callback)); | 47 callback)); |
46 return; | 48 return; |
47 } | 49 } |
48 | 50 |
49 DCHECK_EQ(MessageLoop::current(), message_loop()); | 51 DCHECK_EQ(MessageLoop::current(), message_loop_); |
50 DCHECK(!demuxer_stream_); | 52 DCHECK(!demuxer_stream_); |
51 DCHECK(!initialize_callback_.get()); | 53 DCHECK(!initialize_callback_.get()); |
52 | 54 |
53 demuxer_stream_ = demuxer_stream; | 55 demuxer_stream_ = demuxer_stream; |
54 initialize_callback_.reset(callback); | 56 initialize_callback_.reset(callback); |
55 | 57 |
56 // Get the AVStream by querying for the provider interface. | 58 // Get the AVStream by querying for the provider interface. |
57 AVStreamProvider* av_stream_provider; | 59 AVStreamProvider* av_stream_provider; |
58 if (!demuxer_stream->QueryInterface(&av_stream_provider)) { | 60 if (!demuxer_stream->QueryInterface(&av_stream_provider)) { |
59 VideoCodecInfo info = {0}; | 61 VideoCodecInfo info = {0}; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
91 config.codec = kCodecMPEG4; break; | 93 config.codec = kCodecMPEG4; break; |
92 case CODEC_ID_VP8: | 94 case CODEC_ID_VP8: |
93 config.codec = kCodecVP8; break; | 95 config.codec = kCodecVP8; break; |
94 default: | 96 default: |
95 NOTREACHED(); | 97 NOTREACHED(); |
96 } | 98 } |
97 config.opaque_context = av_stream; | 99 config.opaque_context = av_stream; |
98 config.width = width_; | 100 config.width = width_; |
99 config.height = height_; | 101 config.height = height_; |
100 state_ = kInitializing; | 102 state_ = kInitializing; |
101 decode_engine_->Initialize(message_loop(), this, NULL, config); | 103 decode_engine_->Initialize(message_loop_, this, NULL, config); |
102 } | 104 } |
103 | 105 |
104 void FFmpegVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) { | 106 void FFmpegVideoDecoder::OnInitializeComplete(const VideoCodecInfo& info) { |
105 DCHECK_EQ(MessageLoop::current(), message_loop()); | 107 DCHECK_EQ(MessageLoop::current(), message_loop_); |
106 DCHECK(initialize_callback_.get()); | 108 DCHECK(initialize_callback_.get()); |
107 | 109 |
108 info_ = info; // Save a copy. | 110 info_ = info; // Save a copy. |
109 | 111 |
110 if (info.success) { | 112 if (info.success) { |
111 media_format_.SetAsString(MediaFormat::kMimeType, | 113 media_format_.SetAsString(MediaFormat::kMimeType, |
112 mime_type::kUncompressedVideo); | 114 mime_type::kUncompressedVideo); |
113 media_format_.SetAsInteger(MediaFormat::kWidth, width_); | 115 media_format_.SetAsInteger(MediaFormat::kWidth, width_); |
114 media_format_.SetAsInteger(MediaFormat::kHeight, height_); | 116 media_format_.SetAsInteger(MediaFormat::kHeight, height_); |
115 media_format_.SetAsInteger( | 117 media_format_.SetAsInteger( |
116 MediaFormat::kSurfaceType, | 118 MediaFormat::kSurfaceType, |
117 static_cast<int>(info.stream_info.surface_type)); | 119 static_cast<int>(info.stream_info.surface_type)); |
118 media_format_.SetAsInteger( | 120 media_format_.SetAsInteger( |
119 MediaFormat::kSurfaceFormat, | 121 MediaFormat::kSurfaceFormat, |
120 static_cast<int>(info.stream_info.surface_format)); | 122 static_cast<int>(info.stream_info.surface_format)); |
121 state_ = kNormal; | 123 state_ = kNormal; |
122 } else { | 124 } else { |
123 host()->SetError(PIPELINE_ERROR_DECODE); | 125 host()->SetError(PIPELINE_ERROR_DECODE); |
124 } | 126 } |
125 | 127 |
126 initialize_callback_->Run(); | 128 initialize_callback_->Run(); |
127 initialize_callback_.reset(); | 129 initialize_callback_.reset(); |
128 } | 130 } |
129 | 131 |
130 void FFmpegVideoDecoder::Stop(FilterCallback* callback) { | 132 void FFmpegVideoDecoder::Stop(FilterCallback* callback) { |
131 if (MessageLoop::current() != message_loop()) { | 133 if (MessageLoop::current() != message_loop_) { |
132 message_loop()->PostTask(FROM_HERE, | 134 message_loop_->PostTask(FROM_HERE, |
133 NewRunnableMethod(this, | 135 NewRunnableMethod(this, |
134 &FFmpegVideoDecoder::Stop, | 136 &FFmpegVideoDecoder::Stop, |
135 callback)); | 137 callback)); |
136 return; | 138 return; |
137 } | 139 } |
138 | 140 |
139 DCHECK_EQ(MessageLoop::current(), message_loop()); | 141 DCHECK_EQ(MessageLoop::current(), message_loop_); |
140 DCHECK(!uninitialize_callback_.get()); | 142 DCHECK(!uninitialize_callback_.get()); |
141 | 143 |
142 uninitialize_callback_.reset(callback); | 144 uninitialize_callback_.reset(callback); |
143 if (state_ != kUnInitialized) | 145 if (state_ != kUnInitialized) |
144 decode_engine_->Uninitialize(); | 146 decode_engine_->Uninitialize(); |
145 else | 147 else |
146 OnUninitializeComplete(); | 148 OnUninitializeComplete(); |
147 } | 149 } |
148 | 150 |
149 void FFmpegVideoDecoder::OnUninitializeComplete() { | 151 void FFmpegVideoDecoder::OnUninitializeComplete() { |
150 DCHECK_EQ(MessageLoop::current(), message_loop()); | 152 DCHECK_EQ(MessageLoop::current(), message_loop_); |
151 DCHECK(uninitialize_callback_.get()); | 153 DCHECK(uninitialize_callback_.get()); |
152 | 154 |
153 AutoCallbackRunner done_runner(uninitialize_callback_.release()); | 155 AutoCallbackRunner done_runner(uninitialize_callback_.release()); |
154 state_ = kStopped; | 156 state_ = kStopped; |
155 | 157 |
156 // TODO(jiesun): Destroy the decoder context. | 158 // TODO(jiesun): Destroy the decoder context. |
157 } | 159 } |
158 | 160 |
159 void FFmpegVideoDecoder::Pause(FilterCallback* callback) { | 161 void FFmpegVideoDecoder::Pause(FilterCallback* callback) { |
160 if (MessageLoop::current() != message_loop()) { | 162 if (MessageLoop::current() != message_loop_) { |
161 message_loop()->PostTask(FROM_HERE, | 163 message_loop_->PostTask(FROM_HERE, |
162 NewRunnableMethod(this, | 164 NewRunnableMethod(this, |
163 &FFmpegVideoDecoder::Pause, | 165 &FFmpegVideoDecoder::Pause, |
164 callback)); | 166 callback)); |
165 return; | 167 return; |
166 } | 168 } |
167 | 169 |
168 AutoCallbackRunner done_runner(callback); | 170 AutoCallbackRunner done_runner(callback); |
169 state_ = kPausing; | 171 state_ = kPausing; |
170 } | 172 } |
171 | 173 |
172 void FFmpegVideoDecoder::Flush(FilterCallback* callback) { | 174 void FFmpegVideoDecoder::Flush(FilterCallback* callback) { |
173 if (MessageLoop::current() != message_loop()) { | 175 if (MessageLoop::current() != message_loop_) { |
174 message_loop()->PostTask(FROM_HERE, | 176 message_loop_->PostTask(FROM_HERE, |
175 NewRunnableMethod(this, | 177 NewRunnableMethod(this, |
176 &FFmpegVideoDecoder::Flush, | 178 &FFmpegVideoDecoder::Flush, |
177 callback)); | 179 callback)); |
178 return; | 180 return; |
179 } | 181 } |
180 | 182 |
181 DCHECK_EQ(MessageLoop::current(), message_loop()); | 183 DCHECK_EQ(MessageLoop::current(), message_loop_); |
182 DCHECK(!flush_callback_.get()); | 184 DCHECK(!flush_callback_.get()); |
183 | 185 |
184 state_ = kFlushing; | 186 state_ = kFlushing; |
185 | 187 |
186 FlushBuffers(); | 188 FlushBuffers(); |
187 | 189 |
188 flush_callback_.reset(callback); | 190 flush_callback_.reset(callback); |
189 | 191 |
190 decode_engine_->Flush(); | 192 decode_engine_->Flush(); |
191 } | 193 } |
192 | 194 |
193 void FFmpegVideoDecoder::OnFlushComplete() { | 195 void FFmpegVideoDecoder::OnFlushComplete() { |
194 DCHECK_EQ(MessageLoop::current(), message_loop()); | 196 DCHECK_EQ(MessageLoop::current(), message_loop_); |
195 DCHECK(flush_callback_.get()); | 197 DCHECK(flush_callback_.get()); |
196 | 198 |
197 AutoCallbackRunner done_runner(flush_callback_.release()); | 199 AutoCallbackRunner done_runner(flush_callback_.release()); |
198 | 200 |
199 // Everything in the presentation time queue is invalid, clear the queue. | 201 // Everything in the presentation time queue is invalid, clear the queue. |
200 while (!pts_heap_.IsEmpty()) | 202 while (!pts_heap_.IsEmpty()) |
201 pts_heap_.Pop(); | 203 pts_heap_.Pop(); |
202 | 204 |
203 // Mark flush operation had been done. | 205 // Mark flush operation had been done. |
204 state_ = kNormal; | 206 state_ = kNormal; |
205 } | 207 } |
206 | 208 |
207 void FFmpegVideoDecoder::Seek(base::TimeDelta time, | 209 void FFmpegVideoDecoder::Seek(base::TimeDelta time, |
208 FilterCallback* callback) { | 210 FilterCallback* callback) { |
209 if (MessageLoop::current() != message_loop()) { | 211 if (MessageLoop::current() != message_loop_) { |
210 message_loop()->PostTask(FROM_HERE, | 212 message_loop_->PostTask(FROM_HERE, |
211 NewRunnableMethod(this, | 213 NewRunnableMethod(this, |
212 &FFmpegVideoDecoder::Seek, | 214 &FFmpegVideoDecoder::Seek, |
213 time, | 215 time, |
214 callback)); | 216 callback)); |
215 return; | 217 return; |
216 } | 218 } |
217 | 219 |
218 DCHECK_EQ(MessageLoop::current(), message_loop()); | 220 DCHECK_EQ(MessageLoop::current(), message_loop_); |
219 DCHECK(!seek_callback_.get()); | 221 DCHECK(!seek_callback_.get()); |
220 | 222 |
221 seek_callback_.reset(callback); | 223 seek_callback_.reset(callback); |
222 decode_engine_->Seek(); | 224 decode_engine_->Seek(); |
223 } | 225 } |
224 | 226 |
225 void FFmpegVideoDecoder::OnSeekComplete() { | 227 void FFmpegVideoDecoder::OnSeekComplete() { |
226 DCHECK_EQ(MessageLoop::current(), message_loop()); | 228 DCHECK_EQ(MessageLoop::current(), message_loop_); |
227 DCHECK(seek_callback_.get()); | 229 DCHECK(seek_callback_.get()); |
228 | 230 |
229 AutoCallbackRunner done_runner(seek_callback_.release()); | 231 AutoCallbackRunner done_runner(seek_callback_.release()); |
230 } | 232 } |
231 | 233 |
232 void FFmpegVideoDecoder::OnError() { | 234 void FFmpegVideoDecoder::OnError() { |
233 NOTIMPLEMENTED(); | 235 NOTIMPLEMENTED(); |
234 } | 236 } |
235 | 237 |
236 void FFmpegVideoDecoder::OnFormatChange(VideoStreamInfo stream_info) { | 238 void FFmpegVideoDecoder::OnFormatChange(VideoStreamInfo stream_info) { |
237 NOTIMPLEMENTED(); | 239 NOTIMPLEMENTED(); |
238 } | 240 } |
239 | 241 |
240 void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) { | 242 void FFmpegVideoDecoder::OnReadComplete(Buffer* buffer_in) { |
241 scoped_refptr<Buffer> buffer(buffer_in); | 243 scoped_refptr<Buffer> buffer(buffer_in); |
242 message_loop()->PostTask( | 244 message_loop_->PostTask( |
243 FROM_HERE, | 245 FROM_HERE, |
244 NewRunnableMethod(this, | 246 NewRunnableMethod(this, |
245 &FFmpegVideoDecoder::OnReadCompleteTask, | 247 &FFmpegVideoDecoder::OnReadCompleteTask, |
246 buffer)); | 248 buffer)); |
247 } | 249 } |
248 | 250 |
249 void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) { | 251 void FFmpegVideoDecoder::OnReadCompleteTask(scoped_refptr<Buffer> buffer) { |
250 DCHECK_EQ(MessageLoop::current(), message_loop()); | 252 DCHECK_EQ(MessageLoop::current(), message_loop_); |
251 DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). | 253 DCHECK_NE(state_, kStopped); // because of Flush() before Stop(). |
252 | 254 |
253 // During decode, because reads are issued asynchronously, it is possible to | 255 // During decode, because reads are issued asynchronously, it is possible to |
254 // receive multiple end of stream buffers since each read is acked. When the | 256 // receive multiple end of stream buffers since each read is acked. When the |
255 // first end of stream buffer is read, FFmpeg may still have frames queued | 257 // first end of stream buffer is read, FFmpeg may still have frames queued |
256 // up in the decoder so we need to go through the decode loop until it stops | 258 // up in the decoder so we need to go through the decode loop until it stops |
257 // giving sensible data. After that, the decoder should output empty | 259 // giving sensible data. After that, the decoder should output empty |
258 // frames. There are three states the decoder can be in: | 260 // frames. There are three states the decoder can be in: |
259 // | 261 // |
260 // kNormal: This is the starting state. Buffers are decoded. Decode errors | 262 // kNormal: This is the starting state. Buffers are decoded. Decode errors |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 // Otherwise, attempt to decode a single frame. | 296 // Otherwise, attempt to decode a single frame. |
295 decode_engine_->ConsumeVideoSample(buffer); | 297 decode_engine_->ConsumeVideoSample(buffer); |
296 } | 298 } |
297 | 299 |
298 const MediaFormat& FFmpegVideoDecoder::media_format() { | 300 const MediaFormat& FFmpegVideoDecoder::media_format() { |
299 return media_format_; | 301 return media_format_; |
300 } | 302 } |
301 | 303 |
302 void FFmpegVideoDecoder::ProduceVideoFrame( | 304 void FFmpegVideoDecoder::ProduceVideoFrame( |
303 scoped_refptr<VideoFrame> video_frame) { | 305 scoped_refptr<VideoFrame> video_frame) { |
304 if (MessageLoop::current() != message_loop()) { | 306 if (MessageLoop::current() != message_loop_) { |
305 message_loop()->PostTask( | 307 message_loop_->PostTask( |
306 FROM_HERE, | 308 FROM_HERE, |
307 NewRunnableMethod(this, | 309 NewRunnableMethod(this, |
308 &FFmpegVideoDecoder::ProduceVideoFrame, video_frame)); | 310 &FFmpegVideoDecoder::ProduceVideoFrame, video_frame)); |
309 return; | 311 return; |
310 } | 312 } |
311 | 313 |
312 DCHECK_EQ(MessageLoop::current(), message_loop()); | 314 DCHECK_EQ(MessageLoop::current(), message_loop_); |
313 | 315 |
314 // Synchronized flushing before stop should prevent this. | 316 // Synchronized flushing before stop should prevent this. |
315 DCHECK_NE(state_, kStopped); | 317 DCHECK_NE(state_, kStopped); |
316 | 318 |
317 // If the decoding is finished, we just always return empty frames. | 319 // If the decoding is finished, we just always return empty frames. |
318 if (state_ == kDecodeFinished) { | 320 if (state_ == kDecodeFinished) { |
319 // Signal VideoRenderer the end of the stream event. | 321 // Signal VideoRenderer the end of the stream event. |
320 scoped_refptr<VideoFrame> empty_frame; | 322 scoped_refptr<VideoFrame> empty_frame; |
321 VideoFrame::CreateEmptyFrame(&empty_frame); | 323 VideoFrame::CreateEmptyFrame(&empty_frame); |
322 VideoFrameReady(empty_frame); | 324 VideoFrameReady(empty_frame); |
323 | 325 |
324 // Fall through, because we still need to keep record of this frame. | 326 // Fall through, because we still need to keep record of this frame. |
325 } | 327 } |
326 | 328 |
327 // Notify decode engine the available of new frame. | 329 // Notify decode engine the available of new frame. |
328 decode_engine_->ProduceVideoFrame(video_frame); | 330 decode_engine_->ProduceVideoFrame(video_frame); |
329 } | 331 } |
330 | 332 |
331 void FFmpegVideoDecoder::ConsumeVideoFrame( | 333 void FFmpegVideoDecoder::ConsumeVideoFrame( |
332 scoped_refptr<VideoFrame> video_frame) { | 334 scoped_refptr<VideoFrame> video_frame) { |
333 DCHECK_EQ(MessageLoop::current(), message_loop()); | 335 DCHECK_EQ(MessageLoop::current(), message_loop_); |
334 DCHECK_NE(state_, kStopped); | 336 DCHECK_NE(state_, kStopped); |
335 | 337 |
336 if (video_frame.get()) { | 338 if (video_frame.get()) { |
337 if (kPausing == state_ || kFlushing == state_) { | 339 if (kPausing == state_ || kFlushing == state_) { |
338 frame_queue_flushed_.push_back(video_frame); | 340 frame_queue_flushed_.push_back(video_frame); |
339 if (kFlushing == state_) | 341 if (kFlushing == state_) |
340 FlushBuffers(); | 342 FlushBuffers(); |
341 return; | 343 return; |
342 } | 344 } |
343 | 345 |
(...skipping 14 matching lines...) Expand all Loading... |
358 // Signal VideoRenderer the end of the stream event. | 360 // Signal VideoRenderer the end of the stream event. |
359 scoped_refptr<VideoFrame> video_frame; | 361 scoped_refptr<VideoFrame> video_frame; |
360 VideoFrame::CreateEmptyFrame(&video_frame); | 362 VideoFrame::CreateEmptyFrame(&video_frame); |
361 VideoFrameReady(video_frame); | 363 VideoFrameReady(video_frame); |
362 } | 364 } |
363 } | 365 } |
364 } | 366 } |
365 | 367 |
366 void FFmpegVideoDecoder::ProduceVideoSample( | 368 void FFmpegVideoDecoder::ProduceVideoSample( |
367 scoped_refptr<Buffer> buffer) { | 369 scoped_refptr<Buffer> buffer) { |
368 DCHECK_EQ(MessageLoop::current(), message_loop()); | 370 DCHECK_EQ(MessageLoop::current(), message_loop_); |
369 DCHECK_NE(state_, kStopped); | 371 DCHECK_NE(state_, kStopped); |
370 | 372 |
371 demuxer_stream_->Read( | 373 demuxer_stream_->Read( |
372 NewCallback(this, &FFmpegVideoDecoder::OnReadComplete)); | 374 NewCallback(this, &FFmpegVideoDecoder::OnReadComplete)); |
373 } | 375 } |
374 | 376 |
375 FFmpegVideoDecoder::TimeTuple FFmpegVideoDecoder::FindPtsAndDuration( | 377 FFmpegVideoDecoder::TimeTuple FFmpegVideoDecoder::FindPtsAndDuration( |
376 const AVRational& time_base, | 378 const AVRational& time_base, |
377 PtsHeap* pts_heap, | 379 PtsHeap* pts_heap, |
378 const TimeTuple& last_pts, | 380 const TimeTuple& last_pts, |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 VideoFrameReady(video_frame); | 442 VideoFrameReady(video_frame); |
441 } | 443 } |
442 } | 444 } |
443 | 445 |
444 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( | 446 void FFmpegVideoDecoder::SetVideoDecodeEngineForTest( |
445 VideoDecodeEngine* engine) { | 447 VideoDecodeEngine* engine) { |
446 decode_engine_.reset(engine); | 448 decode_engine_.reset(engine); |
447 } | 449 } |
448 | 450 |
449 } // namespace media | 451 } // namespace media |
OLD | NEW |