• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MseTrackBuffer(ChunkDemuxerStream * stream)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 
~MseTrackBuffer()23 MseTrackBuffer::~MseTrackBuffer() {
24   DVLOG(2) << __FUNCTION__ << "()";
25 }
26 
Reset()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 
SetHighestPresentationTimestampIfIncreased(base::TimeDelta timestamp)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 
FrameProcessorBase()44 FrameProcessorBase::FrameProcessorBase()
45     : sequence_mode_(false),
46       group_start_timestamp_(kNoTimestamp()) {}
47 
~FrameProcessorBase()48 FrameProcessorBase::~FrameProcessorBase() {
49   DVLOG(2) << __FUNCTION__ << "()";
50 
51   STLDeleteValues(&track_buffers_);
52 }
53 
SetGroupStartTimestampIfInSequenceMode(base::TimeDelta timestamp_offset)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 
AddTrack(StreamParser::TrackId id,ChunkDemuxerStream * stream)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 
UpdateTrack(StreamParser::TrackId old_id,StreamParser::TrackId new_id)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 
SetAllTrackBuffersNeedRandomAccessPoint()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 
Reset()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 
FindTrack(StreamParser::TrackId id)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 
NotifyNewMediaSegmentStarting(base::TimeDelta segment_timestamp)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 
HandlePartialAppendWindowTrimming(base::TimeDelta append_window_start,base::TimeDelta append_window_end,const scoped_refptr<StreamParserBuffer> & buffer)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 
OnPossibleAudioConfigUpdate(const AudioDecoderConfig & config)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