summaryrefslogtreecommitdiffstats
path: root/media/base/audio_discard_helper.cc
blob: 3088130b00c48ca9f3c7966a4c45eac53e4ca87c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/base/audio_discard_helper.h"

#include <algorithm>

#include "base/logging.h"
#include "media/base/audio_buffer.h"
#include "media/base/buffers.h"
#include "media/base/decoder_buffer.h"

namespace media {

static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,
                                         base::TimeDelta current_timestamp) {
  if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp)
    return;

  const base::TimeDelta diff = current_timestamp - last_timestamp;
  DLOG(WARNING) << "Input timestamps are not monotonically increasing! "
                << " ts " << current_timestamp.InMicroseconds() << " us"
                << " diff " << diff.InMicroseconds() << " us";
}

AudioDiscardHelper::AudioDiscardHelper(int sample_rate)
    : sample_rate_(sample_rate),
      timestamp_helper_(sample_rate_),
      discard_frames_(0),
      last_input_timestamp_(kNoTimestamp()) {
  DCHECK_GT(sample_rate_, 0);
}

AudioDiscardHelper::~AudioDiscardHelper() {
}

size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const {
  DCHECK(duration >= base::TimeDelta());
  return duration.InSecondsF() * sample_rate_ + 0.5;
}

void AudioDiscardHelper::Reset(size_t initial_discard) {
  discard_frames_ = initial_discard;
  last_input_timestamp_ = kNoTimestamp();
  timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
}

bool AudioDiscardHelper::ProcessBuffers(
    const scoped_refptr<DecoderBuffer>& encoded_buffer,
    const scoped_refptr<AudioBuffer>& decoded_buffer) {
  DCHECK(!encoded_buffer->end_of_stream());
  DCHECK(encoded_buffer->timestamp() != kNoTimestamp());

  // Issue a debug warning when we see non-monotonic timestamps.  Only a warning
  // to allow chained OGG playback.
  WarnOnNonMonotonicTimestamps(last_input_timestamp_,
                               encoded_buffer->timestamp());
  last_input_timestamp_ = encoded_buffer->timestamp();

  // If this is the first buffer seen, setup the timestamp helper.
  if (!initialized()) {
    // Clamp the base timestamp to zero.
    timestamp_helper_.SetBaseTimestamp(
        std::max(base::TimeDelta(), encoded_buffer->timestamp()));
  }
  DCHECK(initialized());

  if (!decoded_buffer || !decoded_buffer->frame_count())
    return false;

  if (discard_frames_ > 0) {
    const size_t decoded_frames = decoded_buffer->frame_count();
    const size_t frames_to_discard = std::min(discard_frames_, decoded_frames);
    discard_frames_ -= frames_to_discard;

    // If everything would be discarded, indicate a new buffer is required.
    if (frames_to_discard == decoded_frames)
      return false;

    decoded_buffer->TrimStart(frames_to_discard);
  }

  // TODO(dalecurtis): Applying the current buffer's discard padding doesn't
  // make sense in the Vorbis case because there is a delay of one buffer before
  // decoded buffers are returned. Fix and add support for more than just end
  // trimming.  See http://crbug.com/360961.
  if (encoded_buffer->discard_padding() > base::TimeDelta()) {
    const size_t decoded_frames = decoded_buffer->frame_count();
    const size_t end_frames_to_discard =
        TimeDeltaToFrames(encoded_buffer->discard_padding());
    if (end_frames_to_discard > decoded_frames) {
      DLOG(ERROR) << "Encountered invalid discard padding value.";
      return false;
    }

    // If everything would be discarded, indicate a new buffer is required.
    if (end_frames_to_discard == decoded_frames)
      return false;

    decoded_buffer->TrimEnd(end_frames_to_discard);
  } else {
    DCHECK(encoded_buffer->discard_padding() == base::TimeDelta());
  }

  // Assign timestamp and duration to the buffer.
  decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
  decoded_buffer->set_duration(
      timestamp_helper_.GetFrameDuration(decoded_buffer->frame_count()));
  timestamp_helper_.AddFrames(decoded_buffer->frame_count());
  return true;
}

}  // namespace media