• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AudioSplicer(int samples_per_second)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 
~AudioSplicer()29 AudioSplicer::~AudioSplicer() {
30 }
31 
Reset()32 void AudioSplicer::Reset() {
33   output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
34   output_buffers_.clear();
35   received_end_of_stream_ = false;
36 }
37 
AddInput(const scoped_refptr<AudioBuffer> & input)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 
HasNextBuffer() const115 bool AudioSplicer::HasNextBuffer() const {
116   return !output_buffers_.empty();
117 }
118 
GetNextBuffer()119 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
120   scoped_refptr<AudioBuffer> ret = output_buffers_.front();
121   output_buffers_.pop_front();
122   return ret;
123 }
124 
AddOutputBuffer(const scoped_refptr<AudioBuffer> & buffer)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