Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(406)

Side by Side Diff: media/filters/frame_processor.cc

Issue 360843002: MSE: Move FrameProcessorBase code into FrameProcessor (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Moves MseTrackBuffer into .cc Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/frame_processor.h ('k') | media/filters/frame_processor_base.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 // Helper class to capture per-track details needed by a frame processor. Some
16 // of this information may be duplicated in the short-term in the associated
17 // ChunkDemuxerStream and SourceBufferStream for a track.
18 // This parallels the MSE spec each of a SourceBuffer's Track Buffers at
19 // http://www.w3.org/TR/media-source/#track-buffers.
20 class MseTrackBuffer {
21 public:
22 explicit MseTrackBuffer(ChunkDemuxerStream* stream);
23 ~MseTrackBuffer();
24
25 // Get/set |last_decode_timestamp_|.
26 base::TimeDelta last_decode_timestamp() const {
27 return last_decode_timestamp_;
28 }
29 void set_last_decode_timestamp(base::TimeDelta timestamp) {
30 last_decode_timestamp_ = timestamp;
31 }
32
33 // Get/set |last_frame_duration_|.
34 base::TimeDelta last_frame_duration() const {
35 return last_frame_duration_;
36 }
37 void set_last_frame_duration(base::TimeDelta duration) {
38 last_frame_duration_ = duration;
39 }
40
41 // Gets |highest_presentation_timestamp_|.
42 base::TimeDelta highest_presentation_timestamp() const {
43 return highest_presentation_timestamp_;
44 }
45
46 // Get/set |needs_random_access_point_|.
47 bool needs_random_access_point() const {
48 return needs_random_access_point_;
49 }
50 void set_needs_random_access_point(bool needs_random_access_point) {
51 needs_random_access_point_ = needs_random_access_point;
52 }
53
54 // Gets a pointer to this track's ChunkDemuxerStream.
55 ChunkDemuxerStream* stream() const { return stream_; }
56
57 // Unsets |last_decode_timestamp_|, unsets |last_frame_duration_|,
58 // unsets |highest_presentation_timestamp_|, and sets
59 // |needs_random_access_point_| to true.
60 void Reset();
61
62 // If |highest_presentation_timestamp_| is unset or |timestamp| is greater
63 // than |highest_presentation_timestamp_|, sets
64 // |highest_presentation_timestamp_| to |timestamp|. Note that bidirectional
65 // prediction between coded frames can cause |timestamp| to not be
66 // monotonically increasing even though the decode timestamps are
67 // monotonically increasing.
68 void SetHighestPresentationTimestampIfIncreased(base::TimeDelta timestamp);
69
70 private:
71 // The decode timestamp of the last coded frame appended in the current coded
72 // frame group. Initially kNoTimestamp(), meaning "unset".
73 base::TimeDelta last_decode_timestamp_;
74
75 // The coded frame duration of the last coded frame appended in the current
76 // coded frame group. Initially kNoTimestamp(), meaning "unset".
77 base::TimeDelta last_frame_duration_;
78
79 // The highest presentation timestamp encountered in a coded frame appended
80 // in the current coded frame group. Initially kNoTimestamp(), meaning
81 // "unset".
82 base::TimeDelta highest_presentation_timestamp_;
83
84 // Keeps track of whether the track buffer is waiting for a random access
85 // point coded frame. Initially set to true to indicate that a random access
86 // point coded frame is needed before anything can be added to the track
87 // buffer.
88 bool needs_random_access_point_;
89
90 // Pointer to the stream associated with this track. The stream is not owned
91 // by |this|.
92 ChunkDemuxerStream* const stream_;
93
94 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer);
95 };
96
97 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream)
98 : last_decode_timestamp_(kNoTimestamp()),
99 last_frame_duration_(kNoTimestamp()),
100 highest_presentation_timestamp_(kNoTimestamp()),
101 needs_random_access_point_(true),
102 stream_(stream) {
103 DCHECK(stream_);
104 }
105
106 MseTrackBuffer::~MseTrackBuffer() {
107 DVLOG(2) << __FUNCTION__ << "()";
108 }
109
110 void MseTrackBuffer::Reset() {
111 DVLOG(2) << __FUNCTION__ << "()";
112
113 last_decode_timestamp_ = kNoTimestamp();
114 last_frame_duration_ = kNoTimestamp();
115 highest_presentation_timestamp_ = kNoTimestamp();
116 needs_random_access_point_ = true;
117 }
118
119 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased(
120 base::TimeDelta timestamp) {
121 if (highest_presentation_timestamp_ == kNoTimestamp() ||
122 timestamp > highest_presentation_timestamp_) {
123 highest_presentation_timestamp_ = timestamp;
124 }
125 }
126
13 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb) 127 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb)
14 : update_duration_cb_(update_duration_cb) { 128 : sequence_mode_(false),
129 group_start_timestamp_(kNoTimestamp()),
130 update_duration_cb_(update_duration_cb) {
15 DVLOG(2) << __FUNCTION__ << "()"; 131 DVLOG(2) << __FUNCTION__ << "()";
16 DCHECK(!update_duration_cb.is_null()); 132 DCHECK(!update_duration_cb.is_null());
17 } 133 }
18 134
19 FrameProcessor::~FrameProcessor() { 135 FrameProcessor::~FrameProcessor() {
20 DVLOG(2) << __FUNCTION__; 136 DVLOG(2) << __FUNCTION__ << "()";
137 STLDeleteValues(&track_buffers_);
21 } 138 }
22 139
23 void FrameProcessor::SetSequenceMode(bool sequence_mode) { 140 void FrameProcessor::SetSequenceMode(bool sequence_mode) {
24 DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")"; 141 DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")";
25 142
26 // Per April 1, 2014 MSE spec editor's draft: 143 // 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 144 // 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 145 // Step 7: If the new mode equals "sequence", then set the group start
29 // timestamp to the group end timestamp. 146 // timestamp to the group end timestamp.
30 if (sequence_mode) { 147 if (sequence_mode) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 } 185 }
69 186
70 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. 187 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element.
71 188
72 // Step 5: 189 // Step 5:
73 update_duration_cb_.Run(group_end_timestamp_); 190 update_duration_cb_.Run(group_end_timestamp_);
74 191
75 return true; 192 return true;
76 } 193 }
77 194
195 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode(
196 base::TimeDelta timestamp_offset) {
197 DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")";
198 DCHECK(kNoTimestamp() != timestamp_offset);
199 if (sequence_mode_)
200 group_start_timestamp_ = timestamp_offset;
201
202 // Changes to timestampOffset should invalidate the preroll buffer.
203 audio_preroll_buffer_ = NULL;
204 }
205
206 bool FrameProcessor::AddTrack(StreamParser::TrackId id,
207 ChunkDemuxerStream* stream) {
208 DVLOG(2) << __FUNCTION__ << "(): id=" << id;
209
210 MseTrackBuffer* existing_track = FindTrack(id);
211 DCHECK(!existing_track);
212 if (existing_track)
213 return false;
214
215 track_buffers_[id] = new MseTrackBuffer(stream);
216 return true;
217 }
218
219 bool FrameProcessor::UpdateTrack(StreamParser::TrackId old_id,
220 StreamParser::TrackId new_id) {
221 DVLOG(2) << __FUNCTION__ << "() : old_id=" << old_id << ", new_id=" << new_id;
222
223 if (old_id == new_id || !FindTrack(old_id) || FindTrack(new_id))
224 return false;
225
226 track_buffers_[new_id] = track_buffers_[old_id];
227 CHECK_EQ(1u, track_buffers_.erase(old_id));
228 return true;
229 }
230
231 void FrameProcessor::SetAllTrackBuffersNeedRandomAccessPoint() {
232 for (TrackBufferMap::iterator itr = track_buffers_.begin();
233 itr != track_buffers_.end();
234 ++itr) {
235 itr->second->set_needs_random_access_point(true);
236 }
237 }
238
239 void FrameProcessor::Reset() {
240 DVLOG(2) << __FUNCTION__ << "()";
241 for (TrackBufferMap::iterator itr = track_buffers_.begin();
242 itr != track_buffers_.end(); ++itr) {
243 itr->second->Reset();
244 }
245 }
246
247 void FrameProcessor::OnPossibleAudioConfigUpdate(
248 const AudioDecoderConfig& config) {
249 DCHECK(config.IsValidConfig());
250
251 // Always clear the preroll buffer when a config update is received.
252 audio_preroll_buffer_ = NULL;
253
254 if (config.Matches(current_audio_config_))
255 return;
256
257 current_audio_config_ = config;
258 sample_duration_ = base::TimeDelta::FromSecondsD(
259 1.0 / current_audio_config_.samples_per_second());
260 }
261
262 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) {
263 TrackBufferMap::iterator itr = track_buffers_.find(id);
264 if (itr == track_buffers_.end())
265 return NULL;
266
267 return itr->second;
268 }
269
270 void FrameProcessor::NotifyNewMediaSegmentStarting(
271 base::TimeDelta segment_timestamp) {
272 DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")";
273
274 for (TrackBufferMap::iterator itr = track_buffers_.begin();
275 itr != track_buffers_.end();
276 ++itr) {
277 itr->second->stream()->OnNewMediaSegment(segment_timestamp);
278 }
279 }
280
281 bool FrameProcessor::HandlePartialAppendWindowTrimming(
282 base::TimeDelta append_window_start,
283 base::TimeDelta append_window_end,
284 const scoped_refptr<StreamParserBuffer>& buffer) {
285 DCHECK(buffer->duration() > base::TimeDelta());
286 DCHECK_EQ(DemuxerStream::AUDIO, buffer->type());
287
288 const base::TimeDelta frame_end_timestamp =
289 buffer->timestamp() + buffer->duration();
290
291 // Ignore any buffers which start after |append_window_start| or end after
292 // |append_window_end|. For simplicity, even those that start before
293 // |append_window_start|.
294 if (buffer->timestamp() > append_window_start ||
295 frame_end_timestamp > append_window_end) {
296 // TODO(dalecurtis): Partial append window trimming could also be done
297 // around |append_window_end|, but is not necessary since splice frames
298 // cover overlaps there.
299 return false;
300 }
301
302 // If the buffer is entirely before |append_window_start|, save it as preroll
303 // for the first buffer which overlaps |append_window_start|.
304 if (buffer->timestamp() < append_window_start &&
305 frame_end_timestamp <= append_window_start) {
306 audio_preroll_buffer_ = buffer;
307 return false;
308 }
309
310 // There's nothing to be done if we have no preroll and the buffer starts on
311 // the append window start.
312 if (buffer->timestamp() == append_window_start && !audio_preroll_buffer_)
313 return false;
314
315 // See if a partial discard can be done around |append_window_start|.
316 DCHECK(buffer->timestamp() <= append_window_start);
317 DCHECK(buffer->IsKeyframe());
318 DVLOG(1) << "Truncating buffer which overlaps append window start."
319 << " presentation_timestamp " << buffer->timestamp().InSecondsF()
320 << " append_window_start " << append_window_start.InSecondsF();
321
322 // If this isn't the first buffer discarded by the append window, try to use
323 // the last buffer discarded for preroll. This ensures that the partially
324 // trimmed buffer can be correctly decoded.
325 if (audio_preroll_buffer_) {
326 // We only want to use the preroll buffer if it directly precedes (less than
327 // one sample apart) the current buffer.
328 const int64 delta = std::abs((audio_preroll_buffer_->timestamp() +
329 audio_preroll_buffer_->duration() -
330 buffer->timestamp()).InMicroseconds());
331 if (delta < sample_duration_.InMicroseconds()) {
332 buffer->SetPrerollBuffer(audio_preroll_buffer_);
333 } else {
334 // TODO(dalecurtis): Add a MEDIA_LOG() for when this is dropped unused.
335 }
336 audio_preroll_buffer_ = NULL;
337 }
338
339 // Decrease the duration appropriately. We only need to shorten the buffer if
340 // it overlaps |append_window_start|.
341 if (buffer->timestamp() < append_window_start) {
342 buffer->set_discard_padding(std::make_pair(
343 append_window_start - buffer->timestamp(), base::TimeDelta()));
344 buffer->set_duration(frame_end_timestamp - append_window_start);
345 }
346
347 // Adjust the timestamp of this buffer forward to |append_window_start|. The
348 // timestamps are always set, even if |buffer|'s timestamp is already set to
349 // |append_window_start|, to ensure the preroll buffer is setup correctly.
350 buffer->set_timestamp(append_window_start);
351 buffer->SetDecodeTimestamp(append_window_start);
352 return true;
353 }
354
78 bool FrameProcessor::ProcessFrame( 355 bool FrameProcessor::ProcessFrame(
79 const scoped_refptr<StreamParserBuffer>& frame, 356 const scoped_refptr<StreamParserBuffer>& frame,
80 base::TimeDelta append_window_start, 357 base::TimeDelta append_window_start,
81 base::TimeDelta append_window_end, 358 base::TimeDelta append_window_end,
82 base::TimeDelta* timestamp_offset, 359 base::TimeDelta* timestamp_offset,
83 bool* new_media_segment) { 360 bool* new_media_segment) {
84 // Implements the loop within step 1 of the coded frame processing algorithm 361 // 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: 362 // 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/ 363 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/
87 // media-source.html#sourcebuffer-coded-frame-processing 364 // media-source.html#sourcebuffer-coded-frame-processing
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
364 DCHECK(group_end_timestamp_ >= base::TimeDelta()); 641 DCHECK(group_end_timestamp_ >= base::TimeDelta());
365 642
366 return true; 643 return true;
367 } 644 }
368 645
369 NOTREACHED(); 646 NOTREACHED();
370 return false; 647 return false;
371 } 648 }
372 649
373 } // namespace media 650 } // namespace media
OLDNEW
« no previous file with comments | « media/filters/frame_processor.h ('k') | media/filters/frame_processor_base.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698