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.
5 #include "media/base/audio_discard_helper.h"
9 #include "base/logging.h"
10 #include "media/base/audio_buffer.h"
14 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp
,
15 base::TimeDelta current_timestamp
) {
16 if (last_timestamp
== kNoTimestamp() || last_timestamp
< current_timestamp
)
19 const base::TimeDelta diff
= current_timestamp
- last_timestamp
;
20 DLOG(WARNING
) << "Input timestamps are not monotonically increasing! "
21 << " ts " << current_timestamp
.InMicroseconds() << " us"
22 << " diff " << diff
.InMicroseconds() << " us";
25 AudioDiscardHelper::AudioDiscardHelper(int sample_rate
, size_t decoder_delay
)
26 : sample_rate_(sample_rate
),
27 decoder_delay_(decoder_delay
),
28 timestamp_helper_(sample_rate_
),
30 last_input_timestamp_(kNoTimestamp()),
31 delayed_discard_(false),
32 delayed_end_discard_(0) {
33 DCHECK_GT(sample_rate_
, 0);
36 AudioDiscardHelper::~AudioDiscardHelper() {
39 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration
) const {
40 DCHECK(duration
>= base::TimeDelta());
41 return duration
.InSecondsF() * sample_rate_
+ 0.5;
44 void AudioDiscardHelper::Reset(size_t initial_discard
) {
45 discard_frames_
= initial_discard
;
46 last_input_timestamp_
= kNoTimestamp();
47 timestamp_helper_
.SetBaseTimestamp(kNoTimestamp());
48 delayed_discard_
= false;
49 delayed_discard_padding_
= DecoderBuffer::DiscardPadding();
52 bool AudioDiscardHelper::ProcessBuffers(
53 const scoped_refptr
<DecoderBuffer
>& encoded_buffer
,
54 const scoped_refptr
<AudioBuffer
>& decoded_buffer
) {
55 DCHECK(!encoded_buffer
->end_of_stream());
56 DCHECK(encoded_buffer
->timestamp() != kNoTimestamp());
58 // Issue a debug warning when we see non-monotonic timestamps. Only a warning
59 // to allow chained OGG playback.
60 WarnOnNonMonotonicTimestamps(last_input_timestamp_
,
61 encoded_buffer
->timestamp());
62 last_input_timestamp_
= encoded_buffer
->timestamp();
64 // If this is the first buffer seen, setup the timestamp helper.
65 const bool first_buffer
= !initialized();
67 // Clamp the base timestamp to zero.
68 timestamp_helper_
.SetBaseTimestamp(
69 std::max(base::TimeDelta(), encoded_buffer
->timestamp()));
71 DCHECK(initialized());
73 if (!decoded_buffer
.get()) {
74 // If there's a one buffer delay for decoding, we need to save it so it can
75 // be processed with the next decoder buffer.
77 delayed_discard_
= true;
78 delayed_discard_padding_
= encoded_buffer
->discard_padding();
83 const size_t original_frame_count
= decoded_buffer
->frame_count();
85 // If there's a one buffer delay for decoding, pick up the last encoded
86 // buffer's discard padding for processing with the current decoded buffer.
87 DecoderBuffer::DiscardPadding current_discard_padding
=
88 encoded_buffer
->discard_padding();
89 if (delayed_discard_
) {
90 // For simplicity disallow cases where decoder delay is present with delayed
91 // discard (no codecs at present). Doing so allows us to avoid complexity
92 // around endpoint tracking when handling complete buffer discards.
93 DCHECK_EQ(decoder_delay_
, 0u);
94 std::swap(current_discard_padding
, delayed_discard_padding_
);
97 if (discard_frames_
> 0) {
98 const size_t decoded_frames
= decoded_buffer
->frame_count();
99 const size_t frames_to_discard
= std::min(discard_frames_
, decoded_frames
);
100 discard_frames_
-= frames_to_discard
;
102 DVLOG(1) << "Initial discard of " << frames_to_discard
<< " out of "
103 << decoded_frames
<< " frames.";
105 // If everything would be discarded, indicate a new buffer is required.
106 if (frames_to_discard
== decoded_frames
) {
107 // For simplicity, we just drop any discard padding if |discard_frames_|
108 // consumes the entire buffer.
112 decoded_buffer
->TrimStart(frames_to_discard
);
115 // Process any delayed end discard from the previous buffer.
116 if (delayed_end_discard_
> 0) {
117 DCHECK_GT(decoder_delay_
, 0u);
119 const size_t discard_index
= decoder_delay_
- delayed_end_discard_
;
120 DCHECK_LT(discard_index
, decoder_delay_
);
122 const size_t decoded_frames
= decoded_buffer
->frame_count();
123 DCHECK_LT(delayed_end_discard_
, decoded_frames
);
125 DVLOG(1) << "Delayed end discard of " << delayed_end_discard_
<< " out of "
126 << decoded_frames
<< " frames starting at " << discard_index
;
128 decoded_buffer
->TrimRange(discard_index
,
129 discard_index
+ delayed_end_discard_
);
130 delayed_end_discard_
= 0;
133 // Handle front discard padding.
134 if (current_discard_padding
.first
> base::TimeDelta()) {
135 const size_t decoded_frames
= decoded_buffer
->frame_count();
137 // If a complete buffer discard is requested and there's no decoder delay,
138 // just discard all remaining frames from this buffer. With decoder delay
139 // we have to estimate the correct number of frames to discard based on the
140 // duration of the encoded buffer.
141 const size_t start_frames_to_discard
=
142 current_discard_padding
.first
== kInfiniteDuration()
143 ? (decoder_delay_
> 0
144 ? TimeDeltaToFrames(encoded_buffer
->duration())
146 : TimeDeltaToFrames(current_discard_padding
.first
);
148 // Regardless of the timestamp on the encoded buffer, the corresponding
149 // decoded output will appear |decoder_delay_| frames later.
150 size_t discard_start
= decoder_delay_
;
151 if (decoder_delay_
> 0) {
152 // If we have a |decoder_delay_| and have already discarded frames from
153 // this buffer, the |discard_start| must be adjusted by the number of
154 // frames already discarded.
155 const size_t frames_discarded_so_far
=
156 original_frame_count
- decoded_buffer
->frame_count();
157 CHECK_LE(frames_discarded_so_far
, decoder_delay_
);
158 discard_start
-= frames_discarded_so_far
;
161 // For simplicity require the start of the discard to be within the current
162 // buffer. Doing so allows us avoid complexity around tracking discards
164 CHECK_LT(discard_start
, decoded_frames
);
166 const size_t frames_to_discard
=
167 std::min(start_frames_to_discard
, decoded_frames
- discard_start
);
169 // Carry over any frames which need to be discarded from the front of the
171 DCHECK(!discard_frames_
);
172 discard_frames_
= start_frames_to_discard
- frames_to_discard
;
174 DVLOG(1) << "Front discard of " << frames_to_discard
<< " out of "
175 << decoded_frames
<< " frames starting at " << discard_start
;
177 // If everything would be discarded, indicate a new buffer is required.
178 if (frames_to_discard
== decoded_frames
) {
179 // The buffer should not have been marked with end discard if the front
180 // discard removes everything.
181 DCHECK(current_discard_padding
.second
== base::TimeDelta());
185 decoded_buffer
->TrimRange(discard_start
, discard_start
+ frames_to_discard
);
187 DCHECK(current_discard_padding
.first
== base::TimeDelta());
190 // Handle end discard padding.
191 if (current_discard_padding
.second
> base::TimeDelta()) {
192 const size_t decoded_frames
= decoded_buffer
->frame_count();
193 size_t end_frames_to_discard
=
194 TimeDeltaToFrames(current_discard_padding
.second
);
196 if (decoder_delay_
) {
197 // Delayed end discard only works if the decoder delay is less than a
199 DCHECK_LT(decoder_delay_
, original_frame_count
);
201 // If the discard is >= the decoder delay, trim everything we can off the
202 // end of this buffer and the rest from the start of the next.
203 if (end_frames_to_discard
>= decoder_delay_
) {
204 DCHECK(!discard_frames_
);
205 discard_frames_
= decoder_delay_
;
206 end_frames_to_discard
-= decoder_delay_
;
208 DCHECK(!delayed_end_discard_
);
209 std::swap(delayed_end_discard_
, end_frames_to_discard
);
213 if (end_frames_to_discard
> decoded_frames
) {
214 DLOG(ERROR
) << "Encountered invalid discard padding value.";
218 if (end_frames_to_discard
> 0) {
219 DVLOG(1) << "End discard of " << end_frames_to_discard
<< " out of "
222 // If everything would be discarded, indicate a new buffer is required.
223 if (end_frames_to_discard
== decoded_frames
)
226 decoded_buffer
->TrimEnd(end_frames_to_discard
);
229 DCHECK(current_discard_padding
.second
== base::TimeDelta());
232 // Assign timestamp to the buffer.
233 decoded_buffer
->set_timestamp(timestamp_helper_
.GetTimestamp());
234 timestamp_helper_
.AddFrames(decoded_buffer
->frame_count());