Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/base/audio_discard_helper.h" | 5 #include "media/base/audio_discard_helper.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "media/base/audio_buffer.h" | 10 #include "media/base/audio_buffer.h" |
| 11 #include "media/base/buffers.h" | 11 #include "media/base/buffers.h" |
| 12 #include "media/base/decoder_buffer.h" | |
| 13 | 12 |
| 14 namespace media { | 13 namespace media { |
| 15 | 14 |
| 16 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp, | 15 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp, |
| 17 base::TimeDelta current_timestamp) { | 16 base::TimeDelta current_timestamp) { |
| 18 if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp) | 17 if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp) |
| 19 return; | 18 return; |
| 20 | 19 |
| 21 const base::TimeDelta diff = current_timestamp - last_timestamp; | 20 const base::TimeDelta diff = current_timestamp - last_timestamp; |
| 22 DLOG(WARNING) << "Input timestamps are not monotonically increasing! " | 21 DLOG(WARNING) << "Input timestamps are not monotonically increasing! " |
| 23 << " ts " << current_timestamp.InMicroseconds() << " us" | 22 << " ts " << current_timestamp.InMicroseconds() << " us" |
| 24 << " diff " << diff.InMicroseconds() << " us"; | 23 << " diff " << diff.InMicroseconds() << " us"; |
| 25 } | 24 } |
| 26 | 25 |
| 27 AudioDiscardHelper::AudioDiscardHelper(int sample_rate) | 26 AudioDiscardHelper::AudioDiscardHelper(int sample_rate, size_t decoder_delay) |
| 28 : sample_rate_(sample_rate), | 27 : sample_rate_(sample_rate), |
| 28 decoder_delay_(decoder_delay), | |
| 29 timestamp_helper_(sample_rate_), | 29 timestamp_helper_(sample_rate_), |
| 30 discard_frames_(0), | 30 discard_frames_(0), |
| 31 last_input_timestamp_(kNoTimestamp()) { | 31 last_input_timestamp_(kNoTimestamp()), |
| 32 delayed_discard_(false) { | |
| 32 DCHECK_GT(sample_rate_, 0); | 33 DCHECK_GT(sample_rate_, 0); |
| 33 } | 34 } |
| 34 | 35 |
| 35 AudioDiscardHelper::~AudioDiscardHelper() { | 36 AudioDiscardHelper::~AudioDiscardHelper() { |
| 36 } | 37 } |
| 37 | 38 |
| 38 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const { | 39 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const { |
| 39 DCHECK(duration >= base::TimeDelta()); | 40 DCHECK(duration >= base::TimeDelta()); |
| 40 return duration.InSecondsF() * sample_rate_ + 0.5; | 41 return duration.InSecondsF() * sample_rate_ + 0.5; |
| 41 } | 42 } |
| 42 | 43 |
| 43 void AudioDiscardHelper::Reset(size_t initial_discard) { | 44 void AudioDiscardHelper::Reset(size_t initial_discard) { |
| 44 discard_frames_ = initial_discard; | 45 discard_frames_ = initial_discard; |
| 45 last_input_timestamp_ = kNoTimestamp(); | 46 last_input_timestamp_ = kNoTimestamp(); |
| 46 timestamp_helper_.SetBaseTimestamp(kNoTimestamp()); | 47 timestamp_helper_.SetBaseTimestamp(kNoTimestamp()); |
| 48 delayed_discard_ = false; | |
| 49 delayed_discard_padding_ = DecoderBuffer::DiscardPadding(); | |
| 47 } | 50 } |
| 48 | 51 |
| 49 bool AudioDiscardHelper::ProcessBuffers( | 52 bool AudioDiscardHelper::ProcessBuffers( |
| 50 const scoped_refptr<DecoderBuffer>& encoded_buffer, | 53 const scoped_refptr<DecoderBuffer>& encoded_buffer, |
| 51 const scoped_refptr<AudioBuffer>& decoded_buffer) { | 54 const scoped_refptr<AudioBuffer>& decoded_buffer) { |
| 52 DCHECK(!encoded_buffer->end_of_stream()); | 55 DCHECK(!encoded_buffer->end_of_stream()); |
| 53 DCHECK(encoded_buffer->timestamp() != kNoTimestamp()); | 56 DCHECK(encoded_buffer->timestamp() != kNoTimestamp()); |
| 54 | 57 |
| 55 // Issue a debug warning when we see non-monotonic timestamps. Only a warning | 58 // Issue a debug warning when we see non-monotonic timestamps. Only a warning |
| 56 // to allow chained OGG playback. | 59 // to allow chained OGG playback. |
| 57 WarnOnNonMonotonicTimestamps(last_input_timestamp_, | 60 WarnOnNonMonotonicTimestamps(last_input_timestamp_, |
| 58 encoded_buffer->timestamp()); | 61 encoded_buffer->timestamp()); |
| 59 last_input_timestamp_ = encoded_buffer->timestamp(); | 62 last_input_timestamp_ = encoded_buffer->timestamp(); |
| 60 | 63 |
| 61 // If this is the first buffer seen, setup the timestamp helper. | 64 // If this is the first buffer seen, setup the timestamp helper. |
| 62 if (!initialized()) { | 65 const bool first_buffer = !initialized(); |
| 66 if (first_buffer) { | |
| 63 // Clamp the base timestamp to zero. | 67 // Clamp the base timestamp to zero. |
| 64 timestamp_helper_.SetBaseTimestamp( | 68 timestamp_helper_.SetBaseTimestamp( |
| 65 std::max(base::TimeDelta(), encoded_buffer->timestamp())); | 69 std::max(base::TimeDelta(), encoded_buffer->timestamp())); |
| 66 } | 70 } |
| 67 DCHECK(initialized()); | 71 DCHECK(initialized()); |
| 68 | 72 |
| 69 if (!decoded_buffer || !decoded_buffer->frame_count()) | 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 } | |
| 70 return false; | 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 std::swap(current_discard_padding, delayed_discard_padding_); | |
| 71 | 91 |
| 72 if (discard_frames_ > 0) { | 92 if (discard_frames_ > 0) { |
| 73 const size_t decoded_frames = decoded_buffer->frame_count(); | 93 const size_t decoded_frames = decoded_buffer->frame_count(); |
| 74 const size_t frames_to_discard = std::min(discard_frames_, decoded_frames); | 94 const size_t frames_to_discard = std::min(discard_frames_, decoded_frames); |
| 75 discard_frames_ -= frames_to_discard; | 95 discard_frames_ -= frames_to_discard; |
| 76 | 96 |
| 77 // If everything would be discarded, indicate a new buffer is required. | 97 // If everything would be discarded, indicate a new buffer is required. |
| 78 if (frames_to_discard == decoded_frames) | 98 if (frames_to_discard == decoded_frames) { |
| 99 // For simplicity disallow cases where a buffer with discard padding is | |
|
wolenetz
2014/05/05 19:01:23
nit: how common is this? should we track it in rel
DaleCurtis
2014/05/05 20:16:03
It shouldn't be possible right now, so the DCHECK(
| |
| 100 // present. Doing so allows us to avoid complexity around tracking | |
| 101 // discards across buffers. | |
| 102 DCHECK(current_discard_padding.first == base::TimeDelta()); | |
| 103 DCHECK(current_discard_padding.second == base::TimeDelta()); | |
| 79 return false; | 104 return false; |
| 105 } | |
| 80 | 106 |
| 81 decoded_buffer->TrimStart(frames_to_discard); | 107 decoded_buffer->TrimStart(frames_to_discard); |
| 82 } | 108 } |
| 83 | 109 |
| 84 // TODO(dalecurtis): Applying the current buffer's discard padding doesn't | 110 // Handle front discard padding. |
| 85 // make sense in the Vorbis case because there is a delay of one buffer before | 111 if (current_discard_padding.first > base::TimeDelta()) { |
| 86 // decoded buffers are returned. Fix and add support for more than just end | 112 const size_t decoded_frames = decoded_buffer->frame_count(); |
| 87 // trimming. See http://crbug.com/360961. | 113 const size_t start_frames_to_discard = |
| 88 if (encoded_buffer->discard_padding() > base::TimeDelta()) { | 114 TimeDeltaToFrames(current_discard_padding.first); |
| 115 | |
| 116 // Regardless of the timestamp on the encoded buffer, the corresponding | |
| 117 // decoded output will appear |decoder_delay_| frames later. | |
| 118 size_t discard_start = decoder_delay_; | |
| 119 if (decoder_delay_ > 0) { | |
| 120 // If we have a |decoder_delay_| and have already discarded frames from | |
| 121 // this buffer, the |discard_start| must be adjusted by the number of | |
| 122 // frames already discarded. | |
| 123 const size_t frames_discarded_so_far = | |
| 124 original_frame_count - decoded_buffer->frame_count(); | |
| 125 CHECK_LE(frames_discarded_so_far, decoder_delay_); | |
| 126 discard_start -= frames_discarded_so_far; | |
| 127 } | |
| 128 | |
| 129 // For simplicity require the start of the discard to be within the current | |
| 130 // buffer. Doing so allows us avoid complexity around tracking discards | |
| 131 // across buffers. | |
| 132 CHECK_LT(discard_start, decoded_frames); | |
| 133 | |
| 134 const size_t frames_to_discard = | |
| 135 std::min(start_frames_to_discard, decoded_frames - discard_start); | |
| 136 | |
| 137 // Carry over any frames which need to be discarded from the front of the | |
| 138 // next buffer. | |
| 139 DCHECK(!discard_frames_); | |
| 140 discard_frames_ = start_frames_to_discard - frames_to_discard; | |
| 141 | |
| 142 // If everything would be discarded, indicate a new buffer is required. | |
| 143 if (frames_to_discard == decoded_frames) { | |
| 144 // The buffer should not have been marked with end discard if the front | |
| 145 // discard removes everything. | |
| 146 DCHECK(current_discard_padding.second == base::TimeDelta()); | |
| 147 return false; | |
| 148 } | |
| 149 | |
| 150 decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard); | |
| 151 } else { | |
| 152 DCHECK(current_discard_padding.first == base::TimeDelta()); | |
| 153 } | |
| 154 | |
| 155 // Handle end discard padding. | |
| 156 if (current_discard_padding.second > base::TimeDelta()) { | |
| 157 // Limit end discarding to when there is no |decoder_delay_|, otherwise it's | |
| 158 // non-trivial determining where to start discarding end frames. | |
| 159 CHECK(!decoder_delay_); | |
| 160 | |
| 89 const size_t decoded_frames = decoded_buffer->frame_count(); | 161 const size_t decoded_frames = decoded_buffer->frame_count(); |
| 90 const size_t end_frames_to_discard = | 162 const size_t end_frames_to_discard = |
| 91 TimeDeltaToFrames(encoded_buffer->discard_padding()); | 163 TimeDeltaToFrames(current_discard_padding.second); |
| 164 | |
| 92 if (end_frames_to_discard > decoded_frames) { | 165 if (end_frames_to_discard > decoded_frames) { |
| 93 DLOG(ERROR) << "Encountered invalid discard padding value."; | 166 DLOG(ERROR) << "Encountered invalid discard padding value."; |
| 94 return false; | 167 return false; |
| 95 } | 168 } |
| 96 | 169 |
| 97 // If everything would be discarded, indicate a new buffer is required. | 170 // If everything would be discarded, indicate a new buffer is required. |
| 98 if (end_frames_to_discard == decoded_frames) | 171 if (end_frames_to_discard == decoded_frames) |
| 99 return false; | 172 return false; |
| 100 | 173 |
| 101 decoded_buffer->TrimEnd(end_frames_to_discard); | 174 decoded_buffer->TrimEnd(end_frames_to_discard); |
| 102 } else { | 175 } else { |
| 103 DCHECK(encoded_buffer->discard_padding() == base::TimeDelta()); | 176 DCHECK(current_discard_padding.second == base::TimeDelta()); |
| 104 } | 177 } |
| 105 | 178 |
| 106 // Assign timestamp to the buffer. | 179 // Assign timestamp to the buffer. |
| 107 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp()); | 180 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp()); |
| 108 timestamp_helper_.AddFrames(decoded_buffer->frame_count()); | 181 timestamp_helper_.AddFrames(decoded_buffer->frame_count()); |
| 109 return true; | 182 return true; |
| 110 } | 183 } |
| 111 | 184 |
| 112 } // namespace media | 185 } // namespace media |
| OLD | NEW |