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 <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <cstdlib> | 9 #include <cstdlib> |
10 | 10 |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
190 // Step 8: Update the attribute to new mode. | 190 // Step 8: Update the attribute to new mode. |
191 sequence_mode_ = sequence_mode; | 191 sequence_mode_ = sequence_mode; |
192 } | 192 } |
193 | 193 |
194 bool FrameProcessor::ProcessFrames( | 194 bool FrameProcessor::ProcessFrames( |
195 const StreamParser::BufferQueue& audio_buffers, | 195 const StreamParser::BufferQueue& audio_buffers, |
196 const StreamParser::BufferQueue& video_buffers, | 196 const StreamParser::BufferQueue& video_buffers, |
197 const StreamParser::TextBufferQueueMap& text_map, | 197 const StreamParser::TextBufferQueueMap& text_map, |
198 base::TimeDelta append_window_start, | 198 base::TimeDelta append_window_start, |
199 base::TimeDelta append_window_end, | 199 base::TimeDelta append_window_end, |
200 bool* new_media_segment, | |
201 base::TimeDelta* timestamp_offset) { | 200 base::TimeDelta* timestamp_offset) { |
202 StreamParser::BufferQueue frames; | 201 StreamParser::BufferQueue frames; |
203 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { | 202 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { |
204 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; | 203 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; |
205 return false; | 204 return false; |
206 } | 205 } |
207 | 206 |
208 DCHECK(!frames.empty()); | 207 DCHECK(!frames.empty()); |
209 | 208 |
210 // Implements the coded frame processing algorithm's outer loop for step 1. | 209 // 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 | 210 // 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 | 211 // 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: | 212 // coded frame" per April 1, 2014 MSE spec editor's draft: |
214 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 213 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
215 // media-source.html#sourcebuffer-coded-frame-processing | 214 // media-source.html#sourcebuffer-coded-frame-processing |
216 // 1. For each coded frame in the media segment run the following steps: | 215 // 1. For each coded frame in the media segment run the following steps: |
217 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); | 216 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); |
218 frames_itr != frames.end(); ++frames_itr) { | 217 frames_itr != frames.end(); ++frames_itr) { |
219 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, | 218 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, |
220 timestamp_offset, new_media_segment)) { | 219 timestamp_offset)) { |
221 FlushProcessedFrames(); | 220 FlushProcessedFrames(); |
222 return false; | 221 return false; |
223 } | 222 } |
224 } | 223 } |
225 | 224 |
226 if (!FlushProcessedFrames()) | 225 if (!FlushProcessedFrames()) |
227 return false; | 226 return false; |
228 | 227 |
229 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 228 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. |
230 | 229 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
287 void FrameProcessor::Reset() { | 286 void FrameProcessor::Reset() { |
288 DVLOG(2) << __FUNCTION__ << "()"; | 287 DVLOG(2) << __FUNCTION__ << "()"; |
289 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 288 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
290 itr != track_buffers_.end(); ++itr) { | 289 itr != track_buffers_.end(); ++itr) { |
291 itr->second->Reset(); | 290 itr->second->Reset(); |
292 } | 291 } |
293 | 292 |
294 if (sequence_mode_) { | 293 if (sequence_mode_) { |
295 DCHECK(kNoTimestamp() != group_end_timestamp_); | 294 DCHECK(kNoTimestamp() != group_end_timestamp_); |
296 group_start_timestamp_ = group_end_timestamp_; | 295 group_start_timestamp_ = group_end_timestamp_; |
296 return; | |
297 } | 297 } |
298 | |
299 in_coded_frame_group_ = false; | |
chcunningham
2016/01/22 01:26:37
Do you not reset this in sequence mode? (return a
wolenetz
2016/01/22 01:50:03
Correct. Sequence mode means everything appended i
wolenetz
2016/01/22 18:59:31
Done.
chcunningham
2016/01/22 19:26:02
Acknowledged.
| |
298 } | 300 } |
299 | 301 |
300 void FrameProcessor::OnPossibleAudioConfigUpdate( | 302 void FrameProcessor::OnPossibleAudioConfigUpdate( |
301 const AudioDecoderConfig& config) { | 303 const AudioDecoderConfig& config) { |
302 DCHECK(config.IsValidConfig()); | 304 DCHECK(config.IsValidConfig()); |
303 | 305 |
304 // Always clear the preroll buffer when a config update is received. | 306 // Always clear the preroll buffer when a config update is received. |
305 audio_preroll_buffer_ = NULL; | 307 audio_preroll_buffer_ = NULL; |
306 | 308 |
307 if (config.Matches(current_audio_config_)) | 309 if (config.Matches(current_audio_config_)) |
308 return; | 310 return; |
309 | 311 |
310 current_audio_config_ = config; | 312 current_audio_config_ = config; |
311 sample_duration_ = base::TimeDelta::FromSecondsD( | 313 sample_duration_ = base::TimeDelta::FromSecondsD( |
312 1.0 / current_audio_config_.samples_per_second()); | 314 1.0 / current_audio_config_.samples_per_second()); |
313 } | 315 } |
314 | 316 |
315 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { | 317 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { |
316 TrackBufferMap::iterator itr = track_buffers_.find(id); | 318 TrackBufferMap::iterator itr = track_buffers_.find(id); |
317 if (itr == track_buffers_.end()) | 319 if (itr == track_buffers_.end()) |
318 return NULL; | 320 return NULL; |
319 | 321 |
320 return itr->second; | 322 return itr->second; |
321 } | 323 } |
322 | 324 |
323 void FrameProcessor::NotifyNewMediaSegmentStarting( | 325 void FrameProcessor::NotifyStartOfCodedFrameGroup( |
324 DecodeTimestamp segment_timestamp) { | 326 DecodeTimestamp start_timestamp) { |
325 DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; | 327 DVLOG(2) << __FUNCTION__ << "(" << start_timestamp.InSecondsF() << ")"; |
326 | 328 |
327 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 329 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
328 itr != track_buffers_.end(); | 330 itr != track_buffers_.end(); |
329 ++itr) { | 331 ++itr) { |
330 itr->second->stream()->OnNewMediaSegment(segment_timestamp); | 332 itr->second->stream()->OnStartOfCodedFrameGroup(start_timestamp); |
331 } | 333 } |
332 } | 334 } |
333 | 335 |
334 bool FrameProcessor::FlushProcessedFrames() { | 336 bool FrameProcessor::FlushProcessedFrames() { |
335 DVLOG(2) << __FUNCTION__ << "()"; | 337 DVLOG(2) << __FUNCTION__ << "()"; |
336 | 338 |
337 bool result = true; | 339 bool result = true; |
338 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 340 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
339 itr != track_buffers_.end(); | 341 itr != track_buffers_.end(); |
340 ++itr) { | 342 ++itr) { |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
441 processed_buffer = true; | 443 processed_buffer = true; |
442 } | 444 } |
443 | 445 |
444 return processed_buffer; | 446 return processed_buffer; |
445 } | 447 } |
446 | 448 |
447 bool FrameProcessor::ProcessFrame( | 449 bool FrameProcessor::ProcessFrame( |
448 const scoped_refptr<StreamParserBuffer>& frame, | 450 const scoped_refptr<StreamParserBuffer>& frame, |
449 base::TimeDelta append_window_start, | 451 base::TimeDelta append_window_start, |
450 base::TimeDelta append_window_end, | 452 base::TimeDelta append_window_end, |
451 base::TimeDelta* timestamp_offset, | 453 base::TimeDelta* timestamp_offset) { |
452 bool* new_media_segment) { | |
453 // Implements the loop within step 1 of the coded frame processing algorithm | 454 // Implements the loop within step 1 of the coded frame processing algorithm |
454 // for a single input frame per April 1, 2014 MSE spec editor's draft: | 455 // for a single input frame per April 1, 2014 MSE spec editor's draft: |
455 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 456 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
456 // media-source.html#sourcebuffer-coded-frame-processing | 457 // media-source.html#sourcebuffer-coded-frame-processing |
457 | 458 |
458 while (true) { | 459 while (true) { |
459 // 1. Loop Top: Let presentation timestamp be a double precision floating | 460 // 1. Loop Top: Let presentation timestamp be a double precision floating |
460 // point representation of the coded frame's presentation timestamp in | 461 // point representation of the coded frame's presentation timestamp in |
461 // seconds. | 462 // seconds. |
462 // 2. Let decode timestamp be a double precision floating point | 463 // 2. Let decode timestamp be a double precision floating point |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
580 << ", and parser track id " << frame->track_id(); | 581 << ", and parser track id " << frame->track_id(); |
581 return false; | 582 return false; |
582 } | 583 } |
583 | 584 |
584 // 7. If last decode timestamp for track buffer is set and decode timestamp | 585 // 7. If last decode timestamp for track buffer is set and decode timestamp |
585 // is less than last decode timestamp | 586 // is less than last decode timestamp |
586 // OR | 587 // OR |
587 // If last decode timestamp for track buffer is set and the difference | 588 // If last decode timestamp for track buffer is set and the difference |
588 // between decode timestamp and last decode timestamp is greater than 2 | 589 // between decode timestamp and last decode timestamp is greater than 2 |
589 // times last frame duration: | 590 // times last frame duration: |
590 DecodeTimestamp last_decode_timestamp = | 591 DecodeTimestamp track_last_decode_timestamp = |
591 track_buffer->last_decode_timestamp(); | 592 track_buffer->last_decode_timestamp(); |
592 if (last_decode_timestamp != kNoDecodeTimestamp()) { | 593 if (track_last_decode_timestamp != kNoDecodeTimestamp()) { |
593 base::TimeDelta dts_delta = decode_timestamp - last_decode_timestamp; | 594 base::TimeDelta track_dts_delta = |
594 if (dts_delta < base::TimeDelta() || | 595 decode_timestamp - track_last_decode_timestamp; |
595 dts_delta > 2 * track_buffer->last_frame_duration()) { | 596 if (track_dts_delta < base::TimeDelta() || |
597 track_dts_delta > 2 * track_buffer->last_frame_duration()) { | |
598 DCHECK(in_coded_frame_group_); | |
596 // 7.1. If mode equals "segments": Set group end timestamp to | 599 // 7.1. If mode equals "segments": Set group end timestamp to |
597 // presentation timestamp. | 600 // presentation timestamp. |
598 // If mode equals "sequence": Set group start timestamp equal to | 601 // If mode equals "sequence": Set group start timestamp equal to |
599 // the group end timestamp. | 602 // the group end timestamp. |
600 if (!sequence_mode_) { | 603 if (!sequence_mode_) { |
601 group_end_timestamp_ = presentation_timestamp; | 604 group_end_timestamp_ = presentation_timestamp; |
602 // This triggers a discontinuity so we need to treat the next frames | 605 // This triggers a discontinuity so we need to treat the next frames |
603 // appended within the append window as if they were the beginning of | 606 // appended within the append window as if they were the beginning of |
604 // a new segment. | 607 // a new coded frame group. |
605 *new_media_segment = true; | 608 in_coded_frame_group_ = false; |
606 } else { | 609 } else { |
607 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " | 610 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " |
608 << group_end_timestamp_.InSecondsF(); | 611 << group_end_timestamp_.InSecondsF(); |
609 DCHECK(kNoTimestamp() != group_end_timestamp_); | 612 DCHECK(kNoTimestamp() != group_end_timestamp_); |
610 group_start_timestamp_ = group_end_timestamp_; | 613 group_start_timestamp_ = group_end_timestamp_; |
611 } | 614 } |
612 | 615 |
613 // 7.2. - 7.5.: | 616 // 7.2. - 7.5.: |
614 Reset(); | 617 Reset(); |
615 | 618 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
685 DVLOG(3) << __FUNCTION__ | 688 DVLOG(3) << __FUNCTION__ |
686 << ": Dropping frame that is not a random access point"; | 689 << ": Dropping frame that is not a random access point"; |
687 return true; | 690 return true; |
688 } | 691 } |
689 | 692 |
690 // 12.2. Set the need random access point flag on track buffer to false. | 693 // 12.2. Set the need random access point flag on track buffer to false. |
691 track_buffer->set_needs_random_access_point(false); | 694 track_buffer->set_needs_random_access_point(false); |
692 } | 695 } |
693 | 696 |
694 // We now have a processed buffer to append to the track buffer's stream. | 697 // We now have a processed buffer to append to the track buffer's stream. |
695 // If it is the first in a new media segment or following a discontinuity, | 698 // If it is the first in a new coded frame group (such as following a |
696 // notify all the track buffers' streams that a new segment is beginning. | 699 // discontinuity), notify all the track buffers' streams that a coded frame |
697 if (*new_media_segment) { | 700 // group is starting. |
698 // First, complete the append to track buffer streams of previous media | 701 if (!in_coded_frame_group_) { |
699 // segment's frames, if any. | 702 // First, complete the append to track buffer streams of the previous |
703 // coded frame group's frames, if any. | |
700 if (!FlushProcessedFrames()) | 704 if (!FlushProcessedFrames()) |
701 return false; | 705 return false; |
702 | 706 |
703 *new_media_segment = false; | |
704 | |
705 // TODO(acolwell/wolenetz): This should be changed to a presentation | 707 // TODO(acolwell/wolenetz): This should be changed to a presentation |
706 // timestamp. See http://crbug.com/402502 | 708 // timestamp. See http://crbug.com/402502 |
707 NotifyNewMediaSegmentStarting(decode_timestamp); | 709 NotifyStartOfCodedFrameGroup(decode_timestamp); |
710 in_coded_frame_group_ = true; | |
708 } | 711 } |
709 | 712 |
710 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " | 713 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " |
711 << "PTS=" << presentation_timestamp.InSecondsF() | 714 << "PTS=" << presentation_timestamp.InSecondsF() |
712 << ", DTS=" << decode_timestamp.InSecondsF(); | 715 << ", DTS=" << decode_timestamp.InSecondsF(); |
713 | 716 |
714 // Steps 13-18: Note, we optimize by appending groups of contiguous | 717 // Steps 13-18: Note, we optimize by appending groups of contiguous |
715 // processed frames for each track buffer at end of ProcessFrames() or prior | 718 // processed frames for each track buffer at end of ProcessFrames() or prior |
716 // to NotifyNewMediaSegmentStarting(). | 719 // to NotifyStartOfCodedFrameGroup(). |
717 // TODO(wolenetz): Refactor SourceBufferStream to conform to spec GC timing. | |
718 // See http://crbug.com/371197. | |
719 track_buffer->EnqueueProcessedFrame(frame); | 720 track_buffer->EnqueueProcessedFrame(frame); |
720 | 721 |
721 // 19. Set last decode timestamp for track buffer to decode timestamp. | 722 // 19. Set last decode timestamp for track buffer to decode timestamp. |
722 track_buffer->set_last_decode_timestamp(decode_timestamp); | 723 track_buffer->set_last_decode_timestamp(decode_timestamp); |
723 | 724 |
724 // 20. Set last frame duration for track buffer to frame duration. | 725 // 20. Set last frame duration for track buffer to frame duration. |
725 track_buffer->set_last_frame_duration(frame_duration); | 726 track_buffer->set_last_frame_duration(frame_duration); |
726 | 727 |
727 // 21. If highest presentation timestamp for track buffer is unset or frame | 728 // 21. If highest presentation timestamp for track buffer is unset or frame |
728 // end timestamp is greater than highest presentation timestamp, then | 729 // end timestamp is greater than highest presentation timestamp, then |
729 // set highest presentation timestamp for track buffer to frame end | 730 // set highest presentation timestamp for track buffer to frame end |
730 // timestamp. | 731 // timestamp. |
731 track_buffer->SetHighestPresentationTimestampIfIncreased( | 732 track_buffer->SetHighestPresentationTimestampIfIncreased( |
732 frame_end_timestamp); | 733 frame_end_timestamp); |
733 | 734 |
734 // 22. If frame end timestamp is greater than group end timestamp, then set | 735 // 22. If frame end timestamp is greater than group end timestamp, then set |
735 // group end timestamp equal to frame end timestamp. | 736 // group end timestamp equal to frame end timestamp. |
736 if (frame_end_timestamp > group_end_timestamp_) | 737 if (frame_end_timestamp > group_end_timestamp_) |
737 group_end_timestamp_ = frame_end_timestamp; | 738 group_end_timestamp_ = frame_end_timestamp; |
738 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 739 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
739 | 740 |
740 return true; | 741 return true; |
741 } | 742 } |
742 | 743 |
743 NOTREACHED(); | 744 NOTREACHED(); |
744 return false; | 745 return false; |
745 } | 746 } |
746 | 747 |
747 } // namespace media | 748 } // namespace media |
OLD | NEW |