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 // TODO(scherkus): forced task post since AudioRendererBase::SamplesReady() |
vrk (LEFT CHROMIUM)
2011/12/03 03:10:17
nit: What's the TODO? Should this just be a commen
scherkus (not reviewing)
2011/12/07 05:25:12
done and updated FFmpegVideoDecoder as well
| |
92 FROM_HERE, | 91 // will call Read() on FFmpegAudioDecoder's thread as we executed |read_cb_|. |
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 CHECK(!callback.is_null()); |
vrk (LEFT CHROMIUM)
2011/12/03 03:10:17
CHECK? Do you mean DCHECK?
scherkus (not reviewing)
2011/12/07 05:25:12
Done.
| |
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); |
vrk (LEFT CHROMIUM)
2011/12/03 03:10:17
nit: remove {}
scherkus (not reviewing)
2011/12/07 05:25:12
other one-line statements in this file + ffmpegvid
| |
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): forced task post since FFmpegDemuxerStream::Read() can |
vrk (LEFT CHROMIUM)
2011/12/03 03:10:17
nit: ditto above comment re: "TODO"
scherkus (not reviewing)
2011/12/07 05:25:12
Done + FFmpegVideoDecoder
| |
255 FROM_HERE, | 251 // immediately execute our callback on FFmpegAudioDecoder's thread. |
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 |