Home | History | Annotate | Download | only in filters
      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/filters/frame_processor_base.h"
      6 
      7 #include <cstdlib>
      8 
      9 #include "base/stl_util.h"
     10 #include "media/base/buffers.h"
     11 
     12 namespace media {
     13 
     14 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream)
     15     : last_decode_timestamp_(kNoTimestamp()),
     16       last_frame_duration_(kNoTimestamp()),
     17       highest_presentation_timestamp_(kNoTimestamp()),
     18       needs_random_access_point_(true),
     19       stream_(stream) {
     20   DCHECK(stream_);
     21 }
     22 
     23 MseTrackBuffer::~MseTrackBuffer() {
     24   DVLOG(2) << __FUNCTION__ << "()";
     25 }
     26 
     27 void MseTrackBuffer::Reset() {
     28   DVLOG(2) << __FUNCTION__ << "()";
     29 
     30   last_decode_timestamp_ = kNoTimestamp();
     31   last_frame_duration_ = kNoTimestamp();
     32   highest_presentation_timestamp_ = kNoTimestamp();
     33   needs_random_access_point_ = true;
     34 }
     35 
     36 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased(
     37     base::TimeDelta timestamp) {
     38   if (highest_presentation_timestamp_ == kNoTimestamp() ||
     39       timestamp > highest_presentation_timestamp_) {
     40     highest_presentation_timestamp_ = timestamp;
     41   }
     42 }
     43 
     44 FrameProcessorBase::FrameProcessorBase()
     45     : sequence_mode_(false),
     46       group_start_timestamp_(kNoTimestamp()) {}
     47 
     48 FrameProcessorBase::~FrameProcessorBase() {
     49   DVLOG(2) << __FUNCTION__ << "()";
     50 
     51   STLDeleteValues(&track_buffers_);
     52 }
     53 
     54 void FrameProcessorBase::SetGroupStartTimestampIfInSequenceMode(
     55     base::TimeDelta timestamp_offset) {
     56   DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")";
     57   DCHECK(kNoTimestamp() != timestamp_offset);
     58   if (sequence_mode_)
     59     group_start_timestamp_ = timestamp_offset;
     60 
     61   // Changes to timestampOffset should invalidate the preroll buffer.
     62   audio_preroll_buffer_ = NULL;
     63 }
     64 
     65 bool FrameProcessorBase::AddTrack(StreamParser::TrackId id,
     66                                   ChunkDemuxerStream* stream) {
     67   DVLOG(2) << __FUNCTION__ << "(): id=" << id;
     68 
     69   MseTrackBuffer* existing_track = FindTrack(id);
     70   DCHECK(!existing_track);
     71   if (existing_track)
     72     return false;
     73 
     74   track_buffers_[id] = new MseTrackBuffer(stream);
     75   return true;
     76 }
     77 
     78 bool FrameProcessorBase::UpdateTrack(StreamParser::TrackId old_id,
     79                                      StreamParser::TrackId new_id) {
     80   DVLOG(2) << __FUNCTION__ << "() : old_id=" << old_id << ", new_id=" << new_id;
     81 
     82   if (old_id == new_id || !FindTrack(old_id) || FindTrack(new_id))
     83     return false;
     84 
     85   track_buffers_[new_id] = track_buffers_[old_id];
     86   CHECK_EQ(1u, track_buffers_.erase(old_id));
     87   return true;
     88 }
     89 
     90 void FrameProcessorBase::SetAllTrackBuffersNeedRandomAccessPoint() {
     91   for (TrackBufferMap::iterator itr = track_buffers_.begin();
     92        itr != track_buffers_.end();
     93        ++itr) {
     94     itr->second->set_needs_random_access_point(true);
     95   }
     96 }
     97 
     98 void FrameProcessorBase::Reset() {
     99   DVLOG(2) << __FUNCTION__ << "()";
    100   for (TrackBufferMap::iterator itr = track_buffers_.begin();
    101        itr != track_buffers_.end(); ++itr) {
    102     itr->second->Reset();
    103   }
    104 }
    105 
    106 MseTrackBuffer* FrameProcessorBase::FindTrack(StreamParser::TrackId id) {
    107   TrackBufferMap::iterator itr = track_buffers_.find(id);
    108   if (itr == track_buffers_.end())
    109     return NULL;
    110 
    111   return itr->second;
    112 }
    113 
    114 void FrameProcessorBase::NotifyNewMediaSegmentStarting(
    115     base::TimeDelta segment_timestamp) {
    116   DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")";
    117 
    118   for (TrackBufferMap::iterator itr = track_buffers_.begin();
    119        itr != track_buffers_.end();
    120        ++itr) {
    121     itr->second->stream()->OnNewMediaSegment(segment_timestamp);
    122   }
    123 }
    124 
    125 bool FrameProcessorBase::HandlePartialAppendWindowTrimming(
    126     base::TimeDelta append_window_start,
    127     base::TimeDelta append_window_end,
    128     const scoped_refptr<StreamParserBuffer>& buffer) {
    129   DCHECK(buffer->duration() > base::TimeDelta());
    130   DCHECK_EQ(DemuxerStream::AUDIO, buffer->type());
    131 
    132   const base::TimeDelta frame_end_timestamp =
    133       buffer->timestamp() + buffer->duration();
    134 
    135   // Ignore any buffers which start after |append_window_start| or end after
    136   // |append_window_end|.  For simplicity, even those that start before
    137   // |append_window_start|.
    138   if (buffer->timestamp() > append_window_start ||
    139       frame_end_timestamp > append_window_end) {
    140     // TODO(dalecurtis): Partial append window trimming could also be done
    141     // around |append_window_end|, but is not necessary since splice frames
    142     // cover overlaps there.
    143     return false;
    144   }
    145 
    146   // If the buffer is entirely before |append_window_start|, save it as preroll
    147   // for the first buffer which overlaps |append_window_start|.
    148   if (buffer->timestamp() < append_window_start &&
    149       frame_end_timestamp <= append_window_start) {
    150     audio_preroll_buffer_ = buffer;
    151     return false;
    152   }
    153 
    154   // There's nothing to be done if we have no preroll and the buffer starts on
    155   // the append window start.
    156   if (buffer->timestamp() == append_window_start && !audio_preroll_buffer_)
    157     return false;
    158 
    159   // See if a partial discard can be done around |append_window_start|.
    160   DCHECK(buffer->timestamp() <= append_window_start);
    161   DCHECK(buffer->IsKeyframe());
    162   DVLOG(1) << "Truncating buffer which overlaps append window start."
    163            << " presentation_timestamp " << buffer->timestamp().InSecondsF()
    164            << " append_window_start " << append_window_start.InSecondsF();
    165 
    166   // If this isn't the first buffer discarded by the append window, try to use
    167   // the last buffer discarded for preroll.  This ensures that the partially
    168   // trimmed buffer can be correctly decoded.
    169   if (audio_preroll_buffer_) {
    170     // We only want to use the preroll buffer if it directly precedes (less than
    171     // one sample apart) the current buffer.
    172     const int64 delta = std::abs((audio_preroll_buffer_->timestamp() +
    173                                   audio_preroll_buffer_->duration() -
    174                                   buffer->timestamp()).InMicroseconds());
    175     if (delta < sample_duration_.InMicroseconds()) {
    176       buffer->SetPrerollBuffer(audio_preroll_buffer_);
    177     } else {
    178       // TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused.
    179     }
    180     audio_preroll_buffer_ = NULL;
    181   }
    182 
    183   // Decrease the duration appropriately.  We only need to shorten the buffer if
    184   // it overlaps |append_window_start|.
    185   if (buffer->timestamp() < append_window_start) {
    186     buffer->set_discard_padding(std::make_pair(
    187         append_window_start - buffer->timestamp(), base::TimeDelta()));
    188     buffer->set_duration(frame_end_timestamp - append_window_start);
    189   }
    190 
    191   // Adjust the timestamp of this buffer forward to |append_window_start|.  The
    192   // timestamps are always set, even if |buffer|'s timestamp is already set to
    193   // |append_window_start|, to ensure the preroll buffer is setup correctly.
    194   buffer->set_timestamp(append_window_start);
    195   buffer->SetDecodeTimestamp(append_window_start);
    196   return true;
    197 }
    198 
    199 void FrameProcessorBase::OnPossibleAudioConfigUpdate(
    200     const AudioDecoderConfig& config) {
    201   DCHECK(config.IsValidConfig());
    202 
    203   // Always clear the preroll buffer when a config update is received.
    204   audio_preroll_buffer_ = NULL;
    205 
    206   if (config.Matches(current_audio_config_))
    207     return;
    208 
    209   current_audio_config_ = config;
    210   sample_duration_ = base::TimeDelta::FromSecondsD(
    211       1.0 / current_audio_config_.samples_per_second());
    212 }
    213 
    214 }  // namespace media
    215