| OLD | NEW | 
|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "media/filters/frame_processor.h" | 5 #include "media/filters/frame_processor.h" | 
| 6 | 6 | 
|  | 7 #include <cstdlib> | 
|  | 8 | 
| 7 #include "base/stl_util.h" | 9 #include "base/stl_util.h" | 
| 8 #include "media/base/buffers.h" | 10 #include "media/base/buffers.h" | 
| 9 #include "media/base/stream_parser_buffer.h" | 11 #include "media/base/stream_parser_buffer.h" | 
| 10 | 12 | 
| 11 namespace media { | 13 namespace media { | 
| 12 | 14 | 
|  | 15 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) | 
|  | 16     : last_decode_timestamp_(kNoTimestamp()), | 
|  | 17       last_frame_duration_(kNoTimestamp()), | 
|  | 18       highest_presentation_timestamp_(kNoTimestamp()), | 
|  | 19       needs_random_access_point_(true), | 
|  | 20       stream_(stream) { | 
|  | 21   DCHECK(stream_); | 
|  | 22 } | 
|  | 23 | 
|  | 24 MseTrackBuffer::~MseTrackBuffer() { | 
|  | 25   DVLOG(2) << __FUNCTION__ << "()"; | 
|  | 26 } | 
|  | 27 | 
|  | 28 void MseTrackBuffer::Reset() { | 
|  | 29   DVLOG(2) << __FUNCTION__ << "()"; | 
|  | 30 | 
|  | 31   last_decode_timestamp_ = kNoTimestamp(); | 
|  | 32   last_frame_duration_ = kNoTimestamp(); | 
|  | 33   highest_presentation_timestamp_ = kNoTimestamp(); | 
|  | 34   needs_random_access_point_ = true; | 
|  | 35 } | 
|  | 36 | 
|  | 37 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased( | 
|  | 38     base::TimeDelta timestamp) { | 
|  | 39   if (highest_presentation_timestamp_ == kNoTimestamp() || | 
|  | 40       timestamp > highest_presentation_timestamp_) { | 
|  | 41     highest_presentation_timestamp_ = timestamp; | 
|  | 42   } | 
|  | 43 } | 
|  | 44 | 
| 13 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb) | 45 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb) | 
| 14     : update_duration_cb_(update_duration_cb) { | 46     : sequence_mode_(false), | 
|  | 47       group_start_timestamp_(kNoTimestamp()), | 
|  | 48       update_duration_cb_(update_duration_cb) { | 
| 15   DVLOG(2) << __FUNCTION__ << "()"; | 49   DVLOG(2) << __FUNCTION__ << "()"; | 
| 16   DCHECK(!update_duration_cb.is_null()); | 50   DCHECK(!update_duration_cb.is_null()); | 
| 17 } | 51 } | 
| 18 | 52 | 
| 19 FrameProcessor::~FrameProcessor() { | 53 FrameProcessor::~FrameProcessor() { | 
| 20   DVLOG(2) << __FUNCTION__; | 54   DVLOG(2) << __FUNCTION__ << "()"; | 
|  | 55   STLDeleteValues(&track_buffers_); | 
| 21 } | 56 } | 
| 22 | 57 | 
| 23 void FrameProcessor::SetSequenceMode(bool sequence_mode) { | 58 void FrameProcessor::SetSequenceMode(bool sequence_mode) { | 
| 24   DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")"; | 59   DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")"; | 
| 25 | 60 | 
| 26   // Per April 1, 2014 MSE spec editor's draft: | 61   // Per April 1, 2014 MSE spec editor's draft: | 
| 27   // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/media-
     source.html#widl-SourceBuffer-mode | 62   // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/media-
     source.html#widl-SourceBuffer-mode | 
| 28   // Step 7: If the new mode equals "sequence", then set the group start | 63   // Step 7: If the new mode equals "sequence", then set the group start | 
| 29   // timestamp to the group end timestamp. | 64   // timestamp to the group end timestamp. | 
| 30   if (sequence_mode) { | 65   if (sequence_mode) { | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 68   } | 103   } | 
| 69 | 104 | 
| 70   // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 105   // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 
| 71 | 106 | 
| 72   // Step 5: | 107   // Step 5: | 
| 73   update_duration_cb_.Run(group_end_timestamp_); | 108   update_duration_cb_.Run(group_end_timestamp_); | 
| 74 | 109 | 
| 75   return true; | 110   return true; | 
| 76 } | 111 } | 
| 77 | 112 | 
|  | 113 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( | 
|  | 114     base::TimeDelta timestamp_offset) { | 
|  | 115   DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")"; | 
|  | 116   DCHECK(kNoTimestamp() != timestamp_offset); | 
|  | 117   if (sequence_mode_) | 
|  | 118     group_start_timestamp_ = timestamp_offset; | 
|  | 119 | 
|  | 120   // Changes to timestampOffset should invalidate the preroll buffer. | 
|  | 121   audio_preroll_buffer_ = NULL; | 
|  | 122 } | 
|  | 123 | 
|  | 124 bool FrameProcessor::AddTrack(StreamParser::TrackId id, | 
|  | 125                               ChunkDemuxerStream* stream) { | 
|  | 126   DVLOG(2) << __FUNCTION__ << "(): id=" << id; | 
|  | 127 | 
|  | 128   MseTrackBuffer* existing_track = FindTrack(id); | 
|  | 129   DCHECK(!existing_track); | 
|  | 130   if (existing_track) | 
|  | 131     return false; | 
|  | 132 | 
|  | 133   track_buffers_[id] = new MseTrackBuffer(stream); | 
|  | 134   return true; | 
|  | 135 } | 
|  | 136 | 
|  | 137 bool FrameProcessor::UpdateTrack(StreamParser::TrackId old_id, | 
|  | 138                                  StreamParser::TrackId new_id) { | 
|  | 139   DVLOG(2) << __FUNCTION__ << "() : old_id=" << old_id << ", new_id=" << new_id; | 
|  | 140 | 
|  | 141   if (old_id == new_id || !FindTrack(old_id) || FindTrack(new_id)) | 
|  | 142     return false; | 
|  | 143 | 
|  | 144   track_buffers_[new_id] = track_buffers_[old_id]; | 
|  | 145   CHECK_EQ(1u, track_buffers_.erase(old_id)); | 
|  | 146   return true; | 
|  | 147 } | 
|  | 148 | 
|  | 149 void FrameProcessor::SetAllTrackBuffersNeedRandomAccessPoint() { | 
|  | 150   for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 
|  | 151        itr != track_buffers_.end(); | 
|  | 152        ++itr) { | 
|  | 153     itr->second->set_needs_random_access_point(true); | 
|  | 154   } | 
|  | 155 } | 
|  | 156 | 
|  | 157 void FrameProcessor::Reset() { | 
|  | 158   DVLOG(2) << __FUNCTION__ << "()"; | 
|  | 159   for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 
|  | 160        itr != track_buffers_.end(); ++itr) { | 
|  | 161     itr->second->Reset(); | 
|  | 162   } | 
|  | 163 } | 
|  | 164 | 
|  | 165 void FrameProcessor::OnPossibleAudioConfigUpdate( | 
|  | 166     const AudioDecoderConfig& config) { | 
|  | 167   DCHECK(config.IsValidConfig()); | 
|  | 168 | 
|  | 169   // Always clear the preroll buffer when a config update is received. | 
|  | 170   audio_preroll_buffer_ = NULL; | 
|  | 171 | 
|  | 172   if (config.Matches(current_audio_config_)) | 
|  | 173     return; | 
|  | 174 | 
|  | 175   current_audio_config_ = config; | 
|  | 176   sample_duration_ = base::TimeDelta::FromSecondsD( | 
|  | 177       1.0 / current_audio_config_.samples_per_second()); | 
|  | 178 } | 
|  | 179 | 
|  | 180 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { | 
|  | 181   TrackBufferMap::iterator itr = track_buffers_.find(id); | 
|  | 182   if (itr == track_buffers_.end()) | 
|  | 183     return NULL; | 
|  | 184 | 
|  | 185   return itr->second; | 
|  | 186 } | 
|  | 187 | 
|  | 188 void FrameProcessor::NotifyNewMediaSegmentStarting( | 
|  | 189     base::TimeDelta segment_timestamp) { | 
|  | 190   DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; | 
|  | 191 | 
|  | 192   for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 
|  | 193        itr != track_buffers_.end(); | 
|  | 194        ++itr) { | 
|  | 195     itr->second->stream()->OnNewMediaSegment(segment_timestamp); | 
|  | 196   } | 
|  | 197 } | 
|  | 198 | 
|  | 199 bool FrameProcessor::HandlePartialAppendWindowTrimming( | 
|  | 200     base::TimeDelta append_window_start, | 
|  | 201     base::TimeDelta append_window_end, | 
|  | 202     const scoped_refptr<StreamParserBuffer>& buffer) { | 
|  | 203   DCHECK(buffer->duration() > base::TimeDelta()); | 
|  | 204   DCHECK_EQ(DemuxerStream::AUDIO, buffer->type()); | 
|  | 205 | 
|  | 206   const base::TimeDelta frame_end_timestamp = | 
|  | 207       buffer->timestamp() + buffer->duration(); | 
|  | 208 | 
|  | 209   // Ignore any buffers which start after |append_window_start| or end after | 
|  | 210   // |append_window_end|.  For simplicity, even those that start before | 
|  | 211   // |append_window_start|. | 
|  | 212   if (buffer->timestamp() > append_window_start || | 
|  | 213       frame_end_timestamp > append_window_end) { | 
|  | 214     // TODO(dalecurtis): Partial append window trimming could also be done | 
|  | 215     // around |append_window_end|, but is not necessary since splice frames | 
|  | 216     // cover overlaps there. | 
|  | 217     return false; | 
|  | 218   } | 
|  | 219 | 
|  | 220   // If the buffer is entirely before |append_window_start|, save it as preroll | 
|  | 221   // for the first buffer which overlaps |append_window_start|. | 
|  | 222   if (buffer->timestamp() < append_window_start && | 
|  | 223       frame_end_timestamp <= append_window_start) { | 
|  | 224     audio_preroll_buffer_ = buffer; | 
|  | 225     return false; | 
|  | 226   } | 
|  | 227 | 
|  | 228   // There's nothing to be done if we have no preroll and the buffer starts on | 
|  | 229   // the append window start. | 
|  | 230   if (buffer->timestamp() == append_window_start && !audio_preroll_buffer_) | 
|  | 231     return false; | 
|  | 232 | 
|  | 233   // See if a partial discard can be done around |append_window_start|. | 
|  | 234   DCHECK(buffer->timestamp() <= append_window_start); | 
|  | 235   DCHECK(buffer->IsKeyframe()); | 
|  | 236   DVLOG(1) << "Truncating buffer which overlaps append window start." | 
|  | 237            << " presentation_timestamp " << buffer->timestamp().InSecondsF() | 
|  | 238            << " append_window_start " << append_window_start.InSecondsF(); | 
|  | 239 | 
|  | 240   // If this isn't the first buffer discarded by the append window, try to use | 
|  | 241   // the last buffer discarded for preroll.  This ensures that the partially | 
|  | 242   // trimmed buffer can be correctly decoded. | 
|  | 243   if (audio_preroll_buffer_) { | 
|  | 244     // We only want to use the preroll buffer if it directly precedes (less than | 
|  | 245     // one sample apart) the current buffer. | 
|  | 246     const int64 delta = std::abs((audio_preroll_buffer_->timestamp() + | 
|  | 247                                   audio_preroll_buffer_->duration() - | 
|  | 248                                   buffer->timestamp()).InMicroseconds()); | 
|  | 249     if (delta < sample_duration_.InMicroseconds()) { | 
|  | 250       buffer->SetPrerollBuffer(audio_preroll_buffer_); | 
|  | 251     } else { | 
|  | 252       // TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused. | 
|  | 253     } | 
|  | 254     audio_preroll_buffer_ = NULL; | 
|  | 255   } | 
|  | 256 | 
|  | 257   // Decrease the duration appropriately.  We only need to shorten the buffer if | 
|  | 258   // it overlaps |append_window_start|. | 
|  | 259   if (buffer->timestamp() < append_window_start) { | 
|  | 260     buffer->set_discard_padding(std::make_pair( | 
|  | 261         append_window_start - buffer->timestamp(), base::TimeDelta())); | 
|  | 262     buffer->set_duration(frame_end_timestamp - append_window_start); | 
|  | 263   } | 
|  | 264 | 
|  | 265   // Adjust the timestamp of this buffer forward to |append_window_start|.  The | 
|  | 266   // timestamps are always set, even if |buffer|'s timestamp is already set to | 
|  | 267   // |append_window_start|, to ensure the preroll buffer is setup correctly. | 
|  | 268   buffer->set_timestamp(append_window_start); | 
|  | 269   buffer->SetDecodeTimestamp(append_window_start); | 
|  | 270   return true; | 
|  | 271 } | 
|  | 272 | 
| 78 bool FrameProcessor::ProcessFrame( | 273 bool FrameProcessor::ProcessFrame( | 
| 79     const scoped_refptr<StreamParserBuffer>& frame, | 274     const scoped_refptr<StreamParserBuffer>& frame, | 
| 80     base::TimeDelta append_window_start, | 275     base::TimeDelta append_window_start, | 
| 81     base::TimeDelta append_window_end, | 276     base::TimeDelta append_window_end, | 
| 82     base::TimeDelta* timestamp_offset, | 277     base::TimeDelta* timestamp_offset, | 
| 83     bool* new_media_segment) { | 278     bool* new_media_segment) { | 
| 84   // Implements the loop within step 1 of the coded frame processing algorithm | 279   // Implements the loop within step 1 of the coded frame processing algorithm | 
| 85   // for a single input frame per April 1, 2014 MSE spec editor's draft: | 280   // for a single input frame per April 1, 2014 MSE spec editor's draft: | 
| 86   // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 281   // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 
| 87   //     media-source.html#sourcebuffer-coded-frame-processing | 282   //     media-source.html#sourcebuffer-coded-frame-processing | 
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 364     DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 559     DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 
| 365 | 560 | 
| 366     return true; | 561     return true; | 
| 367   } | 562   } | 
| 368 | 563 | 
| 369   NOTREACHED(); | 564   NOTREACHED(); | 
| 370   return false; | 565   return false; | 
| 371 } | 566 } | 
| 372 | 567 | 
| 373 }  // namespace media | 568 }  // namespace media | 
| OLD | NEW | 
|---|