Material PDF: Restore animated entry and exit of viewer toolbars
[chromium-blink-merge.git] / media / base / audio_discard_helper.cc
blobf7c645cc20e6b2098e2b90764eb4ff75ee5424e7
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"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "media/base/audio_buffer.h"
12 namespace media {
14 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,
15 base::TimeDelta current_timestamp) {
16 if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp)
17 return;
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_),
29 discard_frames_(0),
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();
66 if (first_buffer) {
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.
76 if (first_buffer) {
77 delayed_discard_ = true;
78 delayed_discard_padding_ = encoded_buffer->discard_padding();
80 return false;
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 disallow cases where a buffer with discard padding is
108 // present. Doing so allows us to avoid complexity around tracking
109 // discards across buffers.
110 DCHECK(current_discard_padding.first == base::TimeDelta());
111 DCHECK(current_discard_padding.second == base::TimeDelta());
112 return false;
115 decoded_buffer->TrimStart(frames_to_discard);
118 // Process any delayed end discard from the previous buffer.
119 if (delayed_end_discard_ > 0) {
120 DCHECK_GT(decoder_delay_, 0u);
122 const size_t discard_index = decoder_delay_ - delayed_end_discard_;
123 DCHECK_LT(discard_index, decoder_delay_);
125 const size_t decoded_frames = decoded_buffer->frame_count();
126 DCHECK_LT(delayed_end_discard_, decoded_frames);
128 DVLOG(1) << "Delayed end discard of " << delayed_end_discard_ << " out of "
129 << decoded_frames << " frames starting at " << discard_index;
131 decoded_buffer->TrimRange(discard_index,
132 discard_index + delayed_end_discard_);
133 delayed_end_discard_ = 0;
136 // Handle front discard padding.
137 if (current_discard_padding.first > base::TimeDelta()) {
138 const size_t decoded_frames = decoded_buffer->frame_count();
140 // If a complete buffer discard is requested and there's no decoder delay,
141 // just discard all remaining frames from this buffer. With decoder delay
142 // we have to estimate the correct number of frames to discard based on the
143 // duration of the encoded buffer.
144 const size_t start_frames_to_discard =
145 current_discard_padding.first == kInfiniteDuration()
146 ? (decoder_delay_ > 0
147 ? TimeDeltaToFrames(encoded_buffer->duration())
148 : decoded_frames)
149 : TimeDeltaToFrames(current_discard_padding.first);
151 // Regardless of the timestamp on the encoded buffer, the corresponding
152 // decoded output will appear |decoder_delay_| frames later.
153 size_t discard_start = decoder_delay_;
154 if (decoder_delay_ > 0) {
155 // If we have a |decoder_delay_| and have already discarded frames from
156 // this buffer, the |discard_start| must be adjusted by the number of
157 // frames already discarded.
158 const size_t frames_discarded_so_far =
159 original_frame_count - decoded_buffer->frame_count();
160 CHECK_LE(frames_discarded_so_far, decoder_delay_);
161 discard_start -= frames_discarded_so_far;
164 // For simplicity require the start of the discard to be within the current
165 // buffer. Doing so allows us avoid complexity around tracking discards
166 // across buffers.
167 CHECK_LT(discard_start, decoded_frames);
169 const size_t frames_to_discard =
170 std::min(start_frames_to_discard, decoded_frames - discard_start);
172 // Carry over any frames which need to be discarded from the front of the
173 // next buffer.
174 DCHECK(!discard_frames_);
175 discard_frames_ = start_frames_to_discard - frames_to_discard;
177 DVLOG(1) << "Front discard of " << frames_to_discard << " out of "
178 << decoded_frames << " frames starting at " << discard_start;
180 // If everything would be discarded, indicate a new buffer is required.
181 if (frames_to_discard == decoded_frames) {
182 // The buffer should not have been marked with end discard if the front
183 // discard removes everything.
184 DCHECK(current_discard_padding.second == base::TimeDelta());
185 return false;
188 decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard);
189 } else {
190 DCHECK(current_discard_padding.first == base::TimeDelta());
193 // Handle end discard padding.
194 if (current_discard_padding.second > base::TimeDelta()) {
195 const size_t decoded_frames = decoded_buffer->frame_count();
196 size_t end_frames_to_discard =
197 TimeDeltaToFrames(current_discard_padding.second);
199 if (decoder_delay_) {
200 // Delayed end discard only works if the decoder delay is less than a
201 // single buffer.
202 DCHECK_LT(decoder_delay_, original_frame_count);
204 // If the discard is >= the decoder delay, trim everything we can off the
205 // end of this buffer and the rest from the start of the next.
206 if (end_frames_to_discard >= decoder_delay_) {
207 DCHECK(!discard_frames_);
208 discard_frames_ = decoder_delay_;
209 end_frames_to_discard -= decoder_delay_;
210 } else {
211 DCHECK(!delayed_end_discard_);
212 std::swap(delayed_end_discard_, end_frames_to_discard);
216 if (end_frames_to_discard > decoded_frames) {
217 DLOG(ERROR) << "Encountered invalid discard padding value.";
218 return false;
221 if (end_frames_to_discard > 0) {
222 DVLOG(1) << "End discard of " << end_frames_to_discard << " out of "
223 << decoded_frames;
225 // If everything would be discarded, indicate a new buffer is required.
226 if (end_frames_to_discard == decoded_frames)
227 return false;
229 decoded_buffer->TrimEnd(end_frames_to_discard);
231 } else {
232 DCHECK(current_discard_padding.second == base::TimeDelta());
235 // Assign timestamp to the buffer.
236 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
237 timestamp_helper_.AddFrames(decoded_buffer->frame_count());
238 return true;
241 } // namespace media