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