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