Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(442)

Side by Side Diff: media/filters/ffmpeg_audio_decoder.cc

Issue 6901135: Rewriting FFmpegAudioDecoder and eliminating DecoderBase. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src
Patch Set: all good Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/ffmpeg_audio_decoder.h ('k') | media/filters/ffmpeg_video_decoder.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "media/base/data_buffer.h" 8 #include "media/base/data_buffer.h"
9 #include "media/base/demuxer.h"
10 #include "media/base/filter_host.h"
8 #include "media/base/limits.h" 11 #include "media/base/limits.h"
9 #include "media/ffmpeg/ffmpeg_common.h" 12 #include "media/ffmpeg/ffmpeg_common.h"
10 #include "media/filters/ffmpeg_demuxer.h"
11
12 #if !defined(USE_SSE)
13 #if defined(__SSE__) || defined(ARCH_CPU_X86_64) || _M_IX86_FP==1
14 #define USE_SSE 1
15 #else
16 #define USE_SSE 0
17 #endif
18 #endif
19 #if USE_SSE
20 #include <xmmintrin.h>
21 #endif
22 13
23 namespace media { 14 namespace media {
24 15
25 // Size of the decoded audio buffer. 16 // Returns true if the decode result was an error.
26 const size_t FFmpegAudioDecoder::kOutputBufferSize = 17 static bool IsErrorResult(int result, int decoded_size) {
27 AVCODEC_MAX_AUDIO_FRAME_SIZE; 18 return result < 0 ||
19 decoded_size < 0 ||
20 decoded_size > AVCODEC_MAX_AUDIO_FRAME_SIZE;
21 }
22
23 // Returns true if the decode result produced audio samples.
24 static bool ProducedAudioSamples(int decoded_size) {
25 return decoded_size > 0;
26 }
27
28 // Returns true if the decode result was a timestamp packet and not actual audio
29 // data.
30 static bool IsTimestampMarkerPacket(int result, Buffer* input) {
31 // We can get a positive result but no decoded data. This is ok because this
32 // this can be a marker packet that only contains timestamp.
33 return result > 0 && !input->IsEndOfStream() &&
34 input->GetTimestamp() != kNoTimestamp &&
35 input->GetDuration() != kNoTimestamp;
36 }
37
38 // Returns true if the decode result was end of stream.
39 static bool IsEndOfStream(int result, int decoded_size, Buffer* input) {
40 // Three conditions to meet to declare end of stream for this decoder:
41 // 1. FFmpeg didn't read anything.
42 // 2. FFmpeg didn't output anything.
43 // 3. An end of stream buffer is received.
44 return result == 0 && decoded_size == 0 && input->IsEndOfStream();
45 }
46
28 47
29 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop) 48 FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop)
30 : DecoderBase<AudioDecoder, Buffer>(message_loop), 49 : message_loop_(message_loop),
31 codec_context_(NULL), 50 codec_context_(NULL),
32 bits_per_channel_(0), 51 bits_per_channel_(0),
33 channel_layout_(CHANNEL_LAYOUT_NONE), 52 channel_layout_(CHANNEL_LAYOUT_NONE),
34 sample_rate_(0), 53 sample_rate_(0),
35 estimated_next_timestamp_(kNoTimestamp) { 54 decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE),
55 decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))),
56 pending_reads_(0) {
36 } 57 }
37 58
38 FFmpegAudioDecoder::~FFmpegAudioDecoder() { 59 FFmpegAudioDecoder::~FFmpegAudioDecoder() {
60 av_free(decoded_audio_);
39 } 61 }
40 62
41 void FFmpegAudioDecoder::DoInitialize(DemuxerStream* demuxer_stream, 63 void FFmpegAudioDecoder::Flush(FilterCallback* callback) {
42 bool* success, 64 message_loop_->PostTask(
43 Task* done_cb) { 65 FROM_HERE,
44 base::ScopedTaskRunner done_runner(done_cb); 66 NewRunnableMethod(this, &FFmpegAudioDecoder::DoFlush, callback));
45 *success = false; 67 }
46 68
47 AVStream* av_stream = demuxer_stream->GetAVStream(); 69 void FFmpegAudioDecoder::Initialize(
48 if (!av_stream) { 70 DemuxerStream* stream,
49 return; 71 FilterCallback* callback,
50 } 72 StatisticsCallback* stats_callback) {
73 // TODO(scherkus): change Initialize() signature to pass |stream| as a
74 // scoped_refptr<>.
75 scoped_refptr<DemuxerStream> ref_stream(stream);
76 message_loop_->PostTask(
77 FROM_HERE,
78 NewRunnableMethod(this, &FFmpegAudioDecoder::DoInitialize,
79 ref_stream, callback, stats_callback));
80 }
81
82 void FFmpegAudioDecoder::ProduceAudioSamples(scoped_refptr<Buffer> buffer) {
83 message_loop_->PostTask(
84 FROM_HERE,
85 NewRunnableMethod(this, &FFmpegAudioDecoder::DoProduceAudioSamples,
86 buffer));
87 }
88
89 int FFmpegAudioDecoder::bits_per_channel() {
90 return bits_per_channel_;
91 }
92
93 ChannelLayout FFmpegAudioDecoder::channel_layout() {
94 return channel_layout_;
95 }
96
97 int FFmpegAudioDecoder::sample_rate() {
98 return sample_rate_;
99 }
100
101 void FFmpegAudioDecoder::DoInitialize(
102 const scoped_refptr<DemuxerStream>& stream,
103 FilterCallback* callback,
104 StatisticsCallback* stats_callback) {
105 scoped_ptr<FilterCallback> c(callback);
106
107 demuxer_stream_ = stream;
108 AVStream* av_stream = demuxer_stream_->GetAVStream();
109 CHECK(av_stream);
110
111 stats_callback_.reset(stats_callback);
51 112
52 // Grab the AVStream's codec context and make sure we have sensible values. 113 // Grab the AVStream's codec context and make sure we have sensible values.
53 codec_context_ = av_stream->codec; 114 codec_context_ = av_stream->codec;
54 int bps = av_get_bits_per_sample_fmt(codec_context_->sample_fmt); 115 int bps = av_get_bits_per_sample_fmt(codec_context_->sample_fmt);
55 if (codec_context_->channels <= 0 || 116 if (codec_context_->channels <= 0 ||
56 codec_context_->channels > Limits::kMaxChannels || 117 codec_context_->channels > Limits::kMaxChannels ||
57 (codec_context_->channel_layout == 0 && codec_context_->channels > 2) || 118 (codec_context_->channel_layout == 0 && codec_context_->channels > 2) ||
58 bps <= 0 || bps > Limits::kMaxBitsPerSample || 119 bps <= 0 || bps > Limits::kMaxBitsPerSample ||
59 codec_context_->sample_rate <= 0 || 120 codec_context_->sample_rate <= 0 ||
60 codec_context_->sample_rate > Limits::kMaxSampleRate) { 121 codec_context_->sample_rate > Limits::kMaxSampleRate) {
61 DLOG(WARNING) << "Invalid audio stream -" 122 DLOG(ERROR) << "Invalid audio stream -"
62 << " channels: " << codec_context_->channels 123 << " channels: " << codec_context_->channels
63 << " channel layout:" << codec_context_->channel_layout 124 << " channel layout:" << codec_context_->channel_layout
64 << " bps: " << bps 125 << " bps: " << bps
65 << " sample rate: " << codec_context_->sample_rate; 126 << " sample rate: " << codec_context_->sample_rate;
127
128 host()->SetError(PIPELINE_ERROR_DECODE);
129 callback->Run();
66 return; 130 return;
67 } 131 }
68 132
69 // Serialize calls to avcodec_open(). 133 // Serialize calls to avcodec_open().
70 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id); 134 AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
71 if (!codec || avcodec_open(codec_context_, codec) < 0) { 135 if (!codec || avcodec_open(codec_context_, codec) < 0) {
136 DLOG(ERROR) << "Could not initialize audio decoder: "
137 << codec_context_->codec_id;
138
139 host()->SetError(PIPELINE_ERROR_DECODE);
140 callback->Run();
72 return; 141 return;
73 } 142 }
74 143
144 // Success!
75 bits_per_channel_ = av_get_bits_per_sample_fmt(codec_context_->sample_fmt); 145 bits_per_channel_ = av_get_bits_per_sample_fmt(codec_context_->sample_fmt);
76 channel_layout_ = 146 channel_layout_ =
77 ChannelLayoutToChromeChannelLayout(codec_context_->channel_layout, 147 ChannelLayoutToChromeChannelLayout(codec_context_->channel_layout,
78 codec_context_->channels); 148 codec_context_->channels);
79 sample_rate_ = codec_context_->sample_rate; 149 sample_rate_ = codec_context_->sample_rate;
80 150
81 // Prepare the output buffer. 151 callback->Run();
82 output_buffer_.reset(static_cast<uint8*>(av_malloc(kOutputBufferSize)));
83 if (!output_buffer_.get()) {
84 host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY);
85 return;
86 }
87 *success = true;
88 } 152 }
89 153
90 void FFmpegAudioDecoder::ProduceAudioSamples(scoped_refptr<Buffer> output) { 154 void FFmpegAudioDecoder::DoFlush(FilterCallback* callback) {
91 DecoderBase<AudioDecoder, Buffer>::PostReadTaskHack(output); 155 avcodec_flush_buffers(codec_context_);
156 estimated_next_timestamp_ = kNoTimestamp;
157
158 callback->Run();
159 delete callback;
92 } 160 }
93 161
94 int FFmpegAudioDecoder::bits_per_channel() { 162 void FFmpegAudioDecoder::DoProduceAudioSamples(
95 return bits_per_channel_; 163 const scoped_refptr<Buffer>& output) {
164 output_buffers_.push_back(output);
165 ReadFromDemuxerStream();
96 } 166 }
97 167
98 ChannelLayout FFmpegAudioDecoder::channel_layout() { 168 void FFmpegAudioDecoder::DoDecodeBuffer(const scoped_refptr<Buffer>& input) {
99 return channel_layout_; 169 DCHECK(!output_buffers_.empty());
100 } 170 DCHECK_GT(pending_reads_, 0);
101 171 pending_reads_--;
102 int FFmpegAudioDecoder::sample_rate() {
103 return sample_rate_;
104 }
105
106 void FFmpegAudioDecoder::DoSeek(base::TimeDelta time, Task* done_cb) {
107 avcodec_flush_buffers(codec_context_);
108 estimated_next_timestamp_ = kNoTimestamp;
109 done_cb->Run();
110 delete done_cb;
111 }
112
113 // ConvertAudioF32ToS32() converts float audio (F32) to int (S32) in place.
114 // This is a temporary solution.
115 // The purpose of this short term fix is to enable WMApro, which decodes to
116 // float.
117 // The audio driver has been tested by passing the float audio thru.
118 // FFmpeg for ChromeOS only exposes U8, S16 and F32.
119 // To properly expose new audio sample types at the audio driver layer, a enum
120 // should be created to represent all suppported types, including types
121 // for Pepper. FFmpeg should be queried for type and passed along.
122
123 // TODO(fbarchard): Remove this function. Expose all FFmpeg types to driver.
124 // TODO(fbarchard): If this function is kept, move it to audio_util.cc
125
126 #if USE_SSE
127 const __m128 kFloatScaler = _mm_set1_ps( 2147483648.0f );
128 static void FloatToIntSaturate(float* p) {
129 __m128 a = _mm_set1_ps(*p);
130 a = _mm_mul_ss(a, kFloatScaler);
131 *reinterpret_cast<int32*>(p) = _mm_cvtss_si32(a);
132 }
133 #else
134 const float kFloatScaler = 2147483648.0f;
135 const int kMinSample = std::numeric_limits<int32>::min();
136 const int kMaxSample = std::numeric_limits<int32>::max();
137 const float kMinSampleFloat =
138 static_cast<float>(std::numeric_limits<int32>::min());
139 const float kMaxSampleFloat =
140 static_cast<float>(std::numeric_limits<int32>::max());
141 static void FloatToIntSaturate(float* p) {
142 float f = *p * kFloatScaler + 0.5f;
143 int sample;
144 if (f <= kMinSampleFloat) {
145 sample = kMinSample;
146 } else if (f >= kMaxSampleFloat) {
147 sample = kMaxSample;
148 } else {
149 sample = static_cast<int32>(f);
150 }
151 *reinterpret_cast<int32*>(p) = sample;
152 }
153 #endif
154 static void ConvertAudioF32ToS32(void* buffer, int buffer_size) {
155 for (int i = 0; i < buffer_size / 4; ++i) {
156 FloatToIntSaturate(reinterpret_cast<float*>(buffer) + i);
157 }
158 }
159
160 void FFmpegAudioDecoder::DoDecode(Buffer* input) {
161 PipelineStatistics statistics;
162 172
163 // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us 173 // FFmpeg tends to seek Ogg audio streams in the middle of nowhere, giving us
164 // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find 174 // a whole bunch of AV_NOPTS_VALUE packets. Discard them until we find
165 // something valid. Refer to http://crbug.com/49709 175 // something valid. Refer to http://crbug.com/49709
166 // TODO(hclam): remove this once fixing the issue in FFmpeg.
167 if (input->GetTimestamp() == kNoTimestamp && 176 if (input->GetTimestamp() == kNoTimestamp &&
168 estimated_next_timestamp_ == kNoTimestamp && 177 estimated_next_timestamp_ == kNoTimestamp &&
169 !input->IsEndOfStream()) { 178 !input->IsEndOfStream()) {
170 DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(statistics); 179 ReadFromDemuxerStream();
171 return; 180 return;
172 } 181 }
173 182
174 // Due to FFmpeg API changes we no longer have const read-only pointers.
175 AVPacket packet; 183 AVPacket packet;
176 av_init_packet(&packet); 184 av_init_packet(&packet);
177 packet.data = const_cast<uint8*>(input->GetData()); 185 if (input->IsEndOfStream()) {
178 packet.size = input->GetDataSize(); 186 packet.data = NULL;
187 packet.size = 0;
188 } else {
189 packet.data = const_cast<uint8*>(input->GetData());
190 packet.size = input->GetDataSize();
191 }
179 192
193 PipelineStatistics statistics;
180 statistics.audio_bytes_decoded = input->GetDataSize(); 194 statistics.audio_bytes_decoded = input->GetDataSize();
181 195
182 int16_t* output_buffer = reinterpret_cast<int16_t*>(output_buffer_.get()); 196 int decoded_audio_size = decoded_audio_size_;
183 int output_buffer_size = kOutputBufferSize; 197 int result = avcodec_decode_audio3(
184 int result = avcodec_decode_audio3(codec_context_, 198 codec_context_, reinterpret_cast<int16_t*>(decoded_audio_),
185 output_buffer, 199 &decoded_audio_size, &packet);
186 &output_buffer_size,
187 &packet);
188 200
189 if (codec_context_->sample_fmt == SAMPLE_FMT_FLT) { 201 if (IsErrorResult(result, decoded_audio_size)) {
190 ConvertAudioF32ToS32(output_buffer, output_buffer_size); 202 DCHECK(!input->IsEndOfStream())
191 } 203 << "End of stream buffer produced an error! "
204 << "This is quite possibly a bug in the audio decoder not handling "
205 << "end of stream AVPackets correctly.";
192 206
193 // TODO(ajwong): Consider if kOutputBufferSize should just be an int instead 207 DLOG(ERROR) << "Error decoding an audio frame with timestamp: "
194 // of a size_t. 208 << input->GetTimestamp().InMicroseconds() << " us, duration: "
195 if (result < 0 || 209 << input->GetDuration().InMicroseconds() << " us, packet size: "
196 output_buffer_size < 0 || 210 << input->GetDataSize() << " bytes";
197 static_cast<size_t>(output_buffer_size) > kOutputBufferSize) { 211
198 VLOG(1) << "Error decoding an audio frame with timestamp: " 212 ReadFromDemuxerStream();
199 << input->GetTimestamp().InMicroseconds() << " us, duration: "
200 << input->GetDuration().InMicroseconds() << " us, packet size: "
201 << input->GetDataSize() << " bytes";
202 DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(statistics);
203 return; 213 return;
204 } 214 }
205 215
206 // If we have decoded something, enqueue the result. 216 scoped_refptr<DataBuffer> output;
207 if (output_buffer_size) {
208 DataBuffer* result_buffer = new DataBuffer(output_buffer_size);
209 result_buffer->SetDataSize(output_buffer_size);
210 uint8* data = result_buffer->GetWritableData();
211 memcpy(data, output_buffer, output_buffer_size);
212 217
213 // We don't trust the demuxer, so always calculate the duration based on 218 if (ProducedAudioSamples(decoded_audio_size)) {
214 // the actual number of samples decoded. 219 // Copy the audio samples into an output buffer.
215 base::TimeDelta duration = CalculateDuration(output_buffer_size); 220 output = new DataBuffer(decoded_audio_size);
216 result_buffer->SetDuration(duration); 221 output->SetDataSize(decoded_audio_size);
222 uint8* data = output->GetWritableData();
223 memcpy(data, decoded_audio_, decoded_audio_size);
217 224
218 // Use an estimated timestamp unless the incoming buffer has a valid one. 225 UpdateDurationAndTimestamp(input, output);
219 if (input->GetTimestamp() == kNoTimestamp) { 226 } else if (IsTimestampMarkerPacket(result, input)) {
220 result_buffer->SetTimestamp(estimated_next_timestamp_); 227 // Nothing else to do here but update our estimation.
228 estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration();
229 } else if (IsEndOfStream(result, decoded_audio_size, input)) {
230 // Create an end of stream output buffer.
231 output = new DataBuffer(0);
232 output->SetTimestamp(input->GetTimestamp());
233 output->SetDuration(input->GetDuration());
234 }
221 235
222 // Keep the estimated timestamp invalid until we get an incoming buffer 236 // Decoding finished successfully, update stats and execute callback.
223 // with a valid timestamp. This can happen during seeks, etc... 237 stats_callback_->Run(statistics);
224 if (estimated_next_timestamp_ != kNoTimestamp) { 238 if (output) {
225 estimated_next_timestamp_ += duration; 239 DCHECK_GT(output_buffers_.size(), 0u);
226 } 240 output_buffers_.pop_front();
227 } else {
228 result_buffer->SetTimestamp(input->GetTimestamp());
229 estimated_next_timestamp_ = input->GetTimestamp() + duration;
230 }
231 241
232 EnqueueResult(result_buffer); 242 ConsumeAudioSamples(output);
233 DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(statistics); 243 } else {
244 ReadFromDemuxerStream();
245 }
246 }
247
248 void FFmpegAudioDecoder::ReadFromDemuxerStream() {
249 DCHECK(!output_buffers_.empty())
250 << "Reads should only occur if there are output buffers.";
251
252 pending_reads_++;
253 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this));
254 }
255
256 void FFmpegAudioDecoder::DecodeBuffer(Buffer* buffer) {
257 // TODO(scherkus): change DemuxerStream::Read() to use scoped_refptr<> for
258 // callback.
259 scoped_refptr<Buffer> ref_buffer(buffer);
260 message_loop_->PostTask(
261 FROM_HERE,
262 NewRunnableMethod(this, &FFmpegAudioDecoder::DoDecodeBuffer, ref_buffer));
263 }
264
265 void FFmpegAudioDecoder::UpdateDurationAndTimestamp(
266 const Buffer* input,
267 DataBuffer* output) {
268 // Always calculate duration based on the actual number of samples decoded.
269 base::TimeDelta duration = CalculateDuration(output->GetDataSize());
270 output->SetDuration(duration);
271
272 // Use the incoming timestamp if it's valid.
273 if (input->GetTimestamp() != kNoTimestamp) {
274 output->SetTimestamp(input->GetTimestamp());
275 estimated_next_timestamp_ = input->GetTimestamp() + duration;
234 return; 276 return;
235 } 277 }
236 278
237 // We can get a positive result but no decoded data. This is ok because this 279 // Otherwise use an estimated timestamp and attempt to update the estimation
238 // this can be a marker packet that only contains timestamp. In this case we 280 // as long as it's valid.
239 // save the timestamp for later use. 281 output->SetTimestamp(estimated_next_timestamp_);
240 if (result && !input->IsEndOfStream() && 282 if (estimated_next_timestamp_ != kNoTimestamp) {
241 input->GetTimestamp() != kNoTimestamp && 283 estimated_next_timestamp_ += duration;
242 input->GetDuration() != kNoTimestamp) {
243 estimated_next_timestamp_ = input->GetTimestamp() + input->GetDuration();
244 DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(statistics);
245 return;
246 } 284 }
247
248 // Three conditions to meet to declare end of stream for this decoder:
249 // 1. FFmpeg didn't read anything.
250 // 2. FFmpeg didn't output anything.
251 // 3. An end of stream buffer is received.
252 if (result == 0 && output_buffer_size == 0 && input->IsEndOfStream()) {
253 DataBuffer* result_buffer = new DataBuffer(0);
254 result_buffer->SetTimestamp(input->GetTimestamp());
255 result_buffer->SetDuration(input->GetDuration());
256 EnqueueResult(result_buffer);
257 }
258 DecoderBase<AudioDecoder, Buffer>::OnDecodeComplete(statistics);
259 } 285 }
260 286
261 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(size_t size) { 287 base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) {
262 int64 denominator = codec_context_->channels * 288 int64 denominator = ChannelLayoutToChannelCount(channel_layout_) *
263 av_get_bits_per_sample_fmt(codec_context_->sample_fmt) / 8 * 289 bits_per_channel_ / 8 * sample_rate_;
264 codec_context_->sample_rate;
265 double microseconds = size / 290 double microseconds = size /
266 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond)); 291 (denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond));
267 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds)); 292 return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds));
268 } 293 }
269 294
270 } // namespace 295 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/ffmpeg_audio_decoder.h ('k') | media/filters/ffmpeg_video_decoder.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698