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 |