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/base/audio_splicer.h" 6 7#include <cstdlib> 8 9#include "base/logging.h" 10#include "media/base/audio_buffer.h" 11#include "media/base/audio_decoder_config.h" 12#include "media/base/audio_timestamp_helper.h" 13#include "media/base/buffers.h" 14 15namespace media { 16 17// Largest gap or overlap allowed by this class. Anything 18// larger than this will trigger an error. 19// This is an arbitrary value, but the initial selection of 50ms 20// roughly represents the duration of 2 compressed AAC or MP3 frames. 21static const int kMaxTimeDeltaInMilliseconds = 50; 22 23AudioSplicer::AudioSplicer(int samples_per_second) 24 : output_timestamp_helper_(samples_per_second), 25 min_gap_size_(2), 26 received_end_of_stream_(false) { 27} 28 29AudioSplicer::~AudioSplicer() { 30} 31 32void AudioSplicer::Reset() { 33 output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp()); 34 output_buffers_.clear(); 35 received_end_of_stream_ = false; 36} 37 38bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) { 39 DCHECK(!received_end_of_stream_ || input->end_of_stream()); 40 41 if (input->end_of_stream()) { 42 output_buffers_.push_back(input); 43 received_end_of_stream_ = true; 44 return true; 45 } 46 47 DCHECK(input->timestamp() != kNoTimestamp()); 48 DCHECK(input->duration() > base::TimeDelta()); 49 DCHECK_GT(input->frame_count(), 0); 50 51 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp()) 52 output_timestamp_helper_.SetBaseTimestamp(input->timestamp()); 53 54 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) { 55 DVLOG(1) << "Input timestamp is before the base timestamp."; 56 return false; 57 } 58 59 base::TimeDelta timestamp = input->timestamp(); 60 base::TimeDelta expected_timestamp = output_timestamp_helper_.GetTimestamp(); 61 base::TimeDelta delta = timestamp - expected_timestamp; 62 63 if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) { 64 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us"; 65 return false; 66 } 67 68 int frames_to_fill = 0; 69 if (delta != base::TimeDelta()) 70 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp); 71 72 if (frames_to_fill == 0 || std::abs(frames_to_fill) < min_gap_size_) { 73 AddOutputBuffer(input); 74 return true; 75 } 76 77 if (frames_to_fill > 0) { 78 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds() 79 << " us: " << delta.InMicroseconds() << " us"; 80 81 // Create a buffer with enough silence samples to fill the gap and 82 // add it to the output buffer. 83 scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer( 84 input->channel_count(), 85 frames_to_fill, 86 expected_timestamp, 87 output_timestamp_helper_.GetFrameDuration(frames_to_fill)); 88 AddOutputBuffer(gap); 89 90 // Add the input buffer now that the gap has been filled. 91 AddOutputBuffer(input); 92 return true; 93 } 94 95 int frames_to_skip = -frames_to_fill; 96 97 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds() 98 << " us: " << -delta.InMicroseconds() << " us"; 99 100 if (input->frame_count() <= frames_to_skip) { 101 DVLOG(1) << "Dropping whole buffer"; 102 return true; 103 } 104 105 // Copy the trailing samples that do not overlap samples already output 106 // into a new buffer. Add this new buffer to the output queue. 107 // 108 // TODO(acolwell): Implement a cross-fade here so the transition is less 109 // jarring. 110 input->TrimStart(frames_to_skip); 111 AddOutputBuffer(input); 112 return true; 113} 114 115bool AudioSplicer::HasNextBuffer() const { 116 return !output_buffers_.empty(); 117} 118 119scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() { 120 scoped_refptr<AudioBuffer> ret = output_buffers_.front(); 121 output_buffers_.pop_front(); 122 return ret; 123} 124 125void AudioSplicer::AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer) { 126 output_timestamp_helper_.AddFrames(buffer->frame_count()); 127 output_buffers_.push_back(buffer); 128} 129 130} // namespace media 131