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 22 matching lines...) Expand all Loading... |
46 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 47 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
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(const MessageLoopCB& message_loop_cb, |
57 const base::Callback<MessageLoop*()>& message_loop_cb, | 58 Decryptor* decryptor) |
58 Decryptor* decryptor) | |
59 : message_loop_factory_cb_(message_loop_cb), | 59 : message_loop_factory_cb_(message_loop_cb), |
60 message_loop_(NULL), | 60 message_loop_(NULL), |
61 state_(kUninitialized), | 61 state_(kUninitialized), |
62 codec_context_(NULL), | 62 codec_context_(NULL), |
63 av_frame_(NULL), | 63 av_frame_(NULL), |
64 decryptor_(decryptor) { | 64 decryptor_(decryptor) { |
65 } | 65 } |
66 | 66 |
67 int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context, | 67 int FFmpegVideoDecoder::GetVideoBuffer(AVCodecContext* codec_context, |
68 AVFrame* frame) { | 68 AVFrame* frame) { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 frame->opaque = NULL; | 132 frame->opaque = NULL; |
133 } | 133 } |
134 | 134 |
135 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, | 135 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
136 const PipelineStatusCB& status_cb, | 136 const PipelineStatusCB& status_cb, |
137 const StatisticsCB& statistics_cb) { | 137 const StatisticsCB& statistics_cb) { |
138 // Ensure FFmpeg has been initialized | 138 // Ensure FFmpeg has been initialized |
139 FFmpegGlue::GetInstance(); | 139 FFmpegGlue::GetInstance(); |
140 | 140 |
141 if (!message_loop_) { | 141 if (!message_loop_) { |
142 message_loop_ = message_loop_factory_cb_.Run(); | 142 message_loop_ = base::ResetAndReturn(&message_loop_factory_cb_).Run(); |
143 message_loop_factory_cb_.Reset(); | |
144 | |
145 message_loop_->PostTask(FROM_HERE, base::Bind( | 143 message_loop_->PostTask(FROM_HERE, base::Bind( |
146 &FFmpegVideoDecoder::Initialize, this, | 144 &FFmpegVideoDecoder::Initialize, this, |
147 stream, status_cb, statistics_cb)); | 145 stream, status_cb, statistics_cb)); |
148 return; | 146 return; |
149 } | 147 } |
150 | 148 |
151 DCHECK_EQ(MessageLoop::current(), message_loop_); | 149 DCHECK(message_loop_->BelongsToCurrentThread()); |
152 DCHECK(!demuxer_stream_); | 150 DCHECK(!demuxer_stream_); |
153 | 151 |
154 if (!stream) { | 152 if (!stream) { |
155 status_cb.Run(PIPELINE_ERROR_DECODE); | 153 status_cb.Run(PIPELINE_ERROR_DECODE); |
156 return; | 154 return; |
157 } | 155 } |
158 | 156 |
159 demuxer_stream_ = stream; | 157 demuxer_stream_ = stream; |
160 statistics_cb_ = statistics_cb; | 158 statistics_cb_ = statistics_cb; |
161 | 159 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 } | 199 } |
202 | 200 |
203 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { | 201 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { |
204 // Complete operation asynchronously on different stack of execution as per | 202 // Complete operation asynchronously on different stack of execution as per |
205 // the API contract of VideoDecoder::Read() | 203 // the API contract of VideoDecoder::Read() |
206 message_loop_->PostTask(FROM_HERE, base::Bind( | 204 message_loop_->PostTask(FROM_HERE, base::Bind( |
207 &FFmpegVideoDecoder::DoRead, this, read_cb)); | 205 &FFmpegVideoDecoder::DoRead, this, read_cb)); |
208 } | 206 } |
209 | 207 |
210 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | 208 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
211 if (MessageLoop::current() != message_loop_) { | 209 if (!message_loop_->BelongsToCurrentThread()) { |
212 message_loop_->PostTask(FROM_HERE, base::Bind( | 210 message_loop_->PostTask(FROM_HERE, base::Bind( |
213 &FFmpegVideoDecoder::Reset, this, closure)); | 211 &FFmpegVideoDecoder::Reset, this, closure)); |
214 return; | 212 return; |
215 } | 213 } |
216 | 214 |
217 reset_cb_ = closure; | 215 reset_cb_ = closure; |
218 | 216 |
219 // Defer the reset if a read is pending. | 217 // Defer the reset if a read is pending. |
220 if (!read_cb_.is_null()) | 218 if (!read_cb_.is_null()) |
221 return; | 219 return; |
222 | 220 |
223 DoReset(); | 221 DoReset(); |
224 } | 222 } |
225 | 223 |
226 void FFmpegVideoDecoder::DoReset() { | 224 void FFmpegVideoDecoder::DoReset() { |
227 DCHECK(read_cb_.is_null()); | 225 DCHECK(read_cb_.is_null()); |
228 | 226 |
229 avcodec_flush_buffers(codec_context_); | 227 avcodec_flush_buffers(codec_context_); |
230 state_ = kNormal; | 228 state_ = kNormal; |
231 reset_cb_.Run(); | 229 reset_cb_.Run(); |
232 reset_cb_.Reset(); | 230 reset_cb_.Reset(); |
233 } | 231 } |
234 | 232 |
235 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { | 233 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { |
236 if (MessageLoop::current() != message_loop_) { | 234 if (!message_loop_->BelongsToCurrentThread()) { |
237 message_loop_->PostTask(FROM_HERE, base::Bind( | 235 message_loop_->PostTask(FROM_HERE, base::Bind( |
238 &FFmpegVideoDecoder::Stop, this, closure)); | 236 &FFmpegVideoDecoder::Stop, this, closure)); |
239 return; | 237 return; |
240 } | 238 } |
241 | 239 |
242 if (decryptor_) | 240 if (decryptor_) |
243 decryptor_->Stop(); | 241 decryptor_->Stop(); |
244 | 242 |
245 stop_cb_ = closure; | 243 stop_cb_ = closure; |
246 | 244 |
247 // Defer stopping if a read is pending. | 245 // Defer stopping if a read is pending. |
248 if (!read_cb_.is_null()) | 246 if (!read_cb_.is_null()) |
249 return; | 247 return; |
250 | 248 |
251 DoStop(); | 249 DoStop(); |
252 } | 250 } |
253 | 251 |
254 void FFmpegVideoDecoder::DoStop() { | 252 void FFmpegVideoDecoder::DoStop() { |
255 ReleaseFFmpegResources(); | 253 ReleaseFFmpegResources(); |
256 state_ = kUninitialized; | 254 state_ = kUninitialized; |
257 base::ResetAndReturn(&stop_cb_).Run(); | 255 base::ResetAndReturn(&stop_cb_).Run(); |
258 } | 256 } |
259 | 257 |
260 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | 258 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
261 ReleaseFFmpegResources(); | 259 ReleaseFFmpegResources(); |
262 } | 260 } |
263 | 261 |
264 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { | 262 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { |
265 DCHECK_EQ(MessageLoop::current(), message_loop_); | 263 DCHECK(message_loop_->BelongsToCurrentThread()); |
266 DCHECK(!read_cb.is_null()); | 264 DCHECK(!read_cb.is_null()); |
267 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | 265 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
268 | 266 |
269 // This can happen during shutdown after Stop() has been called. | 267 // This can happen during shutdown after Stop() has been called. |
270 if (state_ == kUninitialized) { | 268 if (state_ == kUninitialized) { |
271 return; | 269 return; |
272 } | 270 } |
273 | 271 |
274 // Return empty frames if decoding has finished. | 272 // Return empty frames if decoding has finished. |
275 if (state_ == kDecodeFinished) { | 273 if (state_ == kDecodeFinished) { |
(...skipping 21 matching lines...) Expand all Loading... |
297 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | 295 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
298 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | 296 // 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. | 297 // callback on the same execution stack so we can get rid of forced task post. |
300 message_loop_->PostTask(FROM_HERE, base::Bind( | 298 message_loop_->PostTask(FROM_HERE, base::Bind( |
301 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); | 299 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); |
302 } | 300 } |
303 | 301 |
304 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( | 302 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( |
305 DemuxerStream::Status status, | 303 DemuxerStream::Status status, |
306 const scoped_refptr<DecoderBuffer>& buffer) { | 304 const scoped_refptr<DecoderBuffer>& buffer) { |
307 DCHECK_EQ(MessageLoop::current(), message_loop_); | 305 DCHECK(message_loop_->BelongsToCurrentThread()); |
308 DCHECK_NE(state_, kUninitialized); | 306 DCHECK_NE(state_, kUninitialized); |
309 DCHECK_NE(state_, kDecodeFinished); | 307 DCHECK_NE(state_, kDecodeFinished); |
310 DCHECK(!read_cb_.is_null()); | 308 DCHECK(!read_cb_.is_null()); |
311 | 309 |
312 if (!stop_cb_.is_null()) { | 310 if (!stop_cb_.is_null()) { |
313 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 311 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
314 DoStop(); | 312 DoStop(); |
315 return; | 313 return; |
316 } | 314 } |
317 | 315 |
(...skipping 24 matching lines...) Expand all Loading... |
342 void FFmpegVideoDecoder::BufferDecrypted( | 340 void FFmpegVideoDecoder::BufferDecrypted( |
343 Decryptor::Status decrypt_status, | 341 Decryptor::Status decrypt_status, |
344 const scoped_refptr<DecoderBuffer>& buffer) { | 342 const scoped_refptr<DecoderBuffer>& buffer) { |
345 message_loop_->PostTask(FROM_HERE, base::Bind( | 343 message_loop_->PostTask(FROM_HERE, base::Bind( |
346 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); | 344 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); |
347 } | 345 } |
348 | 346 |
349 void FFmpegVideoDecoder::DoBufferDecrypted( | 347 void FFmpegVideoDecoder::DoBufferDecrypted( |
350 Decryptor::Status decrypt_status, | 348 Decryptor::Status decrypt_status, |
351 const scoped_refptr<DecoderBuffer>& buffer) { | 349 const scoped_refptr<DecoderBuffer>& buffer) { |
352 DCHECK_EQ(MessageLoop::current(), message_loop_); | 350 DCHECK(message_loop_->BelongsToCurrentThread()); |
353 DCHECK_NE(state_, kUninitialized); | 351 DCHECK_NE(state_, kUninitialized); |
354 DCHECK_NE(state_, kDecodeFinished); | 352 DCHECK_NE(state_, kDecodeFinished); |
355 DCHECK(!read_cb_.is_null()); | 353 DCHECK(!read_cb_.is_null()); |
356 | 354 |
357 if (!reset_cb_.is_null()) { | 355 if (!reset_cb_.is_null()) { |
358 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 356 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
359 DoReset(); | 357 DoReset(); |
360 return; | 358 return; |
361 } | 359 } |
362 | 360 |
363 if (decrypt_status == Decryptor::kNoKey || | 361 if (decrypt_status == Decryptor::kNoKey || |
364 decrypt_status == Decryptor::kError) { | 362 decrypt_status == Decryptor::kError) { |
365 state_ = kDecodeFinished; | 363 state_ = kDecodeFinished; |
366 base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); | 364 base::ResetAndReturn(&read_cb_).Run(kDecryptError, NULL); |
367 return; | 365 return; |
368 } | 366 } |
369 | 367 |
370 DCHECK_EQ(Decryptor::kSuccess, decrypt_status); | 368 DCHECK_EQ(Decryptor::kSuccess, decrypt_status); |
371 DCHECK(buffer); | 369 DCHECK(buffer); |
372 DCHECK(buffer->GetDataSize()); | 370 DCHECK(buffer->GetDataSize()); |
373 DCHECK(!buffer->GetDecryptConfig()); | 371 DCHECK(!buffer->GetDecryptConfig()); |
374 DecodeBuffer(buffer); | 372 DecodeBuffer(buffer); |
375 } | 373 } |
376 | 374 |
377 void FFmpegVideoDecoder::DecodeBuffer( | 375 void FFmpegVideoDecoder::DecodeBuffer( |
378 const scoped_refptr<DecoderBuffer>& buffer) { | 376 const scoped_refptr<DecoderBuffer>& buffer) { |
379 DCHECK_EQ(MessageLoop::current(), message_loop_); | 377 DCHECK(message_loop_->BelongsToCurrentThread()); |
380 DCHECK_NE(state_, kUninitialized); | 378 DCHECK_NE(state_, kUninitialized); |
381 DCHECK_NE(state_, kDecodeFinished); | 379 DCHECK_NE(state_, kDecodeFinished); |
382 DCHECK(reset_cb_.is_null()); | 380 DCHECK(reset_cb_.is_null()); |
383 DCHECK(!read_cb_.is_null()); | 381 DCHECK(!read_cb_.is_null()); |
384 DCHECK(buffer); | 382 DCHECK(buffer); |
385 | 383 |
386 // During decode, because reads are issued asynchronously, it is possible to | 384 // 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 | 385 // 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 | 386 // 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 | 387 // 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_); | 517 av_free(codec_context_); |
520 codec_context_ = NULL; | 518 codec_context_ = NULL; |
521 } | 519 } |
522 if (av_frame_) { | 520 if (av_frame_) { |
523 av_free(av_frame_); | 521 av_free(av_frame_); |
524 av_frame_ = NULL; | 522 av_frame_ = NULL; |
525 } | 523 } |
526 } | 524 } |
527 | 525 |
528 } // namespace media | 526 } // namespace media |
OLD | NEW |