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" | 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 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_ = NULL; | |
47 } | 49 } |
48 | 50 |
49 bool AudioDiscardHelper::ProcessBuffers( | 51 bool AudioDiscardHelper::ProcessBuffers( |
50 const scoped_refptr<DecoderBuffer>& encoded_buffer, | 52 const scoped_refptr<DecoderBuffer>& encoded_buffer, |
51 const scoped_refptr<AudioBuffer>& decoded_buffer) { | 53 const scoped_refptr<AudioBuffer>& decoded_buffer) { |
52 DCHECK(!encoded_buffer->end_of_stream()); | 54 DCHECK(!encoded_buffer->end_of_stream()); |
53 DCHECK(encoded_buffer->timestamp() != kNoTimestamp()); | 55 DCHECK(encoded_buffer->timestamp() != kNoTimestamp()); |
54 | 56 |
55 // Issue a debug warning when we see non-monotonic timestamps. Only a warning | 57 // Issue a debug warning when we see non-monotonic timestamps. Only a warning |
56 // to allow chained OGG playback. | 58 // to allow chained OGG playback. |
57 WarnOnNonMonotonicTimestamps(last_input_timestamp_, | 59 WarnOnNonMonotonicTimestamps(last_input_timestamp_, |
58 encoded_buffer->timestamp()); | 60 encoded_buffer->timestamp()); |
59 last_input_timestamp_ = encoded_buffer->timestamp(); | 61 last_input_timestamp_ = encoded_buffer->timestamp(); |
60 | 62 |
61 // If this is the first buffer seen, setup the timestamp helper. | 63 // If this is the first buffer seen, setup the timestamp helper. |
62 if (!initialized()) { | 64 const bool first_buffer = !initialized(); |
65 if (first_buffer) { | |
63 // Clamp the base timestamp to zero. | 66 // Clamp the base timestamp to zero. |
64 timestamp_helper_.SetBaseTimestamp( | 67 timestamp_helper_.SetBaseTimestamp( |
65 std::max(base::TimeDelta(), encoded_buffer->timestamp())); | 68 std::max(base::TimeDelta(), encoded_buffer->timestamp())); |
66 } | 69 } |
67 DCHECK(initialized()); | 70 DCHECK(initialized()); |
68 | 71 |
69 if (!decoded_buffer || !decoded_buffer->frame_count()) | 72 if (!decoded_buffer) { |
73 // If there's a one buffer delay for decoding, we need to save it so it can | |
74 // be processed with the next decoder buffer. | |
75 if (first_buffer) | |
76 delayed_discard_ = encoded_buffer; | |
70 return false; | 77 return false; |
78 } | |
79 | |
80 const size_t original_frame_count = decoded_buffer->frame_count(); | |
81 | |
82 // If there's a one buffer delay for decoding, pick up the last encoded buffer | |
83 // for processing with the current decoded buffer. | |
84 scoped_refptr<DecoderBuffer> current_encoded_buffer = encoded_buffer; | |
85 if (delayed_discard_) | |
86 delayed_discard_.swap(current_encoded_buffer); | |
acolwell GONE FROM CHROMIUM
2014/05/01 01:08:34
Why do you need the whole buffer? It looks like yo
DaleCurtis
2014/05/01 01:26:32
Good point, I'll only store that in the next patch
DaleCurtis
2014/05/01 19:21:27
Done.
| |
71 | 87 |
72 if (discard_frames_ > 0) { | 88 if (discard_frames_ > 0) { |
73 const size_t decoded_frames = decoded_buffer->frame_count(); | 89 const size_t decoded_frames = decoded_buffer->frame_count(); |
74 const size_t frames_to_discard = std::min(discard_frames_, decoded_frames); | 90 const size_t frames_to_discard = std::min(discard_frames_, decoded_frames); |
75 discard_frames_ -= frames_to_discard; | 91 discard_frames_ -= frames_to_discard; |
76 | 92 |
77 // If everything would be discarded, indicate a new buffer is required. | 93 // If everything would be discarded, indicate a new buffer is required. |
78 if (frames_to_discard == decoded_frames) | 94 if (frames_to_discard == decoded_frames) { |
95 // For simplicity disallow cases where a buffer with discard padding is | |
96 // present. Doing so allows us to avoid complexity around tracking | |
97 // discards across buffers. | |
98 DCHECK(current_encoded_buffer->discard_padding().first == | |
99 base::TimeDelta()); | |
100 DCHECK(current_encoded_buffer->discard_padding().second == | |
101 base::TimeDelta()); | |
79 return false; | 102 return false; |
103 } | |
80 | 104 |
81 decoded_buffer->TrimStart(frames_to_discard); | 105 decoded_buffer->TrimStart(frames_to_discard); |
82 } | 106 } |
83 | 107 |
84 // TODO(dalecurtis): Applying the current buffer's discard padding doesn't | 108 // Handle front discard padding. |
85 // make sense in the Vorbis case because there is a delay of one buffer before | 109 if (current_encoded_buffer->discard_padding().first > base::TimeDelta()) { |
86 // decoded buffers are returned. Fix and add support for more than just end | 110 const size_t decoded_frames = decoded_buffer->frame_count(); |
87 // trimming. See http://crbug.com/360961. | 111 const size_t start_frames_to_discard = |
88 if (encoded_buffer->discard_padding() > base::TimeDelta()) { | 112 TimeDeltaToFrames(current_encoded_buffer->discard_padding().first); |
acolwell GONE FROM CHROMIUM
2014/05/01 01:08:34
Why isn't discard padding in frames? You appear to
DaleCurtis
2014/05/01 01:26:32
It's in TimeDelta since the MSE case will be based
acolwell GONE FROM CHROMIUM
2014/05/01 17:44:26
Ok. I see. I don't have a good alternte proposal r
| |
113 | |
114 // Regardless of the timestamp on the encoded buffer, the corresponding | |
115 // decoded output will appear |codec_delay_| frames later. | |
116 size_t discard_start = codec_delay_; | |
acolwell GONE FROM CHROMIUM
2014/05/01 01:08:34
Isn't this baking in an assumption that discard_pa
DaleCurtis
2014/05/01 01:26:32
No, this code should work for start discard paddin
| |
117 if (codec_delay_ > 0) { | |
acolwell GONE FROM CHROMIUM
2014/05/01 17:44:26
I think this should be renamed too because this na
DaleCurtis
2014/05/01 19:21:27
Done.
| |
118 // If we have a |codec_delay_| and have already discarded frames from this | |
119 // buffer, the |discard_start| must be adjusted by the number of frames | |
120 // already discarded. | |
121 const size_t frames_discarded_so_far = | |
122 original_frame_count - decoded_buffer->frame_count(); | |
123 CHECK_LE(frames_discarded_so_far, codec_delay_); | |
124 discard_start -= frames_discarded_so_far; | |
125 } | |
126 | |
127 // For simplicity require the start of the discard to be within the current | |
128 // buffer. Doing so allows us avoid complexity around tracking discards | |
129 // across buffers. | |
130 CHECK_LT(discard_start, decoded_frames); | |
acolwell GONE FROM CHROMIUM
2014/05/01 01:08:34
Does this hold for Opus? I was under the impressio
DaleCurtis
2014/05/01 01:26:32
We may need to discuss this part more, since I'm a
| |
131 | |
132 const size_t frames_to_discard = | |
133 std::min(start_frames_to_discard, decoded_frames - discard_start); | |
134 | |
135 // Carry over any frames which need to be discarded from the front of the | |
136 // next buffer. | |
137 DCHECK(!discard_frames_); | |
138 discard_frames_ = start_frames_to_discard - frames_to_discard; | |
139 | |
140 // If everything would be discarded, indicate a new buffer is required. | |
141 if (frames_to_discard == decoded_frames) { | |
142 // The buffer should not have been marked with end discard if the front | |
143 // discard removes everything. | |
144 DCHECK(current_encoded_buffer->discard_padding().second == | |
145 base::TimeDelta()); | |
146 return false; | |
147 } | |
148 | |
149 decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard); | |
150 } | |
151 | |
152 // Handle end discard padding. | |
153 if (current_encoded_buffer->discard_padding().second > base::TimeDelta()) { | |
154 // Limit end discarding to when there is no |codec_delay_|, otherwise it's | |
155 // non-trivial determining where to start discarding end frames. | |
156 CHECK(!codec_delay_); | |
acolwell GONE FROM CHROMIUM
2014/05/01 01:08:34
Please add a TODO and file a bug for this since I
DaleCurtis
2014/05/01 01:26:32
As far as I can tell this all works fine. See my c
| |
157 | |
89 const size_t decoded_frames = decoded_buffer->frame_count(); | 158 const size_t decoded_frames = decoded_buffer->frame_count(); |
90 const size_t end_frames_to_discard = | 159 const size_t end_frames_to_discard = |
91 TimeDeltaToFrames(encoded_buffer->discard_padding()); | 160 TimeDeltaToFrames(current_encoded_buffer->discard_padding().second); |
161 | |
92 if (end_frames_to_discard > decoded_frames) { | 162 if (end_frames_to_discard > decoded_frames) { |
93 DLOG(ERROR) << "Encountered invalid discard padding value."; | 163 DLOG(ERROR) << "Encountered invalid discard padding value."; |
94 return false; | 164 return false; |
95 } | 165 } |
96 | 166 |
97 // If everything would be discarded, indicate a new buffer is required. | 167 // If everything would be discarded, indicate a new buffer is required. |
98 if (end_frames_to_discard == decoded_frames) | 168 if (end_frames_to_discard == decoded_frames) |
99 return false; | 169 return false; |
100 | 170 |
101 decoded_buffer->TrimEnd(end_frames_to_discard); | 171 decoded_buffer->TrimEnd(end_frames_to_discard); |
102 } else { | 172 } else { |
103 DCHECK(encoded_buffer->discard_padding() == base::TimeDelta()); | 173 DCHECK(current_encoded_buffer->discard_padding().second == |
174 base::TimeDelta()); | |
104 } | 175 } |
105 | 176 |
106 // Assign timestamp to the buffer. | 177 // Assign timestamp to the buffer. |
107 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp()); | 178 decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp()); |
108 timestamp_helper_.AddFrames(decoded_buffer->frame_count()); | 179 timestamp_helper_.AddFrames(decoded_buffer->frame_count()); |
109 return true; | 180 return true; |
110 } | 181 } |
111 | 182 |
112 } // namespace media | 183 } // namespace media |
OLD | NEW |