1 // Copyright 2014 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_discard_helper.h"
6
7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "media/base/audio_buffer.h"
11 #include "media/base/buffers.h"
12
13 namespace media {
14
WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,base::TimeDelta current_timestamp)15 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,
16 base::TimeDelta current_timestamp) {
17 if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp)
18 return;
19
20 const base::TimeDelta diff = current_timestamp - last_timestamp;
21 DLOG(WARNING) << "Input timestamps are not monotonically increasing! "
22 << " ts " << current_timestamp.InMicroseconds() << " us"
23 << " diff " << diff.InMicroseconds() << " us";
24 }
25
AudioDiscardHelper(int sample_rate,size_t decoder_delay)26 AudioDiscardHelper::AudioDiscardHelper(int sample_rate, size_t decoder_delay)
27 : sample_rate_(sample_rate),
28 decoder_delay_(decoder_delay),
29 timestamp_helper_(sample_rate_),
30 discard_frames_(0),
31 last_input_timestamp_(kNoTimestamp()),
32 delayed_discard_(false),
33 delayed_end_discard_(0) {
34 DCHECK_GT(sample_rate_, 0);
35 }
36
~AudioDiscardHelper()37 AudioDiscardHelper::~AudioDiscardHelper() {
38 }
39
TimeDeltaToFrames(base::TimeDelta duration) const40 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const {
41 DCHECK(duration >= base::TimeDelta());
42 return duration.InSecondsF() * sample_rate_ + 0.5;
43 }
44
Reset(size_t initial_discard)45 void AudioDiscardHelper::Reset(size_t initial_discard) {
46 discard_frames_ = initial_discard;
47 last_input_timestamp_ = kNoTimestamp();
48 timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
49 delayed_discard_ = false;
50 delayed_discard_padding_ = DecoderBuffer::DiscardPadding();
51 }
52
ProcessBuffers(const scoped_refptr<DecoderBuffer> & encoded_buffer,const scoped_refptr<AudioBuffer> & decoded_buffer)53 bool AudioDiscardHelper::ProcessBuffers(
54 const scoped_refptr<DecoderBuffer>& encoded_buffer,
55 const scoped_refptr<AudioBuffer>& decoded_buffer) {
56 DCHECK(!encoded_buffer->end_of_stream());
57 DCHECK(encoded_buffer->timestamp() != kNoTimestamp());
58
59 // Issue a debug warning when we see non-monotonic timestamps. Only a warning
60 // to allow chained OGG playback.
61 WarnOnNonMonotonicTimestamps(last_input_timestamp_,
62 encoded_buffer->timestamp());
63 last_input_timestamp_ = encoded_buffer->timestamp();
64
65 // If this is the first buffer seen, setup the timestamp helper.
66 const bool first_buffer = !initialized();
67 if (first_buffer) {
68 // Clamp the base timestamp to zero.
69 timestamp_helper_.SetBaseTimestamp(
70 std::max(base::TimeDelta(), encoded_buffer->timestamp()));
71 }
72 DCHECK(initialized());
73
74 if (!decoded_buffer.get()) {
75 // If there's a one buffer delay for decoding, we need to save it so it can
76 // be processed with the next decoder buffer.
77 if (first_buffer) {
78 delayed_discard_ = true;
79 delayed_discard_padding_ = encoded_buffer->discard_padding();
80 }
81 return false;
82 }
83
84 const size_t original_frame_count = decoded_buffer->frame_count();
85
86 // If there's a one buffer delay for decoding, pick up the last encoded
87 // buffer's discard padding for processing with the current decoded buffer.
88 DecoderBuffer::DiscardPadding current_discard_padding =
89 encoded_buffer->discard_padding();
90 if (delayed_discard_) {
91 // For simplicity disallow cases where decoder delay is present with delayed
92 // discard (no codecs at present). Doing so allows us to avoid complexity
93 // around endpoint tracking when handling complete buffer discards.
94 DCHECK_EQ(decoder_delay_, 0u);
95 std::swap(current_discard_padding, delayed_discard_padding_);
96 }
97
98 if (discard_frames_ > 0) {
99 const size_t decoded_frames = decoded_buffer->frame_count();
100 const size_t frames_to_discard = std::min(discard_frames_, decoded_frames);
101 discard_frames_ -= frames_to_discard;
102
103 DVLOG(1) << "Initial discard of " << frames_to_discard << " out of "
104 << decoded_frames << " frames.";
105
106 // If everything would be discarded, indicate a new buffer is required.
107 if (frames_to_discard == decoded_frames) {
108 // For simplicity disallow cases where a buffer with discard padding is
109 // present. Doing so allows us to avoid complexity around tracking
110 // discards across buffers.
111 DCHECK(current_discard_padding.first == base::TimeDelta());
112 DCHECK(current_discard_padding.second == base::TimeDelta());
113 return false;
114 }
115
116 decoded_buffer->TrimStart(frames_to_discard);
117 }
118
119 // Process any delayed end discard from the previous buffer.
120 if (delayed_end_discard_ > 0) {
121 DCHECK_GT(decoder_delay_, 0u);
122
123 const size_t discard_index = decoder_delay_ - delayed_end_discard_;
124 DCHECK_LT(discard_index, decoder_delay_);
125
126 const size_t decoded_frames = decoded_buffer->frame_count();
127 DCHECK_LT(delayed_end_discard_, decoded_frames);
128
129 DVLOG(1) << "Delayed end discard of " << delayed_end_discard_ << " out of "
130 << decoded_frames << " frames starting at " << discard_index;
131
132 decoded_buffer->TrimRange(discard_index,
133 discard_index + delayed_end_discard_);
134 delayed_end_discard_ = 0;
135 }
136
137 // Handle front discard padding.
138 if (current_discard_padding.first > base::TimeDelta()) {
139 const size_t decoded_frames = decoded_buffer->frame_count();
140
141 // If a complete buffer discard is requested and there's no decoder delay,
142 // just discard all remaining frames from this buffer. With decoder delay
143 // we have to estimate the correct number of frames to discard based on the
144 // duration of the encoded buffer.
145 const size_t start_frames_to_discard =
146 current_discard_padding.first == kInfiniteDuration()
147 ? (decoder_delay_ > 0
148 ? TimeDeltaToFrames(encoded_buffer->duration())
149 : decoded_frames)
150 : TimeDeltaToFrames(current_discard_padding.first);
151
152 // Regardless of the timestamp on the encoded buffer, the corresponding
153 // decoded output will appear |decoder_delay_| frames later.
154 size_t discard_start = decoder_delay_;
155 if (decoder_delay_ > 0) {
156 // If we have a |decoder_delay_| and have already discarded frames from
157 // this buffer, the |discard_start| must be adjusted by the number of
158 // frames already discarded.
159 const size_t frames_discarded_so_far =
160 original_frame_count - decoded_buffer->frame_count();
161 CHECK_LE(frames_discarded_so_far, decoder_delay_);
162 discard_start -= frames_discarded_so_far;
163 }
164
165 // For simplicity require the start of the discard to be within the current
166 // buffer. Doing so allows us avoid complexity around tracking discards
167 // across buffers.
168 CHECK_LT(discard_start, decoded_frames);
169
170 const size_t frames_to_discard =
171 std::min(start_frames_to_discard, decoded_frames - discard_start);
172
173 // Carry over any frames which need to be discarded from the front of the
174 // next buffer.
175 DCHECK(!discard_frames_);
176 discard_frames_ = start_frames_to_discard - frames_to_discard;
177
178 DVLOG(1) << "Front discard of " << frames_to_discard << " out of "
179 << decoded_frames << " frames starting at " << discard_start;
180
181 // If everything would be discarded, indicate a new buffer is required.
182 if (frames_to_discard == decoded_frames) {
183 // The buffer should not have been marked with end discard if the front
184 // discard removes everything.
185 DCHECK(current_discard_padding.second == base::TimeDelta());
186 return false;
187 }
188
189 decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard);
190 } else {
191 DCHECK(current_discard_padding.first == base::TimeDelta());
192 }
193
194 // Handle end discard padding.
195 if (current_discard_padding.second > base::TimeDelta()) {
196 const size_t decoded_frames = decoded_buffer->frame_count();
197 size_t end_frames_to_discard =
198 TimeDeltaToFrames(current_discard_padding.second);
199
200 if (decoder_delay_) {
201 // Delayed end discard only works if the decoder delay is less than a
202 // single buffer.
203 DCHECK_LT(decoder_delay_, original_frame_count);
204
205 // If the discard is >= the decoder delay, trim everything we can off the
206 // end of this buffer and the rest from the start of the next.
207 if (end_frames_to_discard >= decoder_delay_) {
208 DCHECK(!discard_frames_);
209 discard_frames_ = decoder_delay_;
210 end_frames_to_discard -= decoder_delay_;
211 } else {
212 DCHECK(!delayed_end_discard_);
213 std::swap(delayed_end_discard_, end_frames_to_discard);
214 }
215 }
216
217 if (end_frames_to_discard > decoded_frames) {
218 DLOG(ERROR) << "Encountered invalid discard padding value.";
219 return false;
220 }
221
222 if (end_frames_to_discard > 0) {
223 DVLOG(1) << "End discard of " << end_frames_to_discard << " out of "
224 << decoded_frames;
225
226 // If everything would be discarded, indicate a new buffer is required.
227 if (end_frames_to_discard == decoded_frames)
228 return false;
229
230 decoded_buffer->TrimEnd(end_frames_to_discard);
231 }
232 } else {
233 DCHECK(current_discard_padding.second == base::TimeDelta());
234 }
235
236 // Assign timestamp to the buffer.
237 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
238 timestamp_helper_.AddFrames(decoded_buffer->frame_count());
239 return true;
240 }
241
242 } // namespace media
243