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