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"
11 #include "media/base/buffers.h"
15 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp
,
16 base::TimeDelta current_timestamp
) {
17 if (last_timestamp
== kNoTimestamp() || last_timestamp
< current_timestamp
)
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";
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_
),
31 last_input_timestamp_(kNoTimestamp()),
32 delayed_discard_(false),
33 delayed_end_discard_(0) {
34 DCHECK_GT(sample_rate_
, 0);
37 AudioDiscardHelper::~AudioDiscardHelper() {
40 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration
) const {
41 DCHECK(duration
>= base::TimeDelta());
42 return duration
.InSecondsF() * sample_rate_
+ 0.5;
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();
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());
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();
65 // If this is the first buffer seen, setup the timestamp helper.
66 const bool first_buffer
= !initialized();
68 // Clamp the base timestamp to zero.
69 timestamp_helper_
.SetBaseTimestamp(
70 std::max(base::TimeDelta(), encoded_buffer
->timestamp()));
72 DCHECK(initialized());
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.
78 delayed_discard_
= true;
79 delayed_discard_padding_
= encoded_buffer
->discard_padding();
84 const size_t original_frame_count
= decoded_buffer
->frame_count();
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_
);
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
;
103 DVLOG(1) << "Initial discard of " << frames_to_discard
<< " out of "
104 << decoded_frames
<< " frames.";
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());
116 decoded_buffer
->TrimStart(frames_to_discard
);
119 // Process any delayed end discard from the previous buffer.
120 if (delayed_end_discard_
> 0) {
121 DCHECK_GT(decoder_delay_
, 0u);
123 const size_t discard_index
= decoder_delay_
- delayed_end_discard_
;
124 DCHECK_LT(discard_index
, decoder_delay_
);
126 const size_t decoded_frames
= decoded_buffer
->frame_count();
127 DCHECK_LT(delayed_end_discard_
, decoded_frames
);
129 DVLOG(1) << "Delayed end discard of " << delayed_end_discard_
<< " out of "
130 << decoded_frames
<< " frames starting at " << discard_index
;
132 decoded_buffer
->TrimRange(discard_index
,
133 discard_index
+ delayed_end_discard_
);
134 delayed_end_discard_
= 0;
137 // Handle front discard padding.
138 if (current_discard_padding
.first
> base::TimeDelta()) {
139 const size_t decoded_frames
= decoded_buffer
->frame_count();
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())
150 : TimeDeltaToFrames(current_discard_padding
.first
);
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
;
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
168 CHECK_LT(discard_start
, decoded_frames
);
170 const size_t frames_to_discard
=
171 std::min(start_frames_to_discard
, decoded_frames
- discard_start
);
173 // Carry over any frames which need to be discarded from the front of the
175 DCHECK(!discard_frames_
);
176 discard_frames_
= start_frames_to_discard
- frames_to_discard
;
178 DVLOG(1) << "Front discard of " << frames_to_discard
<< " out of "
179 << decoded_frames
<< " frames starting at " << discard_start
;
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());
189 decoded_buffer
->TrimRange(discard_start
, discard_start
+ frames_to_discard
);
191 DCHECK(current_discard_padding
.first
== base::TimeDelta());
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
);
200 if (decoder_delay_
) {
201 // Delayed end discard only works if the decoder delay is less than a
203 DCHECK_LT(decoder_delay_
, original_frame_count
);
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_
;
212 DCHECK(!delayed_end_discard_
);
213 std::swap(delayed_end_discard_
, end_frames_to_discard
);
217 if (end_frames_to_discard
> decoded_frames
) {
218 DLOG(ERROR
) << "Encountered invalid discard padding value.";
222 if (end_frames_to_discard
> 0) {
223 DVLOG(1) << "End discard of " << end_frames_to_discard
<< " out of "
226 // If everything would be discarded, indicate a new buffer is required.
227 if (end_frames_to_discard
== decoded_frames
)
230 decoded_buffer
->TrimEnd(end_frames_to_discard
);
233 DCHECK(current_discard_padding
.second
== base::TimeDelta());
236 // Assign timestamp to the buffer.
237 decoded_buffer
->set_timestamp(timestamp_helper_
.GetTimestamp());
238 timestamp_helper_
.AddFrames(decoded_buffer
->frame_count());