OLD | NEW |
| (Empty) |
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 <stdint.h> | |
8 #include <cstdlib> | |
9 #include <deque> | |
10 #include <utility> | |
11 | |
12 #include "base/logging.h" | |
13 #include "base/macros.h" | |
14 #include "media/base/audio_buffer.h" | |
15 #include "media/base/audio_bus.h" | |
16 #include "media/base/audio_decoder_config.h" | |
17 #include "media/base/audio_timestamp_helper.h" | |
18 #include "media/base/media_log.h" | |
19 #include "media/base/vector_math.h" | |
20 | |
21 namespace media { | |
22 | |
23 namespace { | |
24 | |
25 enum { | |
26 // Minimum gap size needed before the splicer will take action to | |
27 // fill a gap. This avoids periodically inserting and then dropping samples | |
28 // when the buffer timestamps are slightly off because of timestamp rounding | |
29 // in the source content. Unit is frames. | |
30 kMinGapSize = 2, | |
31 | |
32 // Limits the number of MEDIA_LOG() per sanitizer instance warning the user | |
33 // about splicer overlaps within |kMaxTimeDeltaInMilliseconds| or gaps larger | |
34 // than |kMinGapSize| and less than |kMaxTimeDeltaInMilliseconds|. These | |
35 // warnings may be frequent for some streams, and number of sanitizer | |
36 // instances may be high, so keep this limit low to help reduce log spam. | |
37 kMaxSanitizerWarningLogs = 5, | |
38 }; | |
39 | |
40 // AudioBuffer::TrimStart() is not as accurate as the timestamp helper, so | |
41 // manually adjust the duration and timestamp after trimming. | |
42 void AccurateTrimStart(int frames_to_trim, | |
43 const scoped_refptr<AudioBuffer> buffer, | |
44 const AudioTimestampHelper& timestamp_helper) { | |
45 buffer->TrimStart(frames_to_trim); | |
46 buffer->set_timestamp(timestamp_helper.GetTimestamp()); | |
47 } | |
48 | |
49 // Returns an AudioBus whose frame buffer is backed by the provided AudioBuffer. | |
50 std::unique_ptr<AudioBus> CreateAudioBufferWrapper( | |
51 const scoped_refptr<AudioBuffer>& buffer) { | |
52 std::unique_ptr<AudioBus> wrapper = | |
53 AudioBus::CreateWrapper(buffer->channel_count()); | |
54 wrapper->set_frames(buffer->frame_count()); | |
55 for (int ch = 0; ch < buffer->channel_count(); ++ch) { | |
56 wrapper->SetChannelData( | |
57 ch, reinterpret_cast<float*>(buffer->channel_data()[ch])); | |
58 } | |
59 return wrapper; | |
60 } | |
61 | |
62 } // namespace | |
63 | |
64 class AudioStreamSanitizer { | |
65 public: | |
66 AudioStreamSanitizer(int samples_per_second, | |
67 const scoped_refptr<MediaLog>& media_log); | |
68 ~AudioStreamSanitizer(); | |
69 | |
70 // Resets the sanitizer state by clearing the output buffers queue, and | |
71 // resetting the timestamp helper. | |
72 void Reset(); | |
73 | |
74 // Similar to Reset(), but initializes the timestamp helper with the given | |
75 // parameters. | |
76 void ResetTimestampState(int64_t frame_count, base::TimeDelta base_timestamp); | |
77 | |
78 // Adds a new buffer full of samples or end of stream buffer to the splicer. | |
79 // Returns true if the buffer was accepted. False is returned if an error | |
80 // occurred. | |
81 bool AddInput(const scoped_refptr<AudioBuffer>& input); | |
82 | |
83 // Returns true if the sanitizer has a buffer to return. | |
84 bool HasNextBuffer() const; | |
85 | |
86 // Removes the next buffer from the output buffer queue and returns it; should | |
87 // only be called if HasNextBuffer() returns true. | |
88 scoped_refptr<AudioBuffer> GetNextBuffer(); | |
89 | |
90 // Returns the total frame count of all buffers available for output. | |
91 int GetFrameCount() const; | |
92 | |
93 const AudioTimestampHelper& timestamp_helper() { | |
94 return output_timestamp_helper_; | |
95 } | |
96 | |
97 // Transfer all buffers into |output|. Returns false if AddInput() on the | |
98 // |output| sanitizer fails for any buffer removed from |this|. | |
99 bool DrainInto(AudioStreamSanitizer* output); | |
100 | |
101 private: | |
102 void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer); | |
103 | |
104 AudioTimestampHelper output_timestamp_helper_; | |
105 bool received_end_of_stream_ = false; | |
106 | |
107 typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue; | |
108 BufferQueue output_buffers_; | |
109 | |
110 scoped_refptr<MediaLog> media_log_; | |
111 | |
112 // To prevent log spam, counts the number of audio gap or overlaps warned in | |
113 // logs. | |
114 int num_warning_logs_ = 0; | |
115 | |
116 DISALLOW_ASSIGN(AudioStreamSanitizer); | |
117 }; | |
118 | |
119 AudioStreamSanitizer::AudioStreamSanitizer( | |
120 int samples_per_second, | |
121 const scoped_refptr<MediaLog>& media_log) | |
122 : output_timestamp_helper_(samples_per_second), media_log_(media_log) {} | |
123 | |
124 AudioStreamSanitizer::~AudioStreamSanitizer() {} | |
125 | |
126 void AudioStreamSanitizer::Reset() { | |
127 ResetTimestampState(0, kNoTimestamp); | |
128 } | |
129 | |
130 void AudioStreamSanitizer::ResetTimestampState(int64_t frame_count, | |
131 base::TimeDelta base_timestamp) { | |
132 output_buffers_.clear(); | |
133 received_end_of_stream_ = false; | |
134 output_timestamp_helper_.SetBaseTimestamp(base_timestamp); | |
135 if (frame_count > 0) | |
136 output_timestamp_helper_.AddFrames(frame_count); | |
137 } | |
138 | |
139 bool AudioStreamSanitizer::AddInput(const scoped_refptr<AudioBuffer>& input) { | |
140 DCHECK(!received_end_of_stream_ || input->end_of_stream()); | |
141 | |
142 if (input->end_of_stream()) { | |
143 output_buffers_.push_back(input); | |
144 received_end_of_stream_ = true; | |
145 return true; | |
146 } | |
147 | |
148 DCHECK(input->timestamp() != kNoTimestamp); | |
149 DCHECK(input->duration() > base::TimeDelta()); | |
150 DCHECK_GT(input->frame_count(), 0); | |
151 | |
152 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp) | |
153 output_timestamp_helper_.SetBaseTimestamp(input->timestamp()); | |
154 | |
155 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) { | |
156 MEDIA_LOG(ERROR, media_log_) | |
157 << "Audio splicing failed: unexpected timestamp sequence. base " | |
158 "timestamp=" | |
159 << output_timestamp_helper_.base_timestamp().InMicroseconds() | |
160 << "us, input timestamp=" << input->timestamp().InMicroseconds() | |
161 << "us"; | |
162 return false; | |
163 } | |
164 | |
165 const base::TimeDelta timestamp = input->timestamp(); | |
166 const base::TimeDelta expected_timestamp = | |
167 output_timestamp_helper_.GetTimestamp(); | |
168 const base::TimeDelta delta = timestamp - expected_timestamp; | |
169 | |
170 if (std::abs(delta.InMilliseconds()) > | |
171 AudioSplicer::kMaxTimeDeltaInMilliseconds) { | |
172 MEDIA_LOG(ERROR, media_log_) | |
173 << "Audio splicing failed: coded frame timestamp differs from " | |
174 "expected timestamp " << expected_timestamp.InMicroseconds() | |
175 << "us by " << delta.InMicroseconds() | |
176 << "us, more than threshold of +/-" | |
177 << AudioSplicer::kMaxTimeDeltaInMilliseconds | |
178 << "ms. Expected timestamp is based on decoded frames and frame rate."; | |
179 return false; | |
180 } | |
181 | |
182 int frames_to_fill = 0; | |
183 if (!delta.is_zero()) | |
184 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp); | |
185 | |
186 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) { | |
187 AddOutputBuffer(input); | |
188 return true; | |
189 } | |
190 | |
191 if (frames_to_fill > 0) { | |
192 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_, | |
193 kMaxSanitizerWarningLogs) | |
194 << "Audio splicer inserting silence for small gap of " | |
195 << delta.InMicroseconds() << "us at time " | |
196 << expected_timestamp.InMicroseconds() << "us."; | |
197 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds() | |
198 << " us: " << delta.InMicroseconds() << " us"; | |
199 | |
200 // Create a buffer with enough silence samples to fill the gap and | |
201 // add it to the output buffer. | |
202 scoped_refptr<AudioBuffer> gap = | |
203 AudioBuffer::CreateEmptyBuffer(input->channel_layout(), | |
204 input->channel_count(), | |
205 input->sample_rate(), | |
206 frames_to_fill, | |
207 expected_timestamp); | |
208 AddOutputBuffer(gap); | |
209 | |
210 // Add the input buffer now that the gap has been filled. | |
211 AddOutputBuffer(input); | |
212 return true; | |
213 } | |
214 | |
215 // Overlapping buffers marked as splice frames are handled by AudioSplicer, | |
216 // but decoder and demuxer quirks may sometimes produce overlapping samples | |
217 // which need to be sanitized. | |
218 // | |
219 // A crossfade can't be done here because only the current buffer is available | |
220 // at this point, not previous buffers. | |
221 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_, | |
222 kMaxSanitizerWarningLogs) | |
223 << "Audio splicer skipping frames for small overlap of " | |
224 << -delta.InMicroseconds() << "us at time " | |
225 << expected_timestamp.InMicroseconds() << "us."; | |
226 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds() | |
227 << " us: " << -delta.InMicroseconds() << " us"; | |
228 | |
229 const int frames_to_skip = -frames_to_fill; | |
230 if (input->frame_count() <= frames_to_skip) { | |
231 DVLOG(1) << "Dropping whole buffer"; | |
232 return true; | |
233 } | |
234 | |
235 // Copy the trailing samples that do not overlap samples already output | |
236 // into a new buffer. Add this new buffer to the output queue. | |
237 // | |
238 // TODO(acolwell): Implement a cross-fade here so the transition is less | |
239 // jarring. | |
240 AccurateTrimStart(frames_to_skip, input, output_timestamp_helper_); | |
241 AddOutputBuffer(input); | |
242 return true; | |
243 } | |
244 | |
245 bool AudioStreamSanitizer::HasNextBuffer() const { | |
246 return !output_buffers_.empty(); | |
247 } | |
248 | |
249 scoped_refptr<AudioBuffer> AudioStreamSanitizer::GetNextBuffer() { | |
250 scoped_refptr<AudioBuffer> ret = output_buffers_.front(); | |
251 output_buffers_.pop_front(); | |
252 return ret; | |
253 } | |
254 | |
255 void AudioStreamSanitizer::AddOutputBuffer( | |
256 const scoped_refptr<AudioBuffer>& buffer) { | |
257 output_timestamp_helper_.AddFrames(buffer->frame_count()); | |
258 output_buffers_.push_back(buffer); | |
259 } | |
260 | |
261 int AudioStreamSanitizer::GetFrameCount() const { | |
262 int frame_count = 0; | |
263 for (const auto& buffer : output_buffers_) | |
264 frame_count += buffer->frame_count(); | |
265 return frame_count; | |
266 } | |
267 | |
268 bool AudioStreamSanitizer::DrainInto(AudioStreamSanitizer* output) { | |
269 while (HasNextBuffer()) { | |
270 if (!output->AddInput(GetNextBuffer())) | |
271 return false; | |
272 } | |
273 return true; | |
274 } | |
275 | |
276 AudioSplicer::AudioSplicer(int samples_per_second, | |
277 const scoped_refptr<MediaLog>& media_log) | |
278 : max_crossfade_duration_( | |
279 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)), | |
280 splice_timestamp_(kNoTimestamp), | |
281 max_splice_end_timestamp_(kNoTimestamp), | |
282 output_sanitizer_( | |
283 new AudioStreamSanitizer(samples_per_second, media_log)), | |
284 pre_splice_sanitizer_( | |
285 new AudioStreamSanitizer(samples_per_second, media_log)), | |
286 post_splice_sanitizer_( | |
287 new AudioStreamSanitizer(samples_per_second, media_log)), | |
288 have_all_pre_splice_buffers_(false) {} | |
289 | |
290 AudioSplicer::~AudioSplicer() {} | |
291 | |
292 void AudioSplicer::Reset() { | |
293 output_sanitizer_->Reset(); | |
294 pre_splice_sanitizer_->Reset(); | |
295 post_splice_sanitizer_->Reset(); | |
296 have_all_pre_splice_buffers_ = false; | |
297 reset_splice_timestamps(); | |
298 } | |
299 | |
300 bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) { | |
301 // If we're not processing a splice, add the input to the output queue. | |
302 if (splice_timestamp_ == kNoTimestamp) { | |
303 DCHECK(!pre_splice_sanitizer_->HasNextBuffer()); | |
304 DCHECK(!post_splice_sanitizer_->HasNextBuffer()); | |
305 return output_sanitizer_->AddInput(input); | |
306 } | |
307 | |
308 const AudioTimestampHelper& output_ts_helper = | |
309 output_sanitizer_->timestamp_helper(); | |
310 | |
311 if (!have_all_pre_splice_buffers_) { | |
312 DCHECK(!input->end_of_stream()); | |
313 | |
314 // If the provided buffer is entirely before the splice point it can also be | |
315 // added to the output queue. | |
316 if (input->timestamp() + input->duration() < splice_timestamp_) { | |
317 DCHECK(!pre_splice_sanitizer_->HasNextBuffer()); | |
318 return output_sanitizer_->AddInput(input); | |
319 } | |
320 | |
321 // If we've encountered the first pre splice buffer, reset the pre splice | |
322 // sanitizer based on |output_sanitizer_|. This is done so that gaps and | |
323 // overlaps between buffers across the sanitizers are accounted for prior | |
324 // to calculating crossfade. | |
325 if (!pre_splice_sanitizer_->HasNextBuffer()) { | |
326 pre_splice_sanitizer_->ResetTimestampState( | |
327 output_ts_helper.frame_count(), output_ts_helper.base_timestamp()); | |
328 } | |
329 | |
330 return pre_splice_sanitizer_->AddInput(input); | |
331 } | |
332 | |
333 // The first post splice buffer is expected to match |splice_timestamp_|. | |
334 if (!post_splice_sanitizer_->HasNextBuffer()) | |
335 CHECK(splice_timestamp_ == input->timestamp()); | |
336 | |
337 // At this point we have all the fade out preroll buffers from the decoder. | |
338 // We now need to wait until we have enough data to perform the crossfade (or | |
339 // we receive an end of stream). | |
340 if (!post_splice_sanitizer_->AddInput(input)) | |
341 return false; | |
342 | |
343 // Ensure |output_sanitizer_| has a valid base timestamp so we can use it for | |
344 // timestamp calculations. | |
345 if (output_ts_helper.base_timestamp() == kNoTimestamp) { | |
346 output_sanitizer_->ResetTimestampState( | |
347 0, pre_splice_sanitizer_->timestamp_helper().base_timestamp()); | |
348 } | |
349 | |
350 // If a splice frame was incorrectly marked due to poor demuxed timestamps, we | |
351 // may not actually have a splice. Here we check if any frames exist before | |
352 // the splice. In this case, just transfer all data to the output sanitizer. | |
353 const int frames_before_splice = | |
354 output_ts_helper.base_timestamp() == kNoTimestamp | |
355 ? 0 | |
356 : output_ts_helper.GetFramesToTarget(splice_timestamp_); | |
357 if (frames_before_splice < 0 || | |
358 pre_splice_sanitizer_->GetFrameCount() <= frames_before_splice) { | |
359 CHECK(pre_splice_sanitizer_->DrainInto(output_sanitizer_.get())); | |
360 | |
361 // If the file contains incorrectly muxed timestamps, there may be huge gaps | |
362 // between the demuxed and decoded timestamps. | |
363 if (!post_splice_sanitizer_->DrainInto(output_sanitizer_.get())) | |
364 return false; | |
365 | |
366 reset_splice_timestamps(); | |
367 return true; | |
368 } | |
369 | |
370 // Wait until we have enough data to crossfade or end of stream. | |
371 if (!input->end_of_stream() && | |
372 input->timestamp() + input->duration() < max_splice_end_timestamp_) { | |
373 return true; | |
374 } | |
375 | |
376 scoped_refptr<AudioBuffer> crossfade_buffer; | |
377 std::unique_ptr<AudioBus> pre_splice = | |
378 ExtractCrossfadeFromPreSplice(&crossfade_buffer); | |
379 | |
380 // Crossfade the pre splice and post splice sections and transfer all relevant | |
381 // buffers into |output_sanitizer_|. | |
382 CrossfadePostSplice(std::move(pre_splice), crossfade_buffer); | |
383 | |
384 // Clear the splice timestamp so new splices can be accepted. | |
385 reset_splice_timestamps(); | |
386 return true; | |
387 } | |
388 | |
389 bool AudioSplicer::HasNextBuffer() const { | |
390 return output_sanitizer_->HasNextBuffer(); | |
391 } | |
392 | |
393 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() { | |
394 return output_sanitizer_->GetNextBuffer(); | |
395 } | |
396 | |
397 void AudioSplicer::SetSpliceTimestamp(base::TimeDelta splice_timestamp) { | |
398 if (splice_timestamp == kNoTimestamp) { | |
399 DCHECK(splice_timestamp_ != kNoTimestamp); | |
400 DCHECK(!have_all_pre_splice_buffers_); | |
401 have_all_pre_splice_buffers_ = true; | |
402 return; | |
403 } | |
404 | |
405 if (splice_timestamp_ == splice_timestamp) | |
406 return; | |
407 | |
408 // TODO(dalecurtis): We may need the concept of a future_splice_timestamp_ to | |
409 // handle cases where another splice comes in before we've received 5ms of | |
410 // data from the last one. Leave this as a CHECK for now to figure out if | |
411 // this case is possible. | |
412 CHECK(splice_timestamp_ == kNoTimestamp); | |
413 splice_timestamp_ = splice_timestamp; | |
414 max_splice_end_timestamp_ = splice_timestamp_ + max_crossfade_duration_; | |
415 pre_splice_sanitizer_->Reset(); | |
416 post_splice_sanitizer_->Reset(); | |
417 have_all_pre_splice_buffers_ = false; | |
418 } | |
419 | |
420 std::unique_ptr<AudioBus> AudioSplicer::ExtractCrossfadeFromPreSplice( | |
421 scoped_refptr<AudioBuffer>* crossfade_buffer) { | |
422 DCHECK(crossfade_buffer); | |
423 const AudioTimestampHelper& output_ts_helper = | |
424 output_sanitizer_->timestamp_helper(); | |
425 | |
426 int frames_before_splice = | |
427 output_ts_helper.GetFramesToTarget(splice_timestamp_); | |
428 | |
429 // Determine crossfade frame count based on available frames in each splicer | |
430 // and capping to the maximum crossfade duration. | |
431 const int max_crossfade_frame_count = | |
432 output_ts_helper.GetFramesToTarget(max_splice_end_timestamp_) - | |
433 frames_before_splice; | |
434 const int frames_to_crossfade = std::min( | |
435 max_crossfade_frame_count, | |
436 std::min(pre_splice_sanitizer_->GetFrameCount() - frames_before_splice, | |
437 post_splice_sanitizer_->GetFrameCount())); | |
438 // There must always be frames to crossfade, otherwise the splice should not | |
439 // have been generated. | |
440 DCHECK_GT(frames_to_crossfade, 0); | |
441 | |
442 int frames_read = 0; | |
443 std::unique_ptr<AudioBus> output_bus; | |
444 while (pre_splice_sanitizer_->HasNextBuffer() && | |
445 frames_read < frames_to_crossfade) { | |
446 scoped_refptr<AudioBuffer> preroll = pre_splice_sanitizer_->GetNextBuffer(); | |
447 | |
448 // We don't know the channel count until we see the first buffer, so wait | |
449 // until the first buffer to allocate the output AudioBus. | |
450 if (!output_bus) { | |
451 output_bus = | |
452 AudioBus::Create(preroll->channel_count(), frames_to_crossfade); | |
453 // Allocate output buffer for crossfade. | |
454 *crossfade_buffer = AudioBuffer::CreateBuffer(kSampleFormatPlanarF32, | |
455 preroll->channel_layout(), | |
456 preroll->channel_count(), | |
457 preroll->sample_rate(), | |
458 frames_to_crossfade); | |
459 } | |
460 | |
461 // There may be enough of a gap introduced during decoding such that an | |
462 // entire buffer exists before the splice point. | |
463 if (frames_before_splice >= preroll->frame_count()) { | |
464 // Adjust the number of frames remaining before the splice. NOTE: This is | |
465 // safe since |pre_splice_sanitizer_| is a continuation of the timeline in | |
466 // |output_sanitizer_|. As such we're guaranteed there are no gaps or | |
467 // overlaps in the timeline between the two sanitizers. | |
468 frames_before_splice -= preroll->frame_count(); | |
469 CHECK(output_sanitizer_->AddInput(preroll)); | |
470 continue; | |
471 } | |
472 | |
473 const int frames_to_read = | |
474 std::min(preroll->frame_count() - frames_before_splice, | |
475 output_bus->frames() - frames_read); | |
476 preroll->ReadFrames( | |
477 frames_to_read, frames_before_splice, frames_read, output_bus.get()); | |
478 frames_read += frames_to_read; | |
479 | |
480 // If only part of the buffer was consumed, trim it appropriately and stick | |
481 // it into the output queue. | |
482 if (frames_before_splice) { | |
483 preroll->TrimEnd(preroll->frame_count() - frames_before_splice); | |
484 CHECK(output_sanitizer_->AddInput(preroll)); | |
485 frames_before_splice = 0; | |
486 } | |
487 } | |
488 | |
489 // Ensure outputs were properly allocated. The method should not have been | |
490 // called if there is not enough data to crossfade. | |
491 // TODO(dalecurtis): Convert to DCHECK() once http://crbug.com/356073 fixed. | |
492 CHECK(output_bus); | |
493 CHECK(crossfade_buffer->get()); | |
494 | |
495 // All necessary buffers have been processed, it's safe to reset. | |
496 pre_splice_sanitizer_->Reset(); | |
497 DCHECK_EQ(output_bus->frames(), frames_read); | |
498 DCHECK_EQ(output_ts_helper.GetFramesToTarget(splice_timestamp_), 0); | |
499 return output_bus; | |
500 } | |
501 | |
502 void AudioSplicer::CrossfadePostSplice( | |
503 std::unique_ptr<AudioBus> pre_splice_bus, | |
504 const scoped_refptr<AudioBuffer>& crossfade_buffer) { | |
505 // Use the calculated timestamp and duration to ensure there's no extra gaps | |
506 // or overlaps to process when adding the buffer to |output_sanitizer_|. | |
507 const AudioTimestampHelper& output_ts_helper = | |
508 output_sanitizer_->timestamp_helper(); | |
509 crossfade_buffer->set_timestamp(output_ts_helper.GetTimestamp()); | |
510 | |
511 // AudioBuffer::ReadFrames() only allows output into an AudioBus, so wrap | |
512 // our AudioBuffer in one so we can avoid extra data copies. | |
513 std::unique_ptr<AudioBus> output_bus = | |
514 CreateAudioBufferWrapper(crossfade_buffer); | |
515 | |
516 // Extract crossfade section from the |post_splice_sanitizer_|. | |
517 int frames_read = 0, frames_to_trim = 0; | |
518 scoped_refptr<AudioBuffer> remainder; | |
519 while (post_splice_sanitizer_->HasNextBuffer() && | |
520 frames_read < output_bus->frames()) { | |
521 scoped_refptr<AudioBuffer> postroll = | |
522 post_splice_sanitizer_->GetNextBuffer(); | |
523 const int frames_to_read = | |
524 std::min(postroll->frame_count(), output_bus->frames() - frames_read); | |
525 postroll->ReadFrames(frames_to_read, 0, frames_read, output_bus.get()); | |
526 frames_read += frames_to_read; | |
527 | |
528 // If only part of the buffer was consumed, save it for after we've added | |
529 // the crossfade buffer | |
530 if (frames_to_read < postroll->frame_count()) { | |
531 DCHECK(!remainder.get()); | |
532 remainder.swap(postroll); | |
533 frames_to_trim = frames_to_read; | |
534 } | |
535 } | |
536 | |
537 DCHECK_EQ(output_bus->frames(), frames_read); | |
538 | |
539 // Crossfade the audio into |crossfade_buffer|. | |
540 for (int ch = 0; ch < output_bus->channels(); ++ch) { | |
541 vector_math::Crossfade(pre_splice_bus->channel(ch), | |
542 pre_splice_bus->frames(), | |
543 output_bus->channel(ch)); | |
544 } | |
545 | |
546 CHECK(output_sanitizer_->AddInput(crossfade_buffer)); | |
547 DCHECK_EQ(crossfade_buffer->frame_count(), output_bus->frames()); | |
548 | |
549 if (remainder.get()) { | |
550 // Trim off consumed frames. | |
551 AccurateTrimStart(frames_to_trim, remainder, output_ts_helper); | |
552 CHECK(output_sanitizer_->AddInput(remainder)); | |
553 } | |
554 | |
555 // Transfer all remaining buffers out and reset once empty. | |
556 CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get())); | |
557 post_splice_sanitizer_->Reset(); | |
558 } | |
559 | |
560 } // namespace media | |
OLD | NEW |