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 11 matching lines...) Expand all Loading... |
22 | 22 |
23 namespace media { | 23 namespace media { |
24 | 24 |
25 // Helper structure for managing multiple decoded audio frames per packet. | 25 // Helper structure for managing multiple decoded audio frames per packet. |
26 struct QueuedAudioBuffer { | 26 struct QueuedAudioBuffer { |
27 AudioDecoder::Status status; | 27 AudioDecoder::Status status; |
28 scoped_refptr<AudioBuffer> buffer; | 28 scoped_refptr<AudioBuffer> buffer; |
29 }; | 29 }; |
30 | 30 |
31 // Returns true if the decode result was end of stream. | 31 // Returns true if the decode result was end of stream. |
32 static inline bool IsEndOfStream(int result, int decoded_size, | 32 static inline bool IsEndOfStream(int result, |
| 33 int decoded_size, |
33 const scoped_refptr<DecoderBuffer>& input) { | 34 const scoped_refptr<DecoderBuffer>& input) { |
34 // Three conditions to meet to declare end of stream for this decoder: | 35 // Three conditions to meet to declare end of stream for this decoder: |
35 // 1. FFmpeg didn't read anything. | 36 // 1. FFmpeg didn't read anything. |
36 // 2. FFmpeg didn't output anything. | 37 // 2. FFmpeg didn't output anything. |
37 // 3. An end of stream buffer is received. | 38 // 3. An end of stream buffer is received. |
38 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); | 39 return result == 0 && decoded_size == 0 && input->end_of_stream(); |
39 } | 40 } |
40 | 41 |
41 FFmpegAudioDecoder::FFmpegAudioDecoder( | 42 FFmpegAudioDecoder::FFmpegAudioDecoder( |
42 const scoped_refptr<base::MessageLoopProxy>& message_loop) | 43 const scoped_refptr<base::MessageLoopProxy>& message_loop) |
43 : message_loop_(message_loop), | 44 : message_loop_(message_loop), |
44 weak_factory_(this), | 45 weak_factory_(this), |
45 demuxer_stream_(NULL), | 46 demuxer_stream_(NULL), |
46 codec_context_(NULL), | 47 codec_context_(NULL), |
47 bits_per_channel_(0), | 48 bits_per_channel_(0), |
48 channel_layout_(CHANNEL_LAYOUT_NONE), | 49 channel_layout_(CHANNEL_LAYOUT_NONE), |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 queued_audio_.front().status, queued_audio_.front().buffer); | 178 queued_audio_.front().status, queued_audio_.front().buffer); |
178 queued_audio_.pop_front(); | 179 queued_audio_.pop_front(); |
179 return; | 180 return; |
180 } | 181 } |
181 | 182 |
182 DCHECK_EQ(status, DemuxerStream::kOk); | 183 DCHECK_EQ(status, DemuxerStream::kOk); |
183 DCHECK(input.get()); | 184 DCHECK(input.get()); |
184 | 185 |
185 // Make sure we are notified if http://crbug.com/49709 returns. Issue also | 186 // Make sure we are notified if http://crbug.com/49709 returns. Issue also |
186 // occurs with some damaged files. | 187 // occurs with some damaged files. |
187 if (!input->IsEndOfStream() && input->GetTimestamp() == kNoTimestamp() && | 188 if (!input->end_of_stream() && input->timestamp() == kNoTimestamp() && |
188 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { | 189 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) { |
189 DVLOG(1) << "Received a buffer without timestamps!"; | 190 DVLOG(1) << "Received a buffer without timestamps!"; |
190 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 191 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
191 return; | 192 return; |
192 } | 193 } |
193 | 194 |
194 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; | 195 bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS; |
195 if (!input->IsEndOfStream()) { | 196 if (!input->end_of_stream()) { |
196 if (last_input_timestamp_ == kNoTimestamp()) { | 197 if (last_input_timestamp_ == kNoTimestamp()) { |
197 if (is_vorbis && (input->GetTimestamp() < base::TimeDelta())) { | 198 if (is_vorbis && (input->timestamp() < base::TimeDelta())) { |
198 // Dropping frames for negative timestamps as outlined in section A.2 | 199 // 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 | 200 // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
200 output_frames_to_drop_ = floor( | 201 output_frames_to_drop_ = floor( |
201 0.5 + -input->GetTimestamp().InSecondsF() * samples_per_second_); | 202 0.5 + -input->timestamp().InSecondsF() * samples_per_second_); |
202 } else { | 203 } else { |
203 last_input_timestamp_ = input->GetTimestamp(); | 204 last_input_timestamp_ = input->timestamp(); |
204 } | 205 } |
205 } else if (input->GetTimestamp() != kNoTimestamp()) { | 206 } else if (input->timestamp() != kNoTimestamp()) { |
206 if (input->GetTimestamp() < last_input_timestamp_) { | 207 if (input->timestamp() < last_input_timestamp_) { |
207 base::TimeDelta diff = input->GetTimestamp() - last_input_timestamp_; | 208 base::TimeDelta diff = input->timestamp() - last_input_timestamp_; |
208 DVLOG(1) << "Input timestamps are not monotonically increasing! " | 209 DVLOG(1) << "Input timestamps are not monotonically increasing! " |
209 << " ts " << input->GetTimestamp().InMicroseconds() << " us" | 210 << " ts " << input->timestamp().InMicroseconds() << " us" |
210 << " diff " << diff.InMicroseconds() << " us"; | 211 << " diff " << diff.InMicroseconds() << " us"; |
211 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); | 212 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL); |
212 return; | 213 return; |
213 } | 214 } |
214 | 215 |
215 last_input_timestamp_ = input->GetTimestamp(); | 216 last_input_timestamp_ = input->timestamp(); |
216 } | 217 } |
217 } | 218 } |
218 | 219 |
219 RunDecodeLoop(input, false); | 220 RunDecodeLoop(input, false); |
220 | 221 |
221 // 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 |
222 // for more data in order to fulfill this read. | 223 // for more data in order to fulfill this read. |
223 if (queued_audio_.empty()) { | 224 if (queued_audio_.empty()) { |
224 ReadFromDemuxerStream(); | 225 ReadFromDemuxerStream(); |
225 return; | 226 return; |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
310 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); | 311 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp()); |
311 last_input_timestamp_ = kNoTimestamp(); | 312 last_input_timestamp_ = kNoTimestamp(); |
312 output_frames_to_drop_ = 0; | 313 output_frames_to_drop_ = 0; |
313 } | 314 } |
314 | 315 |
315 void FFmpegAudioDecoder::RunDecodeLoop( | 316 void FFmpegAudioDecoder::RunDecodeLoop( |
316 const scoped_refptr<DecoderBuffer>& input, | 317 const scoped_refptr<DecoderBuffer>& input, |
317 bool skip_eos_append) { | 318 bool skip_eos_append) { |
318 AVPacket packet; | 319 AVPacket packet; |
319 av_init_packet(&packet); | 320 av_init_packet(&packet); |
320 if (input->IsEndOfStream()) { | 321 if (input->end_of_stream()) { |
321 packet.data = NULL; | 322 packet.data = NULL; |
322 packet.size = 0; | 323 packet.size = 0; |
323 } else { | 324 } else { |
324 packet.data = const_cast<uint8*>(input->GetData()); | 325 packet.data = const_cast<uint8*>(input->data()); |
325 packet.size = input->GetDataSize(); | 326 packet.size = input->data_size(); |
326 } | 327 } |
327 | 328 |
328 // Each audio packet may contain several frames, so we must call the decoder | 329 // Each audio packet may contain several frames, so we must call the decoder |
329 // until we've exhausted the packet. Regardless of the packet size we always | 330 // until we've exhausted the packet. Regardless of the packet size we always |
330 // want to hand it to the decoder at least once, otherwise we would end up | 331 // want to hand it to the decoder at least once, otherwise we would end up |
331 // skipping end of stream packets since they have a size of zero. | 332 // skipping end of stream packets since they have a size of zero. |
332 do { | 333 do { |
333 // Reset frame to default values. | 334 // Reset frame to default values. |
334 avcodec_get_frame_defaults(av_frame_); | 335 avcodec_get_frame_defaults(av_frame_); |
335 | 336 |
336 int frame_decoded = 0; | 337 int frame_decoded = 0; |
337 int result = avcodec_decode_audio4( | 338 int result = avcodec_decode_audio4( |
338 codec_context_, av_frame_, &frame_decoded, &packet); | 339 codec_context_, av_frame_, &frame_decoded, &packet); |
339 | 340 |
340 if (result < 0) { | 341 if (result < 0) { |
341 DCHECK(!input->IsEndOfStream()) | 342 DCHECK(!input->end_of_stream()) |
342 << "End of stream buffer produced an error! " | 343 << "End of stream buffer produced an error! " |
343 << "This is quite possibly a bug in the audio decoder not handling " | 344 << "This is quite possibly a bug in the audio decoder not handling " |
344 << "end of stream AVPackets correctly."; | 345 << "end of stream AVPackets correctly."; |
345 | 346 |
346 DLOG(ERROR) | 347 DLOG(ERROR) |
347 << "Error decoding an audio frame with timestamp: " | 348 << "Error decoding an audio frame with timestamp: " |
348 << input->GetTimestamp().InMicroseconds() << " us, duration: " | 349 << input->timestamp().InMicroseconds() << " us, duration: " |
349 << input->GetDuration().InMicroseconds() << " us, packet size: " | 350 << input->duration().InMicroseconds() << " us, packet size: " |
350 << input->GetDataSize() << " bytes"; | 351 << input->data_size() << " bytes"; |
351 | 352 |
352 // TODO(dalecurtis): We should return a kDecodeError here instead: | 353 // TODO(dalecurtis): We should return a kDecodeError here instead: |
353 // http://crbug.com/145276 | 354 // http://crbug.com/145276 |
354 break; | 355 break; |
355 } | 356 } |
356 | 357 |
357 // Update packet size and data pointer in case we need to call the decoder | 358 // Update packet size and data pointer in case we need to call the decoder |
358 // with the remaining bytes from this packet. | 359 // with the remaining bytes from this packet. |
359 packet.size -= result; | 360 packet.size -= result; |
360 packet.data += result; | 361 packet.data += result; |
361 | 362 |
362 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && | 363 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() && |
363 !input->IsEndOfStream()) { | 364 !input->end_of_stream()) { |
364 DCHECK(input->GetTimestamp() != kNoTimestamp()); | 365 DCHECK(input->timestamp() != kNoTimestamp()); |
365 if (output_frames_to_drop_ > 0) { | 366 if (output_frames_to_drop_ > 0) { |
366 // Currently Vorbis is the only codec that causes us to drop samples. | 367 // Currently Vorbis is the only codec that causes us to drop samples. |
367 // If we have to drop samples it always means the timeline starts at 0. | 368 // If we have to drop samples it always means the timeline starts at 0. |
368 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); | 369 DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS); |
369 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); | 370 output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta()); |
370 } else { | 371 } else { |
371 output_timestamp_helper_->SetBaseTimestamp(input->GetTimestamp()); | 372 output_timestamp_helper_->SetBaseTimestamp(input->timestamp()); |
372 } | 373 } |
373 } | 374 } |
374 | 375 |
375 int decoded_frames = 0; | 376 int decoded_frames = 0; |
376 #ifdef CHROMIUM_NO_AVFRAME_CHANNELS | 377 #ifdef CHROMIUM_NO_AVFRAME_CHANNELS |
377 int channels = av_get_channel_layout_nb_channels( | 378 int channels = av_get_channel_layout_nb_channels( |
378 av_frame_->channel_layout); | 379 av_frame_->channel_layout); |
379 #else | 380 #else |
380 int channels = av_frame_->channels; | 381 int channels = av_frame_->channels; |
381 #endif | 382 #endif |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 // Decoding finished successfully, update statistics. | 441 // Decoding finished successfully, update statistics. |
441 if (result > 0) { | 442 if (result > 0) { |
442 PipelineStatistics statistics; | 443 PipelineStatistics statistics; |
443 statistics.audio_bytes_decoded = result; | 444 statistics.audio_bytes_decoded = result; |
444 statistics_cb_.Run(statistics); | 445 statistics_cb_.Run(statistics); |
445 } | 446 } |
446 } while (packet.size > 0); | 447 } while (packet.size > 0); |
447 } | 448 } |
448 | 449 |
449 } // namespace media | 450 } // namespace media |
OLD | NEW |