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

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

Issue 11416367: Add Opus decode wrapper to media. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased (changed machines) Address most comments. Added TODO for Opus buffering question. Created 8 years 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/filters/opus_audio_decoder.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/location.h"
10 #include "base/message_loop_proxy.h"
11 #include "base/sys_byteorder.h"
12 #include "media/base/audio_decoder_config.h"
13 #include "media/base/audio_timestamp_helper.h"
14 #include "media/base/data_buffer.h"
15 #include "media/base/decoder_buffer.h"
16 #include "media/base/demuxer.h"
17 #include "media/base/pipeline.h"
18 #include "third_party/opus/src/include/opus.h"
19 #include "third_party/opus/src/include/opus_multistream.h"
20
21 namespace media {
22
23 static uint16 ReadLE16(const uint8* data, size_t data_size, int read_offset) {
24 DCHECK(data);
25 uint16 value = 0;
26 DCHECK_LE(read_offset + sizeof(value), data_size);
27 memcpy(&value, data + read_offset, sizeof(value));
28 return base::ByteSwapToLE16(value);
29 }
30
31 // Returns true if the decode result was end of stream.
32 static inline bool IsEndOfStream(int decoded_size, Buffer* input) {
33 // Two conditions to meet to declare end of stream for this decoder:
34 // 1. Opus didn't output anything.
35 // 2. An end of stream buffer is received.
36 return decoded_size == 0 && input->IsEndOfStream();
37 }
38
39 // The Opus specification is part of IETF RFC 6716:
40 // http://tools.ietf.org/html/rfc6716
41
42 // Opus uses Vorbis channel mapping, and Vorbis channel mapping specifies
43 // mappings for up to 8 channels. See section 4.3.9 of the vorbis
44 // specification:
45 // http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html
46 static const int kMaxVorbisChannels = 8;
47
48 // Opus allows for decode of S16 or float samples. OpusAudioDecoder always uses
49 // S16 samples.
50 static const int kBitsPerChannel = 16;
51 static const int kBytesPerChannel = kBitsPerChannel / 8;
52
53 // Maximum packet size used in Xiph's opusdec and FFmpeg's libopusdec.
54 static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
55 static const int kMaxOpusOutputPacketSizeBytes =
56 kMaxOpusOutputPacketSizeSamples * kBytesPerChannel;
57
58 static bool RemapOpusChannelLayout(const uint8* opus_mapping,
59 int num_channels,
60 uint8* channel_layout) {
61 DCHECK(opus_mapping);
62 DCHECK(channel_layout);
scherkus (not reviewing) 2012/12/14 21:13:34 you handle having a null channel_layout on line 64
Tom Finegan 2012/12/14 23:19:52 Done.
63 DCHECK_LE(num_channels, kMaxVorbisChannels);
scherkus (not reviewing) 2012/12/14 21:13:34 ditto for this check -- you handle it gracefully o
Tom Finegan 2012/12/14 23:19:52 Function returns void now.
64 if (!channel_layout || num_channels > kMaxVorbisChannels)
65 return false;
66
67 // Opus uses Vorbis channel layout.
68 const int32 num_layouts = kMaxVorbisChannels;
69 const int32 num_layout_values = kMaxVorbisChannels;
70 const uint8 kVorbisChannelLayouts[num_layouts][num_layout_values] = {
71 { 0 },
72 { 0, 1 },
73 { 0, 2, 1 },
74 { 0, 1, 2, 3 },
75 { 0, 2, 1, 3, 4 },
76 { 0, 2, 1, 5, 3, 4 },
77 { 0, 2, 1, 6, 5, 3, 4 },
78 { 0, 2, 1, 7, 5, 6, 3, 4 },
79 };
80
81 // Reorder the channels to produce the same ordering as FFmpeg, which is
82 // what the pipeline expects.
83 const uint8* vorbis_layout_offset = kVorbisChannelLayouts[num_channels - 1];
84 for (int channel = 0; channel < num_channels; ++channel)
85 channel_layout[channel] = opus_mapping[vorbis_layout_offset[channel]];
86
87 return true;
88 }
89
90 // Opus Header contents:
91 // - "OpusHead" (64 bits)
92 // - version number (8 bits)
93 // - Channels C (8 bits)
94 // - Pre-skip (16 bits)
95 // - Sampling rate (32 bits)
96 // - Gain in dB (16 bits, S7.8)
97 // - Mapping (8 bits, 0=single stream (mono/stereo) 1=Vorbis mapping,
98 // 2..254: reserved, 255: multistream with no mapping)
99 //
100 // - if (mapping != 0)
101 // - N = totel number of streams (8 bits)
102 // - M = number of paired streams (8 bits)
103 // - C times channel origin
104 // - if (C<2*M)
105 // - stream = byte/2
106 // - if (byte&0x1 == 0)
107 // - left
108 // else
109 // - right
110 // - else
111 // - stream = byte-M
112
113 // Default audio output channel layout. Used to initialize |stream_map| in
114 // OpusHeader, and passed to |opus_multistream_decoder_create()| when the
scherkus (not reviewing) 2012/12/14 21:13:34 nit: function names don't need || around them
Tom Finegan 2012/12/14 23:19:52 Done.
115 // header does not contain mapping information. The values are valid only for
116 // mono and stereo output: Opus streams with more than 2 channels require a
117 // stream map.
118 static const int kMaxChannelsWithDefaultLayout = 2;
119 static const uint8 kDefaultOpusChannelLayout[kMaxChannelsWithDefaultLayout] = {
120 0, 1 };
121
122 // Size of the Opus header excluding optional mapping information.
123 static const int kOpusHeaderSize = 19;
124
125 // Offset to the channel count byte in the Opus header.
126 static const int kOpusHeaderChannelsOffset = 9;
127
128 // Offset to the pre-skip value in the Opus header.
129 static const int kOpusHeaderSkipSamplesOffset = 10;
130
131 // Offset to the channel mapping byte in the Opus header.
132 static const int kOpusHeaderChannelMappingOffset = 18;
133
134 // Header contains a stream map. The mapping values are in extra data beyond
135 // the always present |kOpusHeaderSize| bytes of data. The mapping data
136 // contains stream count, coupling information, and per channel mapping values:
137 // - Byte 0: Number of streams.
138 // - Byte 1: Number coupled.
139 // - Byte 2: Starting at byte 2 are |header->channels| uint8 mapping values.
140 static const int kOpusHeaderNumStreamsOffset = kOpusHeaderSize;
141 static const int kOpusHeaderNumCoupledOffset = kOpusHeaderNumStreamsOffset + 1;
142 static const int kOpusHeaderStreamMapOffset = kOpusHeaderNumStreamsOffset + 2;
143
144 struct OpusHeader {
145 OpusHeader()
146 : channels(0),
147 skip_samples(0),
148 channel_mapping(0),
149 num_streams(0),
150 num_coupled(0) {
151 memcpy(stream_map,
152 kDefaultOpusChannelLayout,
153 kMaxChannelsWithDefaultLayout);
154 }
155 int channels;
156 int skip_samples;
157 int channel_mapping;
158 int num_streams;
159 int num_coupled;
160 uint8 stream_map[kMaxVorbisChannels];
161 };
162
163 // Returns true when able to successfully parse and store Opus header data in
164 // data parsed in |header|. Based on opus header parsing code in libopusdec
165 // from FFmpeg, and opus_header from Xiph's opus-tools project.
166 static bool ParseOpusHeader(const uint8* data, int data_size,
167 const AudioDecoderConfig& config,
168 OpusHeader* header) {
169 DCHECK(data);
170 DCHECK(header);
171 DCHECK_GE(data_size, kOpusHeaderSize);
scherkus (not reviewing) 2012/12/14 21:13:34 ditto for all these dchecks try to aim for having
Tom Finegan 2012/12/14 23:19:52 Done.
172
173 if (!data || data_size < kOpusHeaderSize || !header)
174 return false;
175
176 header->channels = *(data + kOpusHeaderChannelsOffset);
177
178 DCHECK(header->channels > 0 && header->channels <= kMaxVorbisChannels);
179 if (header->channels <= 0 || header->channels > kMaxVorbisChannels) {
180 LOG(ERROR) << "ParseOpusHeader(): invalid channel count in header "
scherkus (not reviewing) 2012/12/14 21:13:34 nit: remove function names from all of these LOG m
Tom Finegan 2012/12/14 23:19:52 Done.
181 << ChannelLayoutToChannelCount(config.channel_layout());
182 return false;
183 }
184
185 header->skip_samples =
186 ReadLE16(data, data_size, kOpusHeaderSkipSamplesOffset);
187
188 header->channel_mapping = *(data + kOpusHeaderChannelMappingOffset);
189
190 if (!header->channel_mapping) {
191 if (header->channels > kMaxChannelsWithDefaultLayout) {
192 LOG(ERROR) << "ParseOpusHeader(): Invalid header, missing stream map.";
193 return false;
194 }
195
196 header->num_streams = 1;
197 header->num_coupled =
198 (ChannelLayoutToChannelCount(config.channel_layout()) > 1) ? 1 : 0;
199 return true;
200 }
201
202 const int mapping_required_size =
203 kOpusHeaderStreamMapOffset + header->channels;
204 if (data_size < mapping_required_size) {
205 LOG(ERROR) << "ParseOpusHeader(): Invalid stream map.";
206 return false;
207 }
208
209 header->num_streams = *(data + kOpusHeaderNumStreamsOffset);
210 header->num_coupled = *(data + kOpusHeaderNumCoupledOffset);
211
212 if (header->num_streams + header->num_coupled != header->channels)
213 LOG(WARNING) << "ParseOpusHeader(): Inconsistent channel mapping.";
214
215 for (int i = 0; i < kMaxVorbisChannels; ++i)
216 header->stream_map[i] = *(data + kOpusHeaderStreamMapOffset + i);
217
218 return true;
219 }
220
221 OpusAudioDecoder::OpusAudioDecoder(
222 const scoped_refptr<base::MessageLoopProxy>& message_loop)
223 : message_loop_(message_loop),
224 opus_decoder_(NULL),
225 bits_per_channel_(0),
226 channel_layout_(CHANNEL_LAYOUT_NONE),
227 samples_per_second_(0),
228 last_input_timestamp_(kNoTimestamp()),
229 output_bytes_to_drop_(0),
230 skip_samples_(0) {
231 }
232
233 void OpusAudioDecoder::Initialize(
234 const scoped_refptr<DemuxerStream>& stream,
235 const PipelineStatusCB& status_cb,
236 const StatisticsCB& statistics_cb) {
237 if (!message_loop_->BelongsToCurrentThread()) {
scherkus (not reviewing) 2012/12/14 21:13:34 replace this trampoline with a DCHECK(BelongsToCur
Tom Finegan 2012/12/14 23:19:52 Done, but reverted; discussed offline. Applies to
238 message_loop_->PostTask(FROM_HERE, base::Bind(
239 &OpusAudioDecoder::DoInitialize, this,
240 stream, status_cb, statistics_cb));
241 return;
242 }
243 DoInitialize(stream, status_cb, statistics_cb);
scherkus (not reviewing) 2012/12/14 21:13:34 inline this method into Initialize()
Tom Finegan 2012/12/14 23:19:52 Done.
244 }
245
246 void OpusAudioDecoder::Read(const ReadCB& read_cb) {
247 // Complete operation asynchronously on different stack of execution as per
scherkus (not reviewing) 2012/12/14 21:13:34 add a DCHECK(BelongsToCurrentThread(), inline the
Tom Finegan 2012/12/14 23:19:52 Done.
248 // the API contract of AudioDecoder::Read()
249 message_loop_->PostTask(FROM_HERE, base::Bind(
250 &OpusAudioDecoder::DoRead, this, read_cb));
251 }
252
253 int OpusAudioDecoder::bits_per_channel() {
254 return bits_per_channel_;
255 }
256
257 ChannelLayout OpusAudioDecoder::channel_layout() {
258 return channel_layout_;
259 }
260
261 int OpusAudioDecoder::samples_per_second() {
262 return samples_per_second_;
263 }
264
265 void OpusAudioDecoder::Reset(const base::Closure& closure) {
266 message_loop_->PostTask(FROM_HERE, base::Bind(
scherkus (not reviewing) 2012/12/14 21:13:34 add a DCHECK(BelongsToCurrentThread(), inline the
Tom Finegan 2012/12/14 23:19:52 Done.
267 &OpusAudioDecoder::DoReset, this, closure));
268 }
269
270 OpusAudioDecoder::~OpusAudioDecoder() {
271 // TODO(scherkus): should we require Stop() to be called? this might end up
272 // getting called on a random thread due to refcounting.
273 CloseDecoder();
274 }
275
276 void OpusAudioDecoder::DoInitialize(
277 const scoped_refptr<DemuxerStream>& stream,
278 const PipelineStatusCB& status_cb,
279 const StatisticsCB& statistics_cb) {
280 if (demuxer_stream_) {
281 // TODO(scherkus): initialization currently happens more than once in
282 // PipelineIntegrationTest.BasicPlayback.
283 LOG(ERROR) << "Initialize has already been called.";
284 CHECK(false);
285 }
286
287 demuxer_stream_ = stream;
288
289 if (!ConfigureDecoder()) {
290 status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
291 return;
292 }
293
294 statistics_cb_ = statistics_cb;
295 status_cb.Run(PIPELINE_OK);
296 }
297
298 void OpusAudioDecoder::DoReset(const base::Closure& closure) {
299 opus_multistream_decoder_ctl(opus_decoder_, OPUS_RESET_STATE);
300 ResetTimestampState();
301 closure.Run();
302 }
303
304 void OpusAudioDecoder::DoRead(const ReadCB& read_cb) {
305 DCHECK(message_loop_->BelongsToCurrentThread());
306 DCHECK(!read_cb.is_null());
307 CHECK(read_cb_.is_null()) << "Overlapping decodes are not supported.";
308
309 read_cb_ = read_cb;
310 ReadFromDemuxerStream();
311 }
312
313 void OpusAudioDecoder::DoDecodeBuffer(
314 DemuxerStream::Status status,
315 const scoped_refptr<DecoderBuffer>& input) {
316 if (!message_loop_->BelongsToCurrentThread()) {
scherkus (not reviewing) 2012/12/14 21:13:34 replace this trampoline with a DCHECK(BelongsToCur
Tom Finegan 2012/12/14 23:19:52 Done.
317 message_loop_->PostTask(FROM_HERE, base::Bind(
318 &OpusAudioDecoder::DoDecodeBuffer, this, status, input));
319 return;
320 }
321
322 DCHECK(!read_cb_.is_null());
323 DCHECK_EQ(status != DemuxerStream::kOk, !input) << status;
324
325 if (status == DemuxerStream::kAborted) {
326 DCHECK(!input);
327 base::ResetAndReturn(&read_cb_).Run(kAborted, NULL);
328 return;
329 }
330
331 if (status == DemuxerStream::kConfigChanged) {
332 DCHECK(!input);
333
334 // TODO(tomfinegan): Confirm that libopus buffers output samples, and that
335 // the |Decode()| call here is necessary.
336
337 scoped_refptr<DataBuffer> output_buffer;
338
339 // Send a "end of stream" buffer to the decode loop
340 // to output any remaining data still in the decoder.
341 if (!Decode(DecoderBuffer::CreateEOSBuffer(), true, &output_buffer)) {
342 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
343 return;
344 }
345
346 DVLOG(1) << "Config changed.";
347
348 if (!ConfigureDecoder()) {
349 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
350 return;
351 }
352
353 ResetTimestampState();
354
355 if (output_buffer) {
356 // Execute callback to return the decoded audio.
357 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer);
358 } else {
359 // We exhausted the input data, but it wasn't enough for a frame. Ask for
360 // more data in order to fulfill this read.
361 ReadFromDemuxerStream();
362 }
363 }
364
365 DCHECK_EQ(status, DemuxerStream::kOk);
366 DCHECK(input);
367
368 // Make sure we are notified if http://crbug.com/49709 returns. Issue also
369 // occurs with some damaged files.
370 if (!input->IsEndOfStream() && input->GetTimestamp() == kNoTimestamp() &&
371 output_timestamp_helper_->base_timestamp() == kNoTimestamp()) {
372 DVLOG(1) << "Received a buffer without timestamps!";
373 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
374 return;
375 }
376
377 if (!input->IsEndOfStream()) {
378 if (last_input_timestamp_ != kNoTimestamp() &&
379 input->GetTimestamp() != kNoTimestamp() &&
380 input->GetTimestamp() < last_input_timestamp_) {
381 base::TimeDelta diff = input->GetTimestamp() - last_input_timestamp_;
382 DVLOG(1) << "Input timestamps are not monotonically increasing! "
383 << " ts " << input->GetTimestamp().InMicroseconds() << " us"
384 << " diff " << diff.InMicroseconds() << " us";
385 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
386 }
387
388 last_input_timestamp_ = input->GetTimestamp();
389 }
390
391 scoped_refptr<DataBuffer> output_buffer;
392
393 if (!Decode(input, false, &output_buffer)) {
394 base::ResetAndReturn(&read_cb_).Run(kDecodeError, NULL);
395 return;
396 }
397
398 if (output_buffer) {
399 // Execute callback to return the decoded audio.
400 base::ResetAndReturn(&read_cb_).Run(kOk, output_buffer);
401 } else {
402 // We exhausted the input data, but it wasn't enough for a frame. Ask for
403 // more data in order to fulfill this read.
404 ReadFromDemuxerStream();
405 }
406 }
407
408 void OpusAudioDecoder::ReadFromDemuxerStream() {
409 DCHECK(!read_cb_.is_null());
410
411 demuxer_stream_->Read(base::Bind(&OpusAudioDecoder::DoDecodeBuffer, this));
412 }
413
414 bool OpusAudioDecoder::ConfigureDecoder() {
415 const AudioDecoderConfig& config = demuxer_stream_->audio_decoder_config();
416
417 if (config.codec() != kCodecOpus) {
418 DLOG(ERROR) << "ConfigureDecoder(): codec must be kCodecOpus.";
419 return false;
420 }
421
422 const int channel_count =
423 ChannelLayoutToChannelCount(config.channel_layout());
424 if (!config.IsValidConfig() || channel_count > kMaxVorbisChannels) {
425 DLOG(ERROR) << "ConfigureDecoder(): Invalid or unsupported audio stream -"
426 << " codec: " << config.codec()
427 << " channel count: " << channel_count
428 << " channel layout: " << config.channel_layout()
429 << " bits per channel: " << config.bits_per_channel()
430 << " samples per second: " << config.samples_per_second();
431 return false;
432 }
433
434 if (config.bits_per_channel() != kBitsPerChannel) {
435 DLOG(ERROR) << "ConfigureDecoder(): 16 bit samples required.";
436 return false;
437 }
438
439 if (config.is_encrypted()) {
440 DLOG(ERROR) << "ConfigureDecoder(): Encrypted audio stream not supported.";
441 return false;
442 }
443
444 if (opus_decoder_ &&
445 (bits_per_channel_ != config.bits_per_channel() ||
446 channel_layout_ != config.channel_layout() ||
447 samples_per_second_ != config.samples_per_second())) {
448 DVLOG(1) << "Unsupported config change :";
449 DVLOG(1) << "\tbits_per_channel : " << bits_per_channel_
450 << " -> " << config.bits_per_channel();
451 DVLOG(1) << "\tchannel_layout : " << channel_layout_
452 << " -> " << config.channel_layout();
453 DVLOG(1) << "\tsample_rate : " << samples_per_second_
454 << " -> " << config.samples_per_second();
455 return false;
456 }
457
458 // Clean up existing decoder if necessary.
459 CloseDecoder();
460
461 // Allocate the output buffer if necessary.
462 if (!output_buffer_)
463 output_buffer_.reset(new int16[kMaxOpusOutputPacketSizeSamples]);
464
465 // Parse the Opus header.
466 OpusHeader opus_header;
467 if (!ParseOpusHeader(config.extra_data(), config.extra_data_size(),
468 config,
469 &opus_header)) {
470 LOG(ERROR) << "ConfigureDecoder(): cannot parse opus header.";
471 return false;
472 }
473
474 skip_samples_ = opus_header.skip_samples;
475
476 if (skip_samples_ > 0)
477 output_bytes_to_drop_ = skip_samples_ * config.bytes_per_frame();
478
479 uint8 channel_mapping[kMaxVorbisChannels];
480 memcpy(&channel_mapping,
481 kDefaultOpusChannelLayout,
482 kMaxChannelsWithDefaultLayout);
483 if (channel_count > kMaxChannelsWithDefaultLayout) {
484 if (!RemapOpusChannelLayout(opus_header.stream_map,
485 channel_count,
486 channel_mapping)) {
487 LOG(ERROR) << "ConfigureDecoder(): unable to remap opus channels.";
488 return false;
489 }
490 }
491
492 // Init Opus.
493 int status = OPUS_INVALID_STATE;
494 opus_decoder_ = opus_multistream_decoder_create(config.samples_per_second(),
495 channel_count,
496 opus_header.num_streams,
497 opus_header.num_coupled,
498 channel_mapping,
499 &status);
500 if (!opus_decoder_ || status != OPUS_OK) {
501 LOG(ERROR) << "ConfigureDecoder(): opus_multistream_decoder_create failed"
502 << " status=" << opus_strerror(status);
503 return false;
504 }
505
506 // TODO(tomfinegan): Handle audio delay once the matroska spec is updated
507 // to represent the value.
508
509 bits_per_channel_ = config.bits_per_channel();
510 channel_layout_ = config.channel_layout();
511 samples_per_second_ = config.samples_per_second();
512 output_timestamp_helper_.reset(new AudioTimestampHelper(
513 config.bytes_per_frame(), config.samples_per_second()));
514 return true;
515 }
516
517 void OpusAudioDecoder::CloseDecoder() {
518 if (opus_decoder_) {
519 opus_multistream_decoder_destroy(opus_decoder_);
520 opus_decoder_ = NULL;
521 }
522 }
523
524 void OpusAudioDecoder::ResetTimestampState() {
525 output_timestamp_helper_->SetBaseTimestamp(kNoTimestamp());
526 last_input_timestamp_ = kNoTimestamp();
527 output_bytes_to_drop_ = 0;
528 }
529
530 bool OpusAudioDecoder::Decode(const scoped_refptr<DecoderBuffer>& input,
531 bool skip_eos_append,
532 scoped_refptr<DataBuffer>* output_buffer) {
533 int samples_decoded =
534 opus_multistream_decode(opus_decoder_,
535 input->GetData(), input->GetDataSize(),
536 &output_buffer_[0],
537 kMaxOpusOutputPacketSizeSamples,
538 0);
539 if (samples_decoded < 0) {
540 DCHECK(!input->IsEndOfStream())
541 << "Decode(): End of stream buffer produced an error!";
542
543 LOG(ERROR) << "ConfigureDecoder(): opus_multistream_decode failed for"
544 << " timestamp: " << input->GetTimestamp().InMicroseconds()
545 << " us, duration: " << input->GetDuration().InMicroseconds()
546 << " us, packet size: " << input->GetDataSize() << " bytes with"
547 << " status: " << opus_strerror(samples_decoded);
548 return false;
549 }
550
551 uint8* decoded_audio_data = reinterpret_cast<uint8*>(&output_buffer_[0]);
552 int decoded_audio_size = samples_decoded *
553 demuxer_stream_->audio_decoder_config().bytes_per_frame();
554 DCHECK_LE(decoded_audio_size, kMaxOpusOutputPacketSizeBytes);
555
556 if (output_timestamp_helper_->base_timestamp() == kNoTimestamp() &&
557 !input->IsEndOfStream()) {
558 DCHECK(input->GetTimestamp() != kNoTimestamp());
559 output_timestamp_helper_->SetBaseTimestamp(input->GetTimestamp());
560 }
561
562 if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) {
563 int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_);
564 DCHECK_EQ(dropped_size % kBytesPerChannel, 0);
565 decoded_audio_data += dropped_size;
566 decoded_audio_size -= dropped_size;
567 output_bytes_to_drop_ -= dropped_size;
568 }
569
570 if (decoded_audio_size > 0) {
571 // Copy the audio samples into an output buffer.
572 *output_buffer = new DataBuffer(decoded_audio_data, decoded_audio_size);
573 (*output_buffer)->SetTimestamp(output_timestamp_helper_->GetTimestamp());
574 (*output_buffer)->SetDuration(
575 output_timestamp_helper_->GetDuration(decoded_audio_size));
576 output_timestamp_helper_->AddBytes(decoded_audio_size);
577 } else if (IsEndOfStream(decoded_audio_size, input) && !skip_eos_append) {
578 DCHECK_EQ(input->GetDataSize(), 0);
579 // Create an end of stream output buffer.
580 *output_buffer = new DataBuffer(0);
581 }
582
583 // Decoding finished successfully, update statistics.
584 PipelineStatistics statistics;
585 statistics.audio_bytes_decoded = decoded_audio_size;
586 statistics_cb_.Run(statistics);
587
588 return true;
589 }
590
591 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698