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

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

Issue 2492953003: media: Delete renderer/demuxer splicing code. (Closed)
Patch Set: Fix/format EsAdapterVideoTest Created 4 years, 1 month 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/base/audio_splicer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "media/base/audio_splicer.h"
6
7 #include <stdint.h>
8 #include <cstdlib>
9 #include <deque>
10 #include <utility>
11
12 #include "base/logging.h"
13 #include "base/macros.h"
14 #include "media/base/audio_buffer.h"
15 #include "media/base/audio_bus.h"
16 #include "media/base/audio_decoder_config.h"
17 #include "media/base/audio_timestamp_helper.h"
18 #include "media/base/media_log.h"
19 #include "media/base/vector_math.h"
20
21 namespace media {
22
23 namespace {
24
25 enum {
26 // Minimum gap size needed before the splicer will take action to
27 // fill a gap. This avoids periodically inserting and then dropping samples
28 // when the buffer timestamps are slightly off because of timestamp rounding
29 // in the source content. Unit is frames.
30 kMinGapSize = 2,
31
32 // Limits the number of MEDIA_LOG() per sanitizer instance warning the user
33 // about splicer overlaps within |kMaxTimeDeltaInMilliseconds| or gaps larger
34 // than |kMinGapSize| and less than |kMaxTimeDeltaInMilliseconds|. These
35 // warnings may be frequent for some streams, and number of sanitizer
36 // instances may be high, so keep this limit low to help reduce log spam.
37 kMaxSanitizerWarningLogs = 5,
38 };
39
40 // AudioBuffer::TrimStart() is not as accurate as the timestamp helper, so
41 // manually adjust the duration and timestamp after trimming.
42 void AccurateTrimStart(int frames_to_trim,
43 const scoped_refptr<AudioBuffer> buffer,
44 const AudioTimestampHelper& timestamp_helper) {
45 buffer->TrimStart(frames_to_trim);
46 buffer->set_timestamp(timestamp_helper.GetTimestamp());
47 }
48
49 // Returns an AudioBus whose frame buffer is backed by the provided AudioBuffer.
50 std::unique_ptr<AudioBus> CreateAudioBufferWrapper(
51 const scoped_refptr<AudioBuffer>& buffer) {
52 std::unique_ptr<AudioBus> wrapper =
53 AudioBus::CreateWrapper(buffer->channel_count());
54 wrapper->set_frames(buffer->frame_count());
55 for (int ch = 0; ch < buffer->channel_count(); ++ch) {
56 wrapper->SetChannelData(
57 ch, reinterpret_cast<float*>(buffer->channel_data()[ch]));
58 }
59 return wrapper;
60 }
61
62 } // namespace
63
64 class AudioStreamSanitizer {
65 public:
66 AudioStreamSanitizer(int samples_per_second,
67 const scoped_refptr<MediaLog>& media_log);
68 ~AudioStreamSanitizer();
69
70 // Resets the sanitizer state by clearing the output buffers queue, and
71 // resetting the timestamp helper.
72 void Reset();
73
74 // Similar to Reset(), but initializes the timestamp helper with the given
75 // parameters.
76 void ResetTimestampState(int64_t frame_count, base::TimeDelta base_timestamp);
77
78 // Adds a new buffer full of samples or end of stream buffer to the splicer.
79 // Returns true if the buffer was accepted. False is returned if an error
80 // occurred.
81 bool AddInput(const scoped_refptr<AudioBuffer>& input);
82
83 // Returns true if the sanitizer has a buffer to return.
84 bool HasNextBuffer() const;
85
86 // Removes the next buffer from the output buffer queue and returns it; should
87 // only be called if HasNextBuffer() returns true.
88 scoped_refptr<AudioBuffer> GetNextBuffer();
89
90 // Returns the total frame count of all buffers available for output.
91 int GetFrameCount() const;
92
93 const AudioTimestampHelper& timestamp_helper() {
94 return output_timestamp_helper_;
95 }
96
97 // Transfer all buffers into |output|. Returns false if AddInput() on the
98 // |output| sanitizer fails for any buffer removed from |this|.
99 bool DrainInto(AudioStreamSanitizer* output);
100
101 private:
102 void AddOutputBuffer(const scoped_refptr<AudioBuffer>& buffer);
103
104 AudioTimestampHelper output_timestamp_helper_;
105 bool received_end_of_stream_ = false;
106
107 typedef std::deque<scoped_refptr<AudioBuffer> > BufferQueue;
108 BufferQueue output_buffers_;
109
110 scoped_refptr<MediaLog> media_log_;
111
112 // To prevent log spam, counts the number of audio gap or overlaps warned in
113 // logs.
114 int num_warning_logs_ = 0;
115
116 DISALLOW_ASSIGN(AudioStreamSanitizer);
117 };
118
119 AudioStreamSanitizer::AudioStreamSanitizer(
120 int samples_per_second,
121 const scoped_refptr<MediaLog>& media_log)
122 : output_timestamp_helper_(samples_per_second), media_log_(media_log) {}
123
124 AudioStreamSanitizer::~AudioStreamSanitizer() {}
125
126 void AudioStreamSanitizer::Reset() {
127 ResetTimestampState(0, kNoTimestamp);
128 }
129
130 void AudioStreamSanitizer::ResetTimestampState(int64_t frame_count,
131 base::TimeDelta base_timestamp) {
132 output_buffers_.clear();
133 received_end_of_stream_ = false;
134 output_timestamp_helper_.SetBaseTimestamp(base_timestamp);
135 if (frame_count > 0)
136 output_timestamp_helper_.AddFrames(frame_count);
137 }
138
139 bool AudioStreamSanitizer::AddInput(const scoped_refptr<AudioBuffer>& input) {
140 DCHECK(!received_end_of_stream_ || input->end_of_stream());
141
142 if (input->end_of_stream()) {
143 output_buffers_.push_back(input);
144 received_end_of_stream_ = true;
145 return true;
146 }
147
148 DCHECK(input->timestamp() != kNoTimestamp);
149 DCHECK(input->duration() > base::TimeDelta());
150 DCHECK_GT(input->frame_count(), 0);
151
152 if (output_timestamp_helper_.base_timestamp() == kNoTimestamp)
153 output_timestamp_helper_.SetBaseTimestamp(input->timestamp());
154
155 if (output_timestamp_helper_.base_timestamp() > input->timestamp()) {
156 MEDIA_LOG(ERROR, media_log_)
157 << "Audio splicing failed: unexpected timestamp sequence. base "
158 "timestamp="
159 << output_timestamp_helper_.base_timestamp().InMicroseconds()
160 << "us, input timestamp=" << input->timestamp().InMicroseconds()
161 << "us";
162 return false;
163 }
164
165 const base::TimeDelta timestamp = input->timestamp();
166 const base::TimeDelta expected_timestamp =
167 output_timestamp_helper_.GetTimestamp();
168 const base::TimeDelta delta = timestamp - expected_timestamp;
169
170 if (std::abs(delta.InMilliseconds()) >
171 AudioSplicer::kMaxTimeDeltaInMilliseconds) {
172 MEDIA_LOG(ERROR, media_log_)
173 << "Audio splicing failed: coded frame timestamp differs from "
174 "expected timestamp " << expected_timestamp.InMicroseconds()
175 << "us by " << delta.InMicroseconds()
176 << "us, more than threshold of +/-"
177 << AudioSplicer::kMaxTimeDeltaInMilliseconds
178 << "ms. Expected timestamp is based on decoded frames and frame rate.";
179 return false;
180 }
181
182 int frames_to_fill = 0;
183 if (!delta.is_zero())
184 frames_to_fill = output_timestamp_helper_.GetFramesToTarget(timestamp);
185
186 if (frames_to_fill == 0 || std::abs(frames_to_fill) < kMinGapSize) {
187 AddOutputBuffer(input);
188 return true;
189 }
190
191 if (frames_to_fill > 0) {
192 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_,
193 kMaxSanitizerWarningLogs)
194 << "Audio splicer inserting silence for small gap of "
195 << delta.InMicroseconds() << "us at time "
196 << expected_timestamp.InMicroseconds() << "us.";
197 DVLOG(1) << "Gap detected @ " << expected_timestamp.InMicroseconds()
198 << " us: " << delta.InMicroseconds() << " us";
199
200 // Create a buffer with enough silence samples to fill the gap and
201 // add it to the output buffer.
202 scoped_refptr<AudioBuffer> gap =
203 AudioBuffer::CreateEmptyBuffer(input->channel_layout(),
204 input->channel_count(),
205 input->sample_rate(),
206 frames_to_fill,
207 expected_timestamp);
208 AddOutputBuffer(gap);
209
210 // Add the input buffer now that the gap has been filled.
211 AddOutputBuffer(input);
212 return true;
213 }
214
215 // Overlapping buffers marked as splice frames are handled by AudioSplicer,
216 // but decoder and demuxer quirks may sometimes produce overlapping samples
217 // which need to be sanitized.
218 //
219 // A crossfade can't be done here because only the current buffer is available
220 // at this point, not previous buffers.
221 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_warning_logs_,
222 kMaxSanitizerWarningLogs)
223 << "Audio splicer skipping frames for small overlap of "
224 << -delta.InMicroseconds() << "us at time "
225 << expected_timestamp.InMicroseconds() << "us.";
226 DVLOG(1) << "Overlap detected @ " << expected_timestamp.InMicroseconds()
227 << " us: " << -delta.InMicroseconds() << " us";
228
229 const int frames_to_skip = -frames_to_fill;
230 if (input->frame_count() <= frames_to_skip) {
231 DVLOG(1) << "Dropping whole buffer";
232 return true;
233 }
234
235 // Copy the trailing samples that do not overlap samples already output
236 // into a new buffer. Add this new buffer to the output queue.
237 //
238 // TODO(acolwell): Implement a cross-fade here so the transition is less
239 // jarring.
240 AccurateTrimStart(frames_to_skip, input, output_timestamp_helper_);
241 AddOutputBuffer(input);
242 return true;
243 }
244
245 bool AudioStreamSanitizer::HasNextBuffer() const {
246 return !output_buffers_.empty();
247 }
248
249 scoped_refptr<AudioBuffer> AudioStreamSanitizer::GetNextBuffer() {
250 scoped_refptr<AudioBuffer> ret = output_buffers_.front();
251 output_buffers_.pop_front();
252 return ret;
253 }
254
255 void AudioStreamSanitizer::AddOutputBuffer(
256 const scoped_refptr<AudioBuffer>& buffer) {
257 output_timestamp_helper_.AddFrames(buffer->frame_count());
258 output_buffers_.push_back(buffer);
259 }
260
261 int AudioStreamSanitizer::GetFrameCount() const {
262 int frame_count = 0;
263 for (const auto& buffer : output_buffers_)
264 frame_count += buffer->frame_count();
265 return frame_count;
266 }
267
268 bool AudioStreamSanitizer::DrainInto(AudioStreamSanitizer* output) {
269 while (HasNextBuffer()) {
270 if (!output->AddInput(GetNextBuffer()))
271 return false;
272 }
273 return true;
274 }
275
276 AudioSplicer::AudioSplicer(int samples_per_second,
277 const scoped_refptr<MediaLog>& media_log)
278 : max_crossfade_duration_(
279 base::TimeDelta::FromMilliseconds(kCrossfadeDurationInMilliseconds)),
280 splice_timestamp_(kNoTimestamp),
281 max_splice_end_timestamp_(kNoTimestamp),
282 output_sanitizer_(
283 new AudioStreamSanitizer(samples_per_second, media_log)),
284 pre_splice_sanitizer_(
285 new AudioStreamSanitizer(samples_per_second, media_log)),
286 post_splice_sanitizer_(
287 new AudioStreamSanitizer(samples_per_second, media_log)),
288 have_all_pre_splice_buffers_(false) {}
289
290 AudioSplicer::~AudioSplicer() {}
291
292 void AudioSplicer::Reset() {
293 output_sanitizer_->Reset();
294 pre_splice_sanitizer_->Reset();
295 post_splice_sanitizer_->Reset();
296 have_all_pre_splice_buffers_ = false;
297 reset_splice_timestamps();
298 }
299
300 bool AudioSplicer::AddInput(const scoped_refptr<AudioBuffer>& input) {
301 // If we're not processing a splice, add the input to the output queue.
302 if (splice_timestamp_ == kNoTimestamp) {
303 DCHECK(!pre_splice_sanitizer_->HasNextBuffer());
304 DCHECK(!post_splice_sanitizer_->HasNextBuffer());
305 return output_sanitizer_->AddInput(input);
306 }
307
308 const AudioTimestampHelper& output_ts_helper =
309 output_sanitizer_->timestamp_helper();
310
311 if (!have_all_pre_splice_buffers_) {
312 DCHECK(!input->end_of_stream());
313
314 // If the provided buffer is entirely before the splice point it can also be
315 // added to the output queue.
316 if (input->timestamp() + input->duration() < splice_timestamp_) {
317 DCHECK(!pre_splice_sanitizer_->HasNextBuffer());
318 return output_sanitizer_->AddInput(input);
319 }
320
321 // If we've encountered the first pre splice buffer, reset the pre splice
322 // sanitizer based on |output_sanitizer_|. This is done so that gaps and
323 // overlaps between buffers across the sanitizers are accounted for prior
324 // to calculating crossfade.
325 if (!pre_splice_sanitizer_->HasNextBuffer()) {
326 pre_splice_sanitizer_->ResetTimestampState(
327 output_ts_helper.frame_count(), output_ts_helper.base_timestamp());
328 }
329
330 return pre_splice_sanitizer_->AddInput(input);
331 }
332
333 // The first post splice buffer is expected to match |splice_timestamp_|.
334 if (!post_splice_sanitizer_->HasNextBuffer())
335 CHECK(splice_timestamp_ == input->timestamp());
336
337 // At this point we have all the fade out preroll buffers from the decoder.
338 // We now need to wait until we have enough data to perform the crossfade (or
339 // we receive an end of stream).
340 if (!post_splice_sanitizer_->AddInput(input))
341 return false;
342
343 // Ensure |output_sanitizer_| has a valid base timestamp so we can use it for
344 // timestamp calculations.
345 if (output_ts_helper.base_timestamp() == kNoTimestamp) {
346 output_sanitizer_->ResetTimestampState(
347 0, pre_splice_sanitizer_->timestamp_helper().base_timestamp());
348 }
349
350 // If a splice frame was incorrectly marked due to poor demuxed timestamps, we
351 // may not actually have a splice. Here we check if any frames exist before
352 // the splice. In this case, just transfer all data to the output sanitizer.
353 const int frames_before_splice =
354 output_ts_helper.base_timestamp() == kNoTimestamp
355 ? 0
356 : output_ts_helper.GetFramesToTarget(splice_timestamp_);
357 if (frames_before_splice < 0 ||
358 pre_splice_sanitizer_->GetFrameCount() <= frames_before_splice) {
359 CHECK(pre_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
360
361 // If the file contains incorrectly muxed timestamps, there may be huge gaps
362 // between the demuxed and decoded timestamps.
363 if (!post_splice_sanitizer_->DrainInto(output_sanitizer_.get()))
364 return false;
365
366 reset_splice_timestamps();
367 return true;
368 }
369
370 // Wait until we have enough data to crossfade or end of stream.
371 if (!input->end_of_stream() &&
372 input->timestamp() + input->duration() < max_splice_end_timestamp_) {
373 return true;
374 }
375
376 scoped_refptr<AudioBuffer> crossfade_buffer;
377 std::unique_ptr<AudioBus> pre_splice =
378 ExtractCrossfadeFromPreSplice(&crossfade_buffer);
379
380 // Crossfade the pre splice and post splice sections and transfer all relevant
381 // buffers into |output_sanitizer_|.
382 CrossfadePostSplice(std::move(pre_splice), crossfade_buffer);
383
384 // Clear the splice timestamp so new splices can be accepted.
385 reset_splice_timestamps();
386 return true;
387 }
388
389 bool AudioSplicer::HasNextBuffer() const {
390 return output_sanitizer_->HasNextBuffer();
391 }
392
393 scoped_refptr<AudioBuffer> AudioSplicer::GetNextBuffer() {
394 return output_sanitizer_->GetNextBuffer();
395 }
396
397 void AudioSplicer::SetSpliceTimestamp(base::TimeDelta splice_timestamp) {
398 if (splice_timestamp == kNoTimestamp) {
399 DCHECK(splice_timestamp_ != kNoTimestamp);
400 DCHECK(!have_all_pre_splice_buffers_);
401 have_all_pre_splice_buffers_ = true;
402 return;
403 }
404
405 if (splice_timestamp_ == splice_timestamp)
406 return;
407
408 // TODO(dalecurtis): We may need the concept of a future_splice_timestamp_ to
409 // handle cases where another splice comes in before we've received 5ms of
410 // data from the last one. Leave this as a CHECK for now to figure out if
411 // this case is possible.
412 CHECK(splice_timestamp_ == kNoTimestamp);
413 splice_timestamp_ = splice_timestamp;
414 max_splice_end_timestamp_ = splice_timestamp_ + max_crossfade_duration_;
415 pre_splice_sanitizer_->Reset();
416 post_splice_sanitizer_->Reset();
417 have_all_pre_splice_buffers_ = false;
418 }
419
420 std::unique_ptr<AudioBus> AudioSplicer::ExtractCrossfadeFromPreSplice(
421 scoped_refptr<AudioBuffer>* crossfade_buffer) {
422 DCHECK(crossfade_buffer);
423 const AudioTimestampHelper& output_ts_helper =
424 output_sanitizer_->timestamp_helper();
425
426 int frames_before_splice =
427 output_ts_helper.GetFramesToTarget(splice_timestamp_);
428
429 // Determine crossfade frame count based on available frames in each splicer
430 // and capping to the maximum crossfade duration.
431 const int max_crossfade_frame_count =
432 output_ts_helper.GetFramesToTarget(max_splice_end_timestamp_) -
433 frames_before_splice;
434 const int frames_to_crossfade = std::min(
435 max_crossfade_frame_count,
436 std::min(pre_splice_sanitizer_->GetFrameCount() - frames_before_splice,
437 post_splice_sanitizer_->GetFrameCount()));
438 // There must always be frames to crossfade, otherwise the splice should not
439 // have been generated.
440 DCHECK_GT(frames_to_crossfade, 0);
441
442 int frames_read = 0;
443 std::unique_ptr<AudioBus> output_bus;
444 while (pre_splice_sanitizer_->HasNextBuffer() &&
445 frames_read < frames_to_crossfade) {
446 scoped_refptr<AudioBuffer> preroll = pre_splice_sanitizer_->GetNextBuffer();
447
448 // We don't know the channel count until we see the first buffer, so wait
449 // until the first buffer to allocate the output AudioBus.
450 if (!output_bus) {
451 output_bus =
452 AudioBus::Create(preroll->channel_count(), frames_to_crossfade);
453 // Allocate output buffer for crossfade.
454 *crossfade_buffer = AudioBuffer::CreateBuffer(kSampleFormatPlanarF32,
455 preroll->channel_layout(),
456 preroll->channel_count(),
457 preroll->sample_rate(),
458 frames_to_crossfade);
459 }
460
461 // There may be enough of a gap introduced during decoding such that an
462 // entire buffer exists before the splice point.
463 if (frames_before_splice >= preroll->frame_count()) {
464 // Adjust the number of frames remaining before the splice. NOTE: This is
465 // safe since |pre_splice_sanitizer_| is a continuation of the timeline in
466 // |output_sanitizer_|. As such we're guaranteed there are no gaps or
467 // overlaps in the timeline between the two sanitizers.
468 frames_before_splice -= preroll->frame_count();
469 CHECK(output_sanitizer_->AddInput(preroll));
470 continue;
471 }
472
473 const int frames_to_read =
474 std::min(preroll->frame_count() - frames_before_splice,
475 output_bus->frames() - frames_read);
476 preroll->ReadFrames(
477 frames_to_read, frames_before_splice, frames_read, output_bus.get());
478 frames_read += frames_to_read;
479
480 // If only part of the buffer was consumed, trim it appropriately and stick
481 // it into the output queue.
482 if (frames_before_splice) {
483 preroll->TrimEnd(preroll->frame_count() - frames_before_splice);
484 CHECK(output_sanitizer_->AddInput(preroll));
485 frames_before_splice = 0;
486 }
487 }
488
489 // Ensure outputs were properly allocated. The method should not have been
490 // called if there is not enough data to crossfade.
491 // TODO(dalecurtis): Convert to DCHECK() once http://crbug.com/356073 fixed.
492 CHECK(output_bus);
493 CHECK(crossfade_buffer->get());
494
495 // All necessary buffers have been processed, it's safe to reset.
496 pre_splice_sanitizer_->Reset();
497 DCHECK_EQ(output_bus->frames(), frames_read);
498 DCHECK_EQ(output_ts_helper.GetFramesToTarget(splice_timestamp_), 0);
499 return output_bus;
500 }
501
502 void AudioSplicer::CrossfadePostSplice(
503 std::unique_ptr<AudioBus> pre_splice_bus,
504 const scoped_refptr<AudioBuffer>& crossfade_buffer) {
505 // Use the calculated timestamp and duration to ensure there's no extra gaps
506 // or overlaps to process when adding the buffer to |output_sanitizer_|.
507 const AudioTimestampHelper& output_ts_helper =
508 output_sanitizer_->timestamp_helper();
509 crossfade_buffer->set_timestamp(output_ts_helper.GetTimestamp());
510
511 // AudioBuffer::ReadFrames() only allows output into an AudioBus, so wrap
512 // our AudioBuffer in one so we can avoid extra data copies.
513 std::unique_ptr<AudioBus> output_bus =
514 CreateAudioBufferWrapper(crossfade_buffer);
515
516 // Extract crossfade section from the |post_splice_sanitizer_|.
517 int frames_read = 0, frames_to_trim = 0;
518 scoped_refptr<AudioBuffer> remainder;
519 while (post_splice_sanitizer_->HasNextBuffer() &&
520 frames_read < output_bus->frames()) {
521 scoped_refptr<AudioBuffer> postroll =
522 post_splice_sanitizer_->GetNextBuffer();
523 const int frames_to_read =
524 std::min(postroll->frame_count(), output_bus->frames() - frames_read);
525 postroll->ReadFrames(frames_to_read, 0, frames_read, output_bus.get());
526 frames_read += frames_to_read;
527
528 // If only part of the buffer was consumed, save it for after we've added
529 // the crossfade buffer
530 if (frames_to_read < postroll->frame_count()) {
531 DCHECK(!remainder.get());
532 remainder.swap(postroll);
533 frames_to_trim = frames_to_read;
534 }
535 }
536
537 DCHECK_EQ(output_bus->frames(), frames_read);
538
539 // Crossfade the audio into |crossfade_buffer|.
540 for (int ch = 0; ch < output_bus->channels(); ++ch) {
541 vector_math::Crossfade(pre_splice_bus->channel(ch),
542 pre_splice_bus->frames(),
543 output_bus->channel(ch));
544 }
545
546 CHECK(output_sanitizer_->AddInput(crossfade_buffer));
547 DCHECK_EQ(crossfade_buffer->frame_count(), output_bus->frames());
548
549 if (remainder.get()) {
550 // Trim off consumed frames.
551 AccurateTrimStart(frames_to_trim, remainder, output_ts_helper);
552 CHECK(output_sanitizer_->AddInput(remainder));
553 }
554
555 // Transfer all remaining buffers out and reset once empty.
556 CHECK(post_splice_sanitizer_->DrainInto(output_sanitizer_.get()));
557 post_splice_sanitizer_->Reset();
558 }
559
560 } // namespace media
OLDNEW
« no previous file with comments | « media/base/audio_splicer.h ('k') | media/base/audio_splicer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698