Chromium Code Reviews| 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 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 DCHECK(!update_duration_cb.is_null()); | 170 DCHECK(!update_duration_cb.is_null()); |
| 171 } | 171 } |
| 172 | 172 |
| 173 FrameProcessor::~FrameProcessor() { | 173 FrameProcessor::~FrameProcessor() { |
| 174 DVLOG(2) << __FUNCTION__ << "()"; | 174 DVLOG(2) << __FUNCTION__ << "()"; |
| 175 STLDeleteValues(&track_buffers_); | 175 STLDeleteValues(&track_buffers_); |
| 176 } | 176 } |
| 177 | 177 |
| 178 void FrameProcessor::SetSequenceMode(bool sequence_mode) { | 178 void FrameProcessor::SetSequenceMode(bool sequence_mode) { |
| 179 DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")"; | 179 DVLOG(2) << __FUNCTION__ << "(" << sequence_mode << ")"; |
| 180 | 180 // Per June 9, 2016 MSE spec editor's draft: |
| 181 // Per April 1, 2014 MSE spec editor's draft: | 181 // https://rawgit.com/w3c/media-source/d8f901f22/ |
| 182 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/media- source.html#widl-SourceBuffer-mode | 182 // index.html#widl-SourceBuffer-mode |
| 183 // Step 7: If the new mode equals "sequence", then set the group start | 183 // Step 7: If the new mode equals "sequence", then set the group start |
| 184 // timestamp to the group end timestamp. | 184 // timestamp to the group end timestamp. |
| 185 if (sequence_mode) { | 185 if (sequence_mode) { |
| 186 DCHECK(kNoTimestamp() != group_end_timestamp_); | 186 DCHECK(kNoTimestamp() != group_end_timestamp_); |
| 187 group_start_timestamp_ = group_end_timestamp_; | 187 group_start_timestamp_ = group_end_timestamp_; |
| 188 } | 188 } |
| 189 | 189 |
| 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 base::TimeDelta* timestamp_offset) { | 200 base::TimeDelta* timestamp_offset) { |
| 201 StreamParser::BufferQueue frames; | 201 StreamParser::BufferQueue frames; |
| 202 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { | 202 if (!MergeBufferQueues(audio_buffers, video_buffers, text_map, &frames)) { |
| 203 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; | 203 MEDIA_LOG(ERROR, media_log_) << "Parsed buffers not in DTS sequence"; |
| 204 return false; | 204 return false; |
| 205 } | 205 } |
| 206 | 206 |
| 207 DCHECK(!frames.empty()); | 207 DCHECK(!frames.empty()); |
| 208 | 208 |
| 209 // 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. |
| 210 // 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 |
| 211 // 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 |
| 212 // coded frame" per April 1, 2014 MSE spec editor's draft: | 212 // coded frame" per June 9, 2016 MSE spec editor's draft: |
| 213 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 213 // https://rawgit.com/w3c/media-source/d8f901f22/ |
|
chcunningham
2016/06/15 18:26:21
I noticed these old links are completely busted. D
wolenetz
2016/06/15 18:31:31
They're better. Permanent they may not be (depends
| |
| 214 // media-source.html#sourcebuffer-coded-frame-processing | 214 // index.html#sourcebuffer-coded-frame-processing |
| 215 // 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: |
| 216 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); | 216 for (StreamParser::BufferQueue::const_iterator frames_itr = frames.begin(); |
| 217 frames_itr != frames.end(); ++frames_itr) { | 217 frames_itr != frames.end(); ++frames_itr) { |
| 218 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, | 218 if (!ProcessFrame(*frames_itr, append_window_start, append_window_end, |
| 219 timestamp_offset)) { | 219 timestamp_offset)) { |
| 220 FlushProcessedFrames(); | 220 FlushProcessedFrames(); |
| 221 return false; | 221 return false; |
| 222 } | 222 } |
| 223 } | 223 } |
| 224 | 224 |
| 225 if (!FlushProcessedFrames()) | 225 if (!FlushProcessedFrames()) |
| 226 return false; | 226 return false; |
| 227 | 227 |
| 228 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 228 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. |
| 229 | 229 |
| 230 // Step 5: | 230 // 5. If the media segment contains data beyond the current duration, then run |
| 231 // the duration change algorithm with new duration set to the maximum of | |
| 232 // the current duration and the group end timestamp. | |
| 231 update_duration_cb_.Run(group_end_timestamp_); | 233 update_duration_cb_.Run(group_end_timestamp_); |
| 232 | 234 |
| 233 return true; | 235 return true; |
| 234 } | 236 } |
| 235 | 237 |
| 236 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( | 238 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( |
| 237 base::TimeDelta timestamp_offset) { | 239 base::TimeDelta timestamp_offset) { |
| 238 DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")"; | 240 DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")"; |
| 239 DCHECK(kNoTimestamp() != timestamp_offset); | 241 DCHECK(kNoTimestamp() != timestamp_offset); |
| 240 if (sequence_mode_) | 242 if (sequence_mode_) |
| (...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 448 | 450 |
| 449 return processed_buffer; | 451 return processed_buffer; |
| 450 } | 452 } |
| 451 | 453 |
| 452 bool FrameProcessor::ProcessFrame( | 454 bool FrameProcessor::ProcessFrame( |
| 453 const scoped_refptr<StreamParserBuffer>& frame, | 455 const scoped_refptr<StreamParserBuffer>& frame, |
| 454 base::TimeDelta append_window_start, | 456 base::TimeDelta append_window_start, |
| 455 base::TimeDelta append_window_end, | 457 base::TimeDelta append_window_end, |
| 456 base::TimeDelta* timestamp_offset) { | 458 base::TimeDelta* timestamp_offset) { |
| 457 // Implements the loop within step 1 of the coded frame processing algorithm | 459 // 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: | 460 // for a single input frame per June 9, 2016 MSE spec editor's draft: |
| 459 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 461 // https://rawgit.com/w3c/media-source/d8f901f22/ |
| 460 // media-source.html#sourcebuffer-coded-frame-processing | 462 // index.html#sourcebuffer-coded-frame-processing |
| 461 | |
| 462 while (true) { | 463 while (true) { |
| 463 // 1. Loop Top: Let presentation timestamp be a double precision floating | 464 // 1. Loop Top: |
| 464 // point representation of the coded frame's presentation timestamp in | 465 // Otherwise case: (See MediaSourceState's |auto_update_timestamp_offset_|, |
|
chcunningham
2016/06/15 18:26:21
Am I right that "generate timestamps" case is hand
wolenetz
2016/06/15 18:31:31
Correct (in conjunction with MediaSourceState logi
| |
| 465 // seconds. | 466 // too). |
| 466 // 2. Let decode timestamp be a double precision floating point | 467 // 1.1. Let presentation timestamp be a double precision floating point |
| 467 // representation of the coded frame's decode timestamp in seconds. | 468 // representation of the coded frame's presentation timestamp in |
| 468 // 3. Let frame duration be a double precision floating point representation | 469 // seconds. |
| 470 // 1.2. Let decode timestamp be a double precision floating point | |
| 471 // representation of the coded frame's decode timestamp in seconds. | |
| 472 // 2. Let frame duration be a double precision floating point representation | |
| 469 // of the coded frame's duration in seconds. | 473 // of the coded frame's duration in seconds. |
| 470 // We use base::TimeDelta and DecodeTimestamp instead of double. | 474 // We use base::TimeDelta and DecodeTimestamp instead of double. |
| 471 base::TimeDelta presentation_timestamp = frame->timestamp(); | 475 base::TimeDelta presentation_timestamp = frame->timestamp(); |
| 472 DecodeTimestamp decode_timestamp = frame->GetDecodeTimestamp(); | 476 DecodeTimestamp decode_timestamp = frame->GetDecodeTimestamp(); |
| 473 base::TimeDelta frame_duration = frame->duration(); | 477 base::TimeDelta frame_duration = frame->duration(); |
| 474 | 478 |
| 475 DVLOG(3) << __FUNCTION__ << ": Processing frame " | 479 DVLOG(3) << __FUNCTION__ << ": Processing frame " |
| 476 << "Type=" << frame->type() | 480 << "Type=" << frame->type() |
| 477 << ", TrackID=" << frame->track_id() | 481 << ", TrackID=" << frame->track_id() |
| 478 << ", PTS=" << presentation_timestamp.InSecondsF() | 482 << ", PTS=" << presentation_timestamp.InSecondsF() |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 499 << "Parsed " << frame->GetTypeName() << " frame has DTS " | 503 << "Parsed " << frame->GetTypeName() << " frame has DTS " |
| 500 << decode_timestamp.InMicroseconds() | 504 << decode_timestamp.InMicroseconds() |
| 501 << "us, which is after the frame's PTS " | 505 << "us, which is after the frame's PTS " |
| 502 << presentation_timestamp.InMicroseconds() << "us"; | 506 << presentation_timestamp.InMicroseconds() << "us"; |
| 503 DVLOG(2) << __FUNCTION__ << ": WARNING: Frame DTS(" | 507 DVLOG(2) << __FUNCTION__ << ": WARNING: Frame DTS(" |
| 504 << decode_timestamp.InSecondsF() << ") > PTS(" | 508 << decode_timestamp.InSecondsF() << ") > PTS(" |
| 505 << presentation_timestamp.InSecondsF() | 509 << presentation_timestamp.InSecondsF() |
| 506 << "), frame type=" << frame->GetTypeName(); | 510 << "), frame type=" << frame->GetTypeName(); |
| 507 } | 511 } |
| 508 | 512 |
| 509 // TODO(acolwell/wolenetz): All stream parsers must emit valid (positive) | 513 // All stream parsers must emit valid (non-negative) frame durations. |
| 510 // frame durations. For now, we allow non-negative frame duration. | 514 // Note that duration of 0 can occur for at least WebM alt-ref frames. |
| 511 // See http://crbug.com/351166. | |
| 512 if (frame_duration == kNoTimestamp()) { | 515 if (frame_duration == kNoTimestamp()) { |
| 513 MEDIA_LOG(ERROR, media_log_) | 516 MEDIA_LOG(ERROR, media_log_) |
| 514 << "Unknown duration for " << frame->GetTypeName() << " frame at PTS " | 517 << "Unknown duration for " << frame->GetTypeName() << " frame at PTS " |
| 515 << presentation_timestamp.InMicroseconds() << "us"; | 518 << presentation_timestamp.InMicroseconds() << "us"; |
| 516 return false; | 519 return false; |
| 517 } | 520 } |
| 518 if (frame_duration < base::TimeDelta()) { | 521 if (frame_duration < base::TimeDelta()) { |
| 519 MEDIA_LOG(ERROR, media_log_) | 522 MEDIA_LOG(ERROR, media_log_) |
| 520 << "Negative duration " << frame_duration.InMicroseconds() | 523 << "Negative duration " << frame_duration.InMicroseconds() |
| 521 << "us for " << frame->GetTypeName() << " frame at PTS " | 524 << "us for " << frame->GetTypeName() << " frame at PTS " |
| 522 << presentation_timestamp.InMicroseconds() << "us"; | 525 << presentation_timestamp.InMicroseconds() << "us"; |
| 523 return false; | 526 return false; |
| 524 } | 527 } |
| 525 | 528 |
| 526 // 4. If mode equals "sequence" and group start timestamp is set, then run | 529 // 3. If mode equals "sequence" and group start timestamp is set, then run |
| 527 // the following steps: | 530 // the following steps: |
| 528 if (sequence_mode_ && group_start_timestamp_ != kNoTimestamp()) { | 531 if (sequence_mode_ && group_start_timestamp_ != kNoTimestamp()) { |
| 529 // 4.1. Set timestampOffset equal to group start timestamp - | 532 // 3.1. Set timestampOffset equal to group start timestamp - |
| 530 // presentation timestamp. | 533 // presentation timestamp. |
| 531 *timestamp_offset = group_start_timestamp_ - presentation_timestamp; | 534 *timestamp_offset = group_start_timestamp_ - presentation_timestamp; |
| 532 | 535 |
| 533 DVLOG(3) << __FUNCTION__ << ": updated timestampOffset is now " | 536 DVLOG(3) << __FUNCTION__ << ": updated timestampOffset is now " |
| 534 << timestamp_offset->InSecondsF(); | 537 << timestamp_offset->InSecondsF(); |
| 535 | 538 |
| 536 // 4.2. Set group end timestamp equal to group start timestamp. | 539 // 3.2. Set group end timestamp equal to group start timestamp. |
| 537 group_end_timestamp_ = group_start_timestamp_; | 540 group_end_timestamp_ = group_start_timestamp_; |
| 538 | 541 |
| 539 // 4.3. Set the need random access point flag on all track buffers to | 542 // 3.3. Set the need random access point flag on all track buffers to |
| 540 // true. | 543 // true. |
| 541 SetAllTrackBuffersNeedRandomAccessPoint(); | 544 SetAllTrackBuffersNeedRandomAccessPoint(); |
| 542 | 545 |
| 543 // 4.4. Unset group start timestamp. | 546 // 3.4. Unset group start timestamp. |
| 544 group_start_timestamp_ = kNoTimestamp(); | 547 group_start_timestamp_ = kNoTimestamp(); |
| 545 } | 548 } |
| 546 | 549 |
| 547 // 5. If timestampOffset is not 0, then run the following steps: | 550 // 4. If timestampOffset is not 0, then run the following steps: |
| 548 if (!timestamp_offset->is_zero()) { | 551 if (!timestamp_offset->is_zero()) { |
| 549 // 5.1. Add timestampOffset to the presentation timestamp. | 552 // 4.1. Add timestampOffset to the presentation timestamp. |
| 550 // Note: |frame| PTS is only updated if it survives discontinuity | 553 // Note: |frame| PTS is only updated if it survives discontinuity |
| 551 // processing. | 554 // processing. |
| 552 presentation_timestamp += *timestamp_offset; | 555 presentation_timestamp += *timestamp_offset; |
| 553 | 556 |
| 554 // 5.2. Add timestampOffset to the decode timestamp. | 557 // 4.2. Add timestampOffset to the decode timestamp. |
| 555 // Frame DTS is only updated if it survives discontinuity processing. | 558 // Frame DTS is only updated if it survives discontinuity processing. |
| 556 decode_timestamp += *timestamp_offset; | 559 decode_timestamp += *timestamp_offset; |
| 557 } | 560 } |
| 558 | 561 |
| 559 // 6. Let track buffer equal the track buffer that the coded frame will be | 562 // 5. Let track buffer equal the track buffer that the coded frame will be |
| 560 // added to. | 563 // added to. |
| 561 | 564 |
| 562 // Remap audio and video track types to their special singleton identifiers. | 565 // Remap audio and video track types to their special singleton identifiers. |
| 563 StreamParser::TrackId track_id = kAudioTrackId; | 566 StreamParser::TrackId track_id = kAudioTrackId; |
| 564 switch (frame->type()) { | 567 switch (frame->type()) { |
| 565 case DemuxerStream::AUDIO: | 568 case DemuxerStream::AUDIO: |
| 566 break; | 569 break; |
| 567 case DemuxerStream::VIDEO: | 570 case DemuxerStream::VIDEO: |
| 568 track_id = kVideoTrackId; | 571 track_id = kVideoTrackId; |
| 569 break; | 572 break; |
| 570 case DemuxerStream::TEXT: | 573 case DemuxerStream::TEXT: |
| 571 track_id = frame->track_id(); | 574 track_id = frame->track_id(); |
| 572 break; | 575 break; |
| 573 case DemuxerStream::UNKNOWN: | 576 case DemuxerStream::UNKNOWN: |
| 574 case DemuxerStream::NUM_TYPES: | 577 case DemuxerStream::NUM_TYPES: |
| 575 DCHECK(false) << ": Invalid frame type " << frame->type(); | 578 DCHECK(false) << ": Invalid frame type " << frame->type(); |
| 576 return false; | 579 return false; |
| 577 } | 580 } |
| 578 | 581 |
| 579 MseTrackBuffer* track_buffer = FindTrack(track_id); | 582 MseTrackBuffer* track_buffer = FindTrack(track_id); |
| 580 if (!track_buffer) { | 583 if (!track_buffer) { |
| 581 MEDIA_LOG(ERROR, media_log_) | 584 MEDIA_LOG(ERROR, media_log_) |
| 582 << "Unknown track with type " << frame->GetTypeName() | 585 << "Unknown track with type " << frame->GetTypeName() |
| 583 << ", frame processor track id " << track_id | 586 << ", frame processor track id " << track_id |
| 584 << ", and parser track id " << frame->track_id(); | 587 << ", and parser track id " << frame->track_id(); |
| 585 return false; | 588 return false; |
| 586 } | 589 } |
| 587 | 590 |
| 588 // 7. If last decode timestamp for track buffer is set and decode timestamp | 591 // 6. If last decode timestamp for track buffer is set and decode timestamp |
| 589 // is less than last decode timestamp | 592 // is less than last decode timestamp |
| 590 // OR | 593 // OR |
| 591 // If last decode timestamp for track buffer is set and the difference | 594 // If last decode timestamp for track buffer is set and the difference |
| 592 // between decode timestamp and last decode timestamp is greater than 2 | 595 // between decode timestamp and last decode timestamp is greater than 2 |
| 593 // times last frame duration: | 596 // times last frame duration: |
| 594 DecodeTimestamp track_last_decode_timestamp = | 597 DecodeTimestamp track_last_decode_timestamp = |
| 595 track_buffer->last_decode_timestamp(); | 598 track_buffer->last_decode_timestamp(); |
| 596 if (track_last_decode_timestamp != kNoDecodeTimestamp()) { | 599 if (track_last_decode_timestamp != kNoDecodeTimestamp()) { |
| 597 base::TimeDelta track_dts_delta = | 600 base::TimeDelta track_dts_delta = |
| 598 decode_timestamp - track_last_decode_timestamp; | 601 decode_timestamp - track_last_decode_timestamp; |
| 599 if (track_dts_delta < base::TimeDelta() || | 602 if (track_dts_delta < base::TimeDelta() || |
| 600 track_dts_delta > 2 * track_buffer->last_frame_duration()) { | 603 track_dts_delta > 2 * track_buffer->last_frame_duration()) { |
| 601 DCHECK(in_coded_frame_group_); | 604 DCHECK(in_coded_frame_group_); |
| 602 // 7.1. If mode equals "segments": Set group end timestamp to | 605 // 6.1. If mode equals "segments": Set group end timestamp to |
| 603 // presentation timestamp. | 606 // presentation timestamp. |
| 604 // If mode equals "sequence": Set group start timestamp equal to | 607 // If mode equals "sequence": Set group start timestamp equal to |
| 605 // the group end timestamp. | 608 // the group end timestamp. |
| 606 if (!sequence_mode_) { | 609 if (!sequence_mode_) { |
| 607 group_end_timestamp_ = presentation_timestamp; | 610 group_end_timestamp_ = presentation_timestamp; |
| 608 // This triggers a discontinuity so we need to treat the next frames | 611 // 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 | 612 // appended within the append window as if they were the beginning of |
| 610 // a new coded frame group. | 613 // a new coded frame group. |
| 611 in_coded_frame_group_ = false; | 614 in_coded_frame_group_ = false; |
| 612 } else { | 615 } else { |
| 613 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " | 616 DVLOG(3) << __FUNCTION__ << " : Sequence mode discontinuity, GETS: " |
| 614 << group_end_timestamp_.InSecondsF(); | 617 << group_end_timestamp_.InSecondsF(); |
| 615 DCHECK(kNoTimestamp() != group_end_timestamp_); | 618 DCHECK(kNoTimestamp() != group_end_timestamp_); |
| 616 group_start_timestamp_ = group_end_timestamp_; | 619 group_start_timestamp_ = group_end_timestamp_; |
| 617 } | 620 } |
| 618 | 621 |
| 619 // 7.2. - 7.5.: | 622 // 6.2. - 6.5.: |
| 620 Reset(); | 623 Reset(); |
| 621 | 624 |
| 622 // 7.6. Jump to the Loop Top step above to restart processing of the | 625 // 6.6. Jump to the Loop Top step above to restart processing of the |
| 623 // current coded frame. | 626 // current coded frame. |
| 624 DVLOG(3) << __FUNCTION__ << ": Discontinuity: reprocessing frame"; | 627 DVLOG(3) << __FUNCTION__ << ": Discontinuity: reprocessing frame"; |
| 625 continue; | 628 continue; |
| 626 } | 629 } |
| 627 } | 630 } |
| 628 | 631 |
| 629 // 9. Let frame end timestamp equal the sum of presentation timestamp and | 632 // 7. Let frame end timestamp equal the sum of presentation timestamp and |
| 630 // frame duration. | 633 // frame duration. |
| 631 base::TimeDelta frame_end_timestamp = | 634 base::TimeDelta frame_end_timestamp = |
| 632 presentation_timestamp + frame_duration; | 635 presentation_timestamp + frame_duration; |
| 633 | 636 |
| 634 // 10. If presentation timestamp is less than appendWindowStart, then set | 637 // 8. If presentation timestamp is less than appendWindowStart, then set |
| 635 // the need random access point flag to true, drop the coded frame, and | 638 // the need random access point flag to true, drop the coded frame, and |
| 636 // jump to the top of the loop to start processing the next coded | 639 // jump to the top of the loop to start processing the next coded |
| 637 // frame. | 640 // frame. |
| 638 // Note: We keep the result of partial discard of a buffer that overlaps | 641 // Note: We keep the result of partial discard of a buffer that overlaps |
| 639 // |append_window_start| and does not end after |append_window_end|. | 642 // |append_window_start| and does not end after |append_window_end|, |
| 640 // 11. If frame end timestamp is greater than appendWindowEnd, then set the | 643 // for streams which support partial trimming. |
| 641 // need random access point flag to true, drop the coded frame, and jump | 644 // 9. If frame end timestamp is greater than appendWindowEnd, then set the |
| 642 // to the top of the loop to start processing the next coded frame. | 645 // need random access point flag to true, drop the coded frame, and jump |
| 646 // to the top of the loop to start processing the next coded frame. | |
| 647 // Note: We keep the result of partial discard of a buffer that overlaps | |
| 648 // |append_window_end|, for streams which support partial trimming. | |
| 643 frame->set_timestamp(presentation_timestamp); | 649 frame->set_timestamp(presentation_timestamp); |
| 644 frame->SetDecodeTimestamp(decode_timestamp); | 650 frame->SetDecodeTimestamp(decode_timestamp); |
| 645 if (track_buffer->stream()->supports_partial_append_window_trimming() && | 651 if (track_buffer->stream()->supports_partial_append_window_trimming() && |
| 646 HandlePartialAppendWindowTrimming(append_window_start, | 652 HandlePartialAppendWindowTrimming(append_window_start, |
| 647 append_window_end, | 653 append_window_end, |
| 648 frame)) { | 654 frame)) { |
| 649 // |frame| has been partially trimmed or had preroll added. Though | 655 // |frame| has been partially trimmed or had preroll added. Though |
| 650 // |frame|'s duration may have changed, do not update |frame_duration| | 656 // |frame|'s duration may have changed, do not update |frame_duration| |
| 651 // here, so |track_buffer|'s last frame duration update uses original | 657 // here, so |track_buffer|'s last frame duration update uses original |
| 652 // frame duration and reduces spurious discontinuity detection. | 658 // frame duration and reduces spurious discontinuity detection. |
| 653 decode_timestamp = frame->GetDecodeTimestamp(); | 659 decode_timestamp = frame->GetDecodeTimestamp(); |
| 654 presentation_timestamp = frame->timestamp(); | 660 presentation_timestamp = frame->timestamp(); |
| 655 frame_end_timestamp = frame->timestamp() + frame->duration(); | 661 frame_end_timestamp = frame->timestamp() + frame->duration(); |
| 656 } | 662 } |
| 657 | 663 |
| 658 if (presentation_timestamp < append_window_start || | 664 if (presentation_timestamp < append_window_start || |
| 659 frame_end_timestamp > append_window_end) { | 665 frame_end_timestamp > append_window_end) { |
| 660 track_buffer->set_needs_random_access_point(true); | 666 track_buffer->set_needs_random_access_point(true); |
| 661 DVLOG(3) << "Dropping frame that is outside append window."; | 667 DVLOG(3) << "Dropping frame that is outside append window."; |
| 662 return true; | 668 return true; |
| 663 } | 669 } |
| 664 | 670 |
| 665 // Note: This step is relocated, versus April 1 spec, to allow append window | |
| 666 // processing to first filter coded frames shifted by |timestamp_offset_| in | |
| 667 // such a way that their PTS is negative. | |
| 668 // 8. If the presentation timestamp or decode timestamp is less than the | |
| 669 // presentation start time, then run the end of stream algorithm with the | |
| 670 // error parameter set to "decode", and abort these steps. | |
| 671 DCHECK(presentation_timestamp >= base::TimeDelta()); | 671 DCHECK(presentation_timestamp >= base::TimeDelta()); |
| 672 if (decode_timestamp < DecodeTimestamp()) { | 672 if (decode_timestamp < DecodeTimestamp()) { |
| 673 // B-frames may still result in negative DTS here after being shifted by | 673 // B-frames may still result in negative DTS here after being shifted by |
| 674 // |timestamp_offset_|. | 674 // |timestamp_offset_|. |
| 675 // TODO(wolenetz): This is no longer a step in the CFP, since negative DTS | |
| 676 // are allowed. Remove this parse failure and error log as part of fixing | |
| 677 // PTS/DTS conflation in SourceBufferStream. See https://crbug.com/398141 | |
| 675 MEDIA_LOG(ERROR, media_log_) | 678 MEDIA_LOG(ERROR, media_log_) |
| 676 << frame->GetTypeName() << " frame with PTS " | 679 << frame->GetTypeName() << " frame with PTS " |
| 677 << presentation_timestamp.InMicroseconds() << "us has negative DTS " | 680 << presentation_timestamp.InMicroseconds() << "us has negative DTS " |
| 678 << decode_timestamp.InMicroseconds() | 681 << decode_timestamp.InMicroseconds() |
| 679 << "us after applying timestampOffset, handling any discontinuity, " | 682 << "us after applying timestampOffset, handling any discontinuity, " |
| 680 "and filtering against append window"; | 683 "and filtering against append window"; |
| 681 return false; | 684 return false; |
| 682 } | 685 } |
| 683 | 686 |
| 684 // 12. If the need random access point flag on track buffer equals true, | 687 // 10. If the need random access point flag on track buffer equals true, |
| 685 // then run the following steps: | 688 // then run the following steps: |
| 686 if (track_buffer->needs_random_access_point()) { | 689 if (track_buffer->needs_random_access_point()) { |
| 687 // 12.1. If the coded frame is not a random access point, then drop the | 690 // 10.1. If the coded frame is not a random access point, then drop the |
| 688 // coded frame and jump to the top of the loop to start processing | 691 // coded frame and jump to the top of the loop to start processing |
| 689 // the next coded frame. | 692 // the next coded frame. |
| 690 if (!frame->is_key_frame()) { | 693 if (!frame->is_key_frame()) { |
| 691 DVLOG(3) << __FUNCTION__ | 694 DVLOG(3) << __FUNCTION__ |
| 692 << ": Dropping frame that is not a random access point"; | 695 << ": Dropping frame that is not a random access point"; |
| 693 return true; | 696 return true; |
| 694 } | 697 } |
| 695 | 698 |
| 696 // 12.2. Set the need random access point flag on track buffer to false. | 699 // 10.2. Set the need random access point flag on track buffer to false. |
| 697 track_buffer->set_needs_random_access_point(false); | 700 track_buffer->set_needs_random_access_point(false); |
| 698 } | 701 } |
| 699 | 702 |
| 700 // We now have a processed buffer to append to the track buffer's stream. | 703 // 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 | 704 // If it is the first in a new coded frame group (such as following a |
| 702 // discontinuity), notify all the track buffers' streams that a coded frame | 705 // discontinuity), notify all the track buffers' streams that a coded frame |
| 703 // group is starting. | 706 // group is starting. |
| 704 if (!in_coded_frame_group_) { | 707 if (!in_coded_frame_group_) { |
| 705 // First, complete the append to track buffer streams of the previous | 708 // First, complete the append to track buffer streams of the previous |
| 706 // coded frame group's frames, if any. | 709 // coded frame group's frames, if any. |
| 707 if (!FlushProcessedFrames()) | 710 if (!FlushProcessedFrames()) |
| 708 return false; | 711 return false; |
| 709 | 712 |
| 710 // TODO(acolwell/wolenetz): This should be changed to a presentation | 713 // TODO(acolwell/wolenetz): This should be changed to a presentation |
| 711 // timestamp. See http://crbug.com/402502 | 714 // timestamp. See http://crbug.com/402502 |
| 712 NotifyStartOfCodedFrameGroup(decode_timestamp); | 715 NotifyStartOfCodedFrameGroup(decode_timestamp); |
| 713 in_coded_frame_group_ = true; | 716 in_coded_frame_group_ = true; |
| 714 } | 717 } |
| 715 | 718 |
| 716 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " | 719 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " |
| 717 << "PTS=" << presentation_timestamp.InSecondsF() | 720 << "PTS=" << presentation_timestamp.InSecondsF() |
| 718 << ", DTS=" << decode_timestamp.InSecondsF(); | 721 << ", DTS=" << decode_timestamp.InSecondsF(); |
| 719 | 722 |
| 720 // Steps 13-18: Note, we optimize by appending groups of contiguous | 723 // Steps 11-16: Note, we optimize by appending groups of contiguous |
| 721 // processed frames for each track buffer at end of ProcessFrames() or prior | 724 // processed frames for each track buffer at end of ProcessFrames() or prior |
| 722 // to NotifyStartOfCodedFrameGroup(). | 725 // to NotifyStartOfCodedFrameGroup(). |
| 723 track_buffer->EnqueueProcessedFrame(frame); | 726 track_buffer->EnqueueProcessedFrame(frame); |
| 724 | 727 |
| 725 // 19. Set last decode timestamp for track buffer to decode timestamp. | 728 // 17. Set last decode timestamp for track buffer to decode timestamp. |
| 726 track_buffer->set_last_decode_timestamp(decode_timestamp); | 729 track_buffer->set_last_decode_timestamp(decode_timestamp); |
| 727 | 730 |
| 728 // 20. Set last frame duration for track buffer to frame duration. | 731 // 18. Set last frame duration for track buffer to frame duration. |
| 729 track_buffer->set_last_frame_duration(frame_duration); | 732 track_buffer->set_last_frame_duration(frame_duration); |
| 730 | 733 |
| 731 // 21. If highest presentation timestamp for track buffer is unset or frame | 734 // 19. If highest presentation timestamp for track buffer is unset or frame |
| 732 // end timestamp is greater than highest presentation timestamp, then | 735 // end timestamp is greater than highest presentation timestamp, then |
| 733 // set highest presentation timestamp for track buffer to frame end | 736 // set highest presentation timestamp for track buffer to frame end |
| 734 // timestamp. | 737 // timestamp. |
| 735 track_buffer->SetHighestPresentationTimestampIfIncreased( | 738 track_buffer->SetHighestPresentationTimestampIfIncreased( |
| 736 frame_end_timestamp); | 739 frame_end_timestamp); |
| 737 | 740 |
| 738 // 22. If frame end timestamp is greater than group end timestamp, then set | 741 // 20. If frame end timestamp is greater than group end timestamp, then set |
| 739 // group end timestamp equal to frame end timestamp. | 742 // group end timestamp equal to frame end timestamp. |
| 740 if (frame_end_timestamp > group_end_timestamp_) | 743 if (frame_end_timestamp > group_end_timestamp_) |
| 741 group_end_timestamp_ = frame_end_timestamp; | 744 group_end_timestamp_ = frame_end_timestamp; |
| 742 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 745 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
| 743 | 746 |
| 747 // Step 21 is currently handled differently. See MediaSourceState's | |
| 748 // |auto_update_timestamp_offset_|. | |
| 744 return true; | 749 return true; |
| 745 } | 750 } |
| 746 | 751 |
| 747 NOTREACHED(); | 752 NOTREACHED(); |
| 748 return false; | 753 return false; |
| 749 } | 754 } |
| 750 | 755 |
| 751 } // namespace media | 756 } // namespace media |
| OLD | NEW |