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