| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "media/base/audio_decoder_config.h" | 8 #include "media/base/audio_decoder_config.h" |
| 9 #include "media/base/data_buffer.h" | 9 #include "media/base/data_buffer.h" |
| 10 #include "media/base/demuxer.h" | 10 #include "media/base/demuxer.h" |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 } | 45 } |
| 46 | 46 |
| 47 | 47 |
| 48 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop) | 48 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop) |
| 49 : message_loop_(message_loop), | 49 : message_loop_(message_loop), |
| 50 codec_context_(NULL), | 50 codec_context_(NULL), |
| 51 bits_per_channel_(0), | 51 bits_per_channel_(0), |
| 52 channel_layout_(CHANNEL_LAYOUT_NONE), | 52 channel_layout_(CHANNEL_LAYOUT_NONE), |
| 53 samples_per_second_(0), | 53 samples_per_second_(0), |
| 54 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE), | 54 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE), |
| 55 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))), | 55 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))) { |
| 56 pending_reads_(0) { | |
| 57 } | 56 } |
| 58 | 57 |
| 59 FFmpegAudioDecoder::~FFmpegAudioDecoder() { | 58 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
| 60 av_free(decoded_audio_); | 59 av_free(decoded_audio_); |
| 61 | 60 |
| 62 // XXX: should we require Stop() to be called? this might end up getting | 61 // XXX: should we require Stop() to be called? this might end up getting |
| 63 // called on a random thread due to refcounting. | 62 // called on a random thread due to refcounting. |
| 64 if (codec_context_) { | 63 if (codec_context_) { |
| 65 av_free(codec_context_->extradata); | 64 av_free(codec_context_->extradata); |
| 66 avcodec_close(codec_context_); | 65 avcodec_close(codec_context_); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 80 const StatisticsCallback& stats_callback) { | 79 const StatisticsCallback& stats_callback) { |
| 81 // TODO(scherkus): change Initialize() signature to pass |stream| as a | 80 // TODO(scherkus): change Initialize() signature to pass |stream| as a |
| 82 // scoped_refptr<>. | 81 // scoped_refptr<>. |
| 83 scoped_refptr<DemuxerStream> ref_stream(stream); | 82 scoped_refptr<DemuxerStream> ref_stream(stream); |
| 84 message_loop_->PostTask( | 83 message_loop_->PostTask( |
| 85 FROM_HERE, | 84 FROM_HERE, |
| 86 base::Bind(&FFmpegAudioDecoder::DoInitialize, this, | 85 base::Bind(&FFmpegAudioDecoder::DoInitialize, this, |
| 87 ref_stream, callback, stats_callback)); | 86 ref_stream, callback, stats_callback)); |
| 88 } | 87 } |
| 89 | 88 |
| 90 void FFmpegAudioDecoder::ProduceAudioSamples(scoped_refptr<Buffer> buffer) { | 89 void FFmpegAudioDecoder::Read(const ReadCB& callback) { |
| 91 message_loop_->PostTask( | 90 // Complete operation asynchronously on different stack of execution as per |
| 92 FROM_HERE, | 91 // the API contract of AudioDecoder::Read() |
| 93 base::Bind(&FFmpegAudioDecoder::DoProduceAudioSamples, this, | 92 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 94 buffer)); | 93 &FFmpegAudioDecoder::DoRead, this, callback)); |
| 95 } | 94 } |
| 96 | 95 |
| 97 int FFmpegAudioDecoder::bits_per_channel() { | 96 int FFmpegAudioDecoder::bits_per_channel() { |
| 98 return bits_per_channel_; | 97 return bits_per_channel_; |
| 99 } | 98 } |
| 100 | 99 |
| 101 ChannelLayout FFmpegAudioDecoder::channel_layout() { | 100 ChannelLayout FFmpegAudioDecoder::channel_layout() { |
| 102 return channel_layout_; | 101 return channel_layout_; |
| 103 } | 102 } |
| 104 | 103 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 | 148 |
| 150 callback.Run(); | 149 callback.Run(); |
| 151 } | 150 } |
| 152 | 151 |
| 153 void FFmpegAudioDecoder::DoFlush(const base::Closure& callback) { | 152 void FFmpegAudioDecoder::DoFlush(const base::Closure& callback) { |
| 154 avcodec_flush_buffers(codec_context_); | 153 avcodec_flush_buffers(codec_context_); |
| 155 estimated_next_timestamp_ = kNoTimestamp; | 154 estimated_next_timestamp_ = kNoTimestamp; |
| 156 callback.Run(); | 155 callback.Run(); |
| 157 } | 156 } |
| 158 | 157 |
| 159 void FFmpegAudioDecoder::DoProduceAudioSamples( | 158 void FFmpegAudioDecoder::DoRead(const ReadCB& callback) { |
| 160 const scoped_refptr<Buffer>& output) { | 159 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 161 output_buffers_.push_back(output); | 160 DCHECK(!callback.is_null()); |
| 161 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
| 162 |
| 163 read_cb_ = callback; |
| 162 ReadFromDemuxerStream(); | 164 ReadFromDemuxerStream(); |
| 163 } | 165 } |
| 164 | 166 |
| 165 void FFmpegAudioDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& input) { | 167 void FFmpegAudioDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& input) { |
| 166 DCHECK(!output_buffers_.empty()); | 168 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 167 DCHECK_GT(pending_reads_, 0); | 169 DCHECK(!read_cb_.is_null()); |
| 168 pending_reads_--; | |
| 169 | 170 |
| 170 // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us | 171 // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us |
| 171 // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find | 172 // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find |
| 172 // something valid. Refer to http://crbug.com/49709 | 173 // something valid. Refer to http://crbug.com/49709 |
| 173 if (input->GetTimestamp() == kNoTimestamp && | 174 if (input->GetTimestamp() == kNoTimestamp && |
| 174 estimated_next_timestamp_ == kNoTimestamp && | 175 estimated_next_timestamp_ == kNoTimestamp && |
| 175 !input->IsEndOfStream()) { | 176 !input->IsEndOfStream()) { |
| 176 ReadFromDemuxerStream(); | 177 ReadFromDemuxerStream(); |
| 177 return; | 178 return; |
| 178 } | 179 } |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 226 } else if (IsEndOfStream(result, decoded_audio_size, input)) { | 227 } else if (IsEndOfStream(result, decoded_audio_size, input)) { |
| 227 // Create an end of stream output buffer. | 228 // Create an end of stream output buffer. |
| 228 output = new DataBuffer(0); | 229 output = new DataBuffer(0); |
| 229 output->SetTimestamp(input->GetTimestamp()); | 230 output->SetTimestamp(input->GetTimestamp()); |
| 230 output->SetDuration(input->GetDuration()); | 231 output->SetDuration(input->GetDuration()); |
| 231 } | 232 } |
| 232 | 233 |
| 233 // Decoding finished successfully, update stats and execute callback. | 234 // Decoding finished successfully, update stats and execute callback. |
| 234 stats_callback_.Run(statistics); | 235 stats_callback_.Run(statistics); |
| 235 if (output) { | 236 if (output) { |
| 236 DCHECK_GT(output_buffers_.size(), 0u); | 237 DeliverSamples(output); |
| 237 output_buffers_.pop_front(); | |
| 238 | |
| 239 ConsumeAudioSamples(output); | |
| 240 } else { | 238 } else { |
| 241 ReadFromDemuxerStream(); | 239 ReadFromDemuxerStream(); |
| 242 } | 240 } |
| 243 } | 241 } |
| 244 | 242 |
| 245 void FFmpegAudioDecoder::ReadFromDemuxerStream() { | 243 void FFmpegAudioDecoder::ReadFromDemuxerStream() { |
| 246 DCHECK(!output_buffers_.empty()) | 244 DCHECK(!read_cb_.is_null()); |
| 247 << "Reads should only occur if there are output buffers."; | |
| 248 | 245 |
| 249 pending_reads_++; | |
| 250 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this)); | 246 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this)); |
| 251 } | 247 } |
| 252 | 248 |
| 253 void FFmpegAudioDecoder::DecodeBuffer(const scoped_refptr<Buffer>& buffer) { | 249 void FFmpegAudioDecoder::DecodeBuffer(const scoped_refptr<Buffer>& buffer) { |
| 254 message_loop_->PostTask( | 250 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read |
| 255 FROM_HERE, | 251 // callback on the same execution stack so we can get rid of forced task post. |
| 256 base::Bind(&FFmpegAudioDecoder::DoDecodeBuffer, this, buffer)); | 252 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 253 &FFmpegAudioDecoder::DoDecodeBuffer, this, buffer)); |
| 257 } | 254 } |
| 258 | 255 |
| 259 void FFmpegAudioDecoder::UpdateDurationAndTimestamp( | 256 void FFmpegAudioDecoder::UpdateDurationAndTimestamp( |
| 260 const Buffer* input, | 257 const Buffer* input, |
| 261 DataBuffer* output) { | 258 DataBuffer* output) { |
| 262 // Always calculate duration based on the actual number of samples decoded. | 259 // Always calculate duration based on the actual number of samples decoded. |
| 263 base::TimeDelta duration = CalculateDuration(output->GetDataSize()); | 260 base::TimeDelta duration = CalculateDuration(output->GetDataSize()); |
| 264 output->SetDuration(duration); | 261 output->SetDuration(duration); |
| 265 | 262 |
| 266 // Use the incoming timestamp if it's valid. | 263 // Use the incoming timestamp if it's valid. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 279 } | 276 } |
| 280 | 277 |
| 281 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) { | 278 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) { |
| 282 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) * | 279 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) * |
| 283 bits_per_channel_ / 8 * samples_per_second_; | 280 bits_per_channel_ / 8 * samples_per_second_; |
| 284 double microseconds = size / | 281 double microseconds = size / |
| 285 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); | 282 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); |
| 286 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); | 283 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); |
| 287 } | 284 } |
| 288 | 285 |
| 286 void FFmpegAudioDecoder::DeliverSamples(const scoped_refptr<Buffer>& samples) { |
| 287 // Reset the callback before running to protect against reentrancy. |
| 288 ReadCB read_cb = read_cb_; |
| 289 read_cb_.Reset(); |
| 290 read_cb.Run(samples); |
| 291 } |
| 292 |
| 289 } // namespace media | 293 } // namespace media |
| OLD | NEW |