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 13namespace media { 14 15static 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 26AudioDiscardHelper::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 delayed_end_discard_(0) { 34 DCHECK_GT(sample_rate_, 0); 35} 36 37AudioDiscardHelper::~AudioDiscardHelper() { 38} 39 40size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const { 41 DCHECK(duration >= base::TimeDelta()); 42 return duration.InSecondsF() * sample_rate_ + 0.5; 43} 44 45void 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(); 51} 52 53bool 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()); 58 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(); 64 65 // If this is the first buffer seen, setup the timestamp helper. 66 const bool first_buffer = !initialized(); 67 if (first_buffer) { 68 // Clamp the base timestamp to zero. 69 timestamp_helper_.SetBaseTimestamp( 70 std::max(base::TimeDelta(), encoded_buffer->timestamp())); 71 } 72 DCHECK(initialized()); 73 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. 77 if (first_buffer) { 78 delayed_discard_ = true; 79 delayed_discard_padding_ = encoded_buffer->discard_padding(); 80 } 81 return false; 82 } 83 84 const size_t original_frame_count = decoded_buffer->frame_count(); 85 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_); 96 } 97 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; 102 103 DVLOG(1) << "Initial discard of " << frames_to_discard << " out of " 104 << decoded_frames << " frames."; 105 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()); 113 return false; 114 } 115 116 decoded_buffer->TrimStart(frames_to_discard); 117 } 118 119 // Process any delayed end discard from the previous buffer. 120 if (delayed_end_discard_ > 0) { 121 DCHECK_GT(decoder_delay_, 0u); 122 123 const size_t discard_index = decoder_delay_ - delayed_end_discard_; 124 DCHECK_LT(discard_index, decoder_delay_); 125 126 const size_t decoded_frames = decoded_buffer->frame_count(); 127 DCHECK_LT(delayed_end_discard_, decoded_frames); 128 129 DVLOG(1) << "Delayed end discard of " << delayed_end_discard_ << " out of " 130 << decoded_frames << " frames starting at " << discard_index; 131 132 decoded_buffer->TrimRange(discard_index, 133 discard_index + delayed_end_discard_); 134 delayed_end_discard_ = 0; 135 } 136 137 // Handle front discard padding. 138 if (current_discard_padding.first > base::TimeDelta()) { 139 const size_t decoded_frames = decoded_buffer->frame_count(); 140 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()) 149 : decoded_frames) 150 : TimeDeltaToFrames(current_discard_padding.first); 151 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; 163 } 164 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 167 // across buffers. 168 CHECK_LT(discard_start, decoded_frames); 169 170 const size_t frames_to_discard = 171 std::min(start_frames_to_discard, decoded_frames - discard_start); 172 173 // Carry over any frames which need to be discarded from the front of the 174 // next buffer. 175 DCHECK(!discard_frames_); 176 discard_frames_ = start_frames_to_discard - frames_to_discard; 177 178 DVLOG(1) << "Front discard of " << frames_to_discard << " out of " 179 << decoded_frames << " frames starting at " << discard_start; 180 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()); 186 return false; 187 } 188 189 decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard); 190 } else { 191 DCHECK(current_discard_padding.first == base::TimeDelta()); 192 } 193 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); 199 200 if (decoder_delay_) { 201 // Delayed end discard only works if the decoder delay is less than a 202 // single buffer. 203 DCHECK_LT(decoder_delay_, original_frame_count); 204 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_; 211 } else { 212 DCHECK(!delayed_end_discard_); 213 std::swap(delayed_end_discard_, end_frames_to_discard); 214 } 215 } 216 217 if (end_frames_to_discard > decoded_frames) { 218 DLOG(ERROR) << "Encountered invalid discard padding value."; 219 return false; 220 } 221 222 if (end_frames_to_discard > 0) { 223 DVLOG(1) << "End discard of " << end_frames_to_discard << " out of " 224 << decoded_frames; 225 226 // If everything would be discarded, indicate a new buffer is required. 227 if (end_frames_to_discard == decoded_frames) 228 return false; 229 230 decoded_buffer->TrimEnd(end_frames_to_discard); 231 } 232 } else { 233 DCHECK(current_discard_padding.second == base::TimeDelta()); 234 } 235 236 // Assign timestamp to the buffer. 237 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp()); 238 timestamp_helper_.AddFrames(decoded_buffer->frame_count()); 239 return true; 240} 241 242} // namespace media 243