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> | 7 #include <cstdlib> |
8 | 8 |
9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
10 #include "media/base/buffers.h" | 10 #include "media/base/buffers.h" |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 void Reset(); | 60 void Reset(); |
61 | 61 |
62 // If |highest_presentation_timestamp_| is unset or |timestamp| is greater | 62 // If |highest_presentation_timestamp_| is unset or |timestamp| is greater |
63 // than |highest_presentation_timestamp_|, sets | 63 // than |highest_presentation_timestamp_|, sets |
64 // |highest_presentation_timestamp_| to |timestamp|. Note that bidirectional | 64 // |highest_presentation_timestamp_| to |timestamp|. Note that bidirectional |
65 // prediction between coded frames can cause |timestamp| to not be | 65 // prediction between coded frames can cause |timestamp| to not be |
66 // monotonically increasing even though the decode timestamps are | 66 // monotonically increasing even though the decode timestamps are |
67 // monotonically increasing. | 67 // monotonically increasing. |
68 void SetHighestPresentationTimestampIfIncreased(base::TimeDelta timestamp); | 68 void SetHighestPresentationTimestampIfIncreased(base::TimeDelta timestamp); |
69 | 69 |
| 70 // Adds |frame| to the end of |processed_frames_|. |
| 71 void EnqueueProcessedFrame(const scoped_refptr<StreamParserBuffer>& frame); |
| 72 |
| 73 // Appends |processed_frames_|, if not empty, to |stream_| and clears |
| 74 // |processed_frames_|. Returns false if append failed, true otherwise. |
| 75 // |processed_frames_| is cleared in both cases. |
| 76 bool FlushProcessedFrames(); |
| 77 |
70 private: | 78 private: |
71 // The decode timestamp of the last coded frame appended in the current coded | 79 // The decode timestamp of the last coded frame appended in the current coded |
72 // frame group. Initially kNoTimestamp(), meaning "unset". | 80 // frame group. Initially kNoTimestamp(), meaning "unset". |
73 base::TimeDelta last_decode_timestamp_; | 81 base::TimeDelta last_decode_timestamp_; |
74 | 82 |
75 // The coded frame duration of the last coded frame appended in the current | 83 // The coded frame duration of the last coded frame appended in the current |
76 // coded frame group. Initially kNoTimestamp(), meaning "unset". | 84 // coded frame group. Initially kNoTimestamp(), meaning "unset". |
77 base::TimeDelta last_frame_duration_; | 85 base::TimeDelta last_frame_duration_; |
78 | 86 |
79 // The highest presentation timestamp encountered in a coded frame appended | 87 // The highest presentation timestamp encountered in a coded frame appended |
80 // in the current coded frame group. Initially kNoTimestamp(), meaning | 88 // in the current coded frame group. Initially kNoTimestamp(), meaning |
81 // "unset". | 89 // "unset". |
82 base::TimeDelta highest_presentation_timestamp_; | 90 base::TimeDelta highest_presentation_timestamp_; |
83 | 91 |
84 // Keeps track of whether the track buffer is waiting for a random access | 92 // 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 | 93 // 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 | 94 // point coded frame is needed before anything can be added to the track |
87 // buffer. | 95 // buffer. |
88 bool needs_random_access_point_; | 96 bool needs_random_access_point_; |
89 | 97 |
90 // Pointer to the stream associated with this track. The stream is not owned | 98 // Pointer to the stream associated with this track. The stream is not owned |
91 // by |this|. | 99 // by |this|. |
92 ChunkDemuxerStream* const stream_; | 100 ChunkDemuxerStream* const stream_; |
93 | 101 |
| 102 // Queue of processed frames that have not yet been appended to |stream_|. |
| 103 // EnqueueProcessedFrame() adds to this queue, and FlushProcessedFrames() |
| 104 // clears it. |
| 105 StreamParser::BufferQueue processed_frames_; |
| 106 |
94 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer); | 107 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer); |
95 }; | 108 }; |
96 | 109 |
97 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) | 110 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) |
98 : last_decode_timestamp_(kNoTimestamp()), | 111 : last_decode_timestamp_(kNoTimestamp()), |
99 last_frame_duration_(kNoTimestamp()), | 112 last_frame_duration_(kNoTimestamp()), |
100 highest_presentation_timestamp_(kNoTimestamp()), | 113 highest_presentation_timestamp_(kNoTimestamp()), |
101 needs_random_access_point_(true), | 114 needs_random_access_point_(true), |
102 stream_(stream) { | 115 stream_(stream) { |
103 DCHECK(stream_); | 116 DCHECK(stream_); |
(...skipping 13 matching lines...) Expand all Loading... |
117 } | 130 } |
118 | 131 |
119 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased( | 132 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased( |
120 base::TimeDelta timestamp) { | 133 base::TimeDelta timestamp) { |
121 if (highest_presentation_timestamp_ == kNoTimestamp() || | 134 if (highest_presentation_timestamp_ == kNoTimestamp() || |
122 timestamp > highest_presentation_timestamp_) { | 135 timestamp > highest_presentation_timestamp_) { |
123 highest_presentation_timestamp_ = timestamp; | 136 highest_presentation_timestamp_ = timestamp; |
124 } | 137 } |
125 } | 138 } |
126 | 139 |
| 140 void MseTrackBuffer::EnqueueProcessedFrame( |
| 141 const scoped_refptr<StreamParserBuffer>& frame) { |
| 142 processed_frames_.push_back(frame); |
| 143 } |
| 144 |
| 145 bool MseTrackBuffer::FlushProcessedFrames() { |
| 146 if (processed_frames_.empty()) |
| 147 return true; |
| 148 |
| 149 bool result = stream_->Append(processed_frames_); |
| 150 processed_frames_.clear(); |
| 151 DVLOG_IF(3, !result) << __FUNCTION__ |
| 152 << "(): Failure appending processed frames to stream"; |
| 153 |
| 154 return result; |
| 155 } |
| 156 |
127 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb) | 157 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb) |
128 : sequence_mode_(false), | 158 : sequence_mode_(false), |
129 group_start_timestamp_(kNoTimestamp()), | 159 group_start_timestamp_(kNoTimestamp()), |
130 update_duration_cb_(update_duration_cb) { | 160 update_duration_cb_(update_duration_cb) { |
131 DVLOG(2) << __FUNCTION__ << "()"; | 161 DVLOG(2) << __FUNCTION__ << "()"; |
132 DCHECK(!update_duration_cb.is_null()); | 162 DCHECK(!update_duration_cb.is_null()); |
133 } | 163 } |
134 | 164 |
135 FrameProcessor::~FrameProcessor() { | 165 FrameProcessor::~FrameProcessor() { |
136 DVLOG(2) << __FUNCTION__ << "()"; | 166 DVLOG(2) << __FUNCTION__ << "()"; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 // Note that ProcessFrame() implements an inner loop for a single frame that | 203 // Note that ProcessFrame() implements an inner loop for a single frame that |
174 // handles "jump to the Loop Top step to restart processing of the current | 204 // handles "jump to the Loop Top step to restart processing of the current |
175 // coded frame" per April 1, 2014 MSE spec editor's draft: | 205 // coded frame" per April 1, 2014 MSE spec editor's draft: |
176 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 206 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
177 // media-source.html#sourcebuffer-coded-frame-processing | 207 // media-source.html#sourcebuffer-coded-frame-processing |
178 // 1. For each coded frame in the media segment run the following steps: | 208 // 1. For each coded frame in the media segment run the following steps: |
179 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); | 209 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); |
180 frames_itr != frames.end(); ++frames_itr) { | 210 frames_itr != frames.end(); ++frames_itr) { |
181 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, | 211 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, |
182 timestamp_offset, new_media_segment)) { | 212 timestamp_offset, new_media_segment)) { |
| 213 FlushProcessedFrames(); |
183 return false; | 214 return false; |
184 } | 215 } |
185 } | 216 } |
186 | 217 |
| 218 if (!FlushProcessedFrames()) |
| 219 return false; |
| 220 |
187 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 221 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. |
188 | 222 |
189 // Step 5: | 223 // Step 5: |
190 update_duration_cb_.Run(group_end_timestamp_); | 224 update_duration_cb_.Run(group_end_timestamp_); |
191 | 225 |
192 return true; | 226 return true; |
193 } | 227 } |
194 | 228 |
195 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( | 229 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( |
196 base::TimeDelta timestamp_offset) { | 230 base::TimeDelta timestamp_offset) { |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
271 base::TimeDelta segment_timestamp) { | 305 base::TimeDelta segment_timestamp) { |
272 DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; | 306 DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; |
273 | 307 |
274 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 308 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
275 itr != track_buffers_.end(); | 309 itr != track_buffers_.end(); |
276 ++itr) { | 310 ++itr) { |
277 itr->second->stream()->OnNewMediaSegment(segment_timestamp); | 311 itr->second->stream()->OnNewMediaSegment(segment_timestamp); |
278 } | 312 } |
279 } | 313 } |
280 | 314 |
| 315 bool FrameProcessor::FlushProcessedFrames() { |
| 316 DVLOG(2) << __FUNCTION__ << "()"; |
| 317 |
| 318 bool result = true; |
| 319 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
| 320 itr != track_buffers_.end(); |
| 321 ++itr) { |
| 322 if (!itr->second->FlushProcessedFrames()) |
| 323 result = false; |
| 324 } |
| 325 |
| 326 return result; |
| 327 } |
| 328 |
281 bool FrameProcessor::HandlePartialAppendWindowTrimming( | 329 bool FrameProcessor::HandlePartialAppendWindowTrimming( |
282 base::TimeDelta append_window_start, | 330 base::TimeDelta append_window_start, |
283 base::TimeDelta append_window_end, | 331 base::TimeDelta append_window_end, |
284 const scoped_refptr<StreamParserBuffer>& buffer) { | 332 const scoped_refptr<StreamParserBuffer>& buffer) { |
285 DCHECK(buffer->duration() > base::TimeDelta()); | 333 DCHECK(buffer->duration() > base::TimeDelta()); |
286 DCHECK_EQ(DemuxerStream::AUDIO, buffer->type()); | 334 DCHECK_EQ(DemuxerStream::AUDIO, buffer->type()); |
287 | 335 |
288 const base::TimeDelta frame_end_timestamp = | 336 const base::TimeDelta frame_end_timestamp = |
289 buffer->timestamp() + buffer->duration(); | 337 buffer->timestamp() + buffer->duration(); |
290 | 338 |
(...skipping 297 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
588 } | 636 } |
589 | 637 |
590 // 12.2. Set the need random access point flag on track buffer to false. | 638 // 12.2. Set the need random access point flag on track buffer to false. |
591 track_buffer->set_needs_random_access_point(false); | 639 track_buffer->set_needs_random_access_point(false); |
592 } | 640 } |
593 | 641 |
594 // We now have a processed buffer to append to the track buffer's stream. | 642 // We now have a processed buffer to append to the track buffer's stream. |
595 // If it is the first in a new media segment or following a discontinuity, | 643 // If it is the first in a new media segment or following a discontinuity, |
596 // notify all the track buffers' streams that a new segment is beginning. | 644 // notify all the track buffers' streams that a new segment is beginning. |
597 if (*new_media_segment) { | 645 if (*new_media_segment) { |
| 646 // First, complete the append to track buffer streams of previous media |
| 647 // segment's frames, if any. |
| 648 if (!FlushProcessedFrames()) |
| 649 return false; |
| 650 |
598 *new_media_segment = false; | 651 *new_media_segment = false; |
599 NotifyNewMediaSegmentStarting(decode_timestamp); | 652 NotifyNewMediaSegmentStarting(decode_timestamp); |
600 } | 653 } |
601 | 654 |
602 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " | 655 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " |
603 << "PTS=" << presentation_timestamp.InSecondsF() | 656 << "PTS=" << presentation_timestamp.InSecondsF() |
604 << ", DTS=" << decode_timestamp.InSecondsF(); | 657 << ", DTS=" << decode_timestamp.InSecondsF(); |
605 | 658 |
606 // Steps 13-18: | 659 // Steps 13-18: Note, we optimize by appending groups of contiguous |
607 // TODO(wolenetz): Collect and emit more than one buffer at a time, if | 660 // processed frames for each track buffer at end of ProcessFrames() or prior |
608 // possible. Also refactor SourceBufferStream to conform to spec GC timing. | 661 // to NotifyNewMediaSegmentStarting(). |
| 662 // TODO(wolenetz): Refactor SourceBufferStream to conform to spec GC timing. |
609 // See http://crbug.com/371197. | 663 // See http://crbug.com/371197. |
610 StreamParser::BufferQueue buffer_to_append; | 664 track_buffer->EnqueueProcessedFrame(frame); |
611 buffer_to_append.push_back(frame); | |
612 if (!track_buffer->stream()->Append(buffer_to_append)) { | |
613 DVLOG(3) << __FUNCTION__ << ": Failure appending frame to stream"; | |
614 return false; | |
615 } | |
616 | 665 |
617 // 19. Set last decode timestamp for track buffer to decode timestamp. | 666 // 19. Set last decode timestamp for track buffer to decode timestamp. |
618 track_buffer->set_last_decode_timestamp(decode_timestamp); | 667 track_buffer->set_last_decode_timestamp(decode_timestamp); |
619 | 668 |
620 // 20. Set last frame duration for track buffer to frame duration. | 669 // 20. Set last frame duration for track buffer to frame duration. |
621 track_buffer->set_last_frame_duration(frame_duration); | 670 track_buffer->set_last_frame_duration(frame_duration); |
622 | 671 |
623 // 21. If highest presentation timestamp for track buffer is unset or frame | 672 // 21. If highest presentation timestamp for track buffer is unset or frame |
624 // end timestamp is greater than highest presentation timestamp, then | 673 // end timestamp is greater than highest presentation timestamp, then |
625 // set highest presentation timestamp for track buffer to frame end | 674 // set highest presentation timestamp for track buffer to frame end |
626 // timestamp. | 675 // timestamp. |
627 track_buffer->SetHighestPresentationTimestampIfIncreased( | 676 track_buffer->SetHighestPresentationTimestampIfIncreased( |
628 frame_end_timestamp); | 677 frame_end_timestamp); |
629 | 678 |
630 // 22. If frame end timestamp is greater than group end timestamp, then set | 679 // 22. If frame end timestamp is greater than group end timestamp, then set |
631 // group end timestamp equal to frame end timestamp. | 680 // group end timestamp equal to frame end timestamp. |
632 if (frame_end_timestamp > group_end_timestamp_) | 681 if (frame_end_timestamp > group_end_timestamp_) |
633 group_end_timestamp_ = frame_end_timestamp; | 682 group_end_timestamp_ = frame_end_timestamp; |
634 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 683 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
635 | 684 |
636 return true; | 685 return true; |
637 } | 686 } |
638 | 687 |
639 NOTREACHED(); | 688 NOTREACHED(); |
640 return false; | 689 return false; |
641 } | 690 } |
642 | 691 |
643 } // namespace media | 692 } // namespace media |
OLD | NEW |