| 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 10 matching lines...) Expand all Loading... |
| 21 | 21 |
| 22 namespace media { | 22 namespace media { |
| 23 | 23 |
| 24 // Helper structure for managing multiple decoded audio frames per packet. | 24 // Helper structure for managing multiple decoded audio frames per packet. |
| 25 struct QueuedAudioBuffer { | 25 struct QueuedAudioBuffer { |
| 26 AudioDecoder::Status status; | 26 AudioDecoder::Status status; |
| 27 scoped_refptr<DataBuffer> buffer; | 27 scoped_refptr<DataBuffer> buffer; |
| 28 }; | 28 }; |
| 29 | 29 |
| 30 // Returns true if the decode result was end of stream. | 30 // Returns true if the decode result was end of stream. |
| 31 static inline bool IsEndOfStream(int result, int decoded_size, | 31 static inline bool is_end_of_stream(int result, int decoded_size, |
| 32 const scoped_refptr<DecoderBuffer>& input) { | 32 const scoped_refptr<DecoderBuffer>& input) { |
| 33 // Three conditions to meet to declare end of stream for this decoder: | 33 // Three conditions to meet to declare end of stream for this decoder: |
| 34 // 1. FFmpeg didn't read anything. | 34 // 1. FFmpeg didn't read anything. |
| 35 // 2. FFmpeg didn't output anything. | 35 // 2. FFmpeg didn't output anything. |
| 36 // 3. An end of stream buffer is received. | 36 // 3. An end of stream buffer is received. |
| 37 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); | 37 return result == 0 && decoded_size == 0 && input->is_end_of_stream(); |
| 38 } | 38 } |
| 39 | 39 |
| 40 FFmpegAudioDecoder::FFmpegAudioDecoder( | 40 FFmpegAudioDecoder::FFmpegAudioDecoder( |
| 41 const scoped_refptr<base::MessageLoopProxy>& message_loop) | 41 const scoped_refptr<base::MessageLoopProxy>& message_loop) |
| 42 : message_loop_(message_loop), | 42 : message_loop_(message_loop), |
| 43 weak_factory_(this), | 43 weak_factory_(this), |
| 44 demuxer_stream_(NULL), | 44 demuxer_stream_(NULL), |
| 45 codec_context_(NULL), | 45 codec_context_(NULL), |
| 46 bits_per_channel_(0), | 46 bits_per_channel_(0), |
| 47 channel_layout_(CHANNEL_LAYOUT_NONE), | 47 channel_layout_(CHANNEL_LAYOUT_NONE), |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 150 DCHECK(!input.get()); | 150 DCHECK(!input.get()); |
| 151 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); | 151 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL); |
| 152 return; | 152 return; |
| 153 } | 153 } |
| 154 | 154 |
| 155 if (status == DemuxerStream::kConfigChanged) { | 155 if (status == DemuxerStream::kConfigChanged) { |
| 156 DCHECK(!input.get()); | 156 DCHECK(!input.get()); |
| 157 | 157 |
| 158 // Send a "end of stream" buffer to the decode loop | 158 // Send a "end of stream" buffer to the decode loop |
| 159 // to output any remaining data still in the decoder. | 159 // to output any remaining data still in the decoder. |
| 160 RunDecodeLoop(DecoderBuffer::CreateEOSBuffer(), true); | 160 RunDecodeLoop(DecoderBuffer::create_eos_buffer(), true); |
| 161 | 161 |
| 162 DVLOG(1) << "Config changed."; | 162 DVLOG(1) << "Config changed."; |
| 163 | 163 |
| 164 if (!ConfigureDecoder()) { | 164 if (!ConfigureDecoder()) { |
| 165 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 165 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 166 return; | 166 return; |
| 167 } | 167 } |
| 168 | 168 |
| 169 ResetTimestampState(); | 169 ResetTimestampState(); |
| 170 | 170 |
| 171 if (queued_audio_.empty()) { | 171 if (queued_audio_.empty()) { |
| 172 ReadFromDemuxerStream(); | 172 ReadFromDemuxerStream(); |
| 173 return; | 173 return; |
| 174 } | 174 } |
| 175 | 175 |
| 176 base::ResetAndReturn(&read_cb_).Run( | 176 base::ResetAndReturn(&read_cb_).Run( |
| 177 queued_audio_.front().status, queued_audio_.front().buffer); | 177 queued_audio_.front().status, queued_audio_.front().buffer); |
| 178 queued_audio_.pop_front(); | 178 queued_audio_.pop_front(); |
| 179 return; | 179 return; |
| 180 } | 180 } |
| 181 | 181 |
| 182 DCHECK_EQ(status, DemuxerStream::kOk); | 182 DCHECK_EQ(status, DemuxerStream::kOk); |
| 183 DCHECK(input.get()); | 183 DCHECK(input.get()); |
| 184 | 184 |
| 185 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 185 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
| 186 // occurs with some damaged files. | 186 // occurs with some damaged files. |
| 187 if (!input->IsEndOfStream() && input->GetTimestamp() == kNoTimestamp() && | 187 if (!input->is_end_of_stream() && input->get_timestamp() == kNoTimestamp() && |
| 188 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { | 188 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { |
| 189 DVLOG(1) << "Received a buffer without timestamps!"; | 189 DVLOG(1) << "Received a buffer without timestamps!"; |
| 190 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 190 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 191 return; | 191 return; |
| 192 } | 192 } |
| 193 | 193 |
| 194 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; | 194 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; |
| 195 if (!input->IsEndOfStream()) { | 195 if (!input->is_end_of_stream()) { |
| 196 if (last_input_timestamp_ == kNoTimestamp()) { | 196 if (last_input_timestamp_ == kNoTimestamp()) { |
| 197 if (is_vorbis && (input->GetTimestamp() < base::TimeDelta())) { | 197 if (is_vorbis && (input->get_timestamp() < base::TimeDelta())) { |
| 198 // Dropping frames for negative timestamps as outlined in section A.2 | 198 // Dropping frames for negative timestamps as outlined in section A.2 |
| 199 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html | 199 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
| 200 int frames_to_drop = floor( | 200 int frames_to_drop = floor( |
| 201 0.5 + -input->GetTimestamp().InSecondsF() * samples_per_second_); | 201 0.5 + -input->get_timestamp().InSecondsF() * samples_per_second_); |
| 202 output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop; | 202 output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop; |
| 203 } else { | 203 } else { |
| 204 last_input_timestamp_ = input->GetTimestamp(); | 204 last_input_timestamp_ = input->get_timestamp(); |
| 205 } | 205 } |
| 206 } else if (input->GetTimestamp() != kNoTimestamp()) { | 206 } else if (input->get_timestamp() != kNoTimestamp()) { |
| 207 if (input->GetTimestamp() < last_input_timestamp_) { | 207 if (input->get_timestamp() < last_input_timestamp_) { |
| 208 base::TimeDelta diff = input->GetTimestamp() - last_input_timestamp_; | 208 base::TimeDelta diff = input->get_timestamp() - last_input_timestamp_; |
| 209 DVLOG(1) << "Input timestamps are not monotonically increasing! " | 209 DVLOG(1) << "Input timestamps are not monotonically increasing! " |
| 210 << " ts " << input->GetTimestamp().InMicroseconds() << " us" | 210 << " ts " << input->get_timestamp().InMicroseconds() << " us" |
| 211 << " diff " << diff.InMicroseconds() << " us"; | 211 << " diff " << diff.InMicroseconds() << " us"; |
| 212 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 212 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
| 213 return; | 213 return; |
| 214 } | 214 } |
| 215 | 215 |
| 216 last_input_timestamp_ = input->GetTimestamp(); | 216 last_input_timestamp_ = input->get_timestamp(); |
| 217 } | 217 } |
| 218 } | 218 } |
| 219 | 219 |
| 220 RunDecodeLoop(input, false); | 220 RunDecodeLoop(input, false); |
| 221 | 221 |
| 222 // We exhausted the provided packet, but it wasn't enough for a frame. Ask | 222 // We exhausted the provided packet, but it wasn't enough for a frame. Ask |
| 223 // for more data in order to fulfill this read. | 223 // for more data in order to fulfill this read. |
| 224 if (queued_audio_.empty()) { | 224 if (queued_audio_.empty()) { |
| 225 ReadFromDemuxerStream(); | 225 ReadFromDemuxerStream(); |
| 226 return; | 226 return; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); | 334 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); |
| 335 last_input_timestamp_ = kNoTimestamp(); | 335 last_input_timestamp_ = kNoTimestamp(); |
| 336 output_bytes_to_drop_ = 0; | 336 output_bytes_to_drop_ = 0; |
| 337 } | 337 } |
| 338 | 338 |
| 339 void FFmpegAudioDecoder::RunDecodeLoop( | 339 void FFmpegAudioDecoder::RunDecodeLoop( |
| 340 const scoped_refptr<DecoderBuffer>& input, | 340 const scoped_refptr<DecoderBuffer>& input, |
| 341 bool skip_eos_append) { | 341 bool skip_eos_append) { |
| 342 AVPacket packet; | 342 AVPacket packet; |
| 343 av_init_packet(&packet); | 343 av_init_packet(&packet); |
| 344 if (input->IsEndOfStream()) { | 344 if (input->is_end_of_stream()) { |
| 345 packet.data = NULL; | 345 packet.data = NULL; |
| 346 packet.size = 0; | 346 packet.size = 0; |
| 347 } else { | 347 } else { |
| 348 packet.data = const_cast<uint8*>(input->GetData()); | 348 packet.data = const_cast<uint8*>(input->get_data()); |
| 349 packet.size = input->GetDataSize(); | 349 packet.size = input->get_data_size(); |
| 350 } | 350 } |
| 351 | 351 |
| 352 // Each audio packet may contain several frames, so we must call the decoder | 352 // Each audio packet may contain several frames, so we must call the decoder |
| 353 // until we've exhausted the packet. Regardless of the packet size we always | 353 // until we've exhausted the packet. Regardless of the packet size we always |
| 354 // want to hand it to the decoder at least once, otherwise we would end up | 354 // want to hand it to the decoder at least once, otherwise we would end up |
| 355 // skipping end of stream packets since they have a size of zero. | 355 // skipping end of stream packets since they have a size of zero. |
| 356 do { | 356 do { |
| 357 // Reset frame to default values. | 357 // Reset frame to default values. |
| 358 avcodec_get_frame_defaults(av_frame_); | 358 avcodec_get_frame_defaults(av_frame_); |
| 359 | 359 |
| 360 int frame_decoded = 0; | 360 int frame_decoded = 0; |
| 361 int result = avcodec_decode_audio4( | 361 int result = avcodec_decode_audio4( |
| 362 codec_context_, av_frame_, &frame_decoded, &packet); | 362 codec_context_, av_frame_, &frame_decoded, &packet); |
| 363 | 363 |
| 364 if (result < 0) { | 364 if (result < 0) { |
| 365 DCHECK(!input->IsEndOfStream()) | 365 DCHECK(!input->is_end_of_stream()) |
| 366 << "End of stream buffer produced an error! " | 366 << "End of stream buffer produced an error! " |
| 367 << "This is quite possibly a bug in the audio decoder not handling " | 367 << "This is quite possibly a bug in the audio decoder not handling " |
| 368 << "end of stream AVPackets correctly."; | 368 << "end of stream AVPackets correctly."; |
| 369 | 369 |
| 370 DLOG(ERROR) | 370 DLOG(ERROR) |
| 371 << "Error decoding an audio frame with timestamp: " | 371 << "Error decoding an audio frame with timestamp: " |
| 372 << input->GetTimestamp().InMicroseconds() << " us, duration: " | 372 << input->get_timestamp().InMicroseconds() << " us, duration: " |
| 373 << input->GetDuration().InMicroseconds() << " us, packet size: " | 373 << input->get_duration().InMicroseconds() << " us, packet size: " |
| 374 << input->GetDataSize() << " bytes"; | 374 << input->get_data_size() << " bytes"; |
| 375 | 375 |
| 376 // TODO(dalecurtis): We should return a kDecodeError here instead: | 376 // TODO(dalecurtis): We should return a kDecodeError here instead: |
| 377 // http://crbug.com/145276 | 377 // http://crbug.com/145276 |
| 378 break; | 378 break; |
| 379 } | 379 } |
| 380 | 380 |
| 381 // Update packet size and data pointer in case we need to call the decoder | 381 // Update packet size and data pointer in case we need to call the decoder |
| 382 // with the remaining bytes from this packet. | 382 // with the remaining bytes from this packet. |
| 383 packet.size -= result; | 383 packet.size -= result; |
| 384 packet.data += result; | 384 packet.data += result; |
| 385 | 385 |
| 386 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && | 386 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && |
| 387 !input->IsEndOfStream()) { | 387 !input->is_end_of_stream()) { |
| 388 DCHECK(input->GetTimestamp() != kNoTimestamp()); | 388 DCHECK(input->get_timestamp() != kNoTimestamp()); |
| 389 if (output_bytes_to_drop_ > 0) { | 389 if (output_bytes_to_drop_ > 0) { |
| 390 // Currently Vorbis is the only codec that causes us to drop samples. | 390 // Currently Vorbis is the only codec that causes us to drop samples. |
| 391 // If we have to drop samples it always means the timeline starts at 0. | 391 // If we have to drop samples it always means the timeline starts at 0. |
| 392 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); | 392 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); |
| 393 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); | 393 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); |
| 394 } else { | 394 } else { |
| 395 output_timestamp_helper_->SetBaseTimestamp(input->GetTimestamp()); | 395 output_timestamp_helper_->SetBaseTimestamp(input->get_timestamp()); |
| 396 } | 396 } |
| 397 } | 397 } |
| 398 | 398 |
| 399 int decoded_audio_size = 0; | 399 int decoded_audio_size = 0; |
| 400 #ifdef CHROMIUM_NO_AVFRAME_CHANNELS | 400 #ifdef CHROMIUM_NO_AVFRAME_CHANNELS |
| 401 int channels = av_get_channel_layout_nb_channels( | 401 int channels = av_get_channel_layout_nb_channels( |
| 402 av_frame_->channel_layout); | 402 av_frame_->channel_layout); |
| 403 #else | 403 #else |
| 404 int channels = av_frame_->channels; | 404 int channels = av_frame_->channels; |
| 405 #endif | 405 #endif |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 478 output->get_writable_data()); | 478 output->get_writable_data()); |
| 479 } else { | 479 } else { |
| 480 output = DataBuffer::copy_from( | 480 output = DataBuffer::copy_from( |
| 481 av_frame_->extended_data[0] + start_sample * bytes_per_frame_, | 481 av_frame_->extended_data[0] + start_sample * bytes_per_frame_, |
| 482 decoded_audio_size); | 482 decoded_audio_size); |
| 483 } | 483 } |
| 484 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); | 484 output->set_timestamp(output_timestamp_helper_->GetTimestamp()); |
| 485 output->set_duration( | 485 output->set_duration( |
| 486 output_timestamp_helper_->GetDuration(decoded_audio_size)); | 486 output_timestamp_helper_->GetDuration(decoded_audio_size)); |
| 487 output_timestamp_helper_->AddBytes(decoded_audio_size); | 487 output_timestamp_helper_->AddBytes(decoded_audio_size); |
| 488 } else if (IsEndOfStream(result, decoded_audio_size, input) && | 488 } else if (is_end_of_stream(result, decoded_audio_size, input) && |
| 489 !skip_eos_append) { | 489 !skip_eos_append) { |
| 490 DCHECK_EQ(packet.size, 0); | 490 DCHECK_EQ(packet.size, 0); |
| 491 output = DataBuffer::create_eos_buffer(); | 491 output = DataBuffer::create_eos_buffer(); |
| 492 } | 492 } |
| 493 | 493 |
| 494 if (output.get()) { | 494 if (output.get()) { |
| 495 QueuedAudioBuffer queue_entry = { kOk, output }; | 495 QueuedAudioBuffer queue_entry = { kOk, output }; |
| 496 queued_audio_.push_back(queue_entry); | 496 queued_audio_.push_back(queue_entry); |
| 497 } | 497 } |
| 498 | 498 |
| 499 // Decoding finished successfully, update statistics. | 499 // Decoding finished successfully, update statistics. |
| 500 if (result > 0) { | 500 if (result > 0) { |
| 501 PipelineStatistics statistics; | 501 PipelineStatistics statistics; |
| 502 statistics.audio_bytes_decoded = result; | 502 statistics.audio_bytes_decoded = result; |
| 503 statistics_cb_.Run(statistics); | 503 statistics_cb_.Run(statistics); |
| 504 } | 504 } |
| 505 } while (packet.size > 0); | 505 } while (packet.size > 0); |
| 506 } | 506 } |
| 507 | 507 |
| 508 } // namespace media | 508 } // namespace media |
| OLD | NEW |