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.
5 #include "media/filters/frame_processor.h"
9 #include "base/stl_util.h"
10 #include "media/base/buffers.h"
11 #include "media/base/stream_parser_buffer.h"
15 const int kMaxDroppedPrerollWarnings
= 10;
16 const int kMaxDtsBeyondPtsWarnings
= 10;
18 // Helper class to capture per-track details needed by a frame processor. Some
19 // of this information may be duplicated in the short-term in the associated
20 // ChunkDemuxerStream and SourceBufferStream for a track.
21 // This parallels the MSE spec each of a SourceBuffer's Track Buffers at
22 // http://www.w3.org/TR/media-source/#track-buffers.
23 class MseTrackBuffer
{
25 explicit MseTrackBuffer(ChunkDemuxerStream
* stream
);
28 // Get/set |last_decode_timestamp_|.
29 DecodeTimestamp
last_decode_timestamp() const {
30 return last_decode_timestamp_
;
32 void set_last_decode_timestamp(DecodeTimestamp timestamp
) {
33 last_decode_timestamp_
= timestamp
;
36 // Get/set |last_frame_duration_|.
37 base::TimeDelta
last_frame_duration() const {
38 return last_frame_duration_
;
40 void set_last_frame_duration(base::TimeDelta duration
) {
41 last_frame_duration_
= duration
;
44 // Gets |highest_presentation_timestamp_|.
45 base::TimeDelta
highest_presentation_timestamp() const {
46 return highest_presentation_timestamp_
;
49 // Get/set |needs_random_access_point_|.
50 bool needs_random_access_point() const {
51 return needs_random_access_point_
;
53 void set_needs_random_access_point(bool needs_random_access_point
) {
54 needs_random_access_point_
= needs_random_access_point
;
57 // Gets a pointer to this track's ChunkDemuxerStream.
58 ChunkDemuxerStream
* stream() const { return stream_
; }
60 // Unsets |last_decode_timestamp_|, unsets |last_frame_duration_|,
61 // unsets |highest_presentation_timestamp_|, and sets
62 // |needs_random_access_point_| to true.
65 // If |highest_presentation_timestamp_| is unset or |timestamp| is greater
66 // than |highest_presentation_timestamp_|, sets
67 // |highest_presentation_timestamp_| to |timestamp|. Note that bidirectional
68 // prediction between coded frames can cause |timestamp| to not be
69 // monotonically increasing even though the decode timestamps are
70 // monotonically increasing.
71 void SetHighestPresentationTimestampIfIncreased(base::TimeDelta timestamp
);
73 // Adds |frame| to the end of |processed_frames_|.
74 void EnqueueProcessedFrame(const scoped_refptr
<StreamParserBuffer
>& frame
);
76 // Appends |processed_frames_|, if not empty, to |stream_| and clears
77 // |processed_frames_|. Returns false if append failed, true otherwise.
78 // |processed_frames_| is cleared in both cases.
79 bool FlushProcessedFrames();
82 // The decode timestamp of the last coded frame appended in the current coded
83 // frame group. Initially kNoTimestamp(), meaning "unset".
84 DecodeTimestamp last_decode_timestamp_
;
86 // The coded frame duration of the last coded frame appended in the current
87 // coded frame group. Initially kNoTimestamp(), meaning "unset".
88 base::TimeDelta last_frame_duration_
;
90 // The highest presentation timestamp encountered in a coded frame appended
91 // in the current coded frame group. Initially kNoTimestamp(), meaning
93 base::TimeDelta highest_presentation_timestamp_
;
95 // Keeps track of whether the track buffer is waiting for a random access
96 // point coded frame. Initially set to true to indicate that a random access
97 // point coded frame is needed before anything can be added to the track
99 bool needs_random_access_point_
;
101 // Pointer to the stream associated with this track. The stream is not owned
103 ChunkDemuxerStream
* const stream_
;
105 // Queue of processed frames that have not yet been appended to |stream_|.
106 // EnqueueProcessedFrame() adds to this queue, and FlushProcessedFrames()
108 StreamParser::BufferQueue processed_frames_
;
110 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer
);
113 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream
* stream
)
114 : last_decode_timestamp_(kNoDecodeTimestamp()),
115 last_frame_duration_(kNoTimestamp()),
116 highest_presentation_timestamp_(kNoTimestamp()),
117 needs_random_access_point_(true),
122 MseTrackBuffer::~MseTrackBuffer() {
123 DVLOG(2) << __FUNCTION__
<< "()";
126 void MseTrackBuffer::Reset() {
127 DVLOG(2) << __FUNCTION__
<< "()";
129 last_decode_timestamp_
= kNoDecodeTimestamp();
130 last_frame_duration_
= kNoTimestamp();
131 highest_presentation_timestamp_
= kNoTimestamp();
132 needs_random_access_point_
= true;
135 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased(
136 base::TimeDelta timestamp
) {
137 if (highest_presentation_timestamp_
== kNoTimestamp() ||
138 timestamp
> highest_presentation_timestamp_
) {
139 highest_presentation_timestamp_
= timestamp
;
143 void MseTrackBuffer::EnqueueProcessedFrame(
144 const scoped_refptr
<StreamParserBuffer
>& frame
) {
145 processed_frames_
.push_back(frame
);
148 bool MseTrackBuffer::FlushProcessedFrames() {
149 if (processed_frames_
.empty())
152 bool result
= stream_
->Append(processed_frames_
);
153 processed_frames_
.clear();
155 DVLOG_IF(3, !result
) << __FUNCTION__
156 << "(): Failure appending processed frames to stream";
161 FrameProcessor::FrameProcessor(const UpdateDurationCB
& update_duration_cb
,
162 const scoped_refptr
<MediaLog
>& media_log
)
163 : sequence_mode_(false),
164 group_start_timestamp_(kNoTimestamp()),
165 update_duration_cb_(update_duration_cb
),
166 media_log_(media_log
),
167 num_dropped_preroll_warnings_(0),
168 num_dts_beyond_pts_warnings_(0) {
169 DVLOG(2) << __FUNCTION__
<< "()";
170 DCHECK(!update_duration_cb
.is_null());
173 FrameProcessor::~FrameProcessor() {
174 DVLOG(2) << __FUNCTION__
<< "()";
175 STLDeleteValues(&track_buffers_
);
178 void FrameProcessor::SetSequenceMode(bool sequence_mode
) {
179 DVLOG(2) << __FUNCTION__
<< "(" << sequence_mode
<< ")";
181 // Per April 1, 2014 MSE spec editor's draft:
182 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/media-source.html#widl-SourceBuffer-mode
183 // Step 7: If the new mode equals "sequence", then set the group start
184 // timestamp to the group end timestamp.
186 DCHECK(kNoTimestamp() != group_end_timestamp_
);
187 group_start_timestamp_
= group_end_timestamp_
;
190 // Step 8: Update the attribute to new mode.
191 sequence_mode_
= sequence_mode
;
194 bool FrameProcessor::ProcessFrames(
195 const StreamParser::BufferQueue
& audio_buffers
,
196 const StreamParser::BufferQueue
& video_buffers
,
197 const StreamParser::TextBufferQueueMap
& text_map
,
198 base::TimeDelta append_window_start
,
199 base::TimeDelta append_window_end
,
200 bool* new_media_segment
,
201 base::TimeDelta
* timestamp_offset
) {
202 StreamParser::BufferQueue frames
;
203 if (!MergeBufferQueues(audio_buffers
, video_buffers
, text_map
, &frames
)) {
204 MEDIA_LOG(ERROR
, media_log_
) << "Parsed buffers not in DTS sequence";
208 DCHECK(!frames
.empty());
210 // Implements the coded frame processing algorithm's outer loop for step 1.
211 // Note that ProcessFrame() implements an inner loop for a single frame that
212 // handles "jump to the Loop Top step to restart processing of the current
213 // coded frame" per April 1, 2014 MSE spec editor's draft:
214 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/
215 // media-source.html#sourcebuffer-coded-frame-processing
216 // 1. For each coded frame in the media segment run the following steps:
217 for (StreamParser::BufferQueue::const_iterator frames_itr
= frames
.begin();
218 frames_itr
!= frames
.end(); ++frames_itr
) {
219 if (!ProcessFrame(*frames_itr
, append_window_start
, append_window_end
,
220 timestamp_offset
, new_media_segment
)) {
221 FlushProcessedFrames();
226 if (!FlushProcessedFrames())
229 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element.
232 update_duration_cb_
.Run(group_end_timestamp_
);
237 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode(
238 base::TimeDelta timestamp_offset
) {
239 DVLOG(2) << __FUNCTION__
<< "(" << timestamp_offset
.InSecondsF() << ")";
240 DCHECK(kNoTimestamp() != timestamp_offset
);
242 group_start_timestamp_
= timestamp_offset
;
244 // Changes to timestampOffset should invalidate the preroll buffer.
245 audio_preroll_buffer_
= NULL
;
248 bool FrameProcessor::AddTrack(StreamParser::TrackId id
,
249 ChunkDemuxerStream
* stream
) {
250 DVLOG(2) << __FUNCTION__
<< "(): id=" << id
;
252 MseTrackBuffer
* existing_track
= FindTrack(id
);
253 DCHECK(!existing_track
);
254 if (existing_track
) {
255 MEDIA_LOG(ERROR
, media_log_
) << "Failure adding track with duplicate ID "
260 track_buffers_
[id
] = new MseTrackBuffer(stream
);
264 bool FrameProcessor::UpdateTrack(StreamParser::TrackId old_id
,
265 StreamParser::TrackId new_id
) {
266 DVLOG(2) << __FUNCTION__
<< "() : old_id=" << old_id
<< ", new_id=" << new_id
;
268 if (old_id
== new_id
|| !FindTrack(old_id
) || FindTrack(new_id
)) {
269 MEDIA_LOG(ERROR
, media_log_
) << "Failure updating track id from " << old_id
274 track_buffers_
[new_id
] = track_buffers_
[old_id
];
275 CHECK_EQ(1u, track_buffers_
.erase(old_id
));
279 void FrameProcessor::SetAllTrackBuffersNeedRandomAccessPoint() {
280 for (TrackBufferMap::iterator itr
= track_buffers_
.begin();
281 itr
!= track_buffers_
.end();
283 itr
->second
->set_needs_random_access_point(true);
287 void FrameProcessor::Reset() {
288 DVLOG(2) << __FUNCTION__
<< "()";
289 for (TrackBufferMap::iterator itr
= track_buffers_
.begin();
290 itr
!= track_buffers_
.end(); ++itr
) {
291 itr
->second
->Reset();
295 void FrameProcessor::OnPossibleAudioConfigUpdate(
296 const AudioDecoderConfig
& config
) {
297 DCHECK(config
.IsValidConfig());
299 // Always clear the preroll buffer when a config update is received.
300 audio_preroll_buffer_
= NULL
;
302 if (config
.Matches(current_audio_config_
))
305 current_audio_config_
= config
;
306 sample_duration_
= base::TimeDelta::FromSecondsD(
307 1.0 / current_audio_config_
.samples_per_second());
310 MseTrackBuffer
* FrameProcessor::FindTrack(StreamParser::TrackId id
) {
311 TrackBufferMap::iterator itr
= track_buffers_
.find(id
);
312 if (itr
== track_buffers_
.end())
318 void FrameProcessor::NotifyNewMediaSegmentStarting(
319 DecodeTimestamp segment_timestamp
) {
320 DVLOG(2) << __FUNCTION__
<< "(" << segment_timestamp
.InSecondsF() << ")";
322 for (TrackBufferMap::iterator itr
= track_buffers_
.begin();
323 itr
!= track_buffers_
.end();
325 itr
->second
->stream()->OnNewMediaSegment(segment_timestamp
);
329 bool FrameProcessor::FlushProcessedFrames() {
330 DVLOG(2) << __FUNCTION__
<< "()";
333 for (TrackBufferMap::iterator itr
= track_buffers_
.begin();
334 itr
!= track_buffers_
.end();
336 if (!itr
->second
->FlushProcessedFrames())
343 bool FrameProcessor::HandlePartialAppendWindowTrimming(
344 base::TimeDelta append_window_start
,
345 base::TimeDelta append_window_end
,
346 const scoped_refptr
<StreamParserBuffer
>& buffer
) {
347 DCHECK(buffer
->duration() > base::TimeDelta());
348 DCHECK_EQ(DemuxerStream::AUDIO
, buffer
->type());
349 DCHECK(buffer
->is_key_frame());
351 const base::TimeDelta frame_end_timestamp
=
352 buffer
->timestamp() + buffer
->duration();
354 // If the buffer is entirely before |append_window_start|, save it as preroll
355 // for the first buffer which overlaps |append_window_start|.
356 if (buffer
->timestamp() < append_window_start
&&
357 frame_end_timestamp
<= append_window_start
) {
358 audio_preroll_buffer_
= buffer
;
362 // If the buffer is entirely after |append_window_end| there's nothing to do.
363 if (buffer
->timestamp() >= append_window_end
)
366 DCHECK(buffer
->timestamp() >= append_window_start
||
367 frame_end_timestamp
> append_window_start
);
369 bool processed_buffer
= false;
371 // If we have a preroll buffer see if we can attach it to the first buffer
372 // overlapping or after |append_window_start|.
373 if (audio_preroll_buffer_
.get()) {
374 // We only want to use the preroll buffer if it directly precedes (less
375 // than one sample apart) the current buffer.
377 (audio_preroll_buffer_
->timestamp() +
378 audio_preroll_buffer_
->duration() - buffer
->timestamp())
380 if (std::abs(delta
) < sample_duration_
.InMicroseconds()) {
381 DVLOG(1) << "Attaching audio preroll buffer ["
382 << audio_preroll_buffer_
->timestamp().InSecondsF() << ", "
383 << (audio_preroll_buffer_
->timestamp() +
384 audio_preroll_buffer_
->duration()).InSecondsF() << ") to "
385 << buffer
->timestamp().InSecondsF();
386 buffer
->SetPrerollBuffer(audio_preroll_buffer_
);
387 processed_buffer
= true;
389 LIMITED_MEDIA_LOG(DEBUG
, media_log_
, num_dropped_preroll_warnings_
,
390 kMaxDroppedPrerollWarnings
)
391 << "Partial append window trimming dropping unused audio preroll "
393 << audio_preroll_buffer_
->timestamp().InMicroseconds()
394 << "us that ends too far (" << delta
395 << "us) from next buffer with PTS "
396 << buffer
->timestamp().InMicroseconds() << "us";
398 audio_preroll_buffer_
= NULL
;
401 // See if a partial discard can be done around |append_window_start|.
402 if (buffer
->timestamp() < append_window_start
) {
403 DVLOG(1) << "Truncating buffer which overlaps append window start."
404 << " presentation_timestamp " << buffer
->timestamp().InSecondsF()
405 << " frame_end_timestamp " << frame_end_timestamp
.InSecondsF()
406 << " append_window_start " << append_window_start
.InSecondsF();
408 // Mark the overlapping portion of the buffer for discard.
409 buffer
->set_discard_padding(std::make_pair(
410 append_window_start
- buffer
->timestamp(), base::TimeDelta()));
412 // Adjust the timestamp of this buffer forward to |append_window_start| and
413 // decrease the duration to compensate. Adjust DTS by the same delta as PTS
414 // to help prevent spurious discontinuities when DTS > PTS.
415 base::TimeDelta pts_delta
= append_window_start
- buffer
->timestamp();
416 buffer
->set_timestamp(append_window_start
);
417 buffer
->SetDecodeTimestamp(buffer
->GetDecodeTimestamp() + pts_delta
);
418 buffer
->set_duration(frame_end_timestamp
- append_window_start
);
419 processed_buffer
= true;
422 // See if a partial discard can be done around |append_window_end|.
423 if (frame_end_timestamp
> append_window_end
) {
424 DVLOG(1) << "Truncating buffer which overlaps append window end."
425 << " presentation_timestamp " << buffer
->timestamp().InSecondsF()
426 << " frame_end_timestamp " << frame_end_timestamp
.InSecondsF()
427 << " append_window_end " << append_window_end
.InSecondsF();
429 // Mark the overlapping portion of the buffer for discard.
430 buffer
->set_discard_padding(
431 std::make_pair(buffer
->discard_padding().first
,
432 frame_end_timestamp
- append_window_end
));
434 // Decrease the duration of the buffer to remove the discarded portion.
435 buffer
->set_duration(append_window_end
- buffer
->timestamp());
436 processed_buffer
= true;
439 return processed_buffer
;
442 bool FrameProcessor::ProcessFrame(
443 const scoped_refptr
<StreamParserBuffer
>& frame
,
444 base::TimeDelta append_window_start
,
445 base::TimeDelta append_window_end
,
446 base::TimeDelta
* timestamp_offset
,
447 bool* new_media_segment
) {
448 // Implements the loop within step 1 of the coded frame processing algorithm
449 // for a single input frame per April 1, 2014 MSE spec editor's draft:
450 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/
451 // media-source.html#sourcebuffer-coded-frame-processing
454 // 1. Loop Top: Let presentation timestamp be a double precision floating
455 // point representation of the coded frame's presentation timestamp in
457 // 2. Let decode timestamp be a double precision floating point
458 // representation of the coded frame's decode timestamp in seconds.
459 // 3. Let frame duration be a double precision floating point representation
460 // of the coded frame's duration in seconds.
461 // We use base::TimeDelta and DecodeTimestamp instead of double.
462 base::TimeDelta presentation_timestamp
= frame
->timestamp();
463 DecodeTimestamp decode_timestamp
= frame
->GetDecodeTimestamp();
464 base::TimeDelta frame_duration
= frame
->duration();
466 DVLOG(3) << __FUNCTION__
<< ": Processing frame "
467 << "Type=" << frame
->type()
468 << ", TrackID=" << frame
->track_id()
469 << ", PTS=" << presentation_timestamp
.InSecondsF()
470 << ", DTS=" << decode_timestamp
.InSecondsF()
471 << ", DUR=" << frame_duration
.InSecondsF()
472 << ", RAP=" << frame
->is_key_frame();
474 // Sanity check the timestamps.
475 if (presentation_timestamp
== kNoTimestamp()) {
476 MEDIA_LOG(ERROR
, media_log_
) << "Unknown PTS for " << frame
->GetTypeName()
480 if (decode_timestamp
== kNoDecodeTimestamp()) {
481 MEDIA_LOG(ERROR
, media_log_
) << "Unknown DTS for " << frame
->GetTypeName()
485 if (decode_timestamp
.ToPresentationTime() > presentation_timestamp
) {
486 // TODO(wolenetz): Determine whether DTS>PTS should really be allowed. See
487 // http://crbug.com/354518.
488 LIMITED_MEDIA_LOG(DEBUG
, media_log_
, num_dts_beyond_pts_warnings_
,
489 kMaxDtsBeyondPtsWarnings
)
490 << "Parsed " << frame
->GetTypeName() << " frame has DTS "
491 << decode_timestamp
.InMicroseconds()
492 << "us, which is after the frame's PTS "
493 << presentation_timestamp
.InMicroseconds() << "us";
494 DVLOG(2) << __FUNCTION__
<< ": WARNING: Frame DTS("
495 << decode_timestamp
.InSecondsF() << ") > PTS("
496 << presentation_timestamp
.InSecondsF()
497 << "), frame type=" << frame
->GetTypeName();
500 // TODO(acolwell/wolenetz): All stream parsers must emit valid (positive)
501 // frame durations. For now, we allow non-negative frame duration.
502 // See http://crbug.com/351166.
503 if (frame_duration
== kNoTimestamp()) {
504 MEDIA_LOG(ERROR
, media_log_
)
505 << "Unknown duration for " << frame
->GetTypeName() << " frame at PTS "
506 << presentation_timestamp
.InMicroseconds() << "us";
509 if (frame_duration
< base::TimeDelta()) {
510 MEDIA_LOG(ERROR
, media_log_
)
511 << "Negative duration " << frame_duration
.InMicroseconds()
512 << "us for " << frame
->GetTypeName() << " frame at PTS "
513 << presentation_timestamp
.InMicroseconds() << "us";
517 // 4. If mode equals "sequence" and group start timestamp is set, then run
518 // the following steps:
519 if (sequence_mode_
&& group_start_timestamp_
!= kNoTimestamp()) {
520 // 4.1. Set timestampOffset equal to group start timestamp -
521 // presentation timestamp.
522 *timestamp_offset
= group_start_timestamp_
- presentation_timestamp
;
524 DVLOG(3) << __FUNCTION__
<< ": updated timestampOffset is now "
525 << timestamp_offset
->InSecondsF();
527 // 4.2. Set group end timestamp equal to group start timestamp.
528 group_end_timestamp_
= group_start_timestamp_
;
530 // 4.3. Set the need random access point flag on all track buffers to
532 SetAllTrackBuffersNeedRandomAccessPoint();
534 // 4.4. Unset group start timestamp.
535 group_start_timestamp_
= kNoTimestamp();
538 // 5. If timestampOffset is not 0, then run the following steps:
539 if (*timestamp_offset
!= base::TimeDelta()) {
540 // 5.1. Add timestampOffset to the presentation timestamp.
541 // Note: |frame| PTS is only updated if it survives discontinuity
543 presentation_timestamp
+= *timestamp_offset
;
545 // 5.2. Add timestampOffset to the decode timestamp.
546 // Frame DTS is only updated if it survives discontinuity processing.
547 decode_timestamp
+= *timestamp_offset
;
550 // 6. Let track buffer equal the track buffer that the coded frame will be
553 // Remap audio and video track types to their special singleton identifiers.
554 StreamParser::TrackId track_id
= kAudioTrackId
;
555 switch (frame
->type()) {
556 case DemuxerStream::AUDIO
:
558 case DemuxerStream::VIDEO
:
559 track_id
= kVideoTrackId
;
561 case DemuxerStream::TEXT
:
562 track_id
= frame
->track_id();
564 case DemuxerStream::UNKNOWN
:
565 case DemuxerStream::NUM_TYPES
:
566 DCHECK(false) << ": Invalid frame type " << frame
->type();
570 MseTrackBuffer
* track_buffer
= FindTrack(track_id
);
572 MEDIA_LOG(ERROR
, media_log_
)
573 << "Unknown track with type " << frame
->GetTypeName()
574 << ", frame processor track id " << track_id
575 << ", and parser track id " << frame
->track_id();
579 // 7. If last decode timestamp for track buffer is set and decode timestamp
580 // is less than last decode timestamp
582 // If last decode timestamp for track buffer is set and the difference
583 // between decode timestamp and last decode timestamp is greater than 2
584 // times last frame duration:
585 DecodeTimestamp last_decode_timestamp
=
586 track_buffer
->last_decode_timestamp();
587 if (last_decode_timestamp
!= kNoDecodeTimestamp()) {
588 base::TimeDelta dts_delta
= decode_timestamp
- last_decode_timestamp
;
589 if (dts_delta
< base::TimeDelta() ||
590 dts_delta
> 2 * track_buffer
->last_frame_duration()) {
591 // 7.1. If mode equals "segments": Set group end timestamp to
592 // presentation timestamp.
593 // If mode equals "sequence": Set group start timestamp equal to
594 // the group end timestamp.
595 if (!sequence_mode_
) {
596 group_end_timestamp_
= presentation_timestamp
;
597 // This triggers a discontinuity so we need to treat the next frames
598 // appended within the append window as if they were the beginning of
600 *new_media_segment
= true;
602 DVLOG(3) << __FUNCTION__
<< " : Sequence mode discontinuity, GETS: "
603 << group_end_timestamp_
.InSecondsF();
604 DCHECK(kNoTimestamp() != group_end_timestamp_
);
605 group_start_timestamp_
= group_end_timestamp_
;
611 // 7.6. Jump to the Loop Top step above to restart processing of the
612 // current coded frame.
613 DVLOG(3) << __FUNCTION__
<< ": Discontinuity: reprocessing frame";
618 // 9. Let frame end timestamp equal the sum of presentation timestamp and
620 base::TimeDelta frame_end_timestamp
=
621 presentation_timestamp
+ frame_duration
;
623 // 10. If presentation timestamp is less than appendWindowStart, then set
624 // the need random access point flag to true, drop the coded frame, and
625 // jump to the top of the loop to start processing the next coded
627 // Note: We keep the result of partial discard of a buffer that overlaps
628 // |append_window_start| and does not end after |append_window_end|.
629 // 11. If frame end timestamp is greater than appendWindowEnd, then set the
630 // need random access point flag to true, drop the coded frame, and jump
631 // to the top of the loop to start processing the next coded frame.
632 frame
->set_timestamp(presentation_timestamp
);
633 frame
->SetDecodeTimestamp(decode_timestamp
);
634 if (track_buffer
->stream()->supports_partial_append_window_trimming() &&
635 HandlePartialAppendWindowTrimming(append_window_start
,
638 // |frame| has been partially trimmed or had preroll added. Though
639 // |frame|'s duration may have changed, do not update |frame_duration|
640 // here, so |track_buffer|'s last frame duration update uses original
641 // frame duration and reduces spurious discontinuity detection.
642 decode_timestamp
= frame
->GetDecodeTimestamp();
643 presentation_timestamp
= frame
->timestamp();
644 frame_end_timestamp
= frame
->timestamp() + frame
->duration();
647 if (presentation_timestamp
< append_window_start
||
648 frame_end_timestamp
> append_window_end
) {
649 track_buffer
->set_needs_random_access_point(true);
650 DVLOG(3) << "Dropping frame that is outside append window.";
654 // Note: This step is relocated, versus April 1 spec, to allow append window
655 // processing to first filter coded frames shifted by |timestamp_offset_| in
656 // such a way that their PTS is negative.
657 // 8. If the presentation timestamp or decode timestamp is less than the
658 // presentation start time, then run the end of stream algorithm with the
659 // error parameter set to "decode", and abort these steps.
660 DCHECK(presentation_timestamp
>= base::TimeDelta());
661 if (decode_timestamp
< DecodeTimestamp()) {
662 // B-frames may still result in negative DTS here after being shifted by
663 // |timestamp_offset_|.
664 MEDIA_LOG(ERROR
, media_log_
)
665 << frame
->GetTypeName() << " frame with PTS "
666 << presentation_timestamp
.InMicroseconds() << "us has negative DTS "
667 << decode_timestamp
.InMicroseconds()
668 << "us after applying timestampOffset, handling any discontinuity, "
669 "and filtering against append window";
673 // 12. If the need random access point flag on track buffer equals true,
674 // then run the following steps:
675 if (track_buffer
->needs_random_access_point()) {
676 // 12.1. If the coded frame is not a random access point, then drop the
677 // coded frame and jump to the top of the loop to start processing
678 // the next coded frame.
679 if (!frame
->is_key_frame()) {
680 DVLOG(3) << __FUNCTION__
681 << ": Dropping frame that is not a random access point";
685 // 12.2. Set the need random access point flag on track buffer to false.
686 track_buffer
->set_needs_random_access_point(false);
689 // We now have a processed buffer to append to the track buffer's stream.
690 // If it is the first in a new media segment or following a discontinuity,
691 // notify all the track buffers' streams that a new segment is beginning.
692 if (*new_media_segment
) {
693 // First, complete the append to track buffer streams of previous media
694 // segment's frames, if any.
695 if (!FlushProcessedFrames())
698 *new_media_segment
= false;
700 // TODO(acolwell/wolenetz): This should be changed to a presentation
701 // timestamp. See http://crbug.com/402502
702 NotifyNewMediaSegmentStarting(decode_timestamp
);
705 DVLOG(3) << __FUNCTION__
<< ": Sending processed frame to stream, "
706 << "PTS=" << presentation_timestamp
.InSecondsF()
707 << ", DTS=" << decode_timestamp
.InSecondsF();
709 // Steps 13-18: Note, we optimize by appending groups of contiguous
710 // processed frames for each track buffer at end of ProcessFrames() or prior
711 // to NotifyNewMediaSegmentStarting().
712 // TODO(wolenetz): Refactor SourceBufferStream to conform to spec GC timing.
713 // See http://crbug.com/371197.
714 track_buffer
->EnqueueProcessedFrame(frame
);
716 // 19. Set last decode timestamp for track buffer to decode timestamp.
717 track_buffer
->set_last_decode_timestamp(decode_timestamp
);
719 // 20. Set last frame duration for track buffer to frame duration.
720 track_buffer
->set_last_frame_duration(frame_duration
);
722 // 21. If highest presentation timestamp for track buffer is unset or frame
723 // end timestamp is greater than highest presentation timestamp, then
724 // set highest presentation timestamp for track buffer to frame end
726 track_buffer
->SetHighestPresentationTimestampIfIncreased(
727 frame_end_timestamp
);
729 // 22. If frame end timestamp is greater than group end timestamp, then set
730 // group end timestamp equal to frame end timestamp.
731 if (frame_end_timestamp
> group_end_timestamp_
)
732 group_end_timestamp_
= frame_end_timestamp
;
733 DCHECK(group_end_timestamp_
>= base::TimeDelta());