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

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

Issue 10869085: Update FFmpegAudioDecoder to support multi-frame packets. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Fix WebAudio. Created 8 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
OLDNEW
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_proxy.h" 10 #include "base/message_loop_proxy.h"
11 #include "media/base/audio_decoder_config.h" 11 #include "media/base/audio_decoder_config.h"
12 #include "media/base/data_buffer.h" 12 #include "media/base/data_buffer.h"
13 #include "media/base/decoder_buffer.h" 13 #include "media/base/decoder_buffer.h"
14 #include "media/base/demuxer.h" 14 #include "media/base/demuxer.h"
15 #include "media/base/pipeline.h" 15 #include "media/base/pipeline.h"
16 #include "media/ffmpeg/ffmpeg_common.h" 16 #include "media/ffmpeg/ffmpeg_common.h"
17 #include "media/filters/ffmpeg_glue.h" 17 #include "media/filters/ffmpeg_glue.h"
18 18
19 namespace media { 19 namespace media {
20 20
21 // Helper structure for managing multiple decoded audio frames per packet.
22 struct QueuedAudioBuffer {
23 AudioDecoder::Status status;
24 scoped_refptr<Buffer> buffer;
25 };
26
21 // Returns true if the decode result was end of stream. 27 // Returns true if the decode result was end of stream.
22 static inline bool IsEndOfStream(int result, int decoded_size, Buffer* input) { 28 static inline bool IsEndOfStream(int result, int decoded_size, Buffer* input) {
23 // Three conditions to meet to declare end of stream for this decoder: 29 // Three conditions to meet to declare end of stream for this decoder:
24 // 1. FFmpeg didn't read anything. 30 // 1. FFmpeg didn't read anything.
25 // 2. FFmpeg didn't output anything. 31 // 2. FFmpeg didn't output anything.
26 // 3. An end of stream buffer is received. 32 // 3. An end of stream buffer is received.
27 return result == 0 && decoded_size == 0 && input->IsEndOfStream(); 33 return result == 0 && decoded_size == 0 && input->IsEndOfStream();
28 } 34 }
29 35
30 FFmpegAudioDecoder::FFmpegAudioDecoder( 36 FFmpegAudioDecoder::FFmpegAudioDecoder(
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; 150 bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8;
145 status_cb.Run(PIPELINE_OK); 151 status_cb.Run(PIPELINE_OK);
146 } 152 }
147 153
148 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) { 154 void FFmpegAudioDecoder::DoReset(const base::Closure& closure) {
149 avcodec_flush_buffers(codec_context_); 155 avcodec_flush_buffers(codec_context_);
150 output_timestamp_base_ = kNoTimestamp(); 156 output_timestamp_base_ = kNoTimestamp();
151 total_frames_decoded_ = 0; 157 total_frames_decoded_ = 0;
152 last_input_timestamp_ = kNoTimestamp(); 158 last_input_timestamp_ = kNoTimestamp();
153 output_bytes_to_drop_ = 0; 159 output_bytes_to_drop_ = 0;
160 queued_audio_.clear();
154 closure.Run(); 161 closure.Run();
155 } 162 }
156 163
157 void FFmpegAudioDecoder::DoRead(const ReadCB& read_cb) { 164 void FFmpegAudioDecoder::DoRead(const ReadCB& read_cb) {
158 DCHECK(message_loop_->BelongsToCurrentThread()); 165 DCHECK(message_loop_->BelongsToCurrentThread());
159 DCHECK(!read_cb.is_null()); 166 DCHECK(!read_cb.is_null());
160 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported."; 167 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
161 168
162 read_cb_ = read_cb; 169 read_cb_ = read_cb;
163 ReadFromDemuxerStream(); 170
171 // If we don't have any queued audio from the last packet we decoded, ask for
172 // more data from the demuxer to satisfy this read.
173 if (queued_audio_.empty()) {
174 ReadFromDemuxerStream();
175 return;
176 }
177
178 base::ResetAndReturn(&read_cb_).Run(
179 queued_audio_.front().status, queued_audio_.front().buffer);
180 queued_audio_.pop_front();
164 } 181 }
165 182
166 void FFmpegAudioDecoder::DoDecodeBuffer( 183 void FFmpegAudioDecoder::DoDecodeBuffer(
167 DemuxerStream::Status status, 184 DemuxerStream::Status status,
168 const scoped_refptr<DecoderBuffer>& input) { 185 const scoped_refptr<DecoderBuffer>& input) {
169 DCHECK(message_loop_->BelongsToCurrentThread()); 186 DCHECK(message_loop_->BelongsToCurrentThread());
170 DCHECK(!read_cb_.is_null()); 187 DCHECK(!read_cb_.is_null());
188 DCHECK(queued_audio_.empty());
171 189
172 if (status != DemuxerStream::kOk) { 190 if (status != DemuxerStream::kOk) {
173 DCHECK(!input); 191 DCHECK(!input);
174 // TODO(acolwell): Add support for reinitializing the decoder when 192 // TODO(acolwell): Add support for reinitializing the decoder when
175 // |status| == kConfigChanged. For now we just trigger a decode error. 193 // |status| == kConfigChanged. For now we just trigger a decode error.
176 AudioDecoder::Status decoder_status = 194 AudioDecoder::Status decoder_status =
177 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError; 195 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError;
178 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL); 196 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL);
179 return; 197 return;
180 } 198 }
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 230
213 last_input_timestamp_ = input->GetTimestamp(); 231 last_input_timestamp_ = input->GetTimestamp();
214 } 232 }
215 } 233 }
216 234
217 AVPacket packet; 235 AVPacket packet;
218 av_init_packet(&packet); 236 av_init_packet(&packet);
219 packet.data = const_cast<uint8*>(input->GetData()); 237 packet.data = const_cast<uint8*>(input->GetData());
220 packet.size = input->GetDataSize(); 238 packet.size = input->GetDataSize();
221 239
222 PipelineStatistics statistics; 240 // Each audio packet may contain several frames, so we must call the decoder
223 statistics.audio_bytes_decoded = input->GetDataSize(); 241 // until we've exhausted the packet. Regardless of the packet size we always
242 // want to hand it to the decoder at least once, otherwise we would end up
243 // skipping end of stream packets since they have a size of zero.
244 do {
245 // Reset frame to default values.
246 avcodec_get_frame_defaults(av_frame_);
224 247
225 // Reset frame to default values. 248 int frame_decoded = 0;
226 avcodec_get_frame_defaults(av_frame_); 249 int result = avcodec_decode_audio4(
250 codec_context_, av_frame_, &frame_decoded, &packet);
227 251
228 int frame_decoded = 0; 252 if (result < 0) {
229 int result = avcodec_decode_audio4( 253 DCHECK(!input->IsEndOfStream())
230 codec_context_, av_frame_, &frame_decoded, &packet); 254 << "End of stream buffer produced an error! "
255 << "This is quite possibly a bug in the audio decoder not handling "
256 << "end of stream AVPackets correctly.";
231 257
232 if (result < 0) { 258 DLOG(ERROR)
233 DCHECK(!input->IsEndOfStream()) 259 << "Error decoding an audio frame with timestamp: "
234 << "End of stream buffer produced an error! " 260 << input->GetTimestamp().InMicroseconds() << " us, duration: "
235 << "This is quite possibly a bug in the audio decoder not handling " 261 << input->GetDuration().InMicroseconds() << " us, packet size: "
236 << "end of stream AVPackets correctly."; 262 << input->GetDataSize() << " bytes";
237 263
238 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " 264 // TODO(dalecurtis): We should return a kDecodeError here instead:
239 << input->GetTimestamp().InMicroseconds() << " us, duration: " 265 // http://crbug.com/145276
240 << input->GetDuration().InMicroseconds() << " us, packet size: " 266 break;
241 << input->GetDataSize() << " bytes"; 267 }
242 268
269 // Update packet size and data pointer in case we need to call the decoder
270 // with the remaining bytes from this packet.
271 packet.size -= result;
272 packet.data += result;
273
274 if (output_timestamp_base_ == kNoTimestamp() && !input->IsEndOfStream()) {
275 DCHECK(input->GetTimestamp() != kNoTimestamp());
276 if (output_bytes_to_drop_ > 0) {
277 // Currently Vorbis is the only codec that causes us to drop samples.
278 // If we have to drop samples it always means the timeline starts at 0.
279 DCHECK(is_vorbis);
280 output_timestamp_base_ = base::TimeDelta();
281 } else {
282 output_timestamp_base_ = input->GetTimestamp();
283 }
284 }
285
286 const uint8* decoded_audio_data = NULL;
287 int decoded_audio_size = 0;
288 if (frame_decoded) {
289 int output_sample_rate = av_frame_->sample_rate;
290 if (output_sample_rate != samples_per_second_) {
291 DLOG(ERROR) << "Output sample rate (" << output_sample_rate
292 << ") doesn't match expected rate " << samples_per_second_;
293
294 // This is an unrecoverable error, so bail out.
295 QueuedAudioBuffer queue_entry = { kDecodeError, NULL };
296 queued_audio_.push_back(queue_entry);
297 break;
298 }
299
300 decoded_audio_data = av_frame_->data[0];
301 decoded_audio_size = av_samples_get_buffer_size(
302 NULL, codec_context_->channels, av_frame_->nb_samples,
303 codec_context_->sample_fmt, 1);
304 }
305
306 scoped_refptr<DataBuffer> output;
307
308 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) {
309 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_);
310 decoded_audio_data += dropped_size;
311 decoded_audio_size -= dropped_size;
312 output_bytes_to_drop_ -= dropped_size;
313 }
314
315 if (decoded_audio_size > 0) {
316 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0)
317 << "Decoder didn't output full frames";
318
319 // Copy the audio samples into an output buffer.
320 output = new DataBuffer(decoded_audio_size);
321 output->SetDataSize(decoded_audio_size);
322 uint8* data = output->GetWritableData();
323 memcpy(data, decoded_audio_data, decoded_audio_size);
324
325 base::TimeDelta timestamp = GetNextOutputTimestamp();
326 total_frames_decoded_ += decoded_audio_size / bytes_per_frame_;
327
328 output->SetTimestamp(timestamp);
329 output->SetDuration(GetNextOutputTimestamp() - timestamp);
330 } else if (IsEndOfStream(result, decoded_audio_size, input)) {
331 DCHECK_EQ(packet.size, 0);
332 // Create an end of stream output buffer.
333 output = new DataBuffer(0);
334 }
335
336 if (output) {
337 QueuedAudioBuffer queue_entry = { kOk, output };
338 queued_audio_.push_back(queue_entry);
339 }
340
341 // Decoding finished successfully, update statistics.
342 if (result > 0) {
343 PipelineStatistics statistics;
344 statistics.audio_bytes_decoded = result;
345 statistics_cb_.Run(statistics);
346 }
347 } while (packet.size > 0);
348
349 // We exhausted the provided packet, but it wasn't enough for a frame. Ask
350 // for more data in order to fulfill this read.
351 if (queued_audio_.empty()) {
243 ReadFromDemuxerStream(); 352 ReadFromDemuxerStream();
244 return; 353 return;
245 } 354 }
246 355
247 if (result > 0) 356 // Execute callback to return the first frame we decoded.
248 DCHECK_EQ(result, input->GetDataSize()); 357 base::ResetAndReturn(&read_cb_).Run(
249 358 queued_audio_.front().status, queued_audio_.front().buffer);
250 if (output_timestamp_base_ == kNoTimestamp() && !input->IsEndOfStream()) { 359 queued_audio_.pop_front();
251 DCHECK(input->GetTimestamp() != kNoTimestamp());
252 if (output_bytes_to_drop_ > 0) {
253 // Currently Vorbis is the only codec that causes us to drop samples.
254 // If we have to drop samples it always means the timeline starts at 0.
255 DCHECK(is_vorbis);
256 output_timestamp_base_ = base::TimeDelta();
257 } else {
258 output_timestamp_base_ = input->GetTimestamp();
259 }
260 }
261
262 const uint8* decoded_audio_data = NULL;
263 int decoded_audio_size = 0;
264 if (frame_decoded) {
265 int output_sample_rate = av_frame_->sample_rate;
266 if (output_sample_rate != samples_per_second_) {
267 DLOG(ERROR) << "Output sample rate (" << output_sample_rate
268 << ") doesn't match expected rate " << samples_per_second_;
269 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
270 return;
271 }
272
273 decoded_audio_data = av_frame_->data[0];
274 decoded_audio_size = av_samples_get_buffer_size(
275 NULL, codec_context_->channels, av_frame_->nb_samples,
276 codec_context_->sample_fmt, 1);
277 }
278
279 scoped_refptr<DataBuffer> output;
280
281 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) {
282 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_);
283 decoded_audio_data += dropped_size;
284 decoded_audio_size -= dropped_size;
285 output_bytes_to_drop_ -= dropped_size;
286 }
287
288 if (decoded_audio_size > 0) {
289 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0)
290 << "Decoder didn't output full frames";
291
292 // Copy the audio samples into an output buffer.
293 output = new DataBuffer(decoded_audio_size);
294 output->SetDataSize(decoded_audio_size);
295 uint8* data = output->GetWritableData();
296 memcpy(data, decoded_audio_data, decoded_audio_size);
297
298 base::TimeDelta timestamp = GetNextOutputTimestamp();
299 total_frames_decoded_ += decoded_audio_size / bytes_per_frame_;
300
301 output->SetTimestamp(timestamp);
302 output->SetDuration(GetNextOutputTimestamp() - timestamp);
303 } else if (IsEndOfStream(result, decoded_audio_size, input)) {
304 // Create an end of stream output buffer.
305 output = new DataBuffer(0);
306 }
307
308 // Decoding finished successfully, update stats and execute callback.
309 statistics_cb_.Run(statistics);
310
311 if (!output) {
312 ReadFromDemuxerStream();
313 return;
314 }
315 base::ResetAndReturn(&read_cb_).Run(kOk, output);
316 } 360 }
317 361
318 void FFmpegAudioDecoder::ReadFromDemuxerStream() { 362 void FFmpegAudioDecoder::ReadFromDemuxerStream() {
319 DCHECK(!read_cb_.is_null()); 363 DCHECK(!read_cb_.is_null());
320 364
321 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this)); 365 demuxer_stream_->Read(base::Bind(&FFmpegAudioDecoder::DecodeBuffer, this));
322 } 366 }
323 367
324 void FFmpegAudioDecoder::DecodeBuffer( 368 void FFmpegAudioDecoder::DecodeBuffer(
325 DemuxerStream::Status status, 369 DemuxerStream::Status status,
326 const scoped_refptr<DecoderBuffer>& buffer) { 370 const scoped_refptr<DecoderBuffer>& buffer) {
327 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status; 371 DCHECK_EQ(status != DemuxerStream::kOk, !buffer) << status;
328 372
329 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read 373 // TODO(scherkus): fix FFmpegDemuxerStream::Read() to not execute our read
330 // callback on the same execution stack so we can get rid of forced task post. 374 // callback on the same execution stack so we can get rid of forced task post.
331 message_loop_->PostTask(FROM_HERE, base::Bind( 375 message_loop_->PostTask(FROM_HERE, base::Bind(
332 &FFmpegAudioDecoder::DoDecodeBuffer, this, status, buffer)); 376 &FFmpegAudioDecoder::DoDecodeBuffer, this, status, buffer));
333 } 377 }
334 378
335 base::TimeDelta FFmpegAudioDecoder::GetNextOutputTimestamp() const { 379 base::TimeDelta FFmpegAudioDecoder::GetNextOutputTimestamp() const {
336 DCHECK(output_timestamp_base_ != kNoTimestamp()); 380 DCHECK(output_timestamp_base_ != kNoTimestamp());
337 double decoded_us = (total_frames_decoded_ / samples_per_second_) * 381 double decoded_us = (total_frames_decoded_ / samples_per_second_) *
338 base::Time::kMicrosecondsPerSecond; 382 base::Time::kMicrosecondsPerSecond;
339 return output_timestamp_base_ + base::TimeDelta::FromMicroseconds(decoded_us); 383 return output_timestamp_base_ + base::TimeDelta::FromMicroseconds(decoded_us);
340 } 384 }
341 } // namespace media 385 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698