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

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: Enable test. Fix style error. 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've got any queued audio from the last packet we decoded, hand it out.
172 if (!queued_audio_.empty()) {
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 flip condition, early return, & drop the else
DaleCurtis 2012/08/28 20:19:16 Done.
173 base::ResetAndReturn(&read_cb_).Run(
174 queued_audio_.front().status, queued_audio_.front().buffer);
175 queued_audio_.erase(queued_audio_.begin());
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 nit: use pop_front()
DaleCurtis 2012/08/28 20:19:16 Done.
176 } else {
177 ReadFromDemuxerStream();
178 }
164 } 179 }
165 180
166 void FFmpegAudioDecoder::DoDecodeBuffer( 181 void FFmpegAudioDecoder::DoDecodeBuffer(
167 DemuxerStream::Status status, 182 DemuxerStream::Status status,
168 const scoped_refptr<DecoderBuffer>& input) { 183 const scoped_refptr<DecoderBuffer>& input) {
169 DCHECK(message_loop_->BelongsToCurrentThread()); 184 DCHECK(message_loop_->BelongsToCurrentThread());
170 DCHECK(!read_cb_.is_null()); 185 DCHECK(!read_cb_.is_null());
186 DCHECK(queued_audio_.empty());
171 187
172 if (status != DemuxerStream::kOk) { 188 if (status != DemuxerStream::kOk) {
173 DCHECK(!input); 189 DCHECK(!input);
174 // TODO(acolwell): Add support for reinitializing the decoder when 190 // TODO(acolwell): Add support for reinitializing the decoder when
175 // |status| == kConfigChanged. For now we just trigger a decode error. 191 // |status| == kConfigChanged. For now we just trigger a decode error.
176 AudioDecoder::Status decoder_status = 192 AudioDecoder::Status decoder_status =
177 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError; 193 (status == DemuxerStream::kAborted) ? kAborted : kDecodeError;
178 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL); 194 base::ResetAndReturn(&read_cb_).Run(decoder_status, NULL);
179 return; 195 return;
180 } 196 }
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
215 } 231 }
216 232
217 AVPacket packet; 233 AVPacket packet;
218 av_init_packet(&packet); 234 av_init_packet(&packet);
219 packet.data = const_cast<uint8*>(input->GetData()); 235 packet.data = const_cast<uint8*>(input->GetData());
220 packet.size = input->GetDataSize(); 236 packet.size = input->GetDataSize();
221 237
222 PipelineStatistics statistics; 238 PipelineStatistics statistics;
223 statistics.audio_bytes_decoded = input->GetDataSize(); 239 statistics.audio_bytes_decoded = input->GetDataSize();
224 240
225 // Reset frame to default values. 241 // Each audio packet may contain several frames, so we must call the decoder
226 avcodec_get_frame_defaults(av_frame_); 242 // until we've exhausted the packet. Regardless of the packet size we always
243 // want to hand it to the decoder at least once, otherwise we would end up
244 // skipping end of stream packets since they have a size of zero.
245 do {
246 // Reset frame to default values.
247 avcodec_get_frame_defaults(av_frame_);
227 248
228 int frame_decoded = 0; 249 int frame_decoded = 0;
229 int result = avcodec_decode_audio4( 250 int result = avcodec_decode_audio4(
230 codec_context_, av_frame_, &frame_decoded, &packet); 251 codec_context_, av_frame_, &frame_decoded, &packet);
231 252
232 if (result < 0) { 253 if (result < 0) {
233 DCHECK(!input->IsEndOfStream()) 254 DCHECK(!input->IsEndOfStream())
234 << "End of stream buffer produced an error! " 255 << "End of stream buffer produced an error! "
235 << "This is quite possibly a bug in the audio decoder not handling " 256 << "This is quite possibly a bug in the audio decoder not handling "
236 << "end of stream AVPackets correctly."; 257 << "end of stream AVPackets correctly.";
237 258
238 DLOG(ERROR) << "Error decoding an audio frame with timestamp: " 259 DLOG(ERROR)
239 << input->GetTimestamp().InMicroseconds() << " us, duration: " 260 << "Error decoding an audio frame with timestamp: "
240 << input->GetDuration().InMicroseconds() << " us, packet size: " 261 << input->GetTimestamp().InMicroseconds() << " us, duration: "
241 << input->GetDataSize() << " bytes"; 262 << input->GetDuration().InMicroseconds() << " us, packet size: "
263 << input->GetDataSize() << " bytes";
242 264
265 // We've hit an error, if we don't already have any decoded audio queued
266 // we need to ask for more data from the demuxer to fulfill this read.
267 if (queued_audio_.empty()) {
268 ReadFromDemuxerStream();
269 return;
270 } else {
271 break;
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 It looks like you should be able to unconditionall
DaleCurtis 2012/08/28 20:19:16 Done.
272 }
273 }
274
275 // Update packet size and data pointer in case we need to call the decoder
276 // with the remaining bytes from this packet.
277 packet.size -= result;
278 packet.data += result;
279
280 if (output_timestamp_base_ == kNoTimestamp() && !input->IsEndOfStream()) {
281 DCHECK(input->GetTimestamp() != kNoTimestamp());
282 if (output_bytes_to_drop_ > 0) {
283 // Currently Vorbis is the only codec that causes us to drop samples.
284 // If we have to drop samples it always means the timeline starts at 0.
285 DCHECK(is_vorbis);
286 output_timestamp_base_ = base::TimeDelta();
287 } else {
288 output_timestamp_base_ = input->GetTimestamp();
289 }
290 }
291
292 const uint8* decoded_audio_data = NULL;
293 int decoded_audio_size = 0;
294 if (frame_decoded) {
295 int output_sample_rate = av_frame_->sample_rate;
296 if (output_sample_rate != samples_per_second_) {
297 DLOG(ERROR) << "Output sample rate (" << output_sample_rate
298 << ") doesn't match expected rate " << samples_per_second_;
299
300 QueuedAudioBuffer queue_entry = { kDecodeError, NULL };
301 queued_audio_.push_back(queue_entry);
302 continue;
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 This should break & not continue decoding.
DaleCurtis 2012/08/28 20:19:16 Done.
303 }
304
305 decoded_audio_data = av_frame_->data[0];
306 decoded_audio_size = av_samples_get_buffer_size(
307 NULL, codec_context_->channels, av_frame_->nb_samples,
308 codec_context_->sample_fmt, 1);
309 }
310
311 scoped_refptr<DataBuffer> output;
312
313 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) {
314 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_);
315 decoded_audio_data += dropped_size;
316 decoded_audio_size -= dropped_size;
317 output_bytes_to_drop_ -= dropped_size;
318 }
319
320 if (decoded_audio_size > 0) {
321 DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0)
322 << "Decoder didn't output full frames";
323
324 // Copy the audio samples into an output buffer.
325 output = new DataBuffer(decoded_audio_size);
326 output->SetDataSize(decoded_audio_size);
327 uint8* data = output->GetWritableData();
328 memcpy(data, decoded_audio_data, decoded_audio_size);
329
330 base::TimeDelta timestamp = GetNextOutputTimestamp();
331 total_frames_decoded_ += decoded_audio_size / bytes_per_frame_;
332
333 output->SetTimestamp(timestamp);
334 output->SetDuration(GetNextOutputTimestamp() - timestamp);
335 } else if (IsEndOfStream(result, decoded_audio_size, input)) {
336 // Create an end of stream output buffer.
337 output = new DataBuffer(0);
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 You should set packet.size = 0 here since we don't
DaleCurtis 2012/08/28 20:19:16 Should already be zero here, changed to DCHECK.
338 }
339
340 if (output) {
341 QueuedAudioBuffer queue_entry = { kOk, output };
342 queued_audio_.push_back(queue_entry);
343 }
344 } while (packet.size > 0);
345
346 // Decoding finished successfully, update statistics.
347 statistics_cb_.Run(statistics);
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 I think this needs to be moved into the bottom of
DaleCurtis 2012/08/28 20:19:16 Done.
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_.erase(queued_audio_.begin());
acolwell GONE FROM CHROMIUM 2012/08/28 14:40:49 use pop_front()
DaleCurtis 2012/08/28 20:19:16 Done.
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
« media/filters/ffmpeg_audio_decoder.h ('K') | « media/filters/ffmpeg_audio_decoder.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698