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 "base/stl_util.h" | 7 #include "base/stl_util.h" |
| 8 #include "media/base/buffers.h" | 8 #include "media/base/buffers.h" |
| 9 #include "media/base/stream_parser_buffer.h" | 9 #include "media/base/stream_parser_buffer.h" |
| 10 | 10 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 68 } | 68 } |
| 69 | 69 |
| 70 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. | 70 // 2. - 4. Are handled by the WebMediaPlayer / Pipeline / Media Element. |
| 71 | 71 |
| 72 // Step 5: | 72 // Step 5: |
| 73 update_duration_cb_.Run(group_end_timestamp_); | 73 update_duration_cb_.Run(group_end_timestamp_); |
| 74 | 74 |
| 75 return true; | 75 return true; |
| 76 } | 76 } |
| 77 | 77 |
| 78 bool FrameProcessor::ProcessFrame(scoped_refptr<StreamParserBuffer> frame, | 78 bool FrameProcessor::ProcessFrame( |
| 79 base::TimeDelta append_window_start, | 79 const scoped_refptr<StreamParserBuffer>& frame, |
| 80 base::TimeDelta append_window_end, | 80 base::TimeDelta append_window_start, |
| 81 base::TimeDelta* timestamp_offset, | 81 base::TimeDelta append_window_end, |
| 82 bool* new_media_segment) { | 82 base::TimeDelta* timestamp_offset, |
| 83 bool* new_media_segment) { | |
| 83 // Implements the loop within step 1 of the coded frame processing algorithm | 84 // Implements the loop within step 1 of the coded frame processing algorithm |
| 84 // for a single input frame per April 1, 2014 MSE spec editor's draft: | 85 // for a single input frame per April 1, 2014 MSE spec editor's draft: |
| 85 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ | 86 // https://dvcs.w3.org/hg/html-media/raw-file/d471a4412040/media-source/ |
| 86 // media-source.html#sourcebuffer-coded-frame-processing | 87 // media-source.html#sourcebuffer-coded-frame-processing |
| 87 | 88 |
| 88 while (true) { | 89 while (true) { |
| 89 // 1. Loop Top: Let presentation timestamp be a double precision floating | 90 // 1. Loop Top: Let presentation timestamp be a double precision floating |
| 90 // point representation of the coded frame's presentation timestamp in | 91 // point representation of the coded frame's presentation timestamp in |
| 91 // seconds. | 92 // seconds. |
| 92 // 2. Let decode timestamp be a double precision floating point | 93 // 2. Let decode timestamp be a double precision floating point |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 154 // true. | 155 // true. |
| 155 SetAllTrackBuffersNeedRandomAccessPoint(); | 156 SetAllTrackBuffersNeedRandomAccessPoint(); |
| 156 | 157 |
| 157 // 4.4. Unset group start timestamp. | 158 // 4.4. Unset group start timestamp. |
| 158 group_start_timestamp_ = kNoTimestamp(); | 159 group_start_timestamp_ = kNoTimestamp(); |
| 159 } | 160 } |
| 160 | 161 |
| 161 // 5. If timestampOffset is not 0, then run the following steps: | 162 // 5. If timestampOffset is not 0, then run the following steps: |
| 162 if (*timestamp_offset != base::TimeDelta()) { | 163 if (*timestamp_offset != base::TimeDelta()) { |
| 163 // 5.1. Add timestampOffset to the presentation timestamp. | 164 // 5.1. Add timestampOffset to the presentation timestamp. |
| 164 // Note: |frame| PTS is only updated if it survives processing. | 165 // Note: |frame| PTS is only updated if it survives processing. |
|
wolenetz
2014/05/30 19:58:45
nit: s/survives/survives discontinuity/
DaleCurtis
2014/05/30 20:54:30
Done.
| |
| 165 presentation_timestamp += *timestamp_offset; | 166 presentation_timestamp += *timestamp_offset; |
| 166 | 167 |
| 167 // 5.2. Add timestampOffset to the decode timestamp. | 168 // 5.2. Add timestampOffset to the decode timestamp. |
| 168 // Frame DTS is only updated if it survives processing. | 169 // Frame DTS is only updated if it survives processing. |
|
wolenetz
2014/05/30 19:58:45
nit: ditto
DaleCurtis
2014/05/30 20:54:30
Done.
| |
| 169 decode_timestamp += *timestamp_offset; | 170 decode_timestamp += *timestamp_offset; |
| 170 } | 171 } |
| 171 | 172 |
| 172 // 6. Let track buffer equal the track buffer that the coded frame will be | 173 // 6. Let track buffer equal the track buffer that the coded frame will be |
| 173 // added to. | 174 // added to. |
| 174 | 175 |
| 175 // Remap audio and video track types to their special singleton identifiers. | 176 // Remap audio and video track types to their special singleton identifiers. |
| 176 StreamParser::TrackId track_id = kAudioTrackId; | 177 StreamParser::TrackId track_id = kAudioTrackId; |
| 177 switch (frame->type()) { | 178 switch (frame->type()) { |
| 178 case DemuxerStream::AUDIO: | 179 case DemuxerStream::AUDIO: |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 DVLOG(2) << __FUNCTION__ | 245 DVLOG(2) << __FUNCTION__ |
| 245 << ": frame PTS=" << presentation_timestamp.InSecondsF() | 246 << ": frame PTS=" << presentation_timestamp.InSecondsF() |
| 246 << " or DTS=" << decode_timestamp.InSecondsF() | 247 << " or DTS=" << decode_timestamp.InSecondsF() |
| 247 << " negative after applying timestampOffset and handling any " | 248 << " negative after applying timestampOffset and handling any " |
| 248 << " discontinuity"; | 249 << " discontinuity"; |
| 249 return false; | 250 return false; |
| 250 } | 251 } |
| 251 | 252 |
| 252 // 9. Let frame end timestamp equal the sum of presentation timestamp and | 253 // 9. Let frame end timestamp equal the sum of presentation timestamp and |
| 253 // frame duration. | 254 // frame duration. |
| 254 base::TimeDelta frame_end_timestamp = presentation_timestamp + | 255 const base::TimeDelta frame_end_timestamp = |
| 255 frame_duration; | 256 presentation_timestamp + frame_duration; |
| 256 | 257 |
| 257 // 10. If presentation timestamp is less than appendWindowStart, then set | 258 // 10. If presentation timestamp is less than appendWindowStart, then set |
| 258 // the need random access point flag to true, drop the coded frame, and | 259 // the need random access point flag to true, drop the coded frame, and |
| 259 // jump to the top of the loop to start processing the next coded | 260 // jump to the top of the loop to start processing the next coded |
| 260 // frame. | 261 // frame. |
| 261 // Note: We keep the result of partial discard of a buffer that overlaps | 262 // Note: We keep the result of partial discard of a buffer that overlaps |
| 262 // |append_window_start| and does not end after |append_window_end|. | 263 // |append_window_start| and does not end after |append_window_end|. |
| 263 // 11. If frame end timestamp is greater than appendWindowEnd, then set the | 264 // 11. If frame end timestamp is greater than appendWindowEnd, then set the |
| 264 // need random access point flag to true, drop the coded frame, and jump | 265 // need random access point flag to true, drop the coded frame, and jump |
| 265 // to the top of the loop to start processing the next coded frame. | 266 // to the top of the loop to start processing the next coded frame. |
| 267 frame->set_timestamp(presentation_timestamp); | |
| 268 frame->SetDecodeTimestamp(decode_timestamp); | |
| 269 if (track_buffer->stream()->supports_partial_append_window_trimming() && | |
| 270 HandlePartialAppendWindowTrimming(append_window_start, | |
| 271 append_window_end, | |
| 272 frame)) { | |
| 273 // |frame| has been partially trimmed or had preroll added. | |
|
wolenetz
2014/05/30 19:58:45
I agree that the false path would set the flags fo
wolenetz
2014/05/30 20:45:12
From our chat, it's also possible that append wind
DaleCurtis
2014/05/30 20:54:30
Done.
| |
| 274 decode_timestamp = frame->GetDecodeTimestamp(); | |
| 275 presentation_timestamp = frame->timestamp(); | |
| 276 frame_duration = frame->duration(); | |
| 277 | |
| 278 // The end timestamp of the frame should be unchanged. | |
| 279 DCHECK(frame_end_timestamp == presentation_timestamp + frame_duration); | |
| 280 } | |
| 281 | |
| 266 if (presentation_timestamp < append_window_start || | 282 if (presentation_timestamp < append_window_start || |
| 267 frame_end_timestamp > append_window_end) { | 283 frame_end_timestamp > append_window_end) { |
| 268 // See if a partial discard can be done around |append_window_start|. | 284 track_buffer->set_needs_random_access_point(true); |
| 269 // TODO(wolenetz): Refactor this into a base helper across legacy and | 285 DVLOG(3) << "Dropping frame that is outside append window."; |
| 270 // new frame processors? | |
| 271 if (track_buffer->stream()->supports_partial_append_window_trimming() && | |
| 272 presentation_timestamp < append_window_start && | |
| 273 frame_end_timestamp > append_window_start && | |
| 274 frame_end_timestamp <= append_window_end) { | |
| 275 DCHECK(frame->IsKeyframe()); | |
| 276 DVLOG(1) << "Truncating buffer which overlaps append window start." | |
| 277 << " presentation_timestamp " | |
| 278 << presentation_timestamp.InSecondsF() | |
| 279 << " append_window_start " << append_window_start.InSecondsF(); | |
| 280 | 286 |
| 281 // Adjust the timestamp of this frame forward to |append_window_start|, | 287 if (!sequence_mode_) { |
|
wolenetz
2014/05/30 20:45:12
From our chat, this condition needs to be dropped.
DaleCurtis
2014/05/30 20:54:30
I defer to you guys to sort this out outside of th
wolenetz
2014/05/30 22:31:42
SGTM. On further inspection, and per our offline c
wolenetz
2014/05/31 01:22:04
I have a separate CL to test/clarify small append
| |
| 282 // while decreasing the duration appropriately. | 288 // This also triggers a discontinuity so we need to treat the next |
| 283 frame->set_discard_padding(std::make_pair( | 289 // frames appended within the append window as if they were the |
| 284 append_window_start - presentation_timestamp, base::TimeDelta())); | 290 // beginning of a new segment. |
| 285 presentation_timestamp = append_window_start; // |frame| updated below. | 291 *new_media_segment = true; |
| 286 decode_timestamp = append_window_start; // |frame| updated below. | 292 } |
| 287 frame_duration = frame_end_timestamp - presentation_timestamp; | |
| 288 frame->set_duration(frame_duration); | |
| 289 | 293 |
| 290 // TODO(dalecurtis): This could also be done with |append_window_end|, | 294 return true; |
| 291 // but is not necessary since splice frames covert the overlap there. | |
| 292 } else { | |
| 293 track_buffer->set_needs_random_access_point(true); | |
| 294 DVLOG(3) << "Dropping frame that is outside append window."; | |
| 295 | |
| 296 if (!sequence_mode_) { | |
| 297 // This also triggers a discontinuity so we need to treat the next | |
| 298 // frames appended within the append window as if they were the | |
| 299 // beginning of a new segment. | |
| 300 *new_media_segment = true; | |
| 301 } | |
| 302 | |
| 303 return true; | |
| 304 } | |
| 305 } | 295 } |
| 306 | 296 |
| 307 // 12. If the need random access point flag on track buffer equals true, | 297 // 12. If the need random access point flag on track buffer equals true, |
| 308 // then run the following steps: | 298 // then run the following steps: |
| 309 if (track_buffer->needs_random_access_point()) { | 299 if (track_buffer->needs_random_access_point()) { |
| 310 // 12.1. If the coded frame is not a random access point, then drop the | 300 // 12.1. If the coded frame is not a random access point, then drop the |
| 311 // coded frame and jump to the top of the loop to start processing | 301 // coded frame and jump to the top of the loop to start processing |
| 312 // the next coded frame. | 302 // the next coded frame. |
| 313 if (!frame->IsKeyframe()) { | 303 if (!frame->IsKeyframe()) { |
| 314 DVLOG(3) << __FUNCTION__ | 304 DVLOG(3) << __FUNCTION__ |
| 315 << ": Dropping frame that is not a random access point"; | 305 << ": Dropping frame that is not a random access point"; |
| 316 return true; | 306 return true; |
| 317 } | 307 } |
| 318 | 308 |
| 319 // 12.2. Set the need random access point flag on track buffer to false. | 309 // 12.2. Set the need random access point flag on track buffer to false. |
| 320 track_buffer->set_needs_random_access_point(false); | 310 track_buffer->set_needs_random_access_point(false); |
| 321 } | 311 } |
| 322 | 312 |
| 323 // We now have a processed buffer to append to the track buffer's stream. | 313 // We now have a processed buffer to append to the track buffer's stream. |
| 324 // If it is the first in a new media segment or following a discontinuity, | 314 // If it is the first in a new media segment or following a discontinuity, |
| 325 // notify all the track buffers' streams that a new segment is beginning. | 315 // notify all the track buffers' streams that a new segment is beginning. |
| 326 if (*new_media_segment) { | 316 if (*new_media_segment) { |
| 327 *new_media_segment = false; | 317 *new_media_segment = false; |
| 328 NotifyNewMediaSegmentStarting(decode_timestamp); | 318 NotifyNewMediaSegmentStarting(decode_timestamp); |
| 329 } | 319 } |
| 330 | 320 |
| 331 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " | 321 DVLOG(3) << __FUNCTION__ << ": Sending processed frame to stream, " |
| 332 << "PTS=" << presentation_timestamp.InSecondsF() | 322 << "PTS=" << presentation_timestamp.InSecondsF() |
| 333 << ", DTS=" << decode_timestamp.InSecondsF(); | 323 << ", DTS=" << decode_timestamp.InSecondsF(); |
| 334 frame->set_timestamp(presentation_timestamp); | |
| 335 frame->SetDecodeTimestamp(decode_timestamp); | |
| 336 | 324 |
| 337 // Steps 13-18: | 325 // Steps 13-18: |
| 338 // TODO(wolenetz): Collect and emit more than one buffer at a time, if | 326 // TODO(wolenetz): Collect and emit more than one buffer at a time, if |
| 339 // possible. Also refactor SourceBufferStream to conform to spec GC timing. | 327 // possible. Also refactor SourceBufferStream to conform to spec GC timing. |
| 340 // See http://crbug.com/371197. | 328 // See http://crbug.com/371197. |
| 341 StreamParser::BufferQueue buffer_to_append; | 329 StreamParser::BufferQueue buffer_to_append; |
| 342 buffer_to_append.push_back(frame); | 330 buffer_to_append.push_back(frame); |
| 343 track_buffer->stream()->Append(buffer_to_append); | 331 track_buffer->stream()->Append(buffer_to_append); |
| 344 | 332 |
| 345 // 19. Set last decode timestamp for track buffer to decode timestamp. | 333 // 19. Set last decode timestamp for track buffer to decode timestamp. |
| 346 track_buffer->set_last_decode_timestamp(decode_timestamp); | 334 track_buffer->set_last_decode_timestamp(decode_timestamp); |
| 347 | 335 |
| 348 // 20. Set last frame duration for track buffer to frame duration. | 336 // 20. Set last frame duration for track buffer to frame duration. |
| 349 track_buffer->set_last_frame_duration(frame_duration); | 337 track_buffer->set_last_frame_duration(frame_duration); |
|
wolenetz
2014/05/30 19:58:45
fyi: A side effect of partial frame discard is tha
| |
| 350 | 338 |
| 351 // 21. If highest presentation timestamp for track buffer is unset or frame | 339 // 21. If highest presentation timestamp for track buffer is unset or frame |
| 352 // end timestamp is greater than highest presentation timestamp, then | 340 // end timestamp is greater than highest presentation timestamp, then |
| 353 // set highest presentation timestamp for track buffer to frame end | 341 // set highest presentation timestamp for track buffer to frame end |
| 354 // timestamp. | 342 // timestamp. |
| 355 track_buffer->SetHighestPresentationTimestampIfIncreased( | 343 track_buffer->SetHighestPresentationTimestampIfIncreased( |
| 356 frame_end_timestamp); | 344 frame_end_timestamp); |
| 357 | 345 |
| 358 // 22. If frame end timestamp is greater than group end timestamp, then set | 346 // 22. If frame end timestamp is greater than group end timestamp, then set |
| 359 // group end timestamp equal to frame end timestamp. | 347 // group end timestamp equal to frame end timestamp. |
| 360 DCHECK(group_end_timestamp_ >= base::TimeDelta()); | 348 DCHECK(group_end_timestamp_ >= base::TimeDelta()); |
| 361 if (frame_end_timestamp > group_end_timestamp_) | 349 if (frame_end_timestamp > group_end_timestamp_) |
| 362 group_end_timestamp_ = frame_end_timestamp; | 350 group_end_timestamp_ = frame_end_timestamp; |
| 363 | 351 |
| 364 return true; | 352 return true; |
| 365 } | 353 } |
| 366 | 354 |
| 367 NOTREACHED(); | 355 NOTREACHED(); |
| 368 return false; | 356 return false; |
| 369 } | 357 } |
| 370 | 358 |
| 371 void FrameProcessor::SetAllTrackBuffersNeedRandomAccessPoint() { | 359 void FrameProcessor::SetAllTrackBuffersNeedRandomAccessPoint() { |
| 372 for (TrackBufferMap::iterator itr = track_buffers_.begin(); | 360 for (TrackBufferMap::iterator itr = track_buffers_.begin(); |
| 373 itr != track_buffers_.end(); ++itr) { | 361 itr != track_buffers_.end(); ++itr) { |
| 374 itr->second->set_needs_random_access_point(true); | 362 itr->second->set_needs_random_access_point(true); |
| 375 } | 363 } |
| 376 } | 364 } |
| 377 | 365 |
| 378 } // namespace media | 366 } // namespace media |
| OLD | NEW |