Home | History | Annotate | Download | only in base
      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 
     15 namespace 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.
     21 static const int kMaxTimeDeltaInMilliseconds = 50;
     22 
     23 AudioSplicer::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 
     29 AudioSplicer::~AudioSplicer() {
     30 }
     31 
     32 void AudioSplicer::Reset() {
     33   output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
     34   output_buffers_.clear();
     35   received_end_of_stream_ = false;
     36 }
     37 
     38 bool 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 
    115 bool AudioSplicer::HasNextBuffer() const {
    116   return !output_buffers_.empty();
    117 }
    118 
    119 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
    120   scoped_refptr<AudioBuffer> ret = output_buffers_.front();
    121   output_buffers_.pop_front();
    122   return ret;
    123 }
    124 
    125 void 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