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/data_buffer.h" | 8 #include "media/base/data_buffer.h" |
9 #include "media/base/demuxer.h" | 9 #include "media/base/demuxer.h" |
10 #include "media/base/filter_host.h" | 10 #include "media/base/filter_host.h" |
11 #include "media/base/limits.h" | |
12 #include "media/ffmpeg/ffmpeg_common.h" | 11 #include "media/ffmpeg/ffmpeg_common.h" |
13 | 12 |
14 namespace media { | 13 namespace media { |
15 | 14 |
16 // Returns true if the decode result was an error. | 15 // Returns true if the decode result was an error. |
17 static bool IsErrorResult(int result, int decoded_size) { | 16 static bool IsErrorResult(int result, int decoded_size) { |
18 return result < 0 || | 17 return result < 0 || |
19 decoded_size < 0 || | 18 decoded_size < 0 || |
20 decoded_size > AVCODEC_MAX_AUDIO_FRAME_SIZE; | 19 decoded_size > AVCODEC_MAX_AUDIO_FRAME_SIZE; |
21 } | 20 } |
(...skipping 21 matching lines...) Expand all Loading... |
43 // 3. An end of stream buffer is received. | 42 // 3. An end of stream buffer is received. |
44 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); | 43 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); |
45 } | 44 } |
46 | 45 |
47 | 46 |
48 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop) | 47 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop) |
49 : message_loop_(message_loop), | 48 : message_loop_(message_loop), |
50 codec_context_(NULL), | 49 codec_context_(NULL), |
51 bits_per_channel_(0), | 50 bits_per_channel_(0), |
52 channel_layout_(CHANNEL_LAYOUT_NONE), | 51 channel_layout_(CHANNEL_LAYOUT_NONE), |
53 sample_rate_(0), | 52 samples_per_second_(0), |
54 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE), | 53 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE), |
55 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))), | 54 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))), |
56 pending_reads_(0) { | 55 pending_reads_(0) { |
57 } | 56 } |
58 | 57 |
59 FFmpegAudioDecoder::~FFmpegAudioDecoder() { | 58 FFmpegAudioDecoder::~FFmpegAudioDecoder() { |
60 av_free(decoded_audio_); | 59 av_free(decoded_audio_); |
| 60 |
| 61 // XXX: should we require Stop() to be called? this might end up getting |
| 62 // called on a random thread due to refcounting. |
| 63 if (codec_context_) { |
| 64 av_free(codec_context_->extradata); |
| 65 avcodec_close(codec_context_); |
| 66 av_free(codec_context_); |
| 67 } |
61 } | 68 } |
62 | 69 |
63 void FFmpegAudioDecoder::Flush(FilterCallback* callback) { | 70 void FFmpegAudioDecoder::Flush(FilterCallback* callback) { |
64 message_loop_->PostTask( | 71 message_loop_->PostTask( |
65 FROM_HERE, | 72 FROM_HERE, |
66 NewRunnableMethod(this, &FFmpegAudioDecoder::DoFlush, callback)); | 73 NewRunnableMethod(this, &FFmpegAudioDecoder::DoFlush, callback)); |
67 } | 74 } |
68 | 75 |
69 void FFmpegAudioDecoder::Initialize( | 76 void FFmpegAudioDecoder::Initialize( |
70 DemuxerStream* stream, | 77 DemuxerStream* stream, |
(...skipping 16 matching lines...) Expand all Loading... |
87 } | 94 } |
88 | 95 |
89 int FFmpegAudioDecoder::bits_per_channel() { | 96 int FFmpegAudioDecoder::bits_per_channel() { |
90 return bits_per_channel_; | 97 return bits_per_channel_; |
91 } | 98 } |
92 | 99 |
93 ChannelLayout FFmpegAudioDecoder::channel_layout() { | 100 ChannelLayout FFmpegAudioDecoder::channel_layout() { |
94 return channel_layout_; | 101 return channel_layout_; |
95 } | 102 } |
96 | 103 |
97 int FFmpegAudioDecoder::sample_rate() { | 104 int FFmpegAudioDecoder::samples_per_second() { |
98 return sample_rate_; | 105 return samples_per_second_; |
99 } | 106 } |
100 | 107 |
101 void FFmpegAudioDecoder::DoInitialize( | 108 void FFmpegAudioDecoder::DoInitialize( |
102 const scoped_refptr<DemuxerStream>& stream, | 109 const scoped_refptr<DemuxerStream>& stream, |
103 FilterCallback* callback, | 110 FilterCallback* callback, |
104 StatisticsCallback* stats_callback) { | 111 StatisticsCallback* stats_callback) { |
105 scoped_ptr<FilterCallback> c(callback); | 112 scoped_ptr<FilterCallback> c(callback); |
106 | 113 |
107 demuxer_stream_ = stream; | 114 demuxer_stream_ = stream; |
108 AVStream* av_stream = demuxer_stream_->GetAVStream(); | 115 const AudioDecoderConfig& config = stream->audio_decoder_config(); |
109 CHECK(av_stream); | |
110 | |
111 stats_callback_.reset(stats_callback); | 116 stats_callback_.reset(stats_callback); |
112 | 117 |
113 // Grab the AVStream's codec context and make sure we have sensible values. | 118 // TODO(scherkus): this check should go in PipelineImpl prior to creating |
114 codec_context_ = av_stream->codec; | 119 // decoder objects. |
115 int bps = av_get_bits_per_sample_fmt(codec_context_->sample_fmt); | 120 if (!config.IsValidConfig()) { |
116 if (codec_context_->channels <= 0 || | |
117 codec_context_->channels > Limits::kMaxChannels || | |
118 (codec_context_->channel_layout == 0 && codec_context_->channels > 2) || | |
119 bps <= 0 || bps > Limits::kMaxBitsPerSample || | |
120 codec_context_->sample_rate <= 0 || | |
121 codec_context_->sample_rate > Limits::kMaxSampleRate) { | |
122 DLOG(ERROR) << "Invalid audio stream -" | 121 DLOG(ERROR) << "Invalid audio stream -" |
123 << " channels: " << codec_context_->channels | 122 << " codec: " << config.codec() |
124 << " channel layout:" << codec_context_->channel_layout | 123 << " channel layout: " << config.channel_layout() |
125 << " bps: " << bps | 124 << " bits per channel: " << config.bits_per_channel() |
126 << " sample rate: " << codec_context_->sample_rate; | 125 << " samples per second: " << config.samples_per_second(); |
127 | 126 |
128 host()->SetError(PIPELINE_ERROR_DECODE); | 127 host()->SetError(DECODER_ERROR_NOT_SUPPORTED); |
129 callback->Run(); | 128 callback->Run(); |
130 return; | 129 return; |
131 } | 130 } |
132 | 131 |
133 // Serialize calls to avcodec_open(). | 132 // Initialize AVCodecContext structure. |
| 133 codec_context_ = avcodec_alloc_context(); |
| 134 AudioDecoderConfigToAVCodecContext(config, codec_context_); |
| 135 |
134 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); | 136 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); |
135 if (!codec || avcodec_open(codec_context_, codec) < 0) { | 137 if (!codec || avcodec_open(codec_context_, codec) < 0) { |
136 DLOG(ERROR) << "Could not initialize audio decoder: " | 138 DLOG(ERROR) << "Could not initialize audio decoder: " |
137 << codec_context_->codec_id; | 139 << codec_context_->codec_id; |
138 | 140 |
139 host()->SetError(PIPELINE_ERROR_DECODE); | 141 host()->SetError(DECODER_ERROR_NOT_SUPPORTED); |
140 callback->Run(); | 142 callback->Run(); |
141 return; | 143 return; |
142 } | 144 } |
143 | 145 |
144 // Success! | 146 // Success! |
145 bits_per_channel_ = av_get_bits_per_sample_fmt(codec_context_->sample_fmt); | 147 bits_per_channel_ = config.bits_per_channel(); |
146 channel_layout_ = | 148 channel_layout_ = config.channel_layout(); |
147 ChannelLayoutToChromeChannelLayout(codec_context_->channel_layout, | 149 samples_per_second_ = config.samples_per_second(); |
148 codec_context_->channels); | |
149 sample_rate_ = codec_context_->sample_rate; | |
150 | 150 |
151 callback->Run(); | 151 callback->Run(); |
152 } | 152 } |
153 | 153 |
154 void FFmpegAudioDecoder::DoFlush(FilterCallback* callback) { | 154 void FFmpegAudioDecoder::DoFlush(FilterCallback* callback) { |
155 avcodec_flush_buffers(codec_context_); | 155 avcodec_flush_buffers(codec_context_); |
156 estimated_next_timestamp_ = kNoTimestamp; | 156 estimated_next_timestamp_ = kNoTimestamp; |
157 | 157 |
158 callback->Run(); | 158 callback->Run(); |
159 delete callback; | 159 delete callback; |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
279 // Otherwise use an estimated timestamp and attempt to update the estimation | 279 // Otherwise use an estimated timestamp and attempt to update the estimation |
280 // as long as it's valid. | 280 // as long as it's valid. |
281 output->SetTimestamp(estimated_next_timestamp_); | 281 output->SetTimestamp(estimated_next_timestamp_); |
282 if (estimated_next_timestamp_ != kNoTimestamp) { | 282 if (estimated_next_timestamp_ != kNoTimestamp) { |
283 estimated_next_timestamp_ += duration; | 283 estimated_next_timestamp_ += duration; |
284 } | 284 } |
285 } | 285 } |
286 | 286 |
287 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) { | 287 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) { |
288 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) * | 288 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) * |
289 bits_per_channel_ / 8 * sample_rate_; | 289 bits_per_channel_ / 8 * samples_per_second_; |
290 double microseconds = size / | 290 double microseconds = size / |
291 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); | 291 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); |
292 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); | 292 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); |
293 } | 293 } |
294 | 294 |
295 } // namespace media | 295 } // namespace media |
OLD | NEW |