Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(533)

Side by Side Diff: media/base/audio_splicer.cc

Issue 156783003: Enhance AudioSplicer to crossfade marked splice frames. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Clarity. Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/base/audio_splicer.h" 5 #include "media/base/audio_splicer.h"
6 6
7 #include <cstdlib> 7 #include <cstdlib>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "media/base/audio_buffer.h" 10 #include "media/base/audio_buffer.h"
11 #include "media/base/audio_decoder_config.h" 11 #include "media/base/audio_decoder_config.h"
12 #include "media/base/audio_timestamp_helper.h" 12 #include "media/base/audio_timestamp_helper.h"
13 #include "media/base/buffers.h" 13 #include "media/base/buffers.h"
14 14
15 namespace media { 15 namespace media {
16 16
17 // Largest gap or overlap allowed by this class. Anything 17 // Largest gap or overlap allowed by this class. Anything
18 // larger than this will trigger an error. 18 // larger than this will trigger an error.
19 // This is an arbitrary value, but the initial selection of 50ms 19 // This is an arbitrary value, but the initial selection of 50ms
20 // roughly represents the duration of 2 compressed AAC or MP3 frames. 20 // roughly represents the duration of 2 compressed AAC or MP3 frames.
21 static const int kMaxTimeDeltaInMilliseconds = 50; 21 static const int kMaxTimeDeltaInMilliseconds = 50;
22 22
23 // Minimum gap size needed before the splicer will take action to
24 // fill a gap. This avoids periodically inserting and then dropping samples
25 // when the buffer timestamps are slightly off because of timestamp rounding
26 // in the source content. Unit is frames.
27 static const int kMinGapSize = 2;
28
29 // The number of milliseconds to crossfade before trimming when buffers overlap.
30 static const int kCrossfadeDurationInMilliseconds = 5;
31
23 AudioSplicer::AudioSplicer(int samples_per_second) 32 AudioSplicer::AudioSplicer(int samples_per_second)
24 : output_timestamp_helper_(samples_per_second), 33 : output_timestamp_helper_(samples_per_second),
25 min_gap_size_(2), 34 received_end_of_stream_(false),
26 received_end_of_stream_(false) { 35 splice_frame_splicer_(samples_per_second) {}
27 }
28 36
29 AudioSplicer::~AudioSplicer() { 37 AudioSplicer::~AudioSplicer() {}
30 }
31 38
32 void AudioSplicer::Reset() { 39 void AudioSplicer::Reset() {
33 output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp()); 40 output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
34 output_buffers_.clear(); 41 output_buffers_.clear();
35 received_end_of_stream_ = false; 42 received_end_of_stream_ = false;
36 } 43 }
37 44
38 bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) { 45 bool AudioSplicer::AddInput(const scoped_refptr<DecoderBuffer>& origin_buffer,
acolwell GONE FROM CHROMIUM 2014/02/10 20:19:57 Passing origin_buffer here doesn't feel right to m
DaleCurtis 2014/02/15 01:20:02 I've reworked this so that the future AudioBufferS
46 const scoped_refptr<AudioBuffer>& input) {
39 DCHECK(!received_end_of_stream_ || input->end_of_stream()); 47 DCHECK(!received_end_of_stream_ || input->end_of_stream());
40 48
41 if (input->end_of_stream()) { 49 if (input->end_of_stream()) {
50 DCHECK(!origin_buffer->splice_preroll());
51 DCHECK(splice_buffers_.empty());
42 output_buffers_.push_back(input); 52 output_buffers_.push_back(input);
43 received_end_of_stream_ = true; 53 received_end_of_stream_ = true;
44 return true; 54 return true;
45 } 55 }
46 56
47 DCHECK(input->timestamp() != kNoTimestamp()); 57 DCHECK(input->timestamp() != kNoTimestamp());
48 DCHECK(input->duration() > base::TimeDelta()); 58 DCHECK(input->duration() > base::TimeDelta());
49 DCHECK_GT(input->frame_count(), 0); 59 DCHECK_GT(input->frame_count(), 0);
50 60
51 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp()) 61 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp())
52 output_timestamp_helper_.SetBaseTimestamp(input->timestamp()); 62 output_timestamp_helper_.SetBaseTimestamp(input->timestamp());
53 63
54 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) { 64 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) {
55 DVLOG(1) << "Input timestamp is before the base timestamp."; 65 DVLOG(1) << "Input timestamp is before the base timestamp.";
56 return false; 66 return false;
57 } 67 }
58 68
59 base::TimeDelta timestamp = input->timestamp(); 69 base::TimeDelta timestamp = input->timestamp();
60 base::TimeDelta expected_timestamp = output_timestamp_helper_.GetTimestamp(); 70 base::TimeDelta expected_timestamp = output_timestamp_helper_.GetTimestamp();
61 base::TimeDelta delta = timestamp - expected_timestamp; 71 base::TimeDelta delta = timestamp - expected_timestamp;
62 72
73 // TODO(dalecurtis): Does this need to be removed to handle overlaps?
63 if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) { 74 if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) {
64 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us"; 75 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us";
65 return false; 76 return false;
66 } 77 }
67 78
79 // It's splicers all the way down!
80 if (origin_buffer->splice_preroll()) {
81 origin_buffer->set_splice_preroll(false); // Pass NULL to inner instead?
82 return splice_frame_splicer_.AddInput(origin_buffer, input);
83 } else if (splice_frame_splicer_->HasNextBuffer()) {
84 origin_buffer->set_splice_preroll(false); // Pass NULL to inner instead?
85 if (!splice_frame_splicer_.AddInput(origin_buffer, input))
86 return false;
87
88 // The inner splicer has now trimmed and crossfaded buffers appropriately,
89 // so we simply need to transfer them into the outer splicer.
90 while (splice_frame_splicer_->HasNextBuffer())
91 AddOutputBuffer(splice_frame_splicer_->GetNextBuffer());
92
93 // The splice frame is complete, so can reset for the next splice frame.
94 splice_frame_splicer_.Reset();
95 return true;
96 }
97
68 int frames_to_fill = 0; 98 int frames_to_fill = 0;
69 if (delta != base::TimeDelta()) 99 if (delta != base::TimeDelta())
70 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp); 100 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp);
71 101
72 if (frames_to_fill == 0 || std::abs(frames_to_fill) < min_gap_size_) { 102 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) {
73 AddOutputBuffer(input); 103 AddOutputBuffer(input);
74 return true; 104 return true;
75 } 105 }
76 106
77 if (frames_to_fill > 0) { 107 if (frames_to_fill > 0) {
78 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds() 108 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
79 << " us: " << delta.InMicroseconds() << " us"; 109 << " us: " << delta.InMicroseconds() << " us";
80 110
81 // Create a buffer with enough silence samples to fill the gap and 111 // Create a buffer with enough silence samples to fill the gap and
82 // add it to the output buffer. 112 // add it to the output buffer.
83 scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer( 113 scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer(
84 input->channel_count(), 114 input->channel_count(),
85 frames_to_fill, 115 frames_to_fill,
86 expected_timestamp, 116 expected_timestamp,
87 output_timestamp_helper_.GetFrameDuration(frames_to_fill)); 117 output_timestamp_helper_.GetFrameDuration(frames_to_fill));
88 AddOutputBuffer(gap); 118 AddOutputBuffer(gap);
89 119
90 // Add the input buffer now that the gap has been filled. 120 // Add the input buffer now that the gap has been filled.
91 AddOutputBuffer(input); 121 AddOutputBuffer(input);
92 return true; 122 return true;
93 } 123 }
94 124
95 int frames_to_skip = -frames_to_fill; 125 int frames_to_skip = -frames_to_fill;
96
97 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds() 126 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
98 << " us: " << -delta.InMicroseconds() << " us"; 127 << " us: " << -delta.InMicroseconds() << " us";
99 128
100 if (input->frame_count() <= frames_to_skip) { 129 // If we have no buffers with which to crossfade, drop overlapping frames.
101 DVLOG(1) << "Dropping whole buffer"; 130 if (output_buffers_.empty()) {
131 if (input->frame_count() <= frames_to_skip) {
132 DVLOG(1) << "Dropping whole buffer";
133 return true;
134 }
135
136 // Copy the trailing samples that do not overlap samples already output
137 // into a new buffer. Add this new buffer to the output queue.
138 input->TrimStart(frames_to_skip);
139 AddOutputBuffer(input);
102 return true; 140 return true;
103 } 141 }
104 142
105 // Copy the trailing samples that do not overlap samples already output 143 const int five_ms_of_frames = output_timestamp_helper_->GetFramesToTarget(
106 // into a new buffer. Add this new buffer to the output queue. 144 output_timestamp_helper_->GetTimestamp() +
107 // 145 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds));
108 // TODO(acolwell): Implement a cross-fade here so the transition is less 146 const int frames_to_crossfade = std::min(frames_to_skip, five_ms_of_frames);
109 // jarring. 147
110 input->TrimStart(frames_to_skip); 148 // Discard frames not used in the crossfade.
111 AddOutputBuffer(input); 149 frames_to_skip = frames_to_skip - frames_to_crossfade;
112 return true; 150 for (int i = output_buffers_.size() - 1; i >= 0 && frames_to_skip > 0; --i) {
151 const int frame_count = output_buffers_.back()->frame_count();
152 if (frame_count <= frames_to_skip)
153 output_buffers_.pop_back();
154 else
155 output_buffers_.back()->TrimEnd(frames_to_skip);
156 frames_to_skip -= frame_count;
157 }
158
159 // TODO(dalecurtis): Crossfade |frames_to_crossfade| of frames from
160 // output_buffers_. See https://codereview.chromium.org/20876002/
113 } 161 }
114 162
115 bool AudioSplicer::HasNextBuffer() const { 163 bool AudioSplicer::HasNextBuffer() const {
116 return !output_buffers_.empty(); 164 return !output_buffers_.empty();
117 } 165 }
118 166
119 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() { 167 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
120 scoped_refptr<AudioBuffer> ret = output_buffers_.front(); 168 scoped_refptr<AudioBuffer> ret = output_buffers_.front();
121 output_buffers_.pop_front(); 169 output_buffers_.pop_front();
122 return ret; 170 return ret;
123 } 171 }
124 172
125 void AudioSplicer::AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer) { 173 void AudioSplicer::AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer) {
126 output_timestamp_helper_.AddFrames(buffer->frame_count()); 174 output_timestamp_helper_.AddFrames(buffer->frame_count());
127 output_buffers_.push_back(buffer); 175 output_buffers_.push_back(buffer);
128 } 176 }
129 177
130 } // namespace media 178 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698