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

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

Issue 1229123002: Log audio splice sanitizer warnings and errors to media-internals (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 5 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
« no previous file with comments | « media/base/audio_splicer.h ('k') | media/renderers/audio_renderer_impl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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_splicer.h" 5 #include "media/base/audio_splicer.h"
6 6
7 #include <cstdlib> 7 #include <cstdlib>
8 #include <deque> 8 #include <deque>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "media/base/audio_buffer.h" 11 #include "media/base/audio_buffer.h"
12 #include "media/base/audio_bus.h" 12 #include "media/base/audio_bus.h"
13 #include "media/base/audio_decoder_config.h" 13 #include "media/base/audio_decoder_config.h"
14 #include "media/base/audio_timestamp_helper.h" 14 #include "media/base/audio_timestamp_helper.h"
15 #include "media/base/media_log.h"
15 #include "media/base/vector_math.h" 16 #include "media/base/vector_math.h"
16 17
17 namespace media { 18 namespace media {
18 19
19 // Minimum gap size needed before the splicer will take action to 20 // Minimum gap size needed before the splicer will take action to
20 // fill a gap. This avoids periodically inserting and then dropping samples 21 // fill a gap. This avoids periodically inserting and then dropping samples
21 // when the buffer timestamps are slightly off because of timestamp rounding 22 // when the buffer timestamps are slightly off because of timestamp rounding
22 // in the source content. Unit is frames. 23 // in the source content. Unit is frames.
23 static const int kMinGapSize = 2; 24 static const int kMinGapSize = 2;
24 25
26 // Limits the number of MEDIA_LOG() per sanitizer instance warning the user
27 // about splicer overlaps within |kMaxTimeDeltaInMilliseconds| or gaps larger
28 // than |kMinGapSize| and less than |kMaxTimeDeltaInMilliseconds|. These
29 // warnings may be frequent for some streams, and number of sanitizer instances
30 // may be high, so keep this limit low to help reduce log spam.
31 static const int kMaxSanitizerWarningLogs = 5;
xhwang 2015/07/09 21:30:59 nit: here and on line 24, not need to use "static"
wolenetz 2015/07/09 22:45:00 Good point. Further, to prevent (and give build er
xhwang 2015/07/09 22:59:36 The last time I looked at this, there's pretty muc
32
25 // AudioBuffer::TrimStart() is not as accurate as the timestamp helper, so 33 // AudioBuffer::TrimStart() is not as accurate as the timestamp helper, so
26 // manually adjust the duration and timestamp after trimming. 34 // manually adjust the duration and timestamp after trimming.
27 static void AccurateTrimStart(int frames_to_trim, 35 static void AccurateTrimStart(int frames_to_trim,
28 const scoped_refptr<AudioBuffer> buffer, 36 const scoped_refptr<AudioBuffer> buffer,
29 const AudioTimestampHelper& timestamp_helper) { 37 const AudioTimestampHelper& timestamp_helper) {
30 buffer->TrimStart(frames_to_trim); 38 buffer->TrimStart(frames_to_trim);
31 buffer->set_timestamp(timestamp_helper.GetTimestamp()); 39 buffer->set_timestamp(timestamp_helper.GetTimestamp());
32 } 40 }
33 41
34 // Returns an AudioBus whose frame buffer is backed by the provided AudioBuffer. 42 // Returns an AudioBus whose frame buffer is backed by the provided AudioBuffer.
35 static scoped_ptr<AudioBus> CreateAudioBufferWrapper( 43 static scoped_ptr<AudioBus> CreateAudioBufferWrapper(
36 const scoped_refptr<AudioBuffer>& buffer) { 44 const scoped_refptr<AudioBuffer>& buffer) {
37 scoped_ptr<AudioBus> wrapper = 45 scoped_ptr<AudioBus> wrapper =
38 AudioBus::CreateWrapper(buffer->channel_count()); 46 AudioBus::CreateWrapper(buffer->channel_count());
39 wrapper->set_frames(buffer->frame_count()); 47 wrapper->set_frames(buffer->frame_count());
40 for (int ch = 0; ch < buffer->channel_count(); ++ch) { 48 for (int ch = 0; ch < buffer->channel_count(); ++ch) {
41 wrapper->SetChannelData( 49 wrapper->SetChannelData(
42 ch, reinterpret_cast<float*>(buffer->channel_data()[ch])); 50 ch, reinterpret_cast<float*>(buffer->channel_data()[ch]));
43 } 51 }
44 return wrapper.Pass(); 52 return wrapper.Pass();
45 } 53 }
46 54
47 class AudioStreamSanitizer { 55 class AudioStreamSanitizer {
48 public: 56 public:
49 explicit AudioStreamSanitizer(int samples_per_second); 57 AudioStreamSanitizer(int samples_per_second,
58 const scoped_refptr<MediaLog>& media_log);
50 ~AudioStreamSanitizer(); 59 ~AudioStreamSanitizer();
51 60
52 // Resets the sanitizer state by clearing the output buffers queue, and 61 // Resets the sanitizer state by clearing the output buffers queue, and
53 // resetting the timestamp helper. 62 // resetting the timestamp helper.
54 void Reset(); 63 void Reset();
55 64
56 // Similar to Reset(), but initializes the timestamp helper with the given 65 // Similar to Reset(), but initializes the timestamp helper with the given
57 // parameters. 66 // parameters.
58 void ResetTimestampState(int64 frame_count, base::TimeDelta base_timestamp); 67 void ResetTimestampState(int64 frame_count, base::TimeDelta base_timestamp);
59 68
(...skipping 22 matching lines...) Expand all
82 91
83 private: 92 private:
84 void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer); 93 void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer);
85 94
86 AudioTimestampHelper output_timestamp_helper_; 95 AudioTimestampHelper output_timestamp_helper_;
87 bool received_end_of_stream_; 96 bool received_end_of_stream_;
88 97
89 typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue; 98 typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue;
90 BufferQueue output_buffers_; 99 BufferQueue output_buffers_;
91 100
101 scoped_refptr<MediaLog> media_log_;
102
103 // To prevent log spam, counts the number of audio gap or overlaps warned in
104 // logs.
105 int num_warning_logs_;
106
92 DISALLOW_ASSIGN(AudioStreamSanitizer); 107 DISALLOW_ASSIGN(AudioStreamSanitizer);
93 }; 108 };
94 109
95 AudioStreamSanitizer::AudioStreamSanitizer(int samples_per_second) 110 AudioStreamSanitizer::AudioStreamSanitizer(
111 int samples_per_second,
112 const scoped_refptr<MediaLog>& media_log)
96 : output_timestamp_helper_(samples_per_second), 113 : output_timestamp_helper_(samples_per_second),
97 received_end_of_stream_(false) {} 114 received_end_of_stream_(false),
115 media_log_(media_log),
116 num_warning_logs_(0) {
117 }
98 118
99 AudioStreamSanitizer::~AudioStreamSanitizer() {} 119 AudioStreamSanitizer::~AudioStreamSanitizer() {}
100 120
101 void AudioStreamSanitizer::Reset() { 121 void AudioStreamSanitizer::Reset() {
102 ResetTimestampState(0, kNoTimestamp()); 122 ResetTimestampState(0, kNoTimestamp());
103 } 123 }
104 124
105 void AudioStreamSanitizer::ResetTimestampState(int64 frame_count, 125 void AudioStreamSanitizer::ResetTimestampState(int64 frame_count,
106 base::TimeDelta base_timestamp) { 126 base::TimeDelta base_timestamp) {
107 output_buffers_.clear(); 127 output_buffers_.clear();
(...skipping 13 matching lines...) Expand all
121 } 141 }
122 142
123 DCHECK(input->timestamp() != kNoTimestamp()); 143 DCHECK(input->timestamp() != kNoTimestamp());
124 DCHECK(input->duration() > base::TimeDelta()); 144 DCHECK(input->duration() > base::TimeDelta());
125 DCHECK_GT(input->frame_count(), 0); 145 DCHECK_GT(input->frame_count(), 0);
126 146
127 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp()) 147 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp())
128 output_timestamp_helper_.SetBaseTimestamp(input->timestamp()); 148 output_timestamp_helper_.SetBaseTimestamp(input->timestamp());
129 149
130 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) { 150 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) {
131 DVLOG(1) << "Input timestamp is before the base timestamp."; 151 MEDIA_LOG(ERROR, media_log_)
152 << "Audio splicing failed: unexpected timestamp sequence. base "
153 "timestamp="
154 << output_timestamp_helper_.base_timestamp().InMicroseconds()
155 << "us, input timestamp=" << input->timestamp().InMicroseconds()
156 << "us";
xhwang 2015/07/09 21:30:59 Is it necessary to use "us" in the last part? I fo
wolenetz 2015/07/09 22:45:00 That high time timestamp (~15 days) is not a commo
132 return false; 157 return false;
133 } 158 }
134 159
135 const base::TimeDelta timestamp = input->timestamp(); 160 const base::TimeDelta timestamp = input->timestamp();
136 const base::TimeDelta expected_timestamp = 161 const base::TimeDelta expected_timestamp =
137 output_timestamp_helper_.GetTimestamp(); 162 output_timestamp_helper_.GetTimestamp();
138 const base::TimeDelta delta = timestamp - expected_timestamp; 163 const base::TimeDelta delta = timestamp - expected_timestamp;
139 164
140 if (std::abs(delta.InMilliseconds()) > 165 if (std::abs(delta.InMilliseconds()) >
141 AudioSplicer::kMaxTimeDeltaInMilliseconds) { 166 AudioSplicer::kMaxTimeDeltaInMilliseconds) {
142 DVLOG(1) << "Timestamp delta too large: " << delta.InMicroseconds() << "us"; 167 MEDIA_LOG(ERROR, media_log_)
168 << "Audio splicing failed: coded frame timestamp differs from "
169 "expected timestamp " << expected_timestamp.InMicroseconds()
170 << "us by " << delta.InMicroseconds()
171 << "us, more than threshold of +/-"
172 << AudioSplicer::kMaxTimeDeltaInMilliseconds
173 << "ms. Expected timestamp is based on decoded frames and frame rate.";
143 return false; 174 return false;
144 } 175 }
145 176
146 int frames_to_fill = 0; 177 int frames_to_fill = 0;
147 if (delta != base::TimeDelta()) 178 if (delta != base::TimeDelta())
148 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp); 179 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp);
149 180
150 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) { 181 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) {
151 AddOutputBuffer(input); 182 AddOutputBuffer(input);
152 return true; 183 return true;
153 } 184 }
154 185
155 if (frames_to_fill > 0) { 186 if (frames_to_fill > 0) {
187 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_,
188 kMaxSanitizerWarningLogs)
189 << "Audio splicer inserting silence for small gap of "
190 << delta.InMicroseconds() << "us at time "
191 << expected_timestamp.InMicroseconds() << "us.";
156 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds() 192 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
wolenetz 2015/07/09 19:28:19 I'm keeping the warnings DVLOGged for now, since t
157 << " us: " << delta.InMicroseconds() << " us"; 193 << " us: " << delta.InMicroseconds() << " us";
158 194
159 // Create a buffer with enough silence samples to fill the gap and 195 // Create a buffer with enough silence samples to fill the gap and
160 // add it to the output buffer. 196 // add it to the output buffer.
161 scoped_refptr<AudioBuffer> gap = 197 scoped_refptr<AudioBuffer> gap =
162 AudioBuffer::CreateEmptyBuffer(input->channel_layout(), 198 AudioBuffer::CreateEmptyBuffer(input->channel_layout(),
163 input->channel_count(), 199 input->channel_count(),
164 input->sample_rate(), 200 input->sample_rate(),
165 frames_to_fill, 201 frames_to_fill,
166 expected_timestamp); 202 expected_timestamp);
167 AddOutputBuffer(gap); 203 AddOutputBuffer(gap);
168 204
169 // Add the input buffer now that the gap has been filled. 205 // Add the input buffer now that the gap has been filled.
170 AddOutputBuffer(input); 206 AddOutputBuffer(input);
171 return true; 207 return true;
172 } 208 }
173 209
174 // Overlapping buffers marked as splice frames are handled by AudioSplicer, 210 // Overlapping buffers marked as splice frames are handled by AudioSplicer,
175 // but decoder and demuxer quirks may sometimes produce overlapping samples 211 // but decoder and demuxer quirks may sometimes produce overlapping samples
176 // which need to be sanitized. 212 // which need to be sanitized.
177 // 213 //
178 // A crossfade can't be done here because only the current buffer is available 214 // A crossfade can't be done here because only the current buffer is available
179 // at this point, not previous buffers. 215 // at this point, not previous buffers.
216 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_,
217 kMaxSanitizerWarningLogs)
218 << "Audio splicer skipping frames for small overlap of "
219 << -delta.InMicroseconds() << "us at time "
220 << expected_timestamp.InMicroseconds() << "us.";
180 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds() 221 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
181 << " us: " << -delta.InMicroseconds() << " us"; 222 << " us: " << -delta.InMicroseconds() << " us";
182 223
183 const int frames_to_skip = -frames_to_fill; 224 const int frames_to_skip = -frames_to_fill;
184 if (input->frame_count() <= frames_to_skip) { 225 if (input->frame_count() <= frames_to_skip) {
185 DVLOG(1) << "Dropping whole buffer"; 226 DVLOG(1) << "Dropping whole buffer";
186 return true; 227 return true;
187 } 228 }
188 229
189 // Copy the trailing samples that do not overlap samples already output 230 // Copy the trailing samples that do not overlap samples already output
(...skipping 30 matching lines...) Expand all
220 } 261 }
221 262
222 bool AudioStreamSanitizer::DrainInto(AudioStreamSanitizer* output) { 263 bool AudioStreamSanitizer::DrainInto(AudioStreamSanitizer* output) {
223 while (HasNextBuffer()) { 264 while (HasNextBuffer()) {
224 if (!output->AddInput(GetNextBuffer())) 265 if (!output->AddInput(GetNextBuffer()))
225 return false; 266 return false;
226 } 267 }
227 return true; 268 return true;
228 } 269 }
229 270
230 AudioSplicer::AudioSplicer(int samples_per_second) 271 AudioSplicer::AudioSplicer(int samples_per_second,
272 const scoped_refptr<MediaLog>& media_log)
231 : max_crossfade_duration_( 273 : max_crossfade_duration_(
232 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)), 274 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)),
233 splice_timestamp_(kNoTimestamp()), 275 splice_timestamp_(kNoTimestamp()),
234 max_splice_end_timestamp_(kNoTimestamp()), 276 max_splice_end_timestamp_(kNoTimestamp()),
235 output_sanitizer_(new AudioStreamSanitizer(samples_per_second)), 277 output_sanitizer_(
236 pre_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)), 278 new AudioStreamSanitizer(samples_per_second, media_log)),
237 post_splice_sanitizer_(new AudioStreamSanitizer(samples_per_second)), 279 pre_splice_sanitizer_(
238 have_all_pre_splice_buffers_(false) {} 280 new AudioStreamSanitizer(samples_per_second, media_log)),
281 post_splice_sanitizer_(
282 new AudioStreamSanitizer(samples_per_second, media_log)),
283 have_all_pre_splice_buffers_(false) {
xhwang 2015/07/09 21:30:59 How often do these logs happen in normal cases? Ar
wolenetz 2015/07/09 22:45:00 If content is muxed correctly, and if the splicer
284 }
239 285
240 AudioSplicer::~AudioSplicer() {} 286 AudioSplicer::~AudioSplicer() {}
241 287
242 void AudioSplicer::Reset() { 288 void AudioSplicer::Reset() {
243 output_sanitizer_->Reset(); 289 output_sanitizer_->Reset();
244 pre_splice_sanitizer_->Reset(); 290 pre_splice_sanitizer_->Reset();
245 post_splice_sanitizer_->Reset(); 291 post_splice_sanitizer_->Reset();
246 have_all_pre_splice_buffers_ = false; 292 have_all_pre_splice_buffers_ = false;
247 reset_splice_timestamps(); 293 reset_splice_timestamps();
248 } 294 }
(...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 AccurateTrimStart(frames_to_trim, remainder, output_ts_helper); 544 AccurateTrimStart(frames_to_trim, remainder, output_ts_helper);
499 CHECK(output_sanitizer_->AddInput(remainder)); 545 CHECK(output_sanitizer_->AddInput(remainder));
500 } 546 }
501 547
502 // Transfer all remaining buffers out and reset once empty. 548 // Transfer all remaining buffers out and reset once empty.
503 CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get())); 549 CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
504 post_splice_sanitizer_->Reset(); 550 post_splice_sanitizer_->Reset();
505 } 551 }
506 552
507 } // namespace media 553 } // namespace media
OLDNEW
« no previous file with comments | « media/base/audio_splicer.h ('k') | media/renderers/audio_renderer_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698