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