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/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/message_loop/message_loop_proxy.h" | 10 #include "base/message_loop/message_loop_proxy.h" |
| (...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 | 350 |
| 351 // Release existing decoder resources if necessary. | 351 // Release existing decoder resources if necessary. |
| 352 ReleaseFFmpegResources(); | 352 ReleaseFFmpegResources(); |
| 353 | 353 |
| 354 // Initialize AVCodecContext structure. | 354 // Initialize AVCodecContext structure. |
| 355 codec_context_ = avcodec_alloc_context3(NULL); | 355 codec_context_ = avcodec_alloc_context3(NULL); |
| 356 AudioDecoderConfigToAVCodecContext(config, codec_context_); | 356 AudioDecoderConfigToAVCodecContext(config, codec_context_); |
| 357 | 357 |
| 358 codec_context_->opaque = this; | 358 codec_context_->opaque = this; |
| 359 codec_context_->get_buffer2 = GetAudioBufferImpl; | 359 codec_context_->get_buffer2 = GetAudioBufferImpl; |
| 360 codec_context_->refcounted_frames = 1; | |
| 360 | 361 |
| 361 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | 362 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
| 362 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { | 363 if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) { |
| 363 DLOG(ERROR) << "Could not initialize audio decoder: " | 364 DLOG(ERROR) << "Could not initialize audio decoder: " |
| 364 << codec_context_->codec_id; | 365 << codec_context_->codec_id; |
| 365 return false; | 366 return false; |
| 366 } | 367 } |
| 367 | 368 |
| 368 // Success! | 369 // Success! |
| 369 av_frame_ = avcodec_alloc_frame(); | 370 av_frame_ = avcodec_alloc_frame(); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 389 return true; | 390 return true; |
| 390 } | 391 } |
| 391 | 392 |
| 392 void FFmpegAudioDecoder::ReleaseFFmpegResources() { | 393 void FFmpegAudioDecoder::ReleaseFFmpegResources() { |
| 393 if (codec_context_) { | 394 if (codec_context_) { |
| 394 av_free(codec_context_->extradata); | 395 av_free(codec_context_->extradata); |
| 395 avcodec_close(codec_context_); | 396 avcodec_close(codec_context_); |
| 396 av_free(codec_context_); | 397 av_free(codec_context_); |
| 397 } | 398 } |
| 398 | 399 |
| 399 if (av_frame_) { | 400 if (av_frame_) |
| 400 av_free(av_frame_); | 401 av_frame_free(&av_frame_); |
| 401 av_frame_ = NULL; | |
| 402 } | |
| 403 } | 402 } |
| 404 | 403 |
| 405 void FFmpegAudioDecoder::ResetTimestampState() { | 404 void FFmpegAudioDecoder::ResetTimestampState() { |
| 406 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); | 405 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); |
| 407 last_input_timestamp_ = kNoTimestamp(); | 406 last_input_timestamp_ = kNoTimestamp(); |
| 408 output_frames_to_drop_ = 0; | 407 output_frames_to_drop_ = 0; |
| 409 } | 408 } |
| 410 | 409 |
| 411 void FFmpegAudioDecoder::RunDecodeLoop( | 410 void FFmpegAudioDecoder::RunDecodeLoop( |
| 412 const scoped_refptr<DecoderBuffer>& input, | 411 const scoped_refptr<DecoderBuffer>& input, |
| 413 bool skip_eos_append) { | 412 bool skip_eos_append) { |
| 414 AVPacket packet; | 413 AVPacket packet; |
| 415 av_init_packet(&packet); | 414 av_init_packet(&packet); |
| 416 if (input->end_of_stream()) { | 415 if (input->end_of_stream()) { |
| 417 packet.data = NULL; | 416 packet.data = NULL; |
| 418 packet.size = 0; | 417 packet.size = 0; |
| 419 } else { | 418 } else { |
| 420 packet.data = const_cast<uint8*>(input->data()); | 419 packet.data = const_cast<uint8*>(input->data()); |
| 421 packet.size = input->data_size(); | 420 packet.size = input->data_size(); |
| 422 } | 421 } |
| 423 | 422 |
| 424 // Each audio packet may contain several frames, so we must call the decoder | 423 // Each audio packet may contain several frames, so we must call the decoder |
| 425 // until we've exhausted the packet. Regardless of the packet size we always | 424 // until we've exhausted the packet. Regardless of the packet size we always |
| 426 // want to hand it to the decoder at least once, otherwise we would end up | 425 // want to hand it to the decoder at least once, otherwise we would end up |
| 427 // skipping end of stream packets since they have a size of zero. | 426 // skipping end of stream packets since they have a size of zero. |
| 428 do { | 427 do { |
| 429 // Reset frame to default values. | |
| 430 avcodec_get_frame_defaults(av_frame_); | |
| 431 | |
| 432 int frame_decoded = 0; | 428 int frame_decoded = 0; |
| 433 int result = avcodec_decode_audio4( | 429 int result = avcodec_decode_audio4( |
| 434 codec_context_, av_frame_, &frame_decoded, &packet); | 430 codec_context_, av_frame_, &frame_decoded, &packet); |
| 435 | 431 |
| 436 if (result < 0) { | 432 if (result < 0) { |
| 437 DCHECK(!input->end_of_stream()) | 433 DCHECK(!input->end_of_stream()) |
| 438 << "End of stream buffer produced an error! " | 434 << "End of stream buffer produced an error! " |
| 439 << "This is quite possibly a bug in the audio decoder not handling " | 435 << "This is quite possibly a bug in the audio decoder not handling " |
| 440 << "end of stream AVPackets correctly."; | 436 << "end of stream AVPackets correctly."; |
| 441 | 437 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 480 << " Sample Rate: " << av_frame_->sample_rate << " vs " | 476 << " Sample Rate: " << av_frame_->sample_rate << " vs " |
| 481 << samples_per_second_ | 477 << samples_per_second_ |
| 482 << ", Channels: " << channels << " vs " | 478 << ", Channels: " << channels << " vs " |
| 483 << channels_ | 479 << channels_ |
| 484 << ", Sample Format: " << av_frame_->format << " vs " | 480 << ", Sample Format: " << av_frame_->format << " vs " |
| 485 << av_sample_format_; | 481 << av_sample_format_; |
| 486 | 482 |
| 487 // This is an unrecoverable error, so bail out. | 483 // This is an unrecoverable error, so bail out. |
| 488 QueuedAudioBuffer queue_entry = { kDecodeError, NULL }; | 484 QueuedAudioBuffer queue_entry = { kDecodeError, NULL }; |
| 489 queued_audio_.push_back(queue_entry); | 485 queued_audio_.push_back(queue_entry); |
| 486 av_frame_unref(av_frame_); | |
|
scherkus (not reviewing)
2013/08/27 00:23:44
when are refs taken?
worth writing a ScopedAVFram
DaleCurtis
2013/08/27 00:30:13
API contract says we have to call this to destroy
DaleCurtis
2013/08/27 00:30:51
(contract when refcounted_frames=1 that is).
| |
| 490 break; | 487 break; |
| 491 } | 488 } |
| 492 | 489 |
| 493 // Get the AudioBuffer that the data was decoded into. Adjust the number | 490 // Get the AudioBuffer that the data was decoded into. Adjust the number |
| 494 // of frames, in case fewer than requested were actually decoded. | 491 // of frames, in case fewer than requested were actually decoded. |
| 495 output = reinterpret_cast<AudioBuffer*>( | 492 output = reinterpret_cast<AudioBuffer*>( |
| 496 av_buffer_get_opaque(av_frame_->buf[0])); | 493 av_buffer_get_opaque(av_frame_->buf[0])); |
| 497 DCHECK_EQ(channels_, output->channel_count()); | 494 DCHECK_EQ(channels_, output->channel_count()); |
| 498 original_frames = av_frame_->nb_samples; | 495 original_frames = av_frame_->nb_samples; |
| 499 int unread_frames = output->frame_count() - original_frames; | 496 int unread_frames = output->frame_count() - original_frames; |
| 500 DCHECK_GE(unread_frames, 0); | 497 DCHECK_GE(unread_frames, 0); |
| 501 if (unread_frames > 0) | 498 if (unread_frames > 0) |
| 502 output->TrimEnd(unread_frames); | 499 output->TrimEnd(unread_frames); |
| 503 | 500 |
| 504 // If there are frames to drop, get rid of as many as we can. | 501 // If there are frames to drop, get rid of as many as we can. |
| 505 if (output_frames_to_drop_ > 0) { | 502 if (output_frames_to_drop_ > 0) { |
| 506 int drop = std::min(output->frame_count(), output_frames_to_drop_); | 503 int drop = std::min(output->frame_count(), output_frames_to_drop_); |
| 507 output->TrimStart(drop); | 504 output->TrimStart(drop); |
| 508 output_frames_to_drop_ -= drop; | 505 output_frames_to_drop_ -= drop; |
| 509 } | 506 } |
| 510 | 507 |
| 511 decoded_frames = output->frame_count(); | 508 decoded_frames = output->frame_count(); |
| 509 av_frame_unref(av_frame_); | |
|
jrummell
2013/08/27 01:05:24
Should this be done outside the if block just in c
DaleCurtis
2013/08/27 01:16:59
I used ffmpeg and ffplay as reference here and the
| |
| 512 } | 510 } |
| 513 | 511 |
| 512 // WARNING: |av_frame_| no longer has valid data at this point. | |
| 513 | |
| 514 if (decoded_frames > 0) { | 514 if (decoded_frames > 0) { |
| 515 // Set the timestamp/duration once all the extra frames have been | 515 // Set the timestamp/duration once all the extra frames have been |
| 516 // discarded. | 516 // discarded. |
| 517 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); | 517 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); |
| 518 output->set_duration( | 518 output->set_duration( |
| 519 output_timestamp_helper_->GetFrameDuration(decoded_frames)); | 519 output_timestamp_helper_->GetFrameDuration(decoded_frames)); |
| 520 output_timestamp_helper_->AddFrames(decoded_frames); | 520 output_timestamp_helper_->AddFrames(decoded_frames); |
| 521 } else if (IsEndOfStream(result, original_frames, input) && | 521 } else if (IsEndOfStream(result, original_frames, input) && |
| 522 !skip_eos_append) { | 522 !skip_eos_append) { |
| 523 DCHECK_EQ(packet.size, 0); | 523 DCHECK_EQ(packet.size, 0); |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 535 // Decoding finished successfully, update statistics. | 535 // Decoding finished successfully, update statistics. |
| 536 if (result > 0) { | 536 if (result > 0) { |
| 537 PipelineStatistics statistics; | 537 PipelineStatistics statistics; |
| 538 statistics.audio_bytes_decoded = result; | 538 statistics.audio_bytes_decoded = result; |
| 539 statistics_cb_.Run(statistics); | 539 statistics_cb_.Run(statistics); |
| 540 } | 540 } |
| 541 } while (packet.size > 0); | 541 } while (packet.size > 0); |
| 542 } | 542 } |
| 543 | 543 |
| 544 } // namespace media | 544 } // namespace media |
| OLD | NEW |