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 |