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