• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   DCHECK_GT(sample_rate_, 0);
34 }
35 
~AudioDiscardHelper()36 AudioDiscardHelper::~AudioDiscardHelper() {
37 }
38 
TimeDeltaToFrames(base::TimeDelta duration) const39 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const {
40   DCHECK(duration >= base::TimeDelta());
41   return duration.InSecondsF() * sample_rate_ + 0.5;
42 }
43 
Reset(size_t initial_discard)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();
50 }
51 
ProcessBuffers(const scoped_refptr<DecoderBuffer> & encoded_buffer,const scoped_refptr<AudioBuffer> & decoded_buffer)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());
57 
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();
63 
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()));
70   }
71   DCHECK(initialized());
72 
73   if (!decoded_buffer) {
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();
79     }
80     return false;
81   }
82 
83   const size_t original_frame_count = decoded_buffer->frame_count();
84 
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_);
95   }
96 
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;
101 
102     // If everything would be discarded, indicate a new buffer is required.
103     if (frames_to_discard == decoded_frames) {
104       // For simplicity disallow cases where a buffer with discard padding is
105       // present.  Doing so allows us to avoid complexity around tracking
106       // discards across buffers.
107       DCHECK(current_discard_padding.first == base::TimeDelta());
108       DCHECK(current_discard_padding.second == base::TimeDelta());
109       return false;
110     }
111 
112     decoded_buffer->TrimStart(frames_to_discard);
113   }
114 
115   // Handle front discard padding.
116   if (current_discard_padding.first > base::TimeDelta()) {
117     const size_t decoded_frames = decoded_buffer->frame_count();
118 
119     // If a complete buffer discard is requested and there's no decoder delay,
120     // just discard all remaining frames from this buffer.  With decoder delay
121     // we have to estimate the correct number of frames to discard based on the
122     // duration of the encoded buffer.
123     const size_t start_frames_to_discard =
124         current_discard_padding.first == kInfiniteDuration()
125             ? (decoder_delay_ > 0
126                    ? TimeDeltaToFrames(encoded_buffer->duration())
127                    : decoded_frames)
128             : TimeDeltaToFrames(current_discard_padding.first);
129 
130     // Regardless of the timestamp on the encoded buffer, the corresponding
131     // decoded output will appear |decoder_delay_| frames later.
132     size_t discard_start = decoder_delay_;
133     if (decoder_delay_ > 0) {
134       // If we have a |decoder_delay_| and have already discarded frames from
135       // this buffer, the |discard_start| must be adjusted by the number of
136       // frames already discarded.
137       const size_t frames_discarded_so_far =
138           original_frame_count - decoded_buffer->frame_count();
139       CHECK_LE(frames_discarded_so_far, decoder_delay_);
140       discard_start -= frames_discarded_so_far;
141     }
142 
143     // For simplicity require the start of the discard to be within the current
144     // buffer.  Doing so allows us avoid complexity around tracking discards
145     // across buffers.
146     CHECK_LT(discard_start, decoded_frames);
147 
148     const size_t frames_to_discard =
149         std::min(start_frames_to_discard, decoded_frames - discard_start);
150 
151     // Carry over any frames which need to be discarded from the front of the
152     // next buffer.
153     DCHECK(!discard_frames_);
154     discard_frames_ = start_frames_to_discard - frames_to_discard;
155 
156     // If everything would be discarded, indicate a new buffer is required.
157     if (frames_to_discard == decoded_frames) {
158       // The buffer should not have been marked with end discard if the front
159       // discard removes everything.
160       DCHECK(current_discard_padding.second == base::TimeDelta());
161       return false;
162     }
163 
164     decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard);
165   } else {
166     DCHECK(current_discard_padding.first == base::TimeDelta());
167   }
168 
169   // Handle end discard padding.
170   if (current_discard_padding.second > base::TimeDelta()) {
171     // Limit end discarding to when there is no |decoder_delay_|, otherwise it's
172     // non-trivial determining where to start discarding end frames.
173     CHECK(!decoder_delay_);
174 
175     const size_t decoded_frames = decoded_buffer->frame_count();
176     const size_t end_frames_to_discard =
177         TimeDeltaToFrames(current_discard_padding.second);
178 
179     if (end_frames_to_discard > decoded_frames) {
180       DLOG(ERROR) << "Encountered invalid discard padding value.";
181       return false;
182     }
183 
184     // If everything would be discarded, indicate a new buffer is required.
185     if (end_frames_to_discard == decoded_frames)
186       return false;
187 
188     decoded_buffer->TrimEnd(end_frames_to_discard);
189   } else {
190     DCHECK(current_discard_padding.second == base::TimeDelta());
191   }
192 
193   // Assign timestamp to the buffer.
194   decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
195   timestamp_helper_.AddFrames(decoded_buffer->frame_count());
196   return true;
197 }
198 
199 }  // namespace media
200