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