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

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: Fixes. Created 6 years, 9 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 #include <deque>
8 9
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "media/base/audio_buffer.h" 11 #include "media/base/audio_buffer.h"
12 #include "media/base/audio_bus.h"
11 #include "media/base/audio_decoder_config.h" 13 #include "media/base/audio_decoder_config.h"
12 #include "media/base/audio_timestamp_helper.h" 14 #include "media/base/audio_timestamp_helper.h"
13 #include "media/base/buffers.h" 15 #include "media/base/buffers.h"
16 #include "media/base/vector_math.h"
14 17
15 namespace media { 18 namespace media {
16 19
17 // Largest gap or overlap allowed by this class. Anything 20 // Largest gap or overlap allowed by this class. Anything
18 // larger than this will trigger an error. 21 // larger than this will trigger an error.
19 // This is an arbitrary value, but the initial selection of 50ms 22 // This is an arbitrary value, but the initial selection of 50ms
20 // roughly represents the duration of 2 compressed AAC or MP3 frames. 23 // roughly represents the duration of 2 compressed AAC or MP3 frames.
21 static const int kMaxTimeDeltaInMilliseconds = 50; 24 static const int kMaxTimeDeltaInMilliseconds = 50;
22 25
23 AudioSplicer::AudioSplicer(int samples_per_second) 26 // Minimum gap size needed before the splicer will take action to
24 : output_timestamp_helper_(samples_per_second), 27 // fill a gap. This avoids periodically inserting and then dropping samples
25 min_gap_size_(2), 28 // when the buffer timestamps are slightly off because of timestamp rounding
26 received_end_of_stream_(false) { 29 // in the source content. Unit is frames.
30 static const int kMinGapSize = 2;
31
32 // The number of milliseconds to crossfade before trimming when buffers overlap.
33 static const int kCrossfadeDurationInMilliseconds = 5;
34
35 // AudioBuffer::TrimStart() is not as accurate as the timestamp helper, so
36 // manually adjust the duration and timestamp after trimming.
37 static void AccurateTrimStart(int frames_to_trim,
38 const scoped_refptr<AudioBuffer> buffer,
39 const AudioTimestampHelper& timestamp_helper) {
40 buffer->TrimStart(frames_to_trim);
41 buffer->set_timestamp(timestamp_helper.GetTimestamp());
42 buffer->set_duration(
43 timestamp_helper.GetFrameDuration(buffer->frame_count()));
27 } 44 }
28 45
29 AudioSplicer::~AudioSplicer() { 46 // AudioBuffer::TrimEnd() is not as accurate as the timestamp helper, so
47 // manually adjust the duration after trimming.
48 static void AccurateTrimEnd(int frames_to_trim,
49 const scoped_refptr<AudioBuffer> buffer,
50 const AudioTimestampHelper& timestamp_helper) {
51 buffer->TrimEnd(frames_to_trim);
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 nit: DCHECK_EQ(buffer->timestamp(), timestamp_help
DaleCurtis 2014/02/28 00:24:41 Done.
52 buffer->set_duration(
53 timestamp_helper.GetFrameDuration(buffer->frame_count()));
30 } 54 }
31 55
32 void AudioSplicer::Reset() { 56 typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue;
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 nit: I think this should be inside the AudioStream
DaleCurtis 2014/02/28 00:24:41 Done.
33 output_timestamp_helper_.SetBaseTimestamp(kNoTimestamp()); 57
58 class AudioStreamSanitizer {
59 public:
60 explicit AudioStreamSanitizer(int samples_per_second);
61 ~AudioStreamSanitizer();
62
63 // Resets the sanitizer state by clearing the output buffers queue, and
64 // resetting the timestamp helper.
65 void Reset();
66
67 // Similar to Reset(), but initializes the timestamp helper with the given
68 // parameters.
69 void ResetTimestampState(int64 frame_count, base::TimeDelta base_timestamp);
70
71 // Adds a new buffer full of samples or end of stream buffer to the splicer.
72 // Returns true if the buffer was accepted. False is returned if an error
73 // occurred.
74 bool AddInput(const scoped_refptr<AudioBuffer>& input);
75
76 // Returns true if the sanitizer has a buffer to return.
77 bool HasNextBuffer() const;
78
79 // Removes the next buffer from the output buffer queue and returns it; should
80 // only be called if HasNextBuffer() returns true.
81 scoped_refptr<AudioBuffer> GetNextBuffer();
82
83 // Returns the total frame count of all buffers available for output.
84 int GetFrameCount() const;
85
86 // Returns the duration of all buffers added to the output queue thus far.
87 base::TimeDelta GetDuration() const;
88
89 // Sets the base timestamp of the AudioTimestampHelper. Must only be called
90 // if no base timestamp has been set.
91 void SetBaseTimestamp(base::TimeDelta base_timestamp);
92
93 const AudioTimestampHelper& timestamp_helper() {
94 return output_timestamp_helper_;
95 }
96
97 private:
98 void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer);
99
100 AudioTimestampHelper output_timestamp_helper_;
101 BufferQueue output_buffers_;
102 bool received_end_of_stream_;
103
104 DISALLOW_ASSIGN(AudioStreamSanitizer);
105 };
106
107 AudioStreamSanitizer::AudioStreamSanitizer(int samples_per_second)
108 : output_timestamp_helper_(samples_per_second),
109 received_end_of_stream_(false) {}
110
111 AudioStreamSanitizer::~AudioStreamSanitizer() {}
112
113 void AudioStreamSanitizer::Reset() {
114 ResetTimestampState(0, kNoTimestamp());
115 }
116
117 void AudioStreamSanitizer::ResetTimestampState(int64 frame_count,
118 base::TimeDelta base_timestamp) {
34 output_buffers_.clear(); 119 output_buffers_.clear();
35 received_end_of_stream_ = false; 120 received_end_of_stream_ = false;
121 output_timestamp_helper_.SetBaseTimestamp(base_timestamp);
122 if (frame_count > 0)
123 output_timestamp_helper_.AddFrames(frame_count);
36 } 124 }
37 125
38 bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) { 126 bool AudioStreamSanitizer::AddInput(const scoped_refptr<AudioBuffer>& input) {
39 DCHECK(!received_end_of_stream_ || input->end_of_stream()); 127 DCHECK(!received_end_of_stream_ || input->end_of_stream());
40 128
41 if (input->end_of_stream()) { 129 if (input->end_of_stream()) {
42 output_buffers_.push_back(input); 130 output_buffers_.push_back(input);
43 received_end_of_stream_ = true; 131 received_end_of_stream_ = true;
44 return true; 132 return true;
45 } 133 }
46 134
47 DCHECK(input->timestamp() != kNoTimestamp()); 135 DCHECK(input->timestamp() != kNoTimestamp());
48 DCHECK(input->duration() > base::TimeDelta()); 136 DCHECK(input->duration() > base::TimeDelta());
49 DCHECK_GT(input->frame_count(), 0); 137 DCHECK_GT(input->frame_count(), 0);
50 138
51 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp()) 139 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp())
52 output_timestamp_helper_.SetBaseTimestamp(input->timestamp()); 140 output_timestamp_helper_.SetBaseTimestamp(input->timestamp());
53 141
54 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) { 142 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) {
55 DVLOG(1) << "Input timestamp is before the base timestamp."; 143 DVLOG(1) << "Input timestamp is before the base timestamp.";
56 return false; 144 return false;
57 } 145 }
58 146
59 base::TimeDelta timestamp = input->timestamp(); 147 const base::TimeDelta timestamp = input->timestamp();
60 base::TimeDelta expected_timestamp = output_timestamp_helper_.GetTimestamp(); 148 const base::TimeDelta expected_timestamp =
61 base::TimeDelta delta = timestamp - expected_timestamp; 149 output_timestamp_helper_.GetTimestamp();
150 const base::TimeDelta delta = timestamp - expected_timestamp;
62 151
63 if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) { 152 if (std::abs(delta.InMilliseconds()) > kMaxTimeDeltaInMilliseconds) {
64 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us"; 153 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us";
65 return false; 154 return false;
66 } 155 }
67 156
68 int frames_to_fill = 0; 157 int frames_to_fill = 0;
69 if (delta != base::TimeDelta()) 158 if (delta != base::TimeDelta())
70 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp); 159 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp);
71 160
72 if (frames_to_fill == 0 || std::abs(frames_to_fill) < min_gap_size_) { 161 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) {
73 AddOutputBuffer(input); 162 AddOutputBuffer(input);
74 return true; 163 return true;
75 } 164 }
76 165
77 if (frames_to_fill > 0) { 166 if (frames_to_fill > 0) {
78 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds() 167 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
79 << " us: " << delta.InMicroseconds() << " us"; 168 << " us: " << delta.InMicroseconds() << " us";
80 169
81 // Create a buffer with enough silence samples to fill the gap and 170 // Create a buffer with enough silence samples to fill the gap and
82 // add it to the output buffer. 171 // add it to the output buffer.
83 scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer( 172 scoped_refptr<AudioBuffer> gap = AudioBuffer::CreateEmptyBuffer(
84 input->channel_count(), 173 input->channel_count(),
85 frames_to_fill, 174 frames_to_fill,
86 expected_timestamp, 175 expected_timestamp,
87 output_timestamp_helper_.GetFrameDuration(frames_to_fill)); 176 output_timestamp_helper_.GetFrameDuration(frames_to_fill));
88 AddOutputBuffer(gap); 177 AddOutputBuffer(gap);
89 178
90 // Add the input buffer now that the gap has been filled. 179 // Add the input buffer now that the gap has been filled.
91 AddOutputBuffer(input); 180 AddOutputBuffer(input);
92 return true; 181 return true;
93 } 182 }
94 183
95 int frames_to_skip = -frames_to_fill; 184 // Overlapping buffers marked as splice frames are handled by AudioSplicer,
185 // but decoder and demuxer quirks may sometimes produce overlapping samples
186 // which need to be sanitized.
187 //
188 // A crossfade can't be done here because only the current buffer is available
189 // at this point, not previous buffers.
190 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
191 << " us: " << -delta.InMicroseconds() << " us";
96 192
97 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds() 193 const int frames_to_skip = -frames_to_fill;
98 << " us: " << -delta.InMicroseconds() << " us";
99
100 if (input->frame_count() <= frames_to_skip) { 194 if (input->frame_count() <= frames_to_skip) {
101 DVLOG(1) << "Dropping whole buffer"; 195 DVLOG(1) << "Dropping whole buffer";
102 return true; 196 return true;
103 } 197 }
104 198
105 // Copy the trailing samples that do not overlap samples already output 199 // Copy the trailing samples that do not overlap samples already output
106 // into a new buffer. Add this new buffer to the output queue. 200 // into a new buffer. Add this new buffer to the output queue.
107 // 201 AccurateTrimStart(frames_to_skip, input, output_timestamp_helper_);
108 // TODO(acolwell): Implement a cross-fade here so the transition is less
109 // jarring.
110 input->TrimStart(frames_to_skip);
111 AddOutputBuffer(input); 202 AddOutputBuffer(input);
112 return true; 203 return true;
113 } 204 }
114 205
115 bool AudioSplicer::HasNextBuffer() const { 206 bool AudioStreamSanitizer::HasNextBuffer() const {
116 return !output_buffers_.empty(); 207 return !output_buffers_.empty();
117 } 208 }
118 209
119 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() { 210 scoped_refptr<AudioBuffer> AudioStreamSanitizer::GetNextBuffer() {
120 scoped_refptr<AudioBuffer> ret = output_buffers_.front(); 211 scoped_refptr<AudioBuffer> ret = output_buffers_.front();
121 output_buffers_.pop_front(); 212 output_buffers_.pop_front();
122 return ret; 213 return ret;
123 } 214 }
124 215
125 void AudioSplicer::AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer) { 216 void AudioStreamSanitizer::AddOutputBuffer(
217 const scoped_refptr<AudioBuffer>& buffer) {
126 output_timestamp_helper_.AddFrames(buffer->frame_count()); 218 output_timestamp_helper_.AddFrames(buffer->frame_count());
127 output_buffers_.push_back(buffer); 219 output_buffers_.push_back(buffer);
128 } 220 }
129 221
222 int AudioStreamSanitizer::GetFrameCount() const {
223 int frame_count = 0;
224 for(BufferQueue::const_iterator it = output_buffers_.begin();
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 nit: space after for
DaleCurtis 2014/02/28 00:24:41 Done.
225 it != output_buffers_.end(); ++it) {
226 frame_count += (*it)->frame_count();
227 }
228 return frame_count;
229 }
230
231 base::TimeDelta AudioStreamSanitizer::GetDuration() const {
232 DCHECK(output_timestamp_helper_.base_timestamp() != kNoTimestamp());
233 return output_timestamp_helper_.GetTimestamp() -
234 output_timestamp_helper_.base_timestamp();
235 }
236
237 void AudioStreamSanitizer::SetBaseTimestamp(base::TimeDelta base_timestamp) {
238 DCHECK(output_timestamp_helper_.base_timestamp() == kNoTimestamp());
239 output_timestamp_helper_.SetBaseTimestamp(base_timestamp);
240 }
241
242 AudioSplicer::AudioSplicer(int samples_per_second)
243 : max_crossfade_duration_(
244 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)),
245 splice_timestamp_(kNoTimestamp()),
246 output_sanitizer_(new AudioStreamSanitizer(samples_per_second)),
247 pre_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)),
248 post_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)) {}
249
250 AudioSplicer::~AudioSplicer() {}
251
252 void AudioSplicer::Reset() {
253 output_sanitizer_->Reset();
254 pre_splice_sanitizer_->Reset();
255 post_splice_sanitizer_->Reset();
256 splice_timestamp_ = kNoTimestamp();
257 }
258
259 bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) {
260 // If we're not processing a splice, add the input to the output queue.
261 if (splice_timestamp_ == kNoTimestamp())
262 return output_sanitizer_->AddInput(input);
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 DCHECK(!pre_splice_sanitizer_->HasNextBuffer()); D
DaleCurtis 2014/02/28 00:24:41 Done.
263
264 // If we're still receiving buffers before the splice point figure out which
265 // sanitizer (if any) to put them in.
266 if (!post_splice_sanitizer_->HasNextBuffer()) {
267 DCHECK(!input->end_of_stream());
268
269 // If the provided buffer is entirely before the splice point it can also be
270 // added to the output queue.
271 if (input->timestamp() + input->duration() < splice_timestamp_)
272 return output_sanitizer_->AddInput(input);
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 DCHECK(!pre_splice_sanitizer_->HasNextBuffer()); h
DaleCurtis 2014/02/28 00:24:41 Done.
273
274 // If we've encountered the first pre splice buffer, reset the pre splice
275 // sanitizer based on |output_sanitizer_|. This is done so that gaps and
276 // overlaps between buffers across the sanitizers are accounted for prior
277 // to calculating crossfade.
278 if (!pre_splice_sanitizer_->HasNextBuffer()) {
279 pre_splice_sanitizer_->ResetTimestampState(
280 output_sanitizer_->timestamp_helper().frame_count(),
281 output_sanitizer_->timestamp_helper().base_timestamp());
282 }
283
284 // If we're processing a splice and the input buffer does not overlap any of
285 // the existing buffers, append it to the splice queue for processing.
286 if (!pre_splice_sanitizer_->HasNextBuffer() ||
287 input->timestamp() != splice_timestamp_) {
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 We can rely on the "input->timestamp() != splice_t
DaleCurtis 2014/02/27 19:45:01 It's only strong enough when included with the !pr
acolwell GONE FROM CHROMIUM 2014/02/28 18:50:27 Ok then some of this should be in the comment then
DaleCurtis 2014/02/28 21:14:26 Done. I also added a CHECK() for the unlikely cas
288 return pre_splice_sanitizer_->AddInput(input);
289 }
290
291 // We've received the first overlapping buffer.
292 }
293
294 // At this point we have all the fade out preroll buffers from the decoder.
295 // We now need to wait until we have enough data to perform the crossfade (or
296 // we receive an end of stream).
297 if (!post_splice_sanitizer_->AddInput(input))
298 return false;
299
300 if (!input->end_of_stream() &&
301 post_splice_sanitizer_->GetDuration() < max_crossfade_duration_) {
302 return true;
303 }
304
305 // Transfer out preroll buffers involved in the splice, drop those not. Since
306 // we don't want to care what format the AudioBuffers are in, we need to use
307 // an intermediary AudioBus to convert the data to float.
308 scoped_ptr<AudioBus> pre_splice_bus = ExtractCrossfadeFromPreSplice();
309
310 // Allocate output buffer for crossfade.
311 scoped_refptr<AudioBuffer> crossfade_buffer =
312 AudioBuffer::CreateBuffer(kSampleFormatPlanarF32,
313 pre_splice_bus->channels(),
314 pre_splice_bus->frames());
315
316 // Use the calculated timestamp and duration to ensure there's no extra gaps
317 // or overlaps to process when adding the buffer to |output_sanitizer_|.
318 const AudioTimestampHelper& output_ts_helper =
319 output_sanitizer_->timestamp_helper();
320 crossfade_buffer->set_timestamp(output_ts_helper.GetTimestamp());
321 crossfade_buffer->set_duration(
322 output_ts_helper.GetFrameDuration(pre_splice_bus->frames()));
323
324 // AudioBuffer::ReadFrames() only allows output into an AudioBus, so wrap
325 // our AudioBuffer in one so we can avoid extra data copies.
326 scoped_ptr<AudioBus> crossfade_bus_wrapper =
327 AudioBus::CreateWrapper(crossfade_buffer->channel_count());
328 crossfade_bus_wrapper->set_frames(crossfade_buffer->frame_count());
329 for (int ch = 0; ch < crossfade_buffer->channel_count(); ++ch) {
330 crossfade_bus_wrapper->SetChannelData(
331 ch, reinterpret_cast<float*>(crossfade_buffer->channel_data()[ch]));
332 }
333
334 // Insert the crossfade buffer into the output queue now so post splice
335 // buffers can be added in processing order. We will still modify the buffer
336 // during the crossfade step.
337 CHECK(output_sanitizer_->AddInput(crossfade_buffer));
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 This seems risky since AddInput() can modify cross
DaleCurtis 2014/02/28 00:24:41 This is messy since then trimming and the accuracy
acolwell GONE FROM CHROMIUM 2014/02/28 18:50:27 I still think this is a bad idea. You could move t
DaleCurtis 2014/02/28 21:14:26 Switching to a CrossfadePostSplice() method looks
338
339 ExtractCrossfadeFromPostSplice(crossfade_bus_wrapper.get());
340
341 // Crossfade the audio into |crossfade_buffer|.
342 for (int ch = 0; ch < crossfade_bus_wrapper->channels(); ++ch) {
343 vector_math::Crossfade(pre_splice_bus->channel(ch),
344 pre_splice_bus->frames(),
345 crossfade_bus_wrapper->channel(ch));
346 }
347
348 // Clear the splice timestamp so new splices can be accepted.
349 splice_timestamp_ = kNoTimestamp();
350 return true;
351 }
352
353 bool AudioSplicer::HasNextBuffer() const {
354 return output_sanitizer_->HasNextBuffer();
355 }
356
357 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
358 return output_sanitizer_->GetNextBuffer();
359 }
360
361 void AudioSplicer::SetSpliceTimestamp(base::TimeDelta splice_timestamp) {
362 DCHECK(splice_timestamp != kNoTimestamp());
363 if (splice_timestamp_ == splice_timestamp)
364 return;
365
366 // TODO(dalecurtis): We may need the concept of a future_splice_timestamp_ to
367 // handle cases where another splice comes in before we've received 5ms of
368 // data from the last one. Leave this as a CHECK for now to figure out if
369 // this case is possible.
370 CHECK(splice_timestamp_ == kNoTimestamp());
371 splice_timestamp_ = splice_timestamp;
372 }
373
374 scoped_ptr<AudioBus> AudioSplicer::ExtractCrossfadeFromPreSplice() {
375 const AudioTimestampHelper& output_ts_helper =
376 output_sanitizer_->timestamp_helper();
377
378 // Ensure |output_sanitizer_| has a valid base timestamp so we can use it for
379 // timestamp calculations.
380 if (output_ts_helper.base_timestamp() == kNoTimestamp()) {
381 output_sanitizer_->SetBaseTimestamp(
acolwell GONE FROM CHROMIUM 2014/02/27 17:10:14 Could you use ResetTimestampState() here instead?
DaleCurtis 2014/02/27 19:45:01 Correct and a good idea, but I I was thinking of c
382 pre_splice_sanitizer_->timestamp_helper().base_timestamp());
383 }
384
385 int frames_before_splice =
386 output_ts_helper.GetFramesToTarget(splice_timestamp_);
387
388 // Determine crossfade frame count based on available frames in each splicer
389 // and capping to the maximum crossfade duration.
390 const int max_crossfade_frame_count =
391 output_ts_helper.GetFramesToTarget(splice_timestamp_ +
392 max_crossfade_duration_) -
393 frames_before_splice;
394 const int frames_to_crossfade = std::min(
395 max_crossfade_frame_count,
396 std::min(pre_splice_sanitizer_->GetFrameCount() - frames_before_splice,
397 post_splice_sanitizer_->GetFrameCount()));
398
399 int frames_read = 0;
400 scoped_ptr<AudioBus> output_bus;
401 while (pre_splice_sanitizer_->HasNextBuffer() &&
402 frames_read < frames_to_crossfade) {
403 scoped_refptr<AudioBuffer> preroll = pre_splice_sanitizer_->GetNextBuffer();
404
405 // We don't know the channel count until we see the first buffer, so wait
406 // until the first buffer to allocate the output AudioBus.
407 if (!output_bus) {
408 output_bus =
409 AudioBus::Create(preroll->channel_count(), frames_to_crossfade);
410 }
411
412 // There may be enough of a gap introduced during decoding such that an
413 // entire buffer exists before the splice point.
414 if (frames_before_splice >= preroll->frame_count()) {
415 frames_before_splice -= preroll->frame_count();
416 CHECK(output_sanitizer_->AddInput(preroll));
417 continue;
418 }
419
420 const int frames_to_read =
421 std::min(preroll->frame_count() - frames_before_splice,
422 output_bus->frames() - frames_read);
423 preroll->ReadFrames(
424 frames_to_read, frames_before_splice, frames_read, output_bus.get());
425 frames_read += frames_to_read;
426
427 // If only part of the buffer was consumed, trim it appropriately and stick
428 // it into the output queue.
429 if (frames_before_splice) {
430 AccurateTrimEnd(preroll->frame_count() - frames_before_splice,
431 preroll,
432 output_ts_helper);
433 CHECK(output_sanitizer_->AddInput(preroll));
434 frames_before_splice = 0;
435 }
436 }
437
438 // All necessary buffers have been processed, it's safe to reset.
439 pre_splice_sanitizer_->Reset();
440 DCHECK_EQ(output_bus->frames(), frames_read);
441 DCHECK_EQ(output_ts_helper.GetFramesToTarget(splice_timestamp_), 0);
442 return output_bus.Pass();
443 }
444
445 void AudioSplicer::ExtractCrossfadeFromPostSplice(AudioBus* output_bus) {
446 int frames_read = 0;
447 while (post_splice_sanitizer_->HasNextBuffer() &&
448 frames_read < output_bus->frames()) {
449 scoped_refptr<AudioBuffer> postroll =
450 post_splice_sanitizer_->GetNextBuffer();
451 const int frames_to_read =
452 std::min(postroll->frame_count(), output_bus->frames() - frames_read);
453 postroll->ReadFrames(frames_to_read, 0, frames_read, output_bus);
454 frames_read += frames_to_read;
455
456 // If only part of the buffer was consumed, trim it appropriately and stick
457 // it into the output queue.
458 if (frames_to_read < postroll->frame_count()) {
459 AccurateTrimStart(
460 frames_to_read, postroll, output_sanitizer_->timestamp_helper());
461 CHECK(output_sanitizer_->AddInput(postroll));
462 }
463 }
464
465 DCHECK_EQ(output_bus->frames(), frames_read);
466
467 // Transfer all remaining buffers out and reset once empty.
468 while (post_splice_sanitizer_->HasNextBuffer())
469 CHECK(output_sanitizer_->AddInput(post_splice_sanitizer_->GetNextBuffer()));
470 post_splice_sanitizer_->Reset();
471 }
472
130 } // namespace media 473 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698