UNPKG

8.01 kBMarkdownView Raw
1# How to get program time from player time
2
3## Definitions
4
5NOTE: All times referenced in seconds unless otherwise specified.
6
7*Player Time*: any time that can be gotten/set from player.currentTime() (e.g., any time within player.seekable().start(0) to player.seekable().end(0)).<br />
8*Stream Time*: any time within one of the stream's segments. Used by video frames (e.g., dts, pts, base media decode time). While these times natively use clock values, throughout the document the times are referenced in seconds.<br />
9*Program Time*: any time referencing the real world (e.g., EXT-X-PROGRAM-DATE-TIME).<br />
10*Start of Segment*: the pts (presentation timestamp) value of the first frame in a segment.<br />
11
12## Overview
13
14In order to convert from a *player time* to a *stream time*, an "anchor point" is required to match up a *player time*, *stream time*, and *program time*.
15
16Two anchor points that are usable are the time since the start of a new timeline (e.g., the time since the last discontinuity or start of the stream), and the start of a segment. Because, in our requirements for this conversion, each segment is tagged with its *program time* in the form of an [EXT-X-PROGRAM-DATE-TIME tag](https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-4.3.2.6), using the segment start as the anchor point is the easiest solution. It's the closest potential anchor point to the time to convert, and it doesn't require us to track time changes across segments (e.g., trimmed or prepended content).
17
18Those time changes are the result of the transmuxer, which can add/remove content in order to keep the content playable (without gaps or other breaking changes between segments), particularly when a segment doesn't start with a key frame.
19
20In order to make use of the segment start, and to calculate the offset between the segment start and the time to convert, a few properties are needed:
21
221. The start of the segment before transmuxing
231. Time changes made to the segment during transmuxing
241. The start of the segment after transmuxing
25
26While the start of the segment before and after transmuxing is trivial to retrieve, getting the time changes made during transmuxing is more complicated, as we must account for any trimming, prepending, and gap filling made during the transmux stage. However, the required use-case only needs the position of a video frame, allowing us to ignore any changes made to the audio timeline (because VHS uses video as the timeline of truth), as well as a couple of the video modifications.
27
28What follows are the changes made to a video stream by the transmuxer that could alter the timeline, and if they must be accounted for in the conversion:
29
30* Keyframe Pulling
31 * Used when: the segment doesn't start with a keyframe.
32 * Impact: the keyframe with the lowest dts value in the segment is "pulled" back to the first dts value in the segment, and all frames in-between are dropped.
33 * Need to account in time conversion? No. If a keyframe is pulled, and frames before it are dropped, then the segment will maintain the same segment duration, and the viewer is only seeing the keyframe during that period.
34* GOP Fusion
35 * Used when: the segment doesn't start with a keyframe.
36 * Impact: if GOPs were saved from previous segment appends, the last GOP will be prepended to the segment.
37 * Need to account in time conversion? Yes. The segment is artificially extended, so while it shouldn't impact the stream time itself (since it will overlap with content already appended), it will impact the post transmux start of segment.
38* GOPS to Align With
39 * Used when: switching renditions, or appending segments with overlapping GOPs (intersecting time ranges).
40 * Impact: GOPs in the segment will be dropped until there are no overlapping GOPs with previous segments.
41 * Need to account in time conversion? No. So long as we aren't switching renditions, and the content is sane enough to not contain overlapping GOPs, this should not have a meaningful impact.
42
43Among the changes, with only GOP Fusion having an impact, the task is simplified. Instead of accounting for any changes to the video stream, only those from GOP Fusion should be accounted for. Since GOP fusion will potentially only prepend frames to the segment, we just need the number of seconds prepended to the segment when offsetting the time. As such, we can add the following properties to each segment:
44
45```
46segment: {
47 // calculated start of segment from either end of previous segment or end of last buffer
48 // (in stream time)
49 start,
50 ...
51 videoTimingInfo: {
52 // number of seconds prepended by GOP fusion
53 transmuxerPrependedSeconds
54 // start of transmuxed segment (in player time)
55 transmuxedPresentationStart
56 }
57}
58```
59
60## The Formula
61
62With the properties listed above, calculating a *program time* from a *player time* is given as follows:
63
64```
65const playerTimeToProgramTime = (playerTime, segment) => {
66 if (!segment.dateTimeObject) {
67 // Can't convert without an "anchor point" for the program time (i.e., a time that can
68 // be used to map the start of a segment with a real world time).
69 return null;
70 }
71
72 const transmuxerPrependedSeconds = segment.videoTimingInfo.transmuxerPrependedSeconds;
73 const transmuxedStart = segment.videoTimingInfo.transmuxedPresentationStart;
74
75 // get the start of the content from before old content is prepended
76 const startOfSegment = transmuxedStart + transmuxerPrependedSeconds;
77 const offsetFromSegmentStart = playerTime - startOfSegment;
78
79 return new Date(segment.dateTimeObject.getTime() + offsetFromSegmentStart * 1000);
80};
81```
82
83## Examples
84
85```
86// Program Times:
87// segment1: 2018-11-10T00:00:30.1Z => 2018-11-10T00:00:32.1Z
88// segment2: 2018-11-10T00:00:32.1Z => 2018-11-10T00:00:34.1Z
89// segment3: 2018-11-10T00:00:34.1Z => 2018-11-10T00:00:36.1Z
90//
91// Player Times:
92// segment1: 0 => 2
93// segment2: 2 => 4
94// segment3: 4 => 6
95
96const segment1 = {
97 dateTimeObject: 2018-11-10T00:00:30.1Z
98 videoTimingInfo: {
99 transmuxerPrependedSeconds: 0,
100 transmuxedPresentationStart: 0
101 }
102};
103playerTimeToProgramTime(0.1, segment1);
104// startOfSegment = 0 + 0 = 0
105// offsetFromSegmentStart = 0.1 - 0 = 0.1
106// return 2018-11-10T00:00:30.1Z + 0.1 = 2018-11-10T00:00:30.2Z
107
108const segment2 = {
109 dateTimeObject: 2018-11-10T00:00:32.1Z
110 videoTimingInfo: {
111 transmuxerPrependedSeconds: 0.3,
112 transmuxedPresentationStart: 1.7
113 }
114};
115playerTimeToProgramTime(2.5, segment2);
116// startOfSegment = 1.7 + 0.3 = 2
117// offsetFromSegmentStart = 2.5 - 2 = 0.5
118// return 2018-11-10T00:00:32.1Z + 0.5 = 2018-11-10T00:00:32.6Z
119
120const segment3 = {
121 dateTimeObject: 2018-11-10T00:00:34.1Z
122 videoTimingInfo: {
123 transmuxerPrependedSeconds: 0.2,
124 transmuxedPresentationStart: 3.8
125 }
126};
127playerTimeToProgramTime(4, segment3);
128// startOfSegment = 3.8 + 0.2 = 4
129// offsetFromSegmentStart = 4 - 4 = 0
130// return 2018-11-10T00:00:34.1Z + 0 = 2018-11-10T00:00:34.1Z
131```
132
133## Transmux Before Append Changes
134
135Even though segment timing values are retained for transmux before append, the formula does not need to change, as all that matters for calculation is the offset from the transmuxed segment start, which can then be applied to the stream time start of segment, or the program time start of segment.
136
137## Getting the Right Segment
138
139In order to make use of the above calculation, the right segment must be chosen for a given player time. This time may be retrieved by simply using the times of the segment after transmuxing (as the start/end pts/dts values then reflect the player time it should slot into in the source buffer). These are included in `videoTimingInfo` as `transmuxedPresentationStart` and `transmuxedPresentationEnd`.
140
141Although there may be a small amount of overlap due to `transmuxerPrependedSeconds`, as long as the search is sequential from the beginning of the playlist to the end, the right segment will be found, as the prepended times will only come from content from prior segments.