OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/callback_helpers.h" | 11 #include "base/callback_helpers.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/message_loop.h" | 13 #include "base/location.h" |
| 14 #include "base/message_loop_proxy.h" |
14 #include "base/string_number_conversions.h" | 15 #include "base/string_number_conversions.h" |
15 #include "media/base/decoder_buffer.h" | 16 #include "media/base/decoder_buffer.h" |
16 #include "media/base/demuxer_stream.h" | 17 #include "media/base/demuxer_stream.h" |
17 #include "media/base/limits.h" | 18 #include "media/base/limits.h" |
18 #include "media/base/media_switches.h" | 19 #include "media/base/media_switches.h" |
19 #include "media/base/pipeline.h" | 20 #include "media/base/pipeline.h" |
20 #include "media/base/video_decoder_config.h" | 21 #include "media/base/video_decoder_config.h" |
21 #include "media/base/video_frame.h" | 22 #include "media/base/video_frame.h" |
22 #include "media/base/video_util.h" | 23 #include "media/base/video_util.h" |
23 #include "media/ffmpeg/ffmpeg_common.h" | 24 #include "media/ffmpeg/ffmpeg_common.h" |
(...skipping 23 matching lines...) Expand all Loading... |
47 std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads)); | 48 std::string threads(cmd_line->GetSwitchValueASCII(switches::kVideoThreads)); |
48 if (threads.empty() || !base::StringToInt(threads, &decode_threads)) | 49 if (threads.empty() || !base::StringToInt(threads, &decode_threads)) |
49 return decode_threads; | 50 return decode_threads; |
50 | 51 |
51 decode_threads = std::max(decode_threads, 0); | 52 decode_threads = std::max(decode_threads, 0); |
52 decode_threads = std::min(decode_threads, kMaxDecodeThreads); | 53 decode_threads = std::min(decode_threads, kMaxDecodeThreads); |
53 return decode_threads; | 54 return decode_threads; |
54 } | 55 } |
55 | 56 |
56 FFmpegVideoDecoder::FFmpegVideoDecoder( | 57 FFmpegVideoDecoder::FFmpegVideoDecoder( |
57 const base::Callback<MessageLoop*()>& message_loop_cb, | 58 const MessageLoopFactoryCB& message_loop_factory_cb, |
58 Decryptor* decryptor) | 59 Decryptor* decryptor) |
59 : message_loop_factory_cb_(message_loop_cb), | 60 : message_loop_factory_cb_(message_loop_factory_cb), |
60 message_loop_(NULL), | 61 message_loop_(NULL), |
61 state_(kUninitialized), | 62 state_(kUninitialized), |
62 codec_context_(NULL), | 63 codec_context_(NULL), |
63 av_frame_(NULL), | 64 av_frame_(NULL), |
64 decryptor_(decryptor) { | 65 decryptor_(decryptor) { |
65 } | 66 } |
66 | 67 |
67 int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context, | 68 int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context, |
68 AVFrame* frame) { | 69 AVFrame* frame) { |
69 // Don't use |codec_context_| here! With threaded decoding, | 70 // Don't use |codec_context_| here! With threaded decoding, |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 frame->opaque = NULL; | 133 frame->opaque = NULL; |
133 } | 134 } |
134 | 135 |
135 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, | 136 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
136 const PipelineStatusCB& status_cb, | 137 const PipelineStatusCB& status_cb, |
137 const StatisticsCB& statistics_cb) { | 138 const StatisticsCB& statistics_cb) { |
138 // Ensure FFmpeg has been initialized | 139 // Ensure FFmpeg has been initialized |
139 FFmpegGlue::GetInstance(); | 140 FFmpegGlue::GetInstance(); |
140 | 141 |
141 if (!message_loop_) { | 142 if (!message_loop_) { |
142 message_loop_ = message_loop_factory_cb_.Run(); | 143 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); |
143 message_loop_factory_cb_.Reset(); | |
144 | |
145 message_loop_->PostTask(FROM_HERE, base::Bind( | 144 message_loop_->PostTask(FROM_HERE, base::Bind( |
146 &FFmpegVideoDecoder::Initialize, this, | 145 &FFmpegVideoDecoder::Initialize, this, |
147 stream, status_cb, statistics_cb)); | 146 stream, status_cb, statistics_cb)); |
148 return; | 147 return; |
149 } | 148 } |
150 | 149 |
151 DCHECK_EQ(MessageLoop::current(), message_loop_); | 150 DCHECK(message_loop_->BelongsToCurrentThread()); |
152 DCHECK(!demuxer_stream_); | 151 DCHECK(!demuxer_stream_); |
153 | 152 |
154 if (!stream) { | 153 if (!stream) { |
155 status_cb.Run(PIPELINE_ERROR_DECODE); | 154 status_cb.Run(PIPELINE_ERROR_DECODE); |
156 return; | 155 return; |
157 } | 156 } |
158 | 157 |
159 demuxer_stream_ = stream; | 158 demuxer_stream_ = stream; |
160 statistics_cb_ = statistics_cb; | 159 statistics_cb_ = statistics_cb; |
161 | 160 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 } | 200 } |
202 | 201 |
203 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { | 202 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { |
204 // Complete operation asynchronously on different stack of execution as per | 203 // Complete operation asynchronously on different stack of execution as per |
205 // the API contract of VideoDecoder::Read() | 204 // the API contract of VideoDecoder::Read() |
206 message_loop_->PostTask(FROM_HERE, base::Bind( | 205 message_loop_->PostTask(FROM_HERE, base::Bind( |
207 &FFmpegVideoDecoder::DoRead, this, read_cb)); | 206 &FFmpegVideoDecoder::DoRead, this, read_cb)); |
208 } | 207 } |
209 | 208 |
210 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | 209 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
211 if (MessageLoop::current() != message_loop_) { | 210 if (!message_loop_->BelongsToCurrentThread()) { |
212 message_loop_->PostTask(FROM_HERE, base::Bind( | 211 message_loop_->PostTask(FROM_HERE, base::Bind( |
213 &FFmpegVideoDecoder::Reset, this, closure)); | 212 &FFmpegVideoDecoder::Reset, this, closure)); |
214 return; | 213 return; |
215 } | 214 } |
216 | 215 |
217 reset_cb_ = closure; | 216 reset_cb_ = closure; |
218 | 217 |
219 // Defer the reset if a read is pending. | 218 // Defer the reset if a read is pending. |
220 if (!read_cb_.is_null()) | 219 if (!read_cb_.is_null()) |
221 return; | 220 return; |
222 | 221 |
223 DoReset(); | 222 DoReset(); |
224 } | 223 } |
225 | 224 |
226 void FFmpegVideoDecoder::DoReset() { | 225 void FFmpegVideoDecoder::DoReset() { |
227 DCHECK(read_cb_.is_null()); | 226 DCHECK(read_cb_.is_null()); |
228 | 227 |
229 avcodec_flush_buffers(codec_context_); | 228 avcodec_flush_buffers(codec_context_); |
230 state_ = kNormal; | 229 state_ = kNormal; |
231 reset_cb_.Run(); | 230 reset_cb_.Run(); |
232 reset_cb_.Reset(); | 231 reset_cb_.Reset(); |
233 } | 232 } |
234 | 233 |
235 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { | 234 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { |
236 if (MessageLoop::current() != message_loop_) { | 235 if (!message_loop_->BelongsToCurrentThread()) { |
237 message_loop_->PostTask(FROM_HERE, base::Bind( | 236 message_loop_->PostTask(FROM_HERE, base::Bind( |
238 &FFmpegVideoDecoder::Stop, this, closure)); | 237 &FFmpegVideoDecoder::Stop, this, closure)); |
239 return; | 238 return; |
240 } | 239 } |
241 | 240 |
242 if (decryptor_) | 241 if (decryptor_) |
243 decryptor_->Stop(); | 242 decryptor_->Stop(); |
244 | 243 |
245 stop_cb_ = closure; | 244 stop_cb_ = closure; |
246 | 245 |
247 // Defer stopping if a read is pending. | 246 // Defer stopping if a read is pending. |
248 if (!read_cb_.is_null()) | 247 if (!read_cb_.is_null()) |
249 return; | 248 return; |
250 | 249 |
251 DoStop(); | 250 DoStop(); |
252 } | 251 } |
253 | 252 |
254 void FFmpegVideoDecoder::DoStop() { | 253 void FFmpegVideoDecoder::DoStop() { |
255 ReleaseFFmpegResources(); | 254 ReleaseFFmpegResources(); |
256 state_ = kUninitialized; | 255 state_ = kUninitialized; |
257 base::ResetAndReturn(&stop_cb_).Run(); | 256 base::ResetAndReturn(&stop_cb_).Run(); |
258 } | 257 } |
259 | 258 |
260 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | 259 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
261 ReleaseFFmpegResources(); | 260 ReleaseFFmpegResources(); |
262 } | 261 } |
263 | 262 |
264 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { | 263 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { |
265 DCHECK_EQ(MessageLoop::current(), message_loop_); | 264 DCHECK(message_loop_->BelongsToCurrentThread()); |
266 DCHECK(!read_cb.is_null()); | 265 DCHECK(!read_cb.is_null()); |
267 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | 266 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
268 | 267 |
269 // This can happen during shutdown after Stop() has been called. | 268 // This can happen during shutdown after Stop() has been called. |
270 if (state_ == kUninitialized) { | 269 if (state_ == kUninitialized) { |
271 return; | 270 return; |
272 } | 271 } |
273 | 272 |
274 // Return empty frames if decoding has finished. | 273 // Return empty frames if decoding has finished. |
275 if (state_ == kDecodeFinished) { | 274 if (state_ == kDecodeFinished) { |
(...skipping 21 matching lines...) Expand all Loading... |
297 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | 296 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
298 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | 297 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read |
299 // callback on the same execution stack so we can get rid of forced task post. | 298 // callback on the same execution stack so we can get rid of forced task post. |
300 message_loop_->PostTask(FROM_HERE, base::Bind( | 299 message_loop_->PostTask(FROM_HERE, base::Bind( |
301 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); | 300 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); |
302 } | 301 } |
303 | 302 |
304 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( | 303 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( |
305 DemuxerStream::Status status, | 304 DemuxerStream::Status status, |
306 const scoped_refptr<DecoderBuffer>& buffer) { | 305 const scoped_refptr<DecoderBuffer>& buffer) { |
307 DCHECK_EQ(MessageLoop::current(), message_loop_); | 306 DCHECK(message_loop_->BelongsToCurrentThread()); |
308 DCHECK_NE(state_, kUninitialized); | 307 DCHECK_NE(state_, kUninitialized); |
309 DCHECK_NE(state_, kDecodeFinished); | 308 DCHECK_NE(state_, kDecodeFinished); |
310 DCHECK(!read_cb_.is_null()); | 309 DCHECK(!read_cb_.is_null()); |
311 | 310 |
312 if (!stop_cb_.is_null()) { | 311 if (!stop_cb_.is_null()) { |
313 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 312 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
314 DoStop(); | 313 DoStop(); |
315 return; | 314 return; |
316 } | 315 } |
317 | 316 |
(...skipping 24 matching lines...) Expand all Loading... |
342 void FFmpegVideoDecoder::BufferDecrypted( | 341 void FFmpegVideoDecoder::BufferDecrypted( |
343 Decryptor::Status decrypt_status, | 342 Decryptor::Status decrypt_status, |
344 const scoped_refptr<DecoderBuffer>& buffer) { | 343 const scoped_refptr<DecoderBuffer>& buffer) { |
345 message_loop_->PostTask(FROM_HERE, base::Bind( | 344 message_loop_->PostTask(FROM_HERE, base::Bind( |
346 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); | 345 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); |
347 } | 346 } |
348 | 347 |
349 void FFmpegVideoDecoder::DoBufferDecrypted( | 348 void FFmpegVideoDecoder::DoBufferDecrypted( |
350 Decryptor::Status decrypt_status, | 349 Decryptor::Status decrypt_status, |
351 const scoped_refptr<DecoderBuffer>& buffer) { | 350 const scoped_refptr<DecoderBuffer>& buffer) { |
352 DCHECK_EQ(MessageLoop::current(), message_loop_); | 351 DCHECK(message_loop_->BelongsToCurrentThread()); |
353 DCHECK_NE(state_, kUninitialized); | 352 DCHECK_NE(state_, kUninitialized); |
354 DCHECK_NE(state_, kDecodeFinished); | 353 DCHECK_NE(state_, kDecodeFinished); |
355 DCHECK(!read_cb_.is_null()); | 354 DCHECK(!read_cb_.is_null()); |
356 | 355 |
357 if (!reset_cb_.is_null()) { | 356 if (!reset_cb_.is_null()) { |
358 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 357 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
359 DoReset(); | 358 DoReset(); |
360 return; | 359 return; |
361 } | 360 } |
362 | 361 |
363 if (decrypt_status == Decryptor::kNoKey || | 362 if (decrypt_status == Decryptor::kNoKey || |
364 decrypt_status == Decryptor::kError) { | 363 decrypt_status == Decryptor::kError) { |
365 state_ = kDecodeFinished; | 364 state_ = kDecodeFinished; |
366 base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); | 365 base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); |
367 return; | 366 return; |
368 } | 367 } |
369 | 368 |
370 DCHECK_EQ(Decryptor::kSuccess, decrypt_status); | 369 DCHECK_EQ(Decryptor::kSuccess, decrypt_status); |
371 DCHECK(buffer); | 370 DCHECK(buffer); |
372 DCHECK(buffer->GetDataSize()); | 371 DCHECK(buffer->GetDataSize()); |
373 DCHECK(!buffer->GetDecryptConfig()); | 372 DCHECK(!buffer->GetDecryptConfig()); |
374 DecodeBuffer(buffer); | 373 DecodeBuffer(buffer); |
375 } | 374 } |
376 | 375 |
377 void FFmpegVideoDecoder::DecodeBuffer( | 376 void FFmpegVideoDecoder::DecodeBuffer( |
378 const scoped_refptr<DecoderBuffer>& buffer) { | 377 const scoped_refptr<DecoderBuffer>& buffer) { |
379 DCHECK_EQ(MessageLoop::current(), message_loop_); | 378 DCHECK(message_loop_->BelongsToCurrentThread()); |
380 DCHECK_NE(state_, kUninitialized); | 379 DCHECK_NE(state_, kUninitialized); |
381 DCHECK_NE(state_, kDecodeFinished); | 380 DCHECK_NE(state_, kDecodeFinished); |
382 DCHECK(reset_cb_.is_null()); | 381 DCHECK(reset_cb_.is_null()); |
383 DCHECK(!read_cb_.is_null()); | 382 DCHECK(!read_cb_.is_null()); |
384 DCHECK(buffer); | 383 DCHECK(buffer); |
385 | 384 |
386 // During decode, because reads are issued asynchronously, it is possible to | 385 // During decode, because reads are issued asynchronously, it is possible to |
387 // receive multiple end of stream buffers since each read is acked. When the | 386 // receive multiple end of stream buffers since each read is acked. When the |
388 // first end of stream buffer is read, FFmpeg may still have frames queued | 387 // first end of stream buffer is read, FFmpeg may still have frames queued |
389 // up in the decoder so we need to go through the decode loop until it stops | 388 // up in the decoder so we need to go through the decode loop until it stops |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 av_free(codec_context_); | 518 av_free(codec_context_); |
520 codec_context_ = NULL; | 519 codec_context_ = NULL; |
521 } | 520 } |
522 if (av_frame_) { | 521 if (av_frame_) { |
523 av_free(av_frame_); | 522 av_free(av_frame_); |
524 av_frame_ = NULL; | 523 av_frame_ = NULL; |
525 } | 524 } |
526 } | 525 } |
527 | 526 |
528 } // namespace media | 527 } // namespace media |
OLD | NEW |