Chromium Code Reviews| 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_audio_decoder.h" | 5 #include "media/filters/ffmpeg_audio_decoder.h" |
| 6 | 6 |
| 7 #include "base/callback_helpers.h" | 7 #include "base/callback_helpers.h" |
| 8 #include "base/single_thread_task_runner.h" | 8 #include "base/single_thread_task_runner.h" |
| 9 #include "media/base/audio_buffer.h" | 9 #include "media/base/audio_buffer.h" |
| 10 #include "media/base/audio_bus.h" | 10 #include "media/base/audio_bus.h" |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 134 log_cb_(log_cb) { | 134 log_cb_(log_cb) { |
| 135 } | 135 } |
| 136 | 136 |
| 137 FFmpegAudioDecoder::~FFmpegAudioDecoder() { | 137 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
| 138 DCHECK_EQ(state_, kUninitialized); | 138 DCHECK_EQ(state_, kUninitialized); |
| 139 DCHECK(!codec_context_); | 139 DCHECK(!codec_context_); |
| 140 DCHECK(!av_frame_); | 140 DCHECK(!av_frame_); |
| 141 } | 141 } |
| 142 | 142 |
| 143 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, | 143 void FFmpegAudioDecoder::Initialize(const AudioDecoderConfig& config, |
| 144 const PipelineStatusCB& status_cb) { | 144 const PipelineStatusCB& status_cb, |
| 145 const OutputCB& output_cb) { | |
| 145 DCHECK(task_runner_->BelongsToCurrentThread()); | 146 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 146 DCHECK(!config.is_encrypted()); | 147 DCHECK(!config.is_encrypted()); |
| 147 | 148 |
| 148 FFmpegGlue::InitializeFFmpeg(); | 149 FFmpegGlue::InitializeFFmpeg(); |
| 149 | 150 |
| 150 config_ = config; | 151 config_ = config; |
| 151 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); | 152 PipelineStatusCB initialize_cb = BindToCurrentLoop(status_cb); |
| 152 | 153 |
| 153 if (!config.IsValidConfig() || !ConfigureDecoder()) { | 154 if (!config.IsValidConfig() || !ConfigureDecoder()) { |
| 154 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 155 initialize_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
| 155 return; | 156 return; |
| 156 } | 157 } |
| 157 | 158 |
| 158 // Success! | 159 // Success! |
| 160 output_cb_ = BindToCurrentLoop(output_cb); | |
| 159 state_ = kNormal; | 161 state_ = kNormal; |
| 160 initialize_cb.Run(PIPELINE_OK); | 162 initialize_cb.Run(PIPELINE_OK); |
| 161 } | 163 } |
| 162 | 164 |
| 163 void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, | 165 void FFmpegAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer, |
| 164 const DecodeCB& decode_cb) { | 166 const DecodeCB& decode_cb) { |
| 165 DCHECK(task_runner_->BelongsToCurrentThread()); | 167 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 166 DCHECK(!decode_cb.is_null()); | 168 DCHECK(!decode_cb.is_null()); |
| 167 CHECK_NE(state_, kUninitialized); | 169 CHECK_NE(state_, kUninitialized); |
| 168 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); | 170 DecodeCB decode_cb_bound = BindToCurrentLoop(decode_cb); |
| 169 | 171 |
| 170 if (state_ == kError) { | 172 if (state_ == kError) { |
| 171 decode_cb_bound.Run(kDecodeError, NULL); | 173 decode_cb_bound.Run(kDecodeError); |
| 172 return; | 174 return; |
| 173 } | 175 } |
| 174 | 176 |
| 175 // Return empty frames if decoding has finished. | 177 // Do nothing if decoding has finished. |
| 176 if (state_ == kDecodeFinished) { | 178 if (state_ == kDecodeFinished) { |
| 177 decode_cb_bound.Run(kOk, AudioBuffer::CreateEOSBuffer()); | 179 decode_cb_bound.Run(kOk); |
| 178 return; | 180 return; |
| 179 } | 181 } |
| 180 | 182 |
| 181 if (!buffer) { | 183 if (!buffer) { |
|
xhwang
2014/06/05 21:53:50
hmm, should this ever happen?
Sergey Ulanov
2014/06/06 22:49:40
Done.
| |
| 182 decode_cb_bound.Run(kAborted, NULL); | 184 decode_cb_bound.Run(kAborted); |
| 183 return; | 185 return; |
| 184 } | 186 } |
| 185 | 187 |
| 186 DecodeBuffer(buffer, decode_cb_bound); | 188 DecodeBuffer(buffer, decode_cb_bound); |
| 187 } | 189 } |
| 188 | 190 |
| 189 scoped_refptr<AudioBuffer> FFmpegAudioDecoder::GetDecodeOutput() { | |
| 190 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 191 if (queued_audio_.empty()) | |
| 192 return NULL; | |
| 193 scoped_refptr<AudioBuffer> out = queued_audio_.front(); | |
| 194 queued_audio_.pop_front(); | |
| 195 return out; | |
| 196 } | |
| 197 | |
| 198 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { | 191 void FFmpegAudioDecoder::Reset(const base::Closure& closure) { |
| 199 DCHECK(task_runner_->BelongsToCurrentThread()); | 192 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 200 | 193 |
| 201 avcodec_flush_buffers(codec_context_.get()); | 194 avcodec_flush_buffers(codec_context_.get()); |
| 202 state_ = kNormal; | 195 state_ = kNormal; |
| 203 ResetTimestampState(); | 196 ResetTimestampState(); |
| 204 task_runner_->PostTask(FROM_HERE, closure); | 197 task_runner_->PostTask(FROM_HERE, closure); |
| 205 } | 198 } |
| 206 | 199 |
| 207 void FFmpegAudioDecoder::Stop() { | 200 void FFmpegAudioDecoder::Stop() { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 250 // When avcodec_decode_audio4() returns 0 data. | 243 // When avcodec_decode_audio4() returns 0 data. |
| 251 // kFlushCodec -> kError: | 244 // kFlushCodec -> kError: |
| 252 // When avcodec_decode_audio4() errors out. | 245 // When avcodec_decode_audio4() errors out. |
| 253 // (any state) -> kNormal: | 246 // (any state) -> kNormal: |
| 254 // Any time Reset() is called. | 247 // Any time Reset() is called. |
| 255 | 248 |
| 256 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 249 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
| 257 // occurs with some damaged files. | 250 // occurs with some damaged files. |
| 258 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp()) { | 251 if (!buffer->end_of_stream() && buffer->timestamp() == kNoTimestamp()) { |
| 259 DVLOG(1) << "Received a buffer without timestamps!"; | 252 DVLOG(1) << "Received a buffer without timestamps!"; |
| 260 decode_cb.Run(kDecodeError, NULL); | 253 decode_cb.Run(kDecodeError); |
| 261 return; | 254 return; |
| 262 } | 255 } |
| 263 | 256 |
| 264 if (!buffer->end_of_stream() && !discard_helper_->initialized() && | 257 if (!buffer->end_of_stream() && !discard_helper_->initialized() && |
| 265 codec_context_->codec_id == AV_CODEC_ID_VORBIS && | 258 codec_context_->codec_id == AV_CODEC_ID_VORBIS && |
| 266 buffer->timestamp() < base::TimeDelta()) { | 259 buffer->timestamp() < base::TimeDelta()) { |
| 267 // Dropping frames for negative timestamps as outlined in section A.2 | 260 // Dropping frames for negative timestamps as outlined in section A.2 |
| 268 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 261 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
| 269 const int discard_frames = | 262 const int discard_frames = |
| 270 discard_helper_->TimeDeltaToFrames(-buffer->timestamp()); | 263 discard_helper_->TimeDeltaToFrames(-buffer->timestamp()); |
| 271 discard_helper_->Reset(discard_frames); | 264 discard_helper_->Reset(discard_frames); |
| 272 } | 265 } |
| 273 | 266 |
| 274 // Transition to kFlushCodec on the first end of stream buffer. | 267 // Transition to kFlushCodec on the first end of stream buffer. |
| 275 if (state_ == kNormal && buffer->end_of_stream()) { | 268 if (state_ == kNormal && buffer->end_of_stream()) { |
| 276 state_ = kFlushCodec; | 269 state_ = kFlushCodec; |
| 277 } | 270 } |
| 278 | 271 |
| 279 if (!FFmpegDecode(buffer)) { | 272 if (!FFmpegDecode(buffer)) { |
| 280 state_ = kError; | 273 state_ = kError; |
| 281 decode_cb.Run(kDecodeError, NULL); | 274 decode_cb.Run(kDecodeError); |
| 282 return; | 275 return; |
| 283 } | 276 } |
| 284 | 277 |
| 285 if (queued_audio_.empty()) { | 278 if (state_ == kFlushCodec) { |
| 286 if (state_ == kFlushCodec) { | 279 state_ = kDecodeFinished; |
| 287 DCHECK(buffer->end_of_stream()); | 280 output_cb_.Run(AudioBuffer::CreateEOSBuffer()); |
|
xhwang
2014/06/05 21:53:50
should we keep calling FFmpegDecode(EOS) until the
Sergey Ulanov
2014/06/06 22:49:40
There is a loop in FFmpegDecode() which should flu
| |
| 288 state_ = kDecodeFinished; | |
| 289 decode_cb.Run(kOk, AudioBuffer::CreateEOSBuffer()); | |
| 290 return; | |
| 291 } | |
| 292 | |
| 293 decode_cb.Run(kNotEnoughData, NULL); | |
| 294 return; | |
| 295 } | 281 } |
| 296 | 282 |
| 297 decode_cb.Run(kOk, queued_audio_.front()); | 283 decode_cb.Run(kOk); |
| 298 queued_audio_.pop_front(); | |
| 299 } | 284 } |
| 300 | 285 |
| 301 bool FFmpegAudioDecoder::FFmpegDecode( | 286 bool FFmpegAudioDecoder::FFmpegDecode( |
| 302 const scoped_refptr<DecoderBuffer>& buffer) { | 287 const scoped_refptr<DecoderBuffer>& buffer) { |
| 303 DCHECK(queued_audio_.empty()); | |
| 304 | |
| 305 AVPacket packet; | 288 AVPacket packet; |
| 306 av_init_packet(&packet); | 289 av_init_packet(&packet); |
| 307 if (buffer->end_of_stream()) { | 290 if (buffer->end_of_stream()) { |
| 308 packet.data = NULL; | 291 packet.data = NULL; |
| 309 packet.size = 0; | 292 packet.size = 0; |
| 310 } else { | 293 } else { |
| 311 packet.data = const_cast<uint8*>(buffer->data()); | 294 packet.data = const_cast<uint8*>(buffer->data()); |
| 312 packet.size = buffer->data_size(); | 295 packet.size = buffer->data_size(); |
| 313 } | 296 } |
| 314 | 297 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 355 << ", Sample Format: " << av_frame_->format << " vs " | 338 << ", Sample Format: " << av_frame_->format << " vs " |
| 356 << av_sample_format_; | 339 << av_sample_format_; |
| 357 | 340 |
| 358 if (config_.codec() == kCodecAAC && | 341 if (config_.codec() == kCodecAAC && |
| 359 av_frame_->sample_rate == 2 * config_.samples_per_second()) { | 342 av_frame_->sample_rate == 2 * config_.samples_per_second()) { |
| 360 MEDIA_LOG(log_cb_) << "Implicit HE-AAC signalling is being used." | 343 MEDIA_LOG(log_cb_) << "Implicit HE-AAC signalling is being used." |
| 361 << " Please use mp4a.40.5 instead of mp4a.40.2 in" | 344 << " Please use mp4a.40.5 instead of mp4a.40.2 in" |
| 362 << " the mimetype."; | 345 << " the mimetype."; |
| 363 } | 346 } |
| 364 // This is an unrecoverable error, so bail out. | 347 // This is an unrecoverable error, so bail out. |
| 365 queued_audio_.clear(); | |
| 366 av_frame_unref(av_frame_.get()); | 348 av_frame_unref(av_frame_.get()); |
| 367 return false; | 349 return false; |
| 368 } | 350 } |
| 369 | 351 |
| 370 // Get the AudioBuffer that the data was decoded into. Adjust the number | 352 // Get the AudioBuffer that the data was decoded into. Adjust the number |
| 371 // of frames, in case fewer than requested were actually decoded. | 353 // of frames, in case fewer than requested were actually decoded. |
| 372 output = reinterpret_cast<AudioBuffer*>( | 354 output = reinterpret_cast<AudioBuffer*>( |
| 373 av_buffer_get_opaque(av_frame_->buf[0])); | 355 av_buffer_get_opaque(av_frame_->buf[0])); |
| 374 | 356 |
| 375 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), | 357 DCHECK_EQ(ChannelLayoutToChannelCount(config_.channel_layout()), |
| 376 output->channel_count()); | 358 output->channel_count()); |
| 377 const int unread_frames = output->frame_count() - av_frame_->nb_samples; | 359 const int unread_frames = output->frame_count() - av_frame_->nb_samples; |
| 378 DCHECK_GE(unread_frames, 0); | 360 DCHECK_GE(unread_frames, 0); |
| 379 if (unread_frames > 0) | 361 if (unread_frames > 0) |
| 380 output->TrimEnd(unread_frames); | 362 output->TrimEnd(unread_frames); |
| 381 | 363 |
| 382 av_frame_unref(av_frame_.get()); | 364 av_frame_unref(av_frame_.get()); |
| 383 } | 365 } |
| 384 | 366 |
| 385 // WARNING: |av_frame_| no longer has valid data at this point. | 367 // WARNING: |av_frame_| no longer has valid data at this point. |
| 386 const int decoded_frames = frame_decoded ? output->frame_count() : 0; | 368 const int decoded_frames = frame_decoded ? output->frame_count() : 0; |
| 387 if (IsEndOfStream(result, decoded_frames, buffer)) { | 369 if (IsEndOfStream(result, decoded_frames, buffer)) { |
| 388 DCHECK_EQ(packet.size, 0); | 370 DCHECK_EQ(packet.size, 0); |
| 389 queued_audio_.push_back(AudioBuffer::CreateEOSBuffer()); | 371 output_cb_.Run(AudioBuffer::CreateEOSBuffer()); |
| 390 } else if (discard_helper_->ProcessBuffers(buffer, output)) { | 372 } else if (discard_helper_->ProcessBuffers(buffer, output)) { |
| 391 queued_audio_.push_back(output); | 373 output_cb_.Run(output); |
| 392 } | 374 } |
| 393 } while (packet.size > 0); | 375 } while (packet.size > 0); |
| 394 | 376 |
| 395 return true; | 377 return true; |
| 396 } | 378 } |
| 397 | 379 |
| 398 void FFmpegAudioDecoder::ReleaseFFmpegResources() { | 380 void FFmpegAudioDecoder::ReleaseFFmpegResources() { |
| 399 codec_context_.reset(); | 381 codec_context_.reset(); |
| 400 av_frame_.reset(); | 382 av_frame_.reset(); |
| 401 } | 383 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 454 | 436 |
| 455 ResetTimestampState(); | 437 ResetTimestampState(); |
| 456 return true; | 438 return true; |
| 457 } | 439 } |
| 458 | 440 |
| 459 void FFmpegAudioDecoder::ResetTimestampState() { | 441 void FFmpegAudioDecoder::ResetTimestampState() { |
| 460 discard_helper_->Reset(config_.codec_delay()); | 442 discard_helper_->Reset(config_.codec_delay()); |
| 461 } | 443 } |
| 462 | 444 |
| 463 } // namespace media | 445 } // namespace media |
| OLD | NEW |