Skip to content

Commit

Permalink
Add support for audio config changes.
Browse files Browse the repository at this point in the history
BUG=151046

Review URL: https://codereview.chromium.org/11419174

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@171249 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
acolwell@chromium.org committed Dec 5, 2012
1 parent 0bf6a06 commit 1bae3ad
Show file tree
Hide file tree
Showing 14 changed files with 1,075 additions and 120 deletions.
8 changes: 8 additions & 0 deletions media/base/audio_decoder_config.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ AudioDecoderConfig::AudioDecoderConfig()
bits_per_channel_(0),
channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED),
samples_per_second_(0),
bytes_per_frame_(0),
extra_data_size_(0),
is_encrypted_(false) {
}
Expand Down Expand Up @@ -73,6 +74,9 @@ void AudioDecoderConfig::Initialize(AudioCodec codec,
}

is_encrypted_ = is_encrypted;

int channels = ChannelLayoutToChannelCount(channel_layout_);
bytes_per_frame_ = channels * bits_per_channel_ / 8;
}

AudioDecoderConfig::~AudioDecoderConfig() {}
Expand Down Expand Up @@ -124,6 +128,10 @@ int AudioDecoderConfig::samples_per_second() const {
return samples_per_second_;
}

int AudioDecoderConfig::bytes_per_frame() const {
return bytes_per_frame_;
}

uint8* AudioDecoderConfig::extra_data() const {
return extra_data_.get();
}
Expand Down
2 changes: 2 additions & 0 deletions media/base/audio_decoder_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class MEDIA_EXPORT AudioDecoderConfig {
int bits_per_channel() const;
ChannelLayout channel_layout() const;
int samples_per_second() const;
int bytes_per_frame() const;

// Optional byte data required to initialize audio decoders such as Vorbis
// codebooks.
Expand All @@ -94,6 +95,7 @@ class MEDIA_EXPORT AudioDecoderConfig {
int bits_per_channel_;
ChannelLayout channel_layout_;
int samples_per_second_;
int bytes_per_frame_;

scoped_array<uint8> extra_data_;
size_t extra_data_size_;
Expand Down
139 changes: 139 additions & 0 deletions media/base/audio_splicer.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/audio_splicer.h"

#include <cstdlib>

#include "base/logging.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/buffers.h"
#include "media/base/data_buffer.h"

namespace media {

// Largest gap or overlap allowed by this class. Anything
// larger than this will trigger an error.
// This is an arbitrary value, but the initial selection of 50ms
// roughly represents the duration of 2 compressed AAC or MP3 frames.
static const int kMaxTimeDeltaInMilliseconds = 50;

AudioSplicer::AudioSplicer(int bytes_per_frame, int samples_per_second)
: output_timestamp_helper_(bytes_per_frame, samples_per_second),
min_gap_size_(2 * bytes_per_frame),
received_end_of_stream_(false) {
}

AudioSplicer::~AudioSplicer() {
}

void AudioSplicer::Reset() {
output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
output_buffers_.clear();
received_end_of_stream_ = false;
}

bool AudioSplicer::AddInput(const scoped_refptr<Buffer>& input){
DCHECK(!received_end_of_stream_ || input->IsEndOfStream());

if (input->IsEndOfStream()) {
output_buffers_.push_back(input);
received_end_of_stream_ = true;
return true;
}

DCHECK(input->GetTimestamp() != kNoTimestamp());
DCHECK(input->GetDuration() > base::TimeDelta());
DCHECK_GT(input->GetDataSize(), 0);

if (output_timestamp_helper_.base_timestamp() == kNoTimestamp())
output_timestamp_helper_.SetBaseTimestamp(input->GetTimestamp());

if (output_timestamp_helper_.base_timestamp() > input->GetTimestamp()) {
DVLOG(1) << "Input timestamp is before the base timestamp.";
return false;
}

base::TimeDelta timestamp = input->GetTimestamp();
base::TimeDelta expected_timestamp = output_timestamp_helper_.GetTimestamp();
base::TimeDelta delta = timestamp - expected_timestamp;

if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) {
DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us";
return false;
}

int bytes_to_fill = 0;
if (delta != base::TimeDelta())
bytes_to_fill = output_timestamp_helper_.GetBytesToTarget(timestamp);

if (bytes_to_fill == 0 || std::abs(bytes_to_fill) < min_gap_size_) {
AddOutputBuffer(input);
return true;
}

if (bytes_to_fill > 0) {
DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
<< " us: " << delta.InMicroseconds() << " us";

// Create a buffer with enough silence samples to fill the gap and
// add it to the output buffer.
scoped_refptr<DataBuffer> gap = new DataBuffer(bytes_to_fill);
gap->SetDataSize(bytes_to_fill);
memset(gap->GetWritableData(), 0, bytes_to_fill);
gap->SetTimestamp(expected_timestamp);
gap->SetDuration(output_timestamp_helper_.GetDuration(bytes_to_fill));
AddOutputBuffer(gap);

// Add the input buffer now that the gap has been filled.
AddOutputBuffer(input);
return true;
}

int bytes_to_skip = -bytes_to_fill;

DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
<< " us: " << -delta.InMicroseconds() << " us";

if (input->GetDataSize() <= bytes_to_skip) {
DVLOG(1) << "Dropping whole buffer";
return true;
}

// Copy the trailing samples that do not overlap samples already output
// into a new buffer. Add this new buffer to the output queue.
//
// TODO(acolwell): Implement a cross-fade here so the transition is less
// jarring.
int new_buffer_size = input->GetDataSize() - bytes_to_skip;

scoped_refptr<DataBuffer> new_buffer = new DataBuffer(new_buffer_size);
new_buffer->SetDataSize(new_buffer_size);
memcpy(new_buffer->GetWritableData(),
input->GetData() + bytes_to_skip,
new_buffer_size);
new_buffer->SetTimestamp(expected_timestamp);
new_buffer->SetDuration(
output_timestamp_helper_.GetDuration(new_buffer_size));
AddOutputBuffer(new_buffer);
return true;
}

bool AudioSplicer::HasNextBuffer() const {
return !output_buffers_.empty();
}

scoped_refptr<Buffer> AudioSplicer::GetNextBuffer() {
scoped_refptr<Buffer> ret = output_buffers_.front();
output_buffers_.pop_front();
return ret;
}

void AudioSplicer::AddOutputBuffer(const scoped_refptr<Buffer>& buffer) {
output_timestamp_helper_.AddBytes(buffer->GetDataSize());
output_buffers_.push_back(buffer);
}

} // namespace media
60 changes: 60 additions & 0 deletions media/base/audio_splicer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_BASE_AUDIO_SPLICER_H_
#define MEDIA_BASE_AUDIO_SPLICER_H_

#include <deque>

#include "base/memory/ref_counted.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/media_export.h"

namespace media {

class AudioDecoderConfig;
class Buffer;

// Helper class that handles filling gaps and resolving overlaps.
class MEDIA_EXPORT AudioSplicer {
public:
AudioSplicer(int bytes_per_frame, int samples_per_second);
~AudioSplicer();

// Resets the splicer state by clearing the output buffers queue,
// and resetting the timestamp helper.
void Reset();

// Adds a new buffer full of samples or end of stream buffer to the splicer.
// Returns true if the buffer was accepted. False is returned if an error
// occurred.
bool AddInput(const scoped_refptr<Buffer>& input);

// Returns true if the splicer has a buffer to return.
bool HasNextBuffer() const;

// Removes the next buffer from the output buffer queue and returns it.
// This should only be called if HasNextBuffer() returns true.
scoped_refptr<Buffer> GetNextBuffer();

private:
void AddOutputBuffer(const scoped_refptr<Buffer>& buffer);

AudioTimestampHelper output_timestamp_helper_;

// Minimum gap size needed before the splicer will take action to
// fill a gap. This avoids periodically inserting and then dropping samples
// when the buffer timestamps are slightly off because of timestamp rounding
// in the source content.
int min_gap_size_;

std::deque<scoped_refptr<Buffer> > output_buffers_;
bool received_end_of_stream_;

DISALLOW_IMPLICIT_CONSTRUCTORS(AudioSplicer);
};

} // namespace media

#endif
Loading

0 comments on commit 1bae3ad

Please sign in to comment.