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