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 DCHECK_GT(sample_rate_, 0); 34} 35 36AudioDiscardHelper::~AudioDiscardHelper() { 37} 38 39size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const { 40 DCHECK(duration >= base::TimeDelta()); 41 return duration.InSecondsF() * sample_rate_ + 0.5; 42} 43 44void 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 52bool 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