| 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 |