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