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 "media/base/audio_decoder_config.h" | 9 #include "media/base/audio_decoder_config.h" |
10 #include "media/base/data_buffer.h" | 10 #include "media/base/data_buffer.h" |
11 #include "media/base/decoder_buffer.h" | 11 #include "media/base/decoder_buffer.h" |
12 #include "media/base/demuxer.h" | 12 #include "media/base/demuxer.h" |
13 #include "media/base/pipeline.h" | 13 #include "media/base/pipeline.h" |
14 #include "media/ffmpeg/ffmpeg_common.h" | 14 #include "media/ffmpeg/ffmpeg_common.h" |
15 #include "media/filters/ffmpeg_glue.h" | 15 #include "media/filters/ffmpeg_glue.h" |
16 | 16 |
17 namespace media { | 17 namespace media { |
18 | 18 |
19 // Returns true if the decode result was a timestamp packet and not actual audio | |
20 // data. | |
21 static inline bool IsTimestampMarkerPacket(int result, Buffer* input) { | |
22 // We can get a positive result but no decoded data. This is ok because this | |
23 // this can be a marker packet that only contains timestamp. | |
24 return result > 0 && !input->IsEndOfStream() && | |
25 input->GetTimestamp() != kNoTimestamp() && | |
26 input->GetDuration() != kNoTimestamp(); | |
27 } | |
28 | |
29 // Returns true if the decode result was end of stream. | 19 // Returns true if the decode result was end of stream. |
30 static inline bool IsEndOfStream(int result, int decoded_size, Buffer* input) { | 20 static inline bool IsEndOfStream(int result, int decoded_size, Buffer* input) { |
31 // Three conditions to meet to declare end of stream for this decoder: | 21 // Three conditions to meet to declare end of stream for this decoder: |
32 // 1. FFmpeg didn't read anything. | 22 // 1. FFmpeg didn't read anything. |
33 // 2. FFmpeg didn't output anything. | 23 // 2. FFmpeg didn't output anything. |
34 // 3. An end of stream buffer is received. | 24 // 3. An end of stream buffer is received. |
35 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); | 25 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); |
36 } | 26 } |
37 | 27 |
38 | |
39 FFmpegAudioDecoder::FFmpegAudioDecoder( | 28 FFmpegAudioDecoder::FFmpegAudioDecoder( |
40 const base::Callback<MessageLoop*()>& message_loop_cb) | 29 const base::Callback<MessageLoop*()>& message_loop_cb) |
41 : message_loop_factory_cb_(message_loop_cb), | 30 : message_loop_factory_cb_(message_loop_cb), |
42 message_loop_(NULL), | 31 message_loop_(NULL), |
43 codec_context_(NULL), | 32 codec_context_(NULL), |
44 bits_per_channel_(0), | 33 bits_per_channel_(0), |
45 channel_layout_(CHANNEL_LAYOUT_NONE), | 34 channel_layout_(CHANNEL_LAYOUT_NONE), |
46 samples_per_second_(0), | 35 samples_per_second_(0), |
36 bytes_per_frame_(0), | |
37 output_timestamp_base_(kNoTimestamp()), | |
38 total_frames_decoded_(0), | |
39 last_input_timestamp_(kNoTimestamp()), | |
40 output_bytes_to_drop_(0), | |
47 av_frame_(NULL) { | 41 av_frame_(NULL) { |
48 } | 42 } |
49 | 43 |
50 void FFmpegAudioDecoder::Initialize( | 44 void FFmpegAudioDecoder::Initialize( |
51 const scoped_refptr<DemuxerStream>& stream, | 45 const scoped_refptr<DemuxerStream>& stream, |
52 const PipelineStatusCB& status_cb, | 46 const PipelineStatusCB& status_cb, |
53 const StatisticsCB& statistics_cb) { | 47 const StatisticsCB& statistics_cb) { |
54 // Ensure FFmpeg has been initialized | 48 // Ensure FFmpeg has been initialized |
55 FFmpegGlue::GetInstance(); | 49 FFmpegGlue::GetInstance(); |
56 | 50 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
139 | 133 |
140 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); | 134 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED); |
141 return; | 135 return; |
142 } | 136 } |
143 | 137 |
144 // Success! | 138 // Success! |
145 av_frame_ = avcodec_alloc_frame(); | 139 av_frame_ = avcodec_alloc_frame(); |
146 bits_per_channel_ = config.bits_per_channel(); | 140 bits_per_channel_ = config.bits_per_channel(); |
147 channel_layout_ = config.channel_layout(); | 141 channel_layout_ = config.channel_layout(); |
148 samples_per_second_ = config.samples_per_second(); | 142 samples_per_second_ = config.samples_per_second(); |
149 | 143 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; |
150 status_cb.Run(PIPELINE_OK); | 144 status_cb.Run(PIPELINE_OK); |
151 } | 145 } |
152 | 146 |
153 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) { | 147 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) { |
154 avcodec_flush_buffers(codec_context_); | 148 avcodec_flush_buffers(codec_context_); |
155 estimated_next_timestamp_ = kNoTimestamp(); | 149 output_timestamp_base_ = kNoTimestamp(); |
150 total_frames_decoded_ = 0; | |
151 last_input_timestamp_ = kNoTimestamp(); | |
152 output_bytes_to_drop_ = 0; | |
156 closure.Run(); | 153 closure.Run(); |
157 } | 154 } |
158 | 155 |
159 void FFmpegAudioDecoder::DoRead(const ReadCB& read_cb) { | 156 void FFmpegAudioDecoder::DoRead(const ReadCB& read_cb) { |
160 DCHECK_EQ(MessageLoop::current(), message_loop_); | 157 DCHECK_EQ(MessageLoop::current(), message_loop_); |
161 DCHECK(!read_cb.is_null()); | 158 DCHECK(!read_cb.is_null()); |
162 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; | 159 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; |
163 | 160 |
164 read_cb_ = read_cb; | 161 read_cb_ = read_cb; |
165 ReadFromDemuxerStream(); | 162 ReadFromDemuxerStream(); |
166 } | 163 } |
167 | 164 |
168 void FFmpegAudioDecoder::DoDecodeBuffer( | 165 void FFmpegAudioDecoder::DoDecodeBuffer( |
169 DemuxerStream::Status status, | 166 DemuxerStream::Status status, |
170 const scoped_refptr<DecoderBuffer>& input) { | 167 const scoped_refptr<DecoderBuffer>& input) { |
171 DCHECK_EQ(MessageLoop::current(), message_loop_); | 168 DCHECK_EQ(MessageLoop::current(), message_loop_); |
172 DCHECK(!read_cb_.is_null()); | 169 DCHECK(!read_cb_.is_null()); |
173 | 170 |
174 if (status != DemuxerStream::kOk) { | 171 if (status != DemuxerStream::kOk) { |
175 DCHECK(!input); | 172 DCHECK(!input); |
176 // TODO(acolwell): Add support for reinitializing the decoder when | 173 // TODO(acolwell): Add support for reinitializing the decoder when |
177 // |status| == kConfigChanged. For now we just trigger a decode error. | 174 // |status| == kConfigChanged. For now we just trigger a decode error. |
178 AudioDecoder::Status decoder_status = | 175 AudioDecoder::Status decoder_status = |
179 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError; | 176 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError; |
180 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL); | 177 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL); |
181 return; | 178 return; |
182 } | 179 } |
183 | 180 |
184 // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us | 181 // Make sure we are notified if http://crbug.com/49709 returns. |
185 // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find | 182 CHECK(input->GetTimestamp() != kNoTimestamp() || |
186 // something valid. Refer to http://crbug.com/49709 | 183 output_timestamp_base_ != kNoTimestamp() || |
187 if (input->GetTimestamp() == kNoTimestamp() && | 184 input->IsEndOfStream()); |
scherkus (not reviewing)
2012/08/03 22:34:57
want to << "some text" so folks know what's up ins
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
Done.
| |
188 estimated_next_timestamp_ == kNoTimestamp() && | 185 |
189 !input->IsEndOfStream()) { | 186 bool is_vorbis = |
190 ReadFromDemuxerStream(); | 187 demuxer_stream_->audio_decoder_config().codec() == kCodecVorbis; |
scherkus (not reviewing)
2012/08/03 22:34:57
is the check done here in case the codec changes o
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
There are no plans to allow the codec to change. A
DaleCurtis
2012/08/03 23:15:36
codec_context_->codec_id == CODEC_ID_VORBIS seems
acolwell GONE FROM CHROMIUM
2012/08/03 23:25:05
Done.
| |
191 return; | 188 |
189 if (!input->IsEndOfStream()) { | |
190 if (last_input_timestamp_ == kNoTimestamp()) { | |
191 if (is_vorbis) { | |
scherkus (not reviewing)
2012/08/03 22:34:57
if there is some online documentation anywhere tha
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
Added a reference to the Vorbis spec.
| |
192 if (input->GetTimestamp() < base::TimeDelta()) { | |
DaleCurtis
2012/08/03 23:15:36
Can avoid double else below by rolling this into i
acolwell GONE FROM CHROMIUM
2012/08/03 23:25:05
Done.
| |
193 int frames_to_drop = floor(0.5 + | |
scherkus (not reviewing)
2012/08/03 22:34:57
technially we'd wrap at the ( on next line, so wha
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
The floor doesn't fit. I've moved the 0.5 + down t
| |
194 -input->GetTimestamp().InSecondsF() * samples_per_second_); | |
scherkus (not reviewing)
2012/08/03 22:34:57
and we have a test that covers this business?
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
Yep. MediaTest.VideoBearTheora uncovered the need
| |
195 output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop; | |
196 } else { | |
197 last_input_timestamp_ = input->GetTimestamp(); | |
198 } | |
199 } else { | |
200 last_input_timestamp_ = input->GetTimestamp(); | |
201 } | |
202 } else if (input->GetTimestamp() < last_input_timestamp_) { | |
203 base::TimeDelta diff = input->GetTimestamp() - last_input_timestamp_; | |
204 DVLOG(1) << "Input timestamps are not monotonically increasing! " | |
205 << " ts " << input->GetTimestamp().InMicroseconds() << " us" | |
206 << " diff " << diff.InMicroseconds() << " us"; | |
207 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | |
208 return; | |
209 } | |
192 } | 210 } |
193 | 211 |
194 AVPacket packet; | 212 AVPacket packet; |
195 av_init_packet(&packet); | 213 av_init_packet(&packet); |
196 packet.data = const_cast<uint8*>(input->GetData()); | 214 packet.data = const_cast<uint8*>(input->GetData()); |
197 packet.size = input->GetDataSize(); | 215 packet.size = input->GetDataSize(); |
198 | 216 |
199 PipelineStatistics statistics; | 217 PipelineStatistics statistics; |
200 statistics.audio_bytes_decoded = input->GetDataSize(); | 218 statistics.audio_bytes_decoded = input->GetDataSize(); |
201 | 219 |
(...skipping 12 matching lines...) Expand all Loading... | |
214 | 232 |
215 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " | 233 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " |
216 << input->GetTimestamp().InMicroseconds() << " us, duration: " | 234 << input->GetTimestamp().InMicroseconds() << " us, duration: " |
217 << input->GetDuration().InMicroseconds() << " us, packet size: " | 235 << input->GetDuration().InMicroseconds() << " us, packet size: " |
218 << input->GetDataSize() << " bytes"; | 236 << input->GetDataSize() << " bytes"; |
219 | 237 |
220 ReadFromDemuxerStream(); | 238 ReadFromDemuxerStream(); |
221 return; | 239 return; |
222 } | 240 } |
223 | 241 |
242 if (result > 0) | |
243 DCHECK_EQ(result, input->GetDataSize()); | |
244 | |
245 if (output_timestamp_base_ == kNoTimestamp() && !input->IsEndOfStream()) { | |
246 DCHECK(input->GetTimestamp() != kNoTimestamp()); | |
247 if (output_bytes_to_drop_ > 0) { | |
248 // Currently Vorbis is the only codec that causes us to drop samples. | |
249 // If we have to drop samples it always means the timeline starts at 0. | |
250 DCHECK(is_vorbis); | |
251 output_timestamp_base_ = base::TimeDelta(); | |
252 } else { | |
253 output_timestamp_base_ = input->GetTimestamp(); | |
254 } | |
255 } | |
256 | |
257 const uint8* decoded_audio_data = NULL; | |
224 int decoded_audio_size = 0; | 258 int decoded_audio_size = 0; |
225 if (frame_decoded) { | 259 if (frame_decoded) { |
226 int output_sample_rate = av_frame_->sample_rate; | 260 int output_sample_rate = av_frame_->sample_rate; |
227 if (output_sample_rate != samples_per_second_) { | 261 if (output_sample_rate != samples_per_second_) { |
228 DLOG(ERROR) << "Output sample rate (" << output_sample_rate | 262 DLOG(ERROR) << "Output sample rate (" << output_sample_rate |
229 << ") doesn't match expected rate " << samples_per_second_; | 263 << ") doesn't match expected rate " << samples_per_second_; |
230 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 264 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
231 return; | 265 return; |
232 } | 266 } |
233 | 267 |
268 decoded_audio_data = av_frame_->data[0]; | |
234 decoded_audio_size = av_samples_get_buffer_size( | 269 decoded_audio_size = av_samples_get_buffer_size( |
235 NULL, codec_context_->channels, av_frame_->nb_samples, | 270 NULL, codec_context_->channels, av_frame_->nb_samples, |
236 codec_context_->sample_fmt, 1); | 271 codec_context_->sample_fmt, 1); |
237 } | 272 } |
238 | 273 |
239 scoped_refptr<DataBuffer> output; | 274 scoped_refptr<DataBuffer> output; |
240 | 275 |
276 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { | |
277 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); | |
278 decoded_audio_data += dropped_size; | |
279 decoded_audio_size -= dropped_size; | |
280 output_bytes_to_drop_ -= dropped_size; | |
281 } | |
282 | |
241 if (decoded_audio_size > 0) { | 283 if (decoded_audio_size > 0) { |
284 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) | |
285 << "Decoder didn't output full frames"; | |
286 | |
242 // Copy the audio samples into an output buffer. | 287 // Copy the audio samples into an output buffer. |
243 output = new DataBuffer(decoded_audio_size); | 288 output = new DataBuffer(decoded_audio_size); |
244 output->SetDataSize(decoded_audio_size); | 289 output->SetDataSize(decoded_audio_size); |
245 uint8* data = output->GetWritableData(); | 290 uint8* data = output->GetWritableData(); |
246 memcpy(data, av_frame_->data[0], decoded_audio_size); | 291 memcpy(data, decoded_audio_data, decoded_audio_size); |
247 | 292 |
248 UpdateDurationAndTimestamp(input, output); | 293 base::TimeDelta timestamp = GetNextOutputTimestamp(); |
249 } else if (IsTimestampMarkerPacket(result, input)) { | 294 total_frames_decoded_ += decoded_audio_size / bytes_per_frame_; |
250 // Nothing else to do here but update our estimation. | 295 |
251 estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration(); | 296 output->SetTimestamp(timestamp); |
297 output->SetDuration(GetNextOutputTimestamp() - timestamp); | |
252 } else if (IsEndOfStream(result, decoded_audio_size, input)) { | 298 } else if (IsEndOfStream(result, decoded_audio_size, input)) { |
scherkus (not reviewing)
2012/08/03 22:34:57
sanity q: if line 279 caused decoded_audio_size ->
acolwell GONE FROM CHROMIUM
2012/08/03 23:14:01
I don't believe so. Even if the decoder outputted
| |
253 // Create an end of stream output buffer. | 299 // Create an end of stream output buffer. |
254 output = new DataBuffer(0); | 300 output = new DataBuffer(0); |
255 output->SetTimestamp(input->GetTimestamp()); | |
256 output->SetDuration(input->GetDuration()); | |
257 } | 301 } |
258 | 302 |
259 // Decoding finished successfully, update stats and execute callback. | 303 // Decoding finished successfully, update stats and execute callback. |
260 statistics_cb_.Run(statistics); | 304 statistics_cb_.Run(statistics); |
261 if (output) | 305 |
262 base::ResetAndReturn(&read_cb_).Run(kOk, output); | 306 if (!output) { |
263 else | |
264 ReadFromDemuxerStream(); | 307 ReadFromDemuxerStream(); |
308 return; | |
309 } | |
310 base::ResetAndReturn(&read_cb_).Run(kOk, output); | |
265 } | 311 } |
266 | 312 |
267 void FFmpegAudioDecoder::ReadFromDemuxerStream() { | 313 void FFmpegAudioDecoder::ReadFromDemuxerStream() { |
268 DCHECK(!read_cb_.is_null()); | 314 DCHECK(!read_cb_.is_null()); |
269 | 315 |
270 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this)); | 316 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this)); |
271 } | 317 } |
272 | 318 |
273 void FFmpegAudioDecoder::DecodeBuffer( | 319 void FFmpegAudioDecoder::DecodeBuffer( |
274 DemuxerStream::Status status, | 320 DemuxerStream::Status status, |
275 const scoped_refptr<DecoderBuffer>& buffer) { | 321 const scoped_refptr<DecoderBuffer>& buffer) { |
276 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; | 322 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; |
277 | 323 |
278 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read | 324 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read |
279 // callback on the same execution stack so we can get rid of forced task post. | 325 // callback on the same execution stack so we can get rid of forced task post. |
280 message_loop_->PostTask(FROM_HERE, base::Bind( | 326 message_loop_->PostTask(FROM_HERE, base::Bind( |
281 &FFmpegAudioDecoder::DoDecodeBuffer, this, status, buffer)); | 327 &FFmpegAudioDecoder::DoDecodeBuffer, this, status, buffer)); |
282 } | 328 } |
283 | 329 |
284 void FFmpegAudioDecoder::UpdateDurationAndTimestamp( | 330 base::TimeDelta FFmpegAudioDecoder::GetNextOutputTimestamp() const { |
285 const Buffer* input, | 331 DCHECK(output_timestamp_base_ != kNoTimestamp()); |
286 DataBuffer* output) { | 332 double decoded_us = (total_frames_decoded_ / samples_per_second_) * |
287 // Always calculate duration based on the actual number of samples decoded. | 333 base::Time::kMicrosecondsPerSecond; |
288 base::TimeDelta duration = CalculateDuration(output->GetDataSize()); | 334 return output_timestamp_base_ + base::TimeDelta::FromMicroseconds(decoded_us); |
289 output->SetDuration(duration); | |
290 | |
291 // Use the incoming timestamp if it's valid. | |
292 if (input->GetTimestamp() != kNoTimestamp()) { | |
293 output->SetTimestamp(input->GetTimestamp()); | |
294 estimated_next_timestamp_ = input->GetTimestamp() + duration; | |
295 return; | |
296 } | |
297 | |
298 // Otherwise use an estimated timestamp and attempt to update the estimation | |
299 // as long as it's valid. | |
300 output->SetTimestamp(estimated_next_timestamp_); | |
301 if (estimated_next_timestamp_ != kNoTimestamp()) { | |
302 estimated_next_timestamp_ += duration; | |
303 } | |
304 } | 335 } |
305 | |
306 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) { | |
307 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) * | |
308 bits_per_channel_ / 8 * samples_per_second_; | |
309 double microseconds = size / | |
310 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); | |
311 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); | |
312 } | |
313 | |
314 } // namespace media | 336 } // namespace media |
OLD | NEW |