| 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/stream_parser_buffer.h" | 10 #include "media/base/stream_parser_buffer.h" |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 | 154 |
| 155 DVLOG_IF(3, !result) << __FUNCTION__ | 155 DVLOG_IF(3, !result) << __FUNCTION__ |
| 156 << "(): Failure appending processed frames to stream"; | 156 << "(): Failure appending processed frames to stream"; |
| 157 | 157 |
| 158 return result; | 158 return result; |
| 159 } | 159 } |
| 160 | 160 |
| 161 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb, | 161 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb, |
| 162 const scoped_refptr<MediaLog>& media_log) | 162 const scoped_refptr<MediaLog>& media_log) |
| 163 : group_start_timestamp_(kNoTimestamp()), | 163 : group_start_timestamp_(kNoTimestamp()), |
| 164 last_processed_decode_timestamp_(kNoDecodeTimestamp()), |
| 164 update_duration_cb_(update_duration_cb), | 165 update_duration_cb_(update_duration_cb), |
| 165 media_log_(media_log) { | 166 media_log_(media_log) { |
| 166 DVLOG(2) << __FUNCTION__ << "()"; | 167 DVLOG(2) << __FUNCTION__ << "()"; |
| 167 DCHECK(!update_duration_cb.is_null()); | 168 DCHECK(!update_duration_cb.is_null()); |
| 168 } | 169 } |
| 169 | 170 |
| 170 FrameProcessor::~FrameProcessor() { | 171 FrameProcessor::~FrameProcessor() { |
| 171 DVLOG(2) << __FUNCTION__ << "()"; | 172 DVLOG(2) << __FUNCTION__ << "()"; |
| 172 STLDeleteValues(&track_buffers_); | 173 STLDeleteValues(&track_buffers_); |
| 173 } | 174 } |
| (...skipping 13 matching lines...) Expand all Loading... |
| 187 // Step 8: Update the attribute to new mode. | 188 // Step 8: Update the attribute to new mode. |
| 188 sequence_mode_ = sequence_mode; | 189 sequence_mode_ = sequence_mode; |
| 189 } | 190 } |
| 190 | 191 |
| 191 bool FrameProcessor::ProcessFrames( | 192 bool FrameProcessor::ProcessFrames( |
| 192 const StreamParser::BufferQueue& audio_buffers, | 193 const StreamParser::BufferQueue& audio_buffers, |
| 193 const StreamParser::BufferQueue& video_buffers, | 194 const StreamParser::BufferQueue& video_buffers, |
| 194 const StreamParser::TextBufferQueueMap& text_map, | 195 const StreamParser::TextBufferQueueMap& text_map, |
| 195 base::TimeDelta append_window_start, | 196 base::TimeDelta append_window_start, |
| 196 base::TimeDelta append_window_end, | 197 base::TimeDelta append_window_end, |
| 197 bool* new_media_segment, | |
| 198 base::TimeDelta* timestamp_offset) { | 198 base::TimeDelta* timestamp_offset) { |
| 199 StreamParser::BufferQueue frames; | 199 StreamParser::BufferQueue frames; |
| 200 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { | 200 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { |
| 201 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; | 201 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; |
| 202 return false; | 202 return false; |
| 203 } | 203 } |
| 204 | 204 |
| 205 DCHECK(!frames.empty()); | 205 DCHECK(!frames.empty()); |
| 206 | 206 |
| 207 // Implements the coded frame processing algorithm's outer loop for step 1. | 207 // Implements the coded frame processing algorithm's outer loop for step 1. |
| 208 // Note that ProcessFrame() implements an inner loop for a single frame that | 208 // Note that ProcessFrame() implements an inner loop for a single frame that |
| 209 // handles "jump to the Loop Top step to restart processing of the current | 209 // handles "jump to the Loop Top step to restart processing of the current |
| 210 // coded frame" per April 1, 2014 MSE spec editor's draft: | 210 // coded frame" per April 1, 2014 MSE spec editor's draft: |
| 211 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 211 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
| 212 // media-source.html#sourcebuffer-coded-frame-processing | 212 // media-source.html#sourcebuffer-coded-frame-processing |
| 213 // 1. For each coded frame in the media segment run the following steps: | 213 // 1. For each coded frame in the media segment run the following steps: |
| 214 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); | 214 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); |
| 215 frames_itr != frames.end(); ++frames_itr) { | 215 frames_itr != frames.end(); ++frames_itr) { |
| 216 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, | 216 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, |
| 217 timestamp_offset, new_media_segment)) { | 217 timestamp_offset)) { |
| 218 FlushProcessedFrames(); | 218 FlushProcessedFrames(); |
| 219 return false; | 219 return false; |
| 220 } | 220 } |
| 221 } | 221 } |
| 222 | 222 |
| 223 if (!FlushProcessedFrames()) | 223 if (!FlushProcessedFrames()) |
| 224 return false; | 224 return false; |
| 225 | 225 |
| 226 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 226 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. |
| 227 | 227 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 itr->second->set_needs_random_access_point(true); | 280 itr->second->set_needs_random_access_point(true); |
| 281 } | 281 } |
| 282 } | 282 } |
| 283 | 283 |
| 284 void FrameProcessor::Reset() { | 284 void FrameProcessor::Reset() { |
| 285 DVLOG(2) << __FUNCTION__ << "()"; | 285 DVLOG(2) << __FUNCTION__ << "()"; |
| 286 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 286 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
| 287 itr != track_buffers_.end(); ++itr) { | 287 itr != track_buffers_.end(); ++itr) { |
| 288 itr->second->Reset(); | 288 itr->second->Reset(); |
| 289 } | 289 } |
| 290 |
| 291 last_processed_decode_timestamp_ = kNoDecodeTimestamp(); |
| 292 in_coded_frame_group_ = |
| 293 false; // BIG TODO: Does this need to be conditioned on appendMode? |
| 290 } | 294 } |
| 291 | 295 |
| 292 void FrameProcessor::OnPossibleAudioConfigUpdate( | 296 void FrameProcessor::OnPossibleAudioConfigUpdate( |
| 293 const AudioDecoderConfig& config) { | 297 const AudioDecoderConfig& config) { |
| 294 DCHECK(config.IsValidConfig()); | 298 DCHECK(config.IsValidConfig()); |
| 295 | 299 |
| 296 // Always clear the preroll buffer when a config update is received. | 300 // Always clear the preroll buffer when a config update is received. |
| 297 audio_preroll_buffer_ = NULL; | 301 audio_preroll_buffer_ = NULL; |
| 298 | 302 |
| 299 if (config.Matches(current_audio_config_)) | 303 if (config.Matches(current_audio_config_)) |
| 300 return; | 304 return; |
| 301 | 305 |
| 302 current_audio_config_ = config; | 306 current_audio_config_ = config; |
| 303 sample_duration_ = base::TimeDelta::FromSecondsD( | 307 sample_duration_ = base::TimeDelta::FromSecondsD( |
| 304 1.0 / current_audio_config_.samples_per_second()); | 308 1.0 / current_audio_config_.samples_per_second()); |
| 305 } | 309 } |
| 306 | 310 |
| 307 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { | 311 MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { |
| 308 TrackBufferMap::iterator itr = track_buffers_.find(id); | 312 TrackBufferMap::iterator itr = track_buffers_.find(id); |
| 309 if (itr == track_buffers_.end()) | 313 if (itr == track_buffers_.end()) |
| 310 return NULL; | 314 return NULL; |
| 311 | 315 |
| 312 return itr->second; | 316 return itr->second; |
| 313 } | 317 } |
| 314 | 318 |
| 315 void FrameProcessor::NotifyNewMediaSegmentStarting( | 319 void FrameProcessor::NotifyStartOfCodedFrameGroup( |
| 316 DecodeTimestamp segment_timestamp) { | 320 DecodeTimestamp start_timestamp) { |
| 317 DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; | 321 DVLOG(2) << __FUNCTION__ << "(" << start_timestamp.InSecondsF() << ")"; |
| 318 | 322 |
| 319 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 323 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
| 320 itr != track_buffers_.end(); | 324 itr != track_buffers_.end(); |
| 321 ++itr) { | 325 ++itr) { |
| 322 itr->second->stream()->OnNewMediaSegment(segment_timestamp); | 326 itr->second->stream()->OnStartOfCodedFrameGroup(start_timestamp); |
| 323 } | 327 } |
| 324 } | 328 } |
| 325 | 329 |
| 326 bool FrameProcessor::FlushProcessedFrames() { | 330 bool FrameProcessor::FlushProcessedFrames() { |
| 327 DVLOG(2) << __FUNCTION__ << "()"; | 331 DVLOG(2) << __FUNCTION__ << "()"; |
| 328 | 332 |
| 329 bool result = true; | 333 bool result = true; |
| 330 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 334 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
| 331 itr != track_buffers_.end(); | 335 itr != track_buffers_.end(); |
| 332 ++itr) { | 336 ++itr) { |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 433 processed_buffer = true; | 437 processed_buffer = true; |
| 434 } | 438 } |
| 435 | 439 |
| 436 return processed_buffer; | 440 return processed_buffer; |
| 437 } | 441 } |
| 438 | 442 |
| 439 bool FrameProcessor::ProcessFrame( | 443 bool FrameProcessor::ProcessFrame( |
| 440 const scoped_refptr<StreamParserBuffer>& frame, | 444 const scoped_refptr<StreamParserBuffer>& frame, |
| 441 base::TimeDelta append_window_start, | 445 base::TimeDelta append_window_start, |
| 442 base::TimeDelta append_window_end, | 446 base::TimeDelta append_window_end, |
| 443 base::TimeDelta* timestamp_offset, | 447 base::TimeDelta* timestamp_offset) { |
| 444 bool* new_media_segment) { | |
| 445 // Implements the loop within step 1 of the coded frame processing algorithm | 448 // Implements the loop within step 1 of the coded frame processing algorithm |
| 446 // for a single input frame per April 1, 2014 MSE spec editor's draft: | 449 // for a single input frame per April 1, 2014 MSE spec editor's draft: |
| 447 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 450 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
| 448 // media-source.html#sourcebuffer-coded-frame-processing | 451 // media-source.html#sourcebuffer-coded-frame-processing |
| 449 | 452 |
| 450 while (true) { | 453 while (true) { |
| 451 // 1. Loop Top: Let presentation timestamp be a double precision floating | 454 // 1. Loop Top: Let presentation timestamp be a double precision floating |
| 452 // point representation of the coded frame's presentation timestamp in | 455 // point representation of the coded frame's presentation timestamp in |
| 453 // seconds. | 456 // seconds. |
| 454 // 2. Let decode timestamp be a double precision floating point | 457 // 2. Let decode timestamp be a double precision floating point |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 574 } | 577 } |
| 575 | 578 |
| 576 // 7. If last decode timestamp for track buffer is set and decode timestamp | 579 // 7. If last decode timestamp for track buffer is set and decode timestamp |
| 577 // is less than last decode timestamp | 580 // is less than last decode timestamp |
| 578 // OR | 581 // OR |
| 579 // If last decode timestamp for track buffer is set and the difference | 582 // If last decode timestamp for track buffer is set and the difference |
| 580 // between decode timestamp and last decode timestamp is greater than 2 | 583 // between decode timestamp and last decode timestamp is greater than 2 |
| 581 // times last frame duration: | 584 // times last frame duration: |
| 582 DecodeTimestamp last_decode_timestamp = | 585 DecodeTimestamp last_decode_timestamp = |
| 583 track_buffer->last_decode_timestamp(); | 586 track_buffer->last_decode_timestamp(); |
| 587 // BIG TODO: continue here, using last_processed_decode_timestamp_. And |
| 588 // conditionally resetting in_coded_frame_group_. And investigating how |
| 589 // common single-stream-cluster appends are in mp4 or other muxed byte |
| 590 // stream formats. |
| 584 if (last_decode_timestamp != kNoDecodeTimestamp()) { | 591 if (last_decode_timestamp != kNoDecodeTimestamp()) { |
| 585 base::TimeDelta dts_delta = decode_timestamp - last_decode_timestamp; | 592 base::TimeDelta dts_delta = decode_timestamp - last_decode_timestamp; |
| 586 if (dts_delta < base::TimeDelta() || | 593 if (dts_delta < base::TimeDelta() || |
| 587 dts_delta > 2 * track_buffer->last_frame_duration()) { | 594 dts_delta > 2 * track_buffer->last_frame_duration()) { |
| 588 // 7.1. If mode equals "segments": Set group end timestamp to | 595 // 7.1. If mode equals "segments": Set group end timestamp to |
| 589 // presentation timestamp. | 596 // presentation timestamp. |
| 590 // If mode equals "sequence": Set group start timestamp equal to | 597 // If mode equals "sequence": Set group start timestamp equal to |
| 591 // the group end timestamp. | 598 // the group end timestamp. |
| 592 if (!sequence_mode_) { | 599 if (!sequence_mode_) { |
| 593 group_end_timestamp_ = presentation_timestamp; | 600 group_end_timestamp_ = presentation_timestamp; |
| 594 // This triggers a discontinuity so we need to treat the next frames | 601 // This triggers a discontinuity so we need to treat the next frames |
| 595 // appended within the append window as if they were the beginning of | 602 // appended within the append window as if they were the beginning of |
| 596 // a new segment. | 603 // a new coded frame group. |
| 597 *new_media_segment = true; | 604 in_coded_frame_group_ = false; |
| 598 } else { | 605 } else { |
| 599 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " | 606 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " |
| 600 << group_end_timestamp_.InSecondsF(); | 607 << group_end_timestamp_.InSecondsF(); |
| 601 DCHECK(kNoTimestamp() != group_end_timestamp_); | 608 DCHECK(kNoTimestamp() != group_end_timestamp_); |
| 602 group_start_timestamp_ = group_end_timestamp_; | 609 group_start_timestamp_ = group_end_timestamp_; |
| 603 } | 610 } |
| 604 | 611 |
| 605 // 7.2. - 7.5.: | 612 // 7.2. - 7.5.: |
| 606 Reset(); | 613 Reset(); |
| 607 | 614 |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 DVLOG(3) << __FUNCTION__ | 684 DVLOG(3) << __FUNCTION__ |
| 678 << ": Dropping frame that is not a random access point"; | 685 << ": Dropping frame that is not a random access point"; |
| 679 return true; | 686 return true; |
| 680 } | 687 } |
| 681 | 688 |
| 682 // 12.2. Set the need random access point flag on track buffer to false. | 689 // 12.2. Set the need random access point flag on track buffer to false. |
| 683 track_buffer->set_needs_random_access_point(false); | 690 track_buffer->set_needs_random_access_point(false); |
| 684 } | 691 } |
| 685 | 692 |
| 686 // We now have a processed buffer to append to the track buffer's stream. | 693 // We now have a processed buffer to append to the track buffer's stream. |
| 687 // If it is the first in a new media segment or following a discontinuity, | 694 // If it is the first in a new coded frame group (such as following a |
| 688 // notify all the track buffers' streams that a new segment is beginning. | 695 // discontinuity), notify all the track buffers' streams that a coded frame |
| 689 if (*new_media_segment) { | 696 // group is starting. |
| 690 // First, complete the append to track buffer streams of previous media | 697 if (!in_coded_frame_group_) { |
| 691 // segment's frames, if any. | 698 // First, complete the append to track buffer streams of the previous |
| 699 // coded frame group's frames, if any. |
| 692 if (!FlushProcessedFrames()) | 700 if (!FlushProcessedFrames()) |
| 693 return false; | 701 return false; |
| 694 | 702 |
| 695 *new_media_segment = false; | |
| 696 | |
| 697 // TODO(acolwell/wolenetz): This should be changed to a presentation | 703 // TODO(acolwell/wolenetz): This should be changed to a presentation |
| 698 // timestamp. See http://crbug.com/402502 | 704 // timestamp. See http://crbug.com/402502 |
| 699 NotifyNewMediaSegmentStarting(decode_timestamp); | 705 NotifyStartOfCodedFrameGroup(decode_timestamp); |
| 706 in_coded_frame_group_ = true; |
| 700 } | 707 } |
| 701 | 708 |
| 702 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " | 709 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " |
| 703 << "PTS=" << presentation_timestamp.InSecondsF() | 710 << "PTS=" << presentation_timestamp.InSecondsF() |
| 704 << ", DTS=" << decode_timestamp.InSecondsF(); | 711 << ", DTS=" << decode_timestamp.InSecondsF(); |
| 705 | 712 |
| 706 // Steps 13-18: Note, we optimize by appending groups of contiguous | 713 // Steps 13-18: Note, we optimize by appending groups of contiguous |
| 707 // processed frames for each track buffer at end of ProcessFrames() or prior | 714 // processed frames for each track buffer at end of ProcessFrames() or prior |
| 708 // to NotifyNewMediaSegmentStarting(). | 715 // to NotifyStartOfCodedFrameGroup(). |
| 709 // TODO(wolenetz): Refactor SourceBufferStream to conform to spec GC timing. | 716 // TODO(wolenetz): Refactor SourceBufferStream to conform to spec GC timing. |
| 710 // See http://crbug.com/371197. | 717 // See http://crbug.com/371197. |
| 711 track_buffer->EnqueueProcessedFrame(frame); | 718 track_buffer->EnqueueProcessedFrame(frame); |
| 712 | 719 |
| 713 // 19. Set last decode timestamp for track buffer to decode timestamp. | 720 // 19. Set last decode timestamp for track buffer to decode timestamp. |
| 714 track_buffer->set_last_decode_timestamp(decode_timestamp); | 721 track_buffer->set_last_decode_timestamp(decode_timestamp); |
| 715 | 722 |
| 716 // 20. Set last frame duration for track buffer to frame duration. | 723 // 20. Set last frame duration for track buffer to frame duration. |
| 717 track_buffer->set_last_frame_duration(frame_duration); | 724 track_buffer->set_last_frame_duration(frame_duration); |
| 718 | 725 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 730 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 737 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
| 731 | 738 |
| 732 return true; | 739 return true; |
| 733 } | 740 } |
| 734 | 741 |
| 735 NOTREACHED(); | 742 NOTREACHED(); |
| 736 return false; | 743 return false; |
| 737 } | 744 } |
| 738 | 745 |
| 739 } // namespace media | 746 } // namespace media |
| OLD | NEW |