Home | History | Annotate | Download | only in base
      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
      3 // found in the LICENSE file.
      4 
      5 #include "media/base/audio_discard_helper.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "media/base/audio_buffer.h"
     11 #include "media/base/buffers.h"
     12 
     13 namespace media {
     14 
     15 static void WarnOnNonMonotonicTimestamps(base::TimeDelta last_timestamp,
     16                                          base::TimeDelta current_timestamp) {
     17   if (last_timestamp == kNoTimestamp() || last_timestamp < current_timestamp)
     18     return;
     19 
     20   const base::TimeDelta diff = current_timestamp - last_timestamp;
     21   DLOG(WARNING) << "Input timestamps are not monotonically increasing! "
     22                 << " ts " << current_timestamp.InMicroseconds() << " us"
     23                 << " diff " << diff.InMicroseconds() << " us";
     24 }
     25 
     26 AudioDiscardHelper::AudioDiscardHelper(int sample_rate, size_t decoder_delay)
     27     : sample_rate_(sample_rate),
     28       decoder_delay_(decoder_delay),
     29       timestamp_helper_(sample_rate_),
     30       discard_frames_(0),
     31       last_input_timestamp_(kNoTimestamp()),
     32       delayed_discard_(false),
     33       delayed_end_discard_(0) {
     34   DCHECK_GT(sample_rate_, 0);
     35 }
     36 
     37 AudioDiscardHelper::~AudioDiscardHelper() {
     38 }
     39 
     40 size_t AudioDiscardHelper::TimeDeltaToFrames(base::TimeDelta duration) const {
     41   DCHECK(duration >= base::TimeDelta());
     42   return duration.InSecondsF() * sample_rate_ + 0.5;
     43 }
     44 
     45 void AudioDiscardHelper::Reset(size_t initial_discard) {
     46   discard_frames_ = initial_discard;
     47   last_input_timestamp_ = kNoTimestamp();
     48   timestamp_helper_.SetBaseTimestamp(kNoTimestamp());
     49   delayed_discard_ = false;
     50   delayed_discard_padding_ = DecoderBuffer::DiscardPadding();
     51 }
     52 
     53 bool AudioDiscardHelper::ProcessBuffers(
     54     const scoped_refptr<DecoderBuffer>& encoded_buffer,
     55     const scoped_refptr<AudioBuffer>& decoded_buffer) {
     56   DCHECK(!encoded_buffer->end_of_stream());
     57   DCHECK(encoded_buffer->timestamp() != kNoTimestamp());
     58 
     59   // Issue a debug warning when we see non-monotonic timestamps.  Only a warning
     60   // to allow chained OGG playback.
     61   WarnOnNonMonotonicTimestamps(last_input_timestamp_,
     62                                encoded_buffer->timestamp());
     63   last_input_timestamp_ = encoded_buffer->timestamp();
     64 
     65   // If this is the first buffer seen, setup the timestamp helper.
     66   const bool first_buffer = !initialized();
     67   if (first_buffer) {
     68     // Clamp the base timestamp to zero.
     69     timestamp_helper_.SetBaseTimestamp(
     70         std::max(base::TimeDelta(), encoded_buffer->timestamp()));
     71   }
     72   DCHECK(initialized());
     73 
     74   if (!decoded_buffer.get()) {
     75     // If there's a one buffer delay for decoding, we need to save it so it can
     76     // be processed with the next decoder buffer.
     77     if (first_buffer) {
     78       delayed_discard_ = true;
     79       delayed_discard_padding_ = encoded_buffer->discard_padding();
     80     }
     81     return false;
     82   }
     83 
     84   const size_t original_frame_count = decoded_buffer->frame_count();
     85 
     86   // If there's a one buffer delay for decoding, pick up the last encoded
     87   // buffer's discard padding for processing with the current decoded buffer.
     88   DecoderBuffer::DiscardPadding current_discard_padding =
     89       encoded_buffer->discard_padding();
     90   if (delayed_discard_) {
     91     // For simplicity disallow cases where decoder delay is present with delayed
     92     // discard (no codecs at present).  Doing so allows us to avoid complexity
     93     // around endpoint tracking when handling complete buffer discards.
     94     DCHECK_EQ(decoder_delay_, 0u);
     95     std::swap(current_discard_padding, delayed_discard_padding_);
     96   }
     97 
     98   if (discard_frames_ > 0) {
     99     const size_t decoded_frames = decoded_buffer->frame_count();
    100     const size_t frames_to_discard = std::min(discard_frames_, decoded_frames);
    101     discard_frames_ -= frames_to_discard;
    102 
    103     DVLOG(1) << "Initial discard of " << frames_to_discard << " out of "
    104              << decoded_frames << " frames.";
    105 
    106     // If everything would be discarded, indicate a new buffer is required.
    107     if (frames_to_discard == decoded_frames) {
    108       // For simplicity disallow cases where a buffer with discard padding is
    109       // present.  Doing so allows us to avoid complexity around tracking
    110       // discards across buffers.
    111       DCHECK(current_discard_padding.first == base::TimeDelta());
    112       DCHECK(current_discard_padding.second == base::TimeDelta());
    113       return false;
    114     }
    115 
    116     decoded_buffer->TrimStart(frames_to_discard);
    117   }
    118 
    119   // Process any delayed end discard from the previous buffer.
    120   if (delayed_end_discard_ > 0) {
    121     DCHECK_GT(decoder_delay_, 0u);
    122 
    123     const size_t discard_index = decoder_delay_ - delayed_end_discard_;
    124     DCHECK_LT(discard_index, decoder_delay_);
    125 
    126     const size_t decoded_frames = decoded_buffer->frame_count();
    127     DCHECK_LT(delayed_end_discard_, decoded_frames);
    128 
    129     DVLOG(1) << "Delayed end discard of " << delayed_end_discard_ << " out of "
    130              << decoded_frames << " frames starting at " << discard_index;
    131 
    132     decoded_buffer->TrimRange(discard_index,
    133                               discard_index + delayed_end_discard_);
    134     delayed_end_discard_ = 0;
    135   }
    136 
    137   // Handle front discard padding.
    138   if (current_discard_padding.first > base::TimeDelta()) {
    139     const size_t decoded_frames = decoded_buffer->frame_count();
    140 
    141     // If a complete buffer discard is requested and there's no decoder delay,
    142     // just discard all remaining frames from this buffer.  With decoder delay
    143     // we have to estimate the correct number of frames to discard based on the
    144     // duration of the encoded buffer.
    145     const size_t start_frames_to_discard =
    146         current_discard_padding.first == kInfiniteDuration()
    147             ? (decoder_delay_ > 0
    148                    ? TimeDeltaToFrames(encoded_buffer->duration())
    149                    : decoded_frames)
    150             : TimeDeltaToFrames(current_discard_padding.first);
    151 
    152     // Regardless of the timestamp on the encoded buffer, the corresponding
    153     // decoded output will appear |decoder_delay_| frames later.
    154     size_t discard_start = decoder_delay_;
    155     if (decoder_delay_ > 0) {
    156       // If we have a |decoder_delay_| and have already discarded frames from
    157       // this buffer, the |discard_start| must be adjusted by the number of
    158       // frames already discarded.
    159       const size_t frames_discarded_so_far =
    160           original_frame_count - decoded_buffer->frame_count();
    161       CHECK_LE(frames_discarded_so_far, decoder_delay_);
    162       discard_start -= frames_discarded_so_far;
    163     }
    164 
    165     // For simplicity require the start of the discard to be within the current
    166     // buffer.  Doing so allows us avoid complexity around tracking discards
    167     // across buffers.
    168     CHECK_LT(discard_start, decoded_frames);
    169 
    170     const size_t frames_to_discard =
    171         std::min(start_frames_to_discard, decoded_frames - discard_start);
    172 
    173     // Carry over any frames which need to be discarded from the front of the
    174     // next buffer.
    175     DCHECK(!discard_frames_);
    176     discard_frames_ = start_frames_to_discard - frames_to_discard;
    177 
    178     DVLOG(1) << "Front discard of " << frames_to_discard << " out of "
    179              << decoded_frames << " frames starting at " << discard_start;
    180 
    181     // If everything would be discarded, indicate a new buffer is required.
    182     if (frames_to_discard == decoded_frames) {
    183       // The buffer should not have been marked with end discard if the front
    184       // discard removes everything.
    185       DCHECK(current_discard_padding.second == base::TimeDelta());
    186       return false;
    187     }
    188 
    189     decoded_buffer->TrimRange(discard_start, discard_start + frames_to_discard);
    190   } else {
    191     DCHECK(current_discard_padding.first == base::TimeDelta());
    192   }
    193 
    194   // Handle end discard padding.
    195   if (current_discard_padding.second > base::TimeDelta()) {
    196     const size_t decoded_frames = decoded_buffer->frame_count();
    197     size_t end_frames_to_discard =
    198         TimeDeltaToFrames(current_discard_padding.second);
    199 
    200     if (decoder_delay_) {
    201       // Delayed end discard only works if the decoder delay is less than a
    202       // single buffer.
    203       DCHECK_LT(decoder_delay_, original_frame_count);
    204 
    205       // If the discard is >= the decoder delay, trim everything we can off the
    206       // end of this buffer and the rest from the start of the next.
    207       if (end_frames_to_discard >= decoder_delay_) {
    208         DCHECK(!discard_frames_);
    209         discard_frames_ = decoder_delay_;
    210         end_frames_to_discard -= decoder_delay_;
    211       } else {
    212         DCHECK(!delayed_end_discard_);
    213         std::swap(delayed_end_discard_, end_frames_to_discard);
    214       }
    215     }
    216 
    217     if (end_frames_to_discard > decoded_frames) {
    218       DLOG(ERROR) << "Encountered invalid discard padding value.";
    219       return false;
    220     }
    221 
    222     if (end_frames_to_discard > 0) {
    223       DVLOG(1) << "End discard of " << end_frames_to_discard << " out of "
    224                << decoded_frames;
    225 
    226       // If everything would be discarded, indicate a new buffer is required.
    227       if (end_frames_to_discard == decoded_frames)
    228         return false;
    229 
    230       decoded_buffer->TrimEnd(end_frames_to_discard);
    231     }
    232   } else {
    233     DCHECK(current_discard_padding.second == base::TimeDelta());
    234   }
    235 
    236   // Assign timestamp to the buffer.
    237   decoded_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
    238   timestamp_helper_.AddFrames(decoded_buffer->frame_count());
    239   return true;
    240 }
    241 
    242 }  // namespace media
    243