This source file includes following definitions.
- AccurateTrimStart
- AccurateTrimEnd
- CreateAudioBufferWrapper
- timestamp_helper
- received_end_of_stream_
- Reset
- ResetTimestampState
- AddInput
- HasNextBuffer
- GetNextBuffer
- AddOutputBuffer
- GetFrameCount
- GetDuration
- DrainInto
- post_splice_sanitizer_
- Reset
- AddInput
- HasNextBuffer
- GetNextBuffer
- SetSpliceTimestamp
- ExtractCrossfadeFromPreSplice
- CrossfadePostSplice
#include "media/base/audio_splicer.h"
#include <cstdlib>
#include <deque>
#include "base/logging.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/vector_math.h"
namespace media {
static const int kMaxTimeDeltaInMilliseconds = 50;
static const int kMinGapSize = 2;
static void AccurateTrimStart(int frames_to_trim,
const scoped_refptr<AudioBuffer> buffer,
const AudioTimestampHelper& timestamp_helper) {
buffer->TrimStart(frames_to_trim);
buffer->set_timestamp(timestamp_helper.GetTimestamp());
buffer->set_duration(
timestamp_helper.GetFrameDuration(buffer->frame_count()));
}
static void AccurateTrimEnd(int frames_to_trim,
const scoped_refptr<AudioBuffer> buffer,
const AudioTimestampHelper& timestamp_helper) {
DCHECK(buffer->timestamp() == timestamp_helper.GetTimestamp());
buffer->TrimEnd(frames_to_trim);
buffer->set_duration(
timestamp_helper.GetFrameDuration(buffer->frame_count()));
}
static scoped_ptr<AudioBus> CreateAudioBufferWrapper(
const scoped_refptr<AudioBuffer>& buffer) {
scoped_ptr<AudioBus> wrapper =
AudioBus::CreateWrapper(buffer->channel_count());
wrapper->set_frames(buffer->frame_count());
for (int ch = 0; ch < buffer->channel_count(); ++ch) {
wrapper->SetChannelData(
ch, reinterpret_cast<float*>(buffer->channel_data()[ch]));
}
return wrapper.Pass();
}
class AudioStreamSanitizer {
public:
explicit AudioStreamSanitizer(int samples_per_second);
~AudioStreamSanitizer();
void Reset();
void ResetTimestampState(int64 frame_count, base::TimeDelta base_timestamp);
bool AddInput(const scoped_refptr<AudioBuffer>& input);
bool HasNextBuffer() const;
scoped_refptr<AudioBuffer> GetNextBuffer();
int GetFrameCount() const;
base::TimeDelta GetDuration() const;
const AudioTimestampHelper& timestamp_helper() {
return output_timestamp_helper_;
}
bool DrainInto(AudioStreamSanitizer* output);
private:
void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer);
AudioTimestampHelper output_timestamp_helper_;
bool received_end_of_stream_;
typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue;
BufferQueue output_buffers_;
DISALLOW_ASSIGN(AudioStreamSanitizer);
};
AudioStreamSanitizer::AudioStreamSanitizer(int samples_per_second)
: output_timestamp_helper_(samples_per_second),
received_end_of_stream_(false) {}
AudioStreamSanitizer::~AudioStreamSanitizer() {}
void AudioStreamSanitizer::Reset() {
ResetTimestampState(0, kNoTimestamp());
}
void AudioStreamSanitizer::ResetTimestampState(int64 frame_count,
base::TimeDelta base_timestamp) {
output_buffers_.clear();
received_end_of_stream_ = false;
output_timestamp_helper_.SetBaseTimestamp(base_timestamp);
if (frame_count > 0)
output_timestamp_helper_.AddFrames(frame_count);
}
bool AudioStreamSanitizer::AddInput(const scoped_refptr<AudioBuffer>& input) {
DCHECK(!received_end_of_stream_ || input->end_of_stream());
if (input->end_of_stream()) {
output_buffers_.push_back(input);
received_end_of_stream_ = true;
return true;
}
DCHECK(input->timestamp() != kNoTimestamp());
DCHECK(input->duration() > base::TimeDelta());
DCHECK_GT(input->frame_count(), 0);
if (output_timestamp_helper_.base_timestamp() == kNoTimestamp())
output_timestamp_helper_.SetBaseTimestamp(input->timestamp());
if (output_timestamp_helper_.base_timestamp() > input->timestamp()) {
DVLOG(1) << "Input timestamp is before the base timestamp.";
return false;
}
const base::TimeDelta timestamp = input->timestamp();
const base::TimeDelta expected_timestamp =
output_timestamp_helper_.GetTimestamp();
const base::TimeDelta delta = timestamp - expected_timestamp;
if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) {
DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us";
return false;
}
int frames_to_fill = 0;
if (delta != base::TimeDelta())
frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp);
if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) {
AddOutputBuffer(input);
return true;
}
if (frames_to_fill > 0) {
DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
<< " us: " << delta.InMicroseconds() << " us";
scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer(
input->channel_layout(),
input->channel_count(),
input->sample_rate(),
frames_to_fill,
expected_timestamp,
output_timestamp_helper_.GetFrameDuration(frames_to_fill));
AddOutputBuffer(gap);
AddOutputBuffer(input);
return true;
}
DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
<< " us: " << -delta.InMicroseconds() << " us";
const int frames_to_skip = -frames_to_fill;
if (input->frame_count() <= frames_to_skip) {
DVLOG(1) << "Dropping whole buffer";
return true;
}
AccurateTrimStart(frames_to_skip, input, output_timestamp_helper_);
AddOutputBuffer(input);
return true;
}
bool AudioStreamSanitizer::HasNextBuffer() const {
return !output_buffers_.empty();
}
scoped_refptr<AudioBuffer> AudioStreamSanitizer::GetNextBuffer() {
scoped_refptr<AudioBuffer> ret = output_buffers_.front();
output_buffers_.pop_front();
return ret;
}
void AudioStreamSanitizer::AddOutputBuffer(
const scoped_refptr<AudioBuffer>& buffer) {
output_timestamp_helper_.AddFrames(buffer->frame_count());
output_buffers_.push_back(buffer);
}
int AudioStreamSanitizer::GetFrameCount() const {
int frame_count = 0;
for (BufferQueue::const_iterator it = output_buffers_.begin();
it != output_buffers_.end(); ++it) {
frame_count += (*it)->frame_count();
}
return frame_count;
}
base::TimeDelta AudioStreamSanitizer::GetDuration() const {
DCHECK(output_timestamp_helper_.base_timestamp() != kNoTimestamp());
return output_timestamp_helper_.GetTimestamp() -
output_timestamp_helper_.base_timestamp();
}
bool AudioStreamSanitizer::DrainInto(AudioStreamSanitizer* output) {
while (HasNextBuffer()) {
if (!output->AddInput(GetNextBuffer()))
return false;
}
return true;
}
AudioSplicer::AudioSplicer(int samples_per_second)
: max_crossfade_duration_(
base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)),
splice_timestamp_(kNoTimestamp()),
max_splice_end_timestamp_(kNoTimestamp()),
output_sanitizer_(new AudioStreamSanitizer(samples_per_second)),
pre_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)),
post_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)) {}
AudioSplicer::~AudioSplicer() {}
void AudioSplicer::Reset() {
output_sanitizer_->Reset();
pre_splice_sanitizer_->Reset();
post_splice_sanitizer_->Reset();
reset_splice_timestamps();
}
bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) {
if (splice_timestamp_ == kNoTimestamp()) {
DCHECK(!pre_splice_sanitizer_->HasNextBuffer());
DCHECK(!post_splice_sanitizer_->HasNextBuffer());
return output_sanitizer_->AddInput(input);
}
const AudioTimestampHelper& output_ts_helper =
output_sanitizer_->timestamp_helper();
if (!post_splice_sanitizer_->HasNextBuffer()) {
if (input->end_of_stream() ||
input->timestamp() >= max_splice_end_timestamp_) {
CHECK(pre_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
reset_splice_timestamps();
return output_sanitizer_->AddInput(input);
}
if (input->timestamp() + input->duration() < splice_timestamp_) {
DCHECK(!pre_splice_sanitizer_->HasNextBuffer());
return output_sanitizer_->AddInput(input);
}
if (!pre_splice_sanitizer_->HasNextBuffer()) {
pre_splice_sanitizer_->ResetTimestampState(
output_ts_helper.frame_count(), output_ts_helper.base_timestamp());
}
if (!pre_splice_sanitizer_->HasNextBuffer() ||
input->timestamp() != splice_timestamp_) {
return pre_splice_sanitizer_->AddInput(input);
}
} else if (!input->end_of_stream() &&
input->timestamp() == splice_timestamp_) {
post_splice_sanitizer_->DrainInto(pre_splice_sanitizer_.get());
post_splice_sanitizer_->Reset();
}
if (!post_splice_sanitizer_->AddInput(input))
return false;
if (output_ts_helper.base_timestamp() == kNoTimestamp()) {
output_sanitizer_->ResetTimestampState(
0, pre_splice_sanitizer_->timestamp_helper().base_timestamp());
}
if (pre_splice_sanitizer_->GetFrameCount() <=
output_ts_helper.GetFramesToTarget(splice_timestamp_)) {
CHECK(pre_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
reset_splice_timestamps();
return true;
}
if (!input->end_of_stream() && input->timestamp() < max_splice_end_timestamp_)
return true;
scoped_refptr<AudioBuffer> crossfade_buffer;
scoped_ptr<AudioBus> pre_splice =
ExtractCrossfadeFromPreSplice(&crossfade_buffer);
CrossfadePostSplice(pre_splice.Pass(), crossfade_buffer);
reset_splice_timestamps();
return true;
}
bool AudioSplicer::HasNextBuffer() const {
return output_sanitizer_->HasNextBuffer();
}
scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
return output_sanitizer_->GetNextBuffer();
}
void AudioSplicer::SetSpliceTimestamp(base::TimeDelta splice_timestamp) {
DCHECK(splice_timestamp != kNoTimestamp());
if (splice_timestamp_ == splice_timestamp)
return;
CHECK(splice_timestamp_ == kNoTimestamp());
splice_timestamp_ = splice_timestamp;
max_splice_end_timestamp_ = splice_timestamp_ + max_crossfade_duration_;
pre_splice_sanitizer_->Reset();
post_splice_sanitizer_->Reset();
}
scoped_ptr<AudioBus> AudioSplicer::ExtractCrossfadeFromPreSplice(
scoped_refptr<AudioBuffer>* crossfade_buffer) {
DCHECK(crossfade_buffer);
const AudioTimestampHelper& output_ts_helper =
output_sanitizer_->timestamp_helper();
int frames_before_splice =
output_ts_helper.GetFramesToTarget(splice_timestamp_);
const int max_crossfade_frame_count =
output_ts_helper.GetFramesToTarget(max_splice_end_timestamp_) -
frames_before_splice;
const int frames_to_crossfade = std::min(
max_crossfade_frame_count,
std::min(pre_splice_sanitizer_->GetFrameCount() - frames_before_splice,
post_splice_sanitizer_->GetFrameCount()));
DCHECK_GT(frames_to_crossfade, 0);
int frames_read = 0;
scoped_ptr<AudioBus> output_bus;
while (pre_splice_sanitizer_->HasNextBuffer() &&
frames_read < frames_to_crossfade) {
scoped_refptr<AudioBuffer> preroll = pre_splice_sanitizer_->GetNextBuffer();
if (!output_bus) {
output_bus =
AudioBus::Create(preroll->channel_count(), frames_to_crossfade);
*crossfade_buffer = AudioBuffer::CreateBuffer(kSampleFormatPlanarF32,
preroll->channel_layout(),
preroll->channel_count(),
preroll->sample_rate(),
frames_to_crossfade);
}
if (frames_before_splice >= preroll->frame_count()) {
frames_before_splice -= preroll->frame_count();
CHECK(output_sanitizer_->AddInput(preroll));
continue;
}
const int frames_to_read =
std::min(preroll->frame_count() - frames_before_splice,
output_bus->frames() - frames_read);
preroll->ReadFrames(
frames_to_read, frames_before_splice, frames_read, output_bus.get());
frames_read += frames_to_read;
if (frames_before_splice) {
AccurateTrimEnd(preroll->frame_count() - frames_before_splice,
preroll,
output_ts_helper);
CHECK(output_sanitizer_->AddInput(preroll));
frames_before_splice = 0;
}
}
pre_splice_sanitizer_->Reset();
DCHECK_EQ(output_bus->frames(), frames_read);
DCHECK_EQ(output_ts_helper.GetFramesToTarget(splice_timestamp_), 0);
return output_bus.Pass();
}
void AudioSplicer::CrossfadePostSplice(
scoped_ptr<AudioBus> pre_splice_bus,
scoped_refptr<AudioBuffer> crossfade_buffer) {
const AudioTimestampHelper& output_ts_helper =
output_sanitizer_->timestamp_helper();
crossfade_buffer->set_timestamp(output_ts_helper.GetTimestamp());
crossfade_buffer->set_duration(
output_ts_helper.GetFrameDuration(pre_splice_bus->frames()));
scoped_ptr<AudioBus> output_bus = CreateAudioBufferWrapper(crossfade_buffer);
int frames_read = 0, frames_to_trim = 0;
scoped_refptr<AudioBuffer> remainder;
while (post_splice_sanitizer_->HasNextBuffer() &&
frames_read < output_bus->frames()) {
scoped_refptr<AudioBuffer> postroll =
post_splice_sanitizer_->GetNextBuffer();
const int frames_to_read =
std::min(postroll->frame_count(), output_bus->frames() - frames_read);
postroll->ReadFrames(frames_to_read, 0, frames_read, output_bus.get());
frames_read += frames_to_read;
if (frames_to_read < postroll->frame_count()) {
DCHECK(!remainder);
remainder.swap(postroll);
frames_to_trim = frames_to_read;
}
}
DCHECK_EQ(output_bus->frames(), frames_read);
for (int ch = 0; ch < output_bus->channels(); ++ch) {
vector_math::Crossfade(pre_splice_bus->channel(ch),
pre_splice_bus->frames(),
output_bus->channel(ch));
}
CHECK(output_sanitizer_->AddInput(crossfade_buffer));
DCHECK_EQ(crossfade_buffer->frame_count(), output_bus->frames());
if (remainder) {
AccurateTrimStart(frames_to_trim, remainder, output_ts_helper);
CHECK(output_sanitizer_->AddInput(remainder));
}
CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
post_splice_sanitizer_->Reset();
}
}