| 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 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 // Adds |frame| to the end of |processed_frames_|. | 76 // Adds |frame| to the end of |processed_frames_|. |
| 77 void EnqueueProcessedFrame(const scoped_refptr<StreamParserBuffer>& frame); | 77 void EnqueueProcessedFrame(const scoped_refptr<StreamParserBuffer>& frame); |
| 78 | 78 |
| 79 // Appends |processed_frames_|, if not empty, to |stream_| and clears | 79 // Appends |processed_frames_|, if not empty, to |stream_| and clears |
| 80 // |processed_frames_|. Returns false if append failed, true otherwise. | 80 // |processed_frames_|. Returns false if append failed, true otherwise. |
| 81 // |processed_frames_| is cleared in both cases. | 81 // |processed_frames_| is cleared in both cases. |
| 82 bool FlushProcessedFrames(); | 82 bool FlushProcessedFrames(); |
| 83 | 83 |
| 84 private: | 84 private: |
| 85 // The decode timestamp of the last coded frame appended in the current coded | 85 // The decode timestamp of the last coded frame appended in the current coded |
| 86 // frame group. Initially kNoTimestamp(), meaning "unset". | 86 // frame group. Initially kNoTimestamp, meaning "unset". |
| 87 DecodeTimestamp last_decode_timestamp_; | 87 DecodeTimestamp last_decode_timestamp_; |
| 88 | 88 |
| 89 // The coded frame duration of the last coded frame appended in the current | 89 // The coded frame duration of the last coded frame appended in the current |
| 90 // coded frame group. Initially kNoTimestamp(), meaning "unset". | 90 // coded frame group. Initially kNoTimestamp, meaning "unset". |
| 91 base::TimeDelta last_frame_duration_; | 91 base::TimeDelta last_frame_duration_; |
| 92 | 92 |
| 93 // The highest presentation timestamp encountered in a coded frame appended | 93 // The highest presentation timestamp encountered in a coded frame appended |
| 94 // in the current coded frame group. Initially kNoTimestamp(), meaning | 94 // in the current coded frame group. Initially kNoTimestamp, meaning |
| 95 // "unset". | 95 // "unset". |
| 96 base::TimeDelta highest_presentation_timestamp_; | 96 base::TimeDelta highest_presentation_timestamp_; |
| 97 | 97 |
| 98 // Keeps track of whether the track buffer is waiting for a random access | 98 // Keeps track of whether the track buffer is waiting for a random access |
| 99 // point coded frame. Initially set to true to indicate that a random access | 99 // point coded frame. Initially set to true to indicate that a random access |
| 100 // point coded frame is needed before anything can be added to the track | 100 // point coded frame is needed before anything can be added to the track |
| 101 // buffer. | 101 // buffer. |
| 102 bool needs_random_access_point_; | 102 bool needs_random_access_point_; |
| 103 | 103 |
| 104 // Pointer to the stream associated with this track. The stream is not owned | 104 // Pointer to the stream associated with this track. The stream is not owned |
| 105 // by |this|. | 105 // by |this|. |
| 106 ChunkDemuxerStream* const stream_; | 106 ChunkDemuxerStream* const stream_; |
| 107 | 107 |
| 108 // Queue of processed frames that have not yet been appended to |stream_|. | 108 // Queue of processed frames that have not yet been appended to |stream_|. |
| 109 // EnqueueProcessedFrame() adds to this queue, and FlushProcessedFrames() | 109 // EnqueueProcessedFrame() adds to this queue, and FlushProcessedFrames() |
| 110 // clears it. | 110 // clears it. |
| 111 StreamParser::BufferQueue processed_frames_; | 111 StreamParser::BufferQueue processed_frames_; |
| 112 | 112 |
| 113 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer); | 113 DISALLOW_COPY_AND_ASSIGN(MseTrackBuffer); |
| 114 }; | 114 }; |
| 115 | 115 |
| 116 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) | 116 MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) |
| 117 : last_decode_timestamp_(kNoDecodeTimestamp()), | 117 : last_decode_timestamp_(kNoDecodeTimestamp()), |
| 118 last_frame_duration_(kNoTimestamp()), | 118 last_frame_duration_(kNoTimestamp), |
| 119 highest_presentation_timestamp_(kNoTimestamp()), | 119 highest_presentation_timestamp_(kNoTimestamp), |
| 120 needs_random_access_point_(true), | 120 needs_random_access_point_(true), |
| 121 stream_(stream) { | 121 stream_(stream) { |
| 122 DCHECK(stream_); | 122 DCHECK(stream_); |
| 123 } | 123 } |
| 124 | 124 |
| 125 MseTrackBuffer::~MseTrackBuffer() { | 125 MseTrackBuffer::~MseTrackBuffer() { |
| 126 DVLOG(2) << __FUNCTION__ << "()"; | 126 DVLOG(2) << __FUNCTION__ << "()"; |
| 127 } | 127 } |
| 128 | 128 |
| 129 void MseTrackBuffer::Reset() { | 129 void MseTrackBuffer::Reset() { |
| 130 DVLOG(2) << __FUNCTION__ << "()"; | 130 DVLOG(2) << __FUNCTION__ << "()"; |
| 131 | 131 |
| 132 last_decode_timestamp_ = kNoDecodeTimestamp(); | 132 last_decode_timestamp_ = kNoDecodeTimestamp(); |
| 133 last_frame_duration_ = kNoTimestamp(); | 133 last_frame_duration_ = kNoTimestamp; |
| 134 highest_presentation_timestamp_ = kNoTimestamp(); | 134 highest_presentation_timestamp_ = kNoTimestamp; |
| 135 needs_random_access_point_ = true; | 135 needs_random_access_point_ = true; |
| 136 } | 136 } |
| 137 | 137 |
| 138 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased( | 138 void MseTrackBuffer::SetHighestPresentationTimestampIfIncreased( |
| 139 base::TimeDelta timestamp) { | 139 base::TimeDelta timestamp) { |
| 140 if (highest_presentation_timestamp_ == kNoTimestamp() || | 140 if (highest_presentation_timestamp_ == kNoTimestamp || |
| 141 timestamp > highest_presentation_timestamp_) { | 141 timestamp > highest_presentation_timestamp_) { |
| 142 highest_presentation_timestamp_ = timestamp; | 142 highest_presentation_timestamp_ = timestamp; |
| 143 } | 143 } |
| 144 } | 144 } |
| 145 | 145 |
| 146 void MseTrackBuffer::EnqueueProcessedFrame( | 146 void MseTrackBuffer::EnqueueProcessedFrame( |
| 147 const scoped_refptr<StreamParserBuffer>& frame) { | 147 const scoped_refptr<StreamParserBuffer>& frame) { |
| 148 processed_frames_.push_back(frame); | 148 processed_frames_.push_back(frame); |
| 149 } | 149 } |
| 150 | 150 |
| 151 bool MseTrackBuffer::FlushProcessedFrames() { | 151 bool MseTrackBuffer::FlushProcessedFrames() { |
| 152 if (processed_frames_.empty()) | 152 if (processed_frames_.empty()) |
| 153 return true; | 153 return true; |
| 154 | 154 |
| 155 bool result = stream_->Append(processed_frames_); | 155 bool result = stream_->Append(processed_frames_); |
| 156 processed_frames_.clear(); | 156 processed_frames_.clear(); |
| 157 | 157 |
| 158 DVLOG_IF(3, !result) << __FUNCTION__ | 158 DVLOG_IF(3, !result) << __FUNCTION__ |
| 159 << "(): Failure appending processed frames to stream"; | 159 << "(): Failure appending processed frames to stream"; |
| 160 | 160 |
| 161 return result; | 161 return result; |
| 162 } | 162 } |
| 163 | 163 |
| 164 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb, | 164 FrameProcessor::FrameProcessor(const UpdateDurationCB& update_duration_cb, |
| 165 const scoped_refptr<MediaLog>& media_log) | 165 const scoped_refptr<MediaLog>& media_log) |
| 166 : group_start_timestamp_(kNoTimestamp()), | 166 : group_start_timestamp_(kNoTimestamp), |
| 167 update_duration_cb_(update_duration_cb), | 167 update_duration_cb_(update_duration_cb), |
| 168 media_log_(media_log) { | 168 media_log_(media_log) { |
| 169 DVLOG(2) << __FUNCTION__ << "()"; | 169 DVLOG(2) << __FUNCTION__ << "()"; |
| 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 // Per June 9, 2016 MSE spec editor's draft: | 180 // Per June 9, 2016 MSE spec editor's draft: |
| 181 // https://rawgit.com/w3c/media-source/d8f901f22/ | 181 // https://rawgit.com/w3c/media-source/d8f901f22/ |
| 182 // index.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 } else if (sequence_mode_) { | 188 } else if (sequence_mode_) { |
| 189 // We're switching from 'sequence' to 'segments' mode. Be safe and signal a | 189 // We're switching from 'sequence' to 'segments' mode. Be safe and signal a |
| 190 // new coded frame group on the next frame emitted. | 190 // new coded frame group on the next frame emitted. |
| 191 coded_frame_group_last_dts_ = kNoDecodeTimestamp(); | 191 coded_frame_group_last_dts_ = kNoDecodeTimestamp(); |
| 192 } | 192 } |
| 193 | 193 |
| 194 // Step 8: Update the attribute to new mode. | 194 // Step 8: Update the attribute to new mode. |
| 195 sequence_mode_ = sequence_mode; | 195 sequence_mode_ = sequence_mode; |
| 196 } | 196 } |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 // the duration change algorithm with new duration set to the maximum of | 235 // the duration change algorithm with new duration set to the maximum of |
| 236 // the current duration and the group end timestamp. | 236 // the current duration and the group end timestamp. |
| 237 update_duration_cb_.Run(group_end_timestamp_); | 237 update_duration_cb_.Run(group_end_timestamp_); |
| 238 | 238 |
| 239 return true; | 239 return true; |
| 240 } | 240 } |
| 241 | 241 |
| 242 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( | 242 void FrameProcessor::SetGroupStartTimestampIfInSequenceMode( |
| 243 base::TimeDelta timestamp_offset) { | 243 base::TimeDelta timestamp_offset) { |
| 244 DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")"; | 244 DVLOG(2) << __FUNCTION__ << "(" << timestamp_offset.InSecondsF() << ")"; |
| 245 DCHECK(kNoTimestamp() != timestamp_offset); | 245 DCHECK(kNoTimestamp != timestamp_offset); |
| 246 if (sequence_mode_) | 246 if (sequence_mode_) |
| 247 group_start_timestamp_ = timestamp_offset; | 247 group_start_timestamp_ = timestamp_offset; |
| 248 | 248 |
| 249 // Changes to timestampOffset should invalidate the preroll buffer. | 249 // Changes to timestampOffset should invalidate the preroll buffer. |
| 250 audio_preroll_buffer_ = NULL; | 250 audio_preroll_buffer_ = NULL; |
| 251 } | 251 } |
| 252 | 252 |
| 253 bool FrameProcessor::AddTrack(StreamParser::TrackId id, | 253 bool FrameProcessor::AddTrack(StreamParser::TrackId id, |
| 254 ChunkDemuxerStream* stream) { | 254 ChunkDemuxerStream* stream) { |
| 255 DVLOG(2) << __FUNCTION__ << "(): id=" << id; | 255 DVLOG(2) << __FUNCTION__ << "(): id=" << id; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 // sequence mode. Reset it here only if in segments mode. In sequence mode, | 300 // sequence mode. Reset it here only if in segments mode. In sequence mode, |
| 301 // the current coded frame group may be continued across Reset() operations to | 301 // the current coded frame group may be continued across Reset() operations to |
| 302 // allow the stream to coaelesce what might otherwise be gaps in the buffered | 302 // allow the stream to coaelesce what might otherwise be gaps in the buffered |
| 303 // ranges. See also the declaration for |coded_frame_group_last_dts_|. | 303 // ranges. See also the declaration for |coded_frame_group_last_dts_|. |
| 304 if (!sequence_mode_) { | 304 if (!sequence_mode_) { |
| 305 coded_frame_group_last_dts_ = kNoDecodeTimestamp(); | 305 coded_frame_group_last_dts_ = kNoDecodeTimestamp(); |
| 306 return; | 306 return; |
| 307 } | 307 } |
| 308 | 308 |
| 309 // Sequence mode | 309 // Sequence mode |
| 310 DCHECK(kNoTimestamp() != group_end_timestamp_); | 310 DCHECK(kNoTimestamp != group_end_timestamp_); |
| 311 group_start_timestamp_ = group_end_timestamp_; | 311 group_start_timestamp_ = group_end_timestamp_; |
| 312 } | 312 } |
| 313 | 313 |
| 314 void FrameProcessor::OnPossibleAudioConfigUpdate( | 314 void FrameProcessor::OnPossibleAudioConfigUpdate( |
| 315 const AudioDecoderConfig& config) { | 315 const AudioDecoderConfig& config) { |
| 316 DCHECK(config.IsValidConfig()); | 316 DCHECK(config.IsValidConfig()); |
| 317 | 317 |
| 318 // Always clear the preroll buffer when a config update is received. | 318 // Always clear the preroll buffer when a config update is received. |
| 319 audio_preroll_buffer_ = NULL; | 319 audio_preroll_buffer_ = NULL; |
| 320 | 320 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 | 485 |
| 486 DVLOG(3) << __FUNCTION__ << ": Processing frame " | 486 DVLOG(3) << __FUNCTION__ << ": Processing frame " |
| 487 << "Type=" << frame->type() | 487 << "Type=" << frame->type() |
| 488 << ", TrackID=" << frame->track_id() | 488 << ", TrackID=" << frame->track_id() |
| 489 << ", PTS=" << presentation_timestamp.InSecondsF() | 489 << ", PTS=" << presentation_timestamp.InSecondsF() |
| 490 << ", DTS=" << decode_timestamp.InSecondsF() | 490 << ", DTS=" << decode_timestamp.InSecondsF() |
| 491 << ", DUR=" << frame_duration.InSecondsF() | 491 << ", DUR=" << frame_duration.InSecondsF() |
| 492 << ", RAP=" << frame->is_key_frame(); | 492 << ", RAP=" << frame->is_key_frame(); |
| 493 | 493 |
| 494 // Sanity check the timestamps. | 494 // Sanity check the timestamps. |
| 495 if (presentation_timestamp == kNoTimestamp()) { | 495 if (presentation_timestamp == kNoTimestamp) { |
| 496 MEDIA_LOG(ERROR, media_log_) << "Unknown PTS for " << frame->GetTypeName() | 496 MEDIA_LOG(ERROR, media_log_) << "Unknown PTS for " << frame->GetTypeName() |
| 497 << " frame"; | 497 << " frame"; |
| 498 return false; | 498 return false; |
| 499 } | 499 } |
| 500 if (decode_timestamp == kNoDecodeTimestamp()) { | 500 if (decode_timestamp == kNoDecodeTimestamp()) { |
| 501 MEDIA_LOG(ERROR, media_log_) << "Unknown DTS for " << frame->GetTypeName() | 501 MEDIA_LOG(ERROR, media_log_) << "Unknown DTS for " << frame->GetTypeName() |
| 502 << " frame"; | 502 << " frame"; |
| 503 return false; | 503 return false; |
| 504 } | 504 } |
| 505 if (decode_timestamp.ToPresentationTime() > presentation_timestamp) { | 505 if (decode_timestamp.ToPresentationTime() > presentation_timestamp) { |
| 506 // TODO(wolenetz): Determine whether DTS>PTS should really be allowed. See | 506 // TODO(wolenetz): Determine whether DTS>PTS should really be allowed. See |
| 507 // http://crbug.com/354518. | 507 // http://crbug.com/354518. |
| 508 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_dts_beyond_pts_warnings_, | 508 LIMITED_MEDIA_LOG(DEBUG, media_log_, num_dts_beyond_pts_warnings_, |
| 509 kMaxDtsBeyondPtsWarnings) | 509 kMaxDtsBeyondPtsWarnings) |
| 510 << "Parsed " << frame->GetTypeName() << " frame has DTS " | 510 << "Parsed " << frame->GetTypeName() << " frame has DTS " |
| 511 << decode_timestamp.InMicroseconds() | 511 << decode_timestamp.InMicroseconds() |
| 512 << "us, which is after the frame's PTS " | 512 << "us, which is after the frame's PTS " |
| 513 << presentation_timestamp.InMicroseconds() << "us"; | 513 << presentation_timestamp.InMicroseconds() << "us"; |
| 514 DVLOG(2) << __FUNCTION__ << ": WARNING: Frame DTS(" | 514 DVLOG(2) << __FUNCTION__ << ": WARNING: Frame DTS(" |
| 515 << decode_timestamp.InSecondsF() << ") > PTS(" | 515 << decode_timestamp.InSecondsF() << ") > PTS(" |
| 516 << presentation_timestamp.InSecondsF() | 516 << presentation_timestamp.InSecondsF() |
| 517 << "), frame type=" << frame->GetTypeName(); | 517 << "), frame type=" << frame->GetTypeName(); |
| 518 } | 518 } |
| 519 | 519 |
| 520 // All stream parsers must emit valid (non-negative) frame durations. | 520 // All stream parsers must emit valid (non-negative) frame durations. |
| 521 // Note that duration of 0 can occur for at least WebM alt-ref frames. | 521 // Note that duration of 0 can occur for at least WebM alt-ref frames. |
| 522 if (frame_duration == kNoTimestamp()) { | 522 if (frame_duration == kNoTimestamp) { |
| 523 MEDIA_LOG(ERROR, media_log_) | 523 MEDIA_LOG(ERROR, media_log_) |
| 524 << "Unknown duration for " << frame->GetTypeName() << " frame at PTS " | 524 << "Unknown duration for " << frame->GetTypeName() << " frame at PTS " |
| 525 << presentation_timestamp.InMicroseconds() << "us"; | 525 << presentation_timestamp.InMicroseconds() << "us"; |
| 526 return false; | 526 return false; |
| 527 } | 527 } |
| 528 if (frame_duration < base::TimeDelta()) { | 528 if (frame_duration < base::TimeDelta()) { |
| 529 MEDIA_LOG(ERROR, media_log_) | 529 MEDIA_LOG(ERROR, media_log_) |
| 530 << "Negative duration " << frame_duration.InMicroseconds() | 530 << "Negative duration " << frame_duration.InMicroseconds() |
| 531 << "us for " << frame->GetTypeName() << " frame at PTS " | 531 << "us for " << frame->GetTypeName() << " frame at PTS " |
| 532 << presentation_timestamp.InMicroseconds() << "us"; | 532 << presentation_timestamp.InMicroseconds() << "us"; |
| 533 return false; | 533 return false; |
| 534 } | 534 } |
| 535 | 535 |
| 536 // 3. If mode equals "sequence" and group start timestamp is set, then run | 536 // 3. If mode equals "sequence" and group start timestamp is set, then run |
| 537 // the following steps: | 537 // the following steps: |
| 538 if (sequence_mode_ && group_start_timestamp_ != kNoTimestamp()) { | 538 if (sequence_mode_ && group_start_timestamp_ != kNoTimestamp) { |
| 539 // 3.1. Set timestampOffset equal to group start timestamp - | 539 // 3.1. Set timestampOffset equal to group start timestamp - |
| 540 // presentation timestamp. | 540 // presentation timestamp. |
| 541 *timestamp_offset = group_start_timestamp_ - presentation_timestamp; | 541 *timestamp_offset = group_start_timestamp_ - presentation_timestamp; |
| 542 | 542 |
| 543 DVLOG(3) << __FUNCTION__ << ": updated timestampOffset is now " | 543 DVLOG(3) << __FUNCTION__ << ": updated timestampOffset is now " |
| 544 << timestamp_offset->InSecondsF(); | 544 << timestamp_offset->InSecondsF(); |
| 545 | 545 |
| 546 // 3.2. Set group end timestamp equal to group start timestamp. | 546 // 3.2. Set group end timestamp equal to group start timestamp. |
| 547 group_end_timestamp_ = group_start_timestamp_; | 547 group_end_timestamp_ = group_start_timestamp_; |
| 548 | 548 |
| 549 // 3.3. Set the need random access point flag on all track buffers to | 549 // 3.3. Set the need random access point flag on all track buffers to |
| 550 // true. | 550 // true. |
| 551 SetAllTrackBuffersNeedRandomAccessPoint(); | 551 SetAllTrackBuffersNeedRandomAccessPoint(); |
| 552 | 552 |
| 553 // 3.4. Unset group start timestamp. | 553 // 3.4. Unset group start timestamp. |
| 554 group_start_timestamp_ = kNoTimestamp(); | 554 group_start_timestamp_ = kNoTimestamp; |
| 555 } | 555 } |
| 556 | 556 |
| 557 // 4. If timestampOffset is not 0, then run the following steps: | 557 // 4. If timestampOffset is not 0, then run the following steps: |
| 558 if (!timestamp_offset->is_zero()) { | 558 if (!timestamp_offset->is_zero()) { |
| 559 // 4.1. Add timestampOffset to the presentation timestamp. | 559 // 4.1. Add timestampOffset to the presentation timestamp. |
| 560 // Note: |frame| PTS is only updated if it survives discontinuity | 560 // Note: |frame| PTS is only updated if it survives discontinuity |
| 561 // processing. | 561 // processing. |
| 562 presentation_timestamp += *timestamp_offset; | 562 presentation_timestamp += *timestamp_offset; |
| 563 | 563 |
| 564 // 4.2. Add timestampOffset to the decode timestamp. | 564 // 4.2. Add timestampOffset to the decode timestamp. |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 760 // Step 21 is currently handled differently. See MediaSourceState's | 760 // Step 21 is currently handled differently. See MediaSourceState's |
| 761 // |auto_update_timestamp_offset_|. | 761 // |auto_update_timestamp_offset_|. |
| 762 return true; | 762 return true; |
| 763 } | 763 } |
| 764 | 764 |
| 765 NOTREACHED(); | 765 NOTREACHED(); |
| 766 return false; | 766 return false; |
| 767 } | 767 } |
| 768 | 768 |
| 769 } // namespace media | 769 } // namespace media |
| OLD | NEW |