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