| 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/location.h" | 13 #include "base/location.h" |
| 14 #include "base/message_loop_proxy.h" | 14 #include "base/message_loop_proxy.h" |
| 15 #include "base/string_number_conversions.h" | 15 #include "base/string_number_conversions.h" |
| 16 #include "media/base/bind_to_loop.h" |
| 16 #include "media/base/decoder_buffer.h" | 17 #include "media/base/decoder_buffer.h" |
| 17 #include "media/base/demuxer_stream.h" | 18 #include "media/base/demuxer_stream.h" |
| 18 #include "media/base/limits.h" | 19 #include "media/base/limits.h" |
| 19 #include "media/base/media_switches.h" | 20 #include "media/base/media_switches.h" |
| 20 #include "media/base/pipeline.h" | 21 #include "media/base/pipeline.h" |
| 21 #include "media/base/video_decoder_config.h" | 22 #include "media/base/video_decoder_config.h" |
| 22 #include "media/base/video_frame.h" | 23 #include "media/base/video_frame.h" |
| 23 #include "media/base/video_util.h" | 24 #include "media/base/video_util.h" |
| 24 #include "media/ffmpeg/ffmpeg_common.h" | 25 #include "media/ffmpeg/ffmpeg_common.h" |
| 25 #include "media/filters/ffmpeg_glue.h" | 26 #include "media/filters/ffmpeg_glue.h" |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 126 | 127 |
| 127 // The FFmpeg API expects us to zero the data pointers in | 128 // The FFmpeg API expects us to zero the data pointers in |
| 128 // this callback | 129 // this callback |
| 129 memset(frame->data, 0, sizeof(frame->data)); | 130 memset(frame->data, 0, sizeof(frame->data)); |
| 130 frame->opaque = NULL; | 131 frame->opaque = NULL; |
| 131 } | 132 } |
| 132 | 133 |
| 133 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, | 134 void FFmpegVideoDecoder::Initialize(const scoped_refptr<DemuxerStream>& stream, |
| 134 const PipelineStatusCB& status_cb, | 135 const PipelineStatusCB& status_cb, |
| 135 const StatisticsCB& statistics_cb) { | 136 const StatisticsCB& statistics_cb) { |
| 136 if (!message_loop_->BelongsToCurrentThread()) { | 137 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 137 message_loop_->PostTask(FROM_HERE, base::Bind( | 138 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
| 138 &FFmpegVideoDecoder::Initialize, this, | |
| 139 stream, status_cb, statistics_cb)); | |
| 140 return; | |
| 141 } | |
| 142 | 139 |
| 143 FFmpegGlue::InitializeFFmpeg(); | 140 FFmpegGlue::InitializeFFmpeg(); |
| 144 DCHECK(!demuxer_stream_) << "Already initialized."; | 141 DCHECK(!demuxer_stream_) << "Already initialized."; |
| 145 | 142 |
| 146 if (!stream) { | 143 if (!stream) { |
| 147 status_cb.Run(PIPELINE_ERROR_DECODE); | 144 initialize_cb.Run(PIPELINE_ERROR_DECODE); |
| 148 return; | 145 return; |
| 149 } | 146 } |
| 150 | 147 |
| 151 demuxer_stream_ = stream; | 148 demuxer_stream_ = stream; |
| 152 statistics_cb_ = statistics_cb; | 149 statistics_cb_ = statistics_cb; |
| 153 | 150 |
| 154 if (!ConfigureDecoder()) { | 151 if (!ConfigureDecoder()) { |
| 155 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 152 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 156 return; | 153 return; |
| 157 } | 154 } |
| 158 | 155 |
| 159 // Success! | 156 // Success! |
| 160 state_ = kNormal; | 157 state_ = kNormal; |
| 161 status_cb.Run(PIPELINE_OK); | 158 initialize_cb.Run(PIPELINE_OK); |
| 162 } | 159 } |
| 163 | 160 |
| 164 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { | 161 void FFmpegVideoDecoder::Read(const ReadCB& read_cb) { |
| 165 // Complete operation asynchronously on different stack of execution as per | 162 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 166 // the API contract of VideoDecoder::Read() | 163 DCHECK(!read_cb.is_null()); |
| 167 message_loop_->PostTask(FROM_HERE, base::Bind( | 164 CHECK_NE(state_, kUninitialized); |
| 168 &FFmpegVideoDecoder::DoRead, this, read_cb)); | 165 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
| 166 read_cb_ = BindToCurrentLoop(read_cb); |
| 167 |
| 168 // Return empty frames if decoding has finished. |
| 169 if (state_ == kDecodeFinished) { |
| 170 base::ResetAndReturn(&read_cb_).Run(kOk, VideoFrame::CreateEmptyFrame()); |
| 171 return; |
| 172 } |
| 173 |
| 174 ReadFromDemuxerStream(); |
| 169 } | 175 } |
| 170 | 176 |
| 171 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { | 177 void FFmpegVideoDecoder::Reset(const base::Closure& closure) { |
| 172 if (!message_loop_->BelongsToCurrentThread()) { | 178 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 173 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 174 &FFmpegVideoDecoder::Reset, this, closure)); | |
| 175 return; | |
| 176 } | |
| 177 | |
| 178 DCHECK(reset_cb_.is_null()); | 179 DCHECK(reset_cb_.is_null()); |
| 179 reset_cb_ = closure; | 180 reset_cb_ = BindToCurrentLoop(closure); |
| 180 | 181 |
| 181 if (decryptor_) | 182 if (decryptor_) |
| 182 decryptor_->CancelDecrypt(Decryptor::kVideo); | 183 decryptor_->CancelDecrypt(Decryptor::kVideo); |
| 183 | 184 |
| 184 // Defer the reset if a read is pending. | 185 // Defer the reset if a read is pending. |
| 185 if (!read_cb_.is_null()) | 186 if (!read_cb_.is_null()) |
| 186 return; | 187 return; |
| 187 | 188 |
| 188 DoReset(); | 189 DoReset(); |
| 189 } | 190 } |
| 190 | 191 |
| 191 void FFmpegVideoDecoder::DoReset() { | 192 void FFmpegVideoDecoder::DoReset() { |
| 192 DCHECK(read_cb_.is_null()); | 193 DCHECK(read_cb_.is_null()); |
| 193 | 194 |
| 194 avcodec_flush_buffers(codec_context_); | 195 avcodec_flush_buffers(codec_context_); |
| 195 state_ = kNormal; | 196 state_ = kNormal; |
| 196 reset_cb_.Run(); | 197 base::ResetAndReturn(&reset_cb_).Run(); |
| 197 reset_cb_.Reset(); | |
| 198 } | 198 } |
| 199 | 199 |
| 200 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { | 200 void FFmpegVideoDecoder::Stop(const base::Closure& closure) { |
| 201 if (!message_loop_->BelongsToCurrentThread()) { | 201 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 202 message_loop_->PostTask(FROM_HERE, base::Bind( | 202 base::ScopedClosureRunner runner(BindToCurrentLoop(closure)); |
| 203 &FFmpegVideoDecoder::Stop, this, closure)); | 203 |
| 204 if (state_ == kUninitialized) |
| 204 return; | 205 return; |
| 205 } | |
| 206 | |
| 207 if (state_ == kUninitialized) { | |
| 208 closure.Run(); | |
| 209 return; | |
| 210 } | |
| 211 | 206 |
| 212 if (decryptor_) | 207 if (decryptor_) |
| 213 decryptor_->CancelDecrypt(Decryptor::kVideo); | 208 decryptor_->CancelDecrypt(Decryptor::kVideo); |
| 214 | 209 |
| 215 if (!read_cb_.is_null()) | 210 if (!read_cb_.is_null()) |
| 216 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 211 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| 217 | 212 |
| 218 ReleaseFFmpegResources(); | 213 ReleaseFFmpegResources(); |
| 219 state_ = kUninitialized; | 214 state_ = kUninitialized; |
| 220 closure.Run(); | |
| 221 } | 215 } |
| 222 | 216 |
| 223 FFmpegVideoDecoder::~FFmpegVideoDecoder() { | 217 FFmpegVideoDecoder::~FFmpegVideoDecoder() { |
| 224 DCHECK_EQ(kUninitialized, state_); | 218 DCHECK_EQ(kUninitialized, state_); |
| 225 DCHECK(!codec_context_); | 219 DCHECK(!codec_context_); |
| 226 DCHECK(!av_frame_); | 220 DCHECK(!av_frame_); |
| 227 } | 221 } |
| 228 | 222 |
| 229 void FFmpegVideoDecoder::DoRead(const ReadCB& read_cb) { | |
| 230 DCHECK(message_loop_->BelongsToCurrentThread()); | |
| 231 DCHECK(!read_cb.is_null()); | |
| 232 CHECK_NE(state_, kUninitialized); | |
| 233 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | |
| 234 | |
| 235 // Return empty frames if decoding has finished. | |
| 236 if (state_ == kDecodeFinished) { | |
| 237 read_cb.Run(kOk, VideoFrame::CreateEmptyFrame()); | |
| 238 return; | |
| 239 } | |
| 240 | |
| 241 read_cb_ = read_cb; | |
| 242 ReadFromDemuxerStream(); | |
| 243 } | |
| 244 | |
| 245 void FFmpegVideoDecoder::ReadFromDemuxerStream() { | 223 void FFmpegVideoDecoder::ReadFromDemuxerStream() { |
| 246 DCHECK_NE(state_, kUninitialized); | 224 DCHECK_NE(state_, kUninitialized); |
| 247 DCHECK_NE(state_, kDecodeFinished); | 225 DCHECK_NE(state_, kDecodeFinished); |
| 248 DCHECK(!read_cb_.is_null()); | 226 DCHECK(!read_cb_.is_null()); |
| 249 | 227 |
| 250 demuxer_stream_->Read(base::Bind( | 228 demuxer_stream_->Read(base::Bind( |
| 251 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this)); | 229 &FFmpegVideoDecoder::DecryptOrDecodeBuffer, this)); |
| 252 } | 230 } |
| 253 | 231 |
| 254 void FFmpegVideoDecoder::DoDecryptOrDecodeBuffer( | 232 void FFmpegVideoDecoder::DecryptOrDecodeBuffer( |
| 255 DemuxerStream::Status status, | 233 DemuxerStream::Status status, |
| 256 const scoped_refptr<DecoderBuffer>& buffer) { | 234 const scoped_refptr<DecoderBuffer>& buffer) { |
| 257 if (!message_loop_->BelongsToCurrentThread()) { | 235 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 258 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 259 &FFmpegVideoDecoder::DoDecryptOrDecodeBuffer, this, status, buffer)); | |
| 260 return; | |
| 261 } | |
| 262 | |
| 263 DCHECK_NE(state_, kDecodeFinished); | 236 DCHECK_NE(state_, kDecodeFinished); |
| 264 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | 237 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
| 265 | 238 |
| 266 if (state_ == kUninitialized) | 239 if (state_ == kUninitialized) |
| 267 return; | 240 return; |
| 268 | 241 |
| 269 DCHECK(!read_cb_.is_null()); | 242 DCHECK(!read_cb_.is_null()); |
| 270 | 243 |
| 271 if (!reset_cb_.is_null()) { | 244 if (!reset_cb_.is_null()) { |
| 272 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 245 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 284 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 257 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 285 return; | 258 return; |
| 286 } | 259 } |
| 287 | 260 |
| 288 ReadFromDemuxerStream(); | 261 ReadFromDemuxerStream(); |
| 289 return; | 262 return; |
| 290 } | 263 } |
| 291 | 264 |
| 292 DCHECK_EQ(status, DemuxerStream::kOk); | 265 DCHECK_EQ(status, DemuxerStream::kOk); |
| 293 | 266 |
| 267 // TODO(xhwang): Remove decryptor after DecryptingDemuxerStream is ready. |
| 294 if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { | 268 if (buffer->GetDecryptConfig() && buffer->GetDataSize()) { |
| 295 decryptor_->Decrypt(Decryptor::kVideo, | 269 decryptor_->Decrypt(Decryptor::kVideo, buffer, BindToCurrentLoop( |
| 296 buffer, | 270 base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this))); |
| 297 base::Bind(&FFmpegVideoDecoder::BufferDecrypted, this)); | |
| 298 return; | 271 return; |
| 299 } | 272 } |
| 300 | 273 |
| 301 DecodeBuffer(buffer); | 274 DecodeBuffer(buffer); |
| 302 } | 275 } |
| 303 | 276 |
| 304 void FFmpegVideoDecoder::BufferDecrypted( | 277 void FFmpegVideoDecoder::BufferDecrypted( |
| 305 Decryptor::Status decrypt_status, | 278 Decryptor::Status decrypt_status, |
| 306 const scoped_refptr<DecoderBuffer>& buffer) { | 279 const scoped_refptr<DecoderBuffer>& buffer) { |
| 307 message_loop_->PostTask(FROM_HERE, base::Bind( | |
| 308 &FFmpegVideoDecoder::DoBufferDecrypted, this, decrypt_status, buffer)); | |
| 309 } | |
| 310 | |
| 311 void FFmpegVideoDecoder::DoBufferDecrypted( | |
| 312 Decryptor::Status decrypt_status, | |
| 313 const scoped_refptr<DecoderBuffer>& buffer) { | |
| 314 DCHECK(message_loop_->BelongsToCurrentThread()); | 280 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 315 DCHECK_NE(state_, kDecodeFinished); | 281 DCHECK_NE(state_, kDecodeFinished); |
| 316 | 282 |
| 317 if (state_ == kUninitialized) | 283 if (state_ == kUninitialized) |
| 318 return; | 284 return; |
| 319 | 285 |
| 320 DCHECK(!read_cb_.is_null()); | 286 DCHECK(!read_cb_.is_null()); |
| 321 | 287 |
| 322 if (!reset_cb_.is_null()) { | 288 if (!reset_cb_.is_null()) { |
| 323 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); | 289 base::ResetAndReturn(&read_cb_).Run(kOk, NULL); |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { | 490 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { |
| 525 ReleaseFFmpegResources(); | 491 ReleaseFFmpegResources(); |
| 526 return false; | 492 return false; |
| 527 } | 493 } |
| 528 | 494 |
| 529 av_frame_ = avcodec_alloc_frame(); | 495 av_frame_ = avcodec_alloc_frame(); |
| 530 return true; | 496 return true; |
| 531 } | 497 } |
| 532 | 498 |
| 533 } // namespace media | 499 } // namespace media |
| OLD | NEW |