Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: media/base/audio_discard_helper.cc

Issue 251893002: Support start trimming post-decoding. Use it with FFmpegDemuxer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698