1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-04 10:19:24 +02:00
Daniel Neto 2023-06-30 08:55:17 -03:00
parent 746e163d01
commit 1c7ea28b46
808 changed files with 316395 additions and 381162 deletions

View file

@ -1,3 +1,14 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Overview](#overview)
- [HTTP Live Streaming](#http-live-streaming)
- [Dynamic Adaptive Streaming over HTTP](#dynamic-adaptive-streaming-over-http)
- [Further Documentation](#further-documentation)
- [Helpful Tools](#helpful-tools)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# Overview
This project supports both [HLS][hls] and [MPEG-DASH][dash] playback in the video.js player. This document is intended as a primer for anyone interested in contributing or just better understanding how bits from a server get turned into video on their display.
@ -7,11 +18,11 @@ This project supports both [HLS][hls] and [MPEG-DASH][dash] playback in the vide
- Delivered over HTTP(S): it uses the standard application protocol of the web to deliver all its data
- Segmented: longer videos are broken up into smaller chunks which can be downloaded independently and switched between at runtime
A standard HLS stream consists of a *Master Playlist* which references one or more *Media Playlists*. Each Media Playlist contains one or more sequential video segments. All these components form a logical hierarchy that informs the player of the different quality levels of the video available and how to address the individual segments of video at each of those levels:
A standard HLS stream consists of a *Main Playlist* which references one or more *Media Playlists*. Each Media Playlist contains one or more sequential video segments. All these components form a logical hierarchy that informs the player of the different quality levels of the video available and how to address the individual segments of video at each of those levels:
![HLS Format](images/hls-format.png)
HLS streams can be delivered in two different modes: a "static" mode for videos that can be played back from any point, often referred to as video-on-demand (VOD); or a "live" mode where later portions of the video become available as time goes by. In the static mode, the Master and Media playlists are fixed. The player is guaranteed that the set of video segments referenced by those playlists will not change over time.
HLS streams can be delivered in two different modes: a "static" mode for videos that can be played back from any point, often referred to as video-on-demand (VOD); or a "live" mode where later portions of the video become available as time goes by. In the static mode, the Main and Media playlists are fixed. The player is guaranteed that the set of video segments referenced by those playlists will not change over time.
Live mode can work in one of two ways. For truly live events, the most common configuration is for each individual Media Playlist to only include the latest video segment and a small number of consecutive previous segments. In this mode, the player may be able to seek backwards a short time in the video but probably not all the way back to the beginning. In the other live configuration, new video segments can be appended to the Media Playlists but older segments are never removed. This configuration allows the player to seek back to the beginning of the stream at any time during the broadcast and transitions seamlessly to the static stream type when the event finishes.

View file

@ -58,25 +58,23 @@ flowchart TD
> * most EME handling for DRM and setup of the [videojs-contrib-eme plugin](https://github.com/videojs/videojs-contrib-eme)
> * mapping/handling of options passed down via Video.js
## MasterPlaylistController
## PlaylistController
One critical object that `VhsHandler`'s constructor creates is a new `MasterPlaylistController`.
One critical object that `VhsHandler`'s constructor creates is a new `PlaylistController`.
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
VhsHandler --> PlaylistController
```
`MasterPlaylistController` is not a great name, and has grown in size to be a bit unwieldy, but it's the hub of VHS. Eventually, it should be broken into smaller pieces, but for now, it handles the creation and management of most of the other VHS modules. Its code can be found in [src/master-playlist-controller.js](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js).
`PlaylistController` is not a great name, and has grown in size to be a bit unwieldy, but it's the hub of VHS. Eventually, it should be broken into smaller pieces, but for now, it handles the creation and management of most of the other VHS modules. Its code can be found in [src/playlist-controller.js](/src/playlist-controller.js).
The best way to think of `MasterPlaylistController` is as Tron's Master Control Program, though hopefully it isn't as evil.
`PlaylistController` is a lot to say. So we often refer to it as PC.
`MasterPlaylistController` is a lot to say. So we often refer to it as MPC.
If you need to find a place where different modules communicate, you will probably end up in PC. Just about all of `VhsHandler` that doesn't interface with Video.js or other plugins, interfaces with PC.
If you need to find a place where different modules communicate, you will probably end up in MPC. Just about all of `VhsHandler` that doesn't interface with Video.js or other plugins, interfaces with MPC.
MPC's [constructor](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L148) does a lot. Instead of listing all of the things it does, let's go step-by-step through the main ones, passing the source we had above.
PC's [constructor](/src/playlist-controller.js#L148) does a lot. Instead of listing all of the things it does, let's go step-by-step through the main ones, passing the source we had above.
```html
<video-js id="myPlayer" class="video-js" data-setup='{}'>
@ -84,18 +82,18 @@ MPC's [constructor](https://github.com/videojs/http-streaming/blob/0964cb4827d9e
</video-js>
```
Looking at the `<source>` tag, `VhsSourceHandler` already used the "type" to tell Video.js that it could handle the source. `VhsHandler` took the manifest URL, in this case "manifest.m3u8" and provided it to the constructor of MPC.
Looking at the `<source>` tag, `VhsSourceHandler` already used the "type" to tell Video.js that it could handle the source. `VhsHandler` took the manifest URL, in this case "manifest.m3u8" and provided it to the constructor of PC.
The first thing that MPC must do is download that source, but it doesn't make the request itself. Instead, it creates [this.masterPlaylistLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L264-L266).
The first thing that PC must do is download that source, but it doesn't make the request itself. Instead, it creates [this.mainPlaylistLoader_](/src/laylist-controller.js#L263-L265).
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
```
`masterPlaylistLoader_` is an instance of either the [HLS PlaylistLoader](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/playlist-loader.js#L379) or the [DashPlaylistLoader](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/dash-playlist-loader.js#L259).
`mainPlaylistLoader_` is an instance of either the [HLS PlaylistLoader](/src/playlist-loader.js#L379) or the [DashPlaylistLoader](/src/dash-playlist-loader.js#L259).
The names betray their use. They load the playlist. The URL ("manifest.m3u8" here) is given, and the manifest/playlist is downloaded and parsed. If the content is live, the playlist loader also handles refreshing the manifest. For HLS, where manifests point to other manifests, the playlist loader requests those as well.
@ -117,9 +115,9 @@ Manifest {
}
```
Many properties are removed for simplicity. This is a top level manifest (often referred to as a master or main manifest), and within it there are playlists, each playlist being a Manifest itself. Since the JSON "schema" for main and media playlists is the same, you will see irrelevant properties within any given manifest object. For instance, you might see a `targetDuration` property on the main manifest object, though a main manifest doesn't have a target duration. You can ignore irrelevant properties. Eventually they should be cleaned up, and a proper schema defined for manifest objects.
Many properties are removed for simplicity. This is a top level manifest (often referred to as a main manifest or a multivariant manifest [from the HLS spec]), and within it there are playlists, each playlist being a Manifest itself. Since the JSON "schema" for main and media playlists is the same, you will see irrelevant properties within any given manifest object. For instance, you might see a `targetDuration` property on the main manifest object, though a main manifest doesn't have a target duration. You can ignore irrelevant properties. Eventually they should be cleaned up, and a proper schema defined for manifest objects.
MPC will also use `masterPlaylistLoader_` to select which media playlist is active (e.g., the 720p rendition or the 480p rendition), so that `masterPlaylistLoader_` will only need to refresh that individual playlist if the stream is live.
PC will also use `mainPlaylistLoader_` to select which media playlist is active (e.g., the 720p rendition or the 480p rendition), so that `mainPlaylistLoader_` will only need to refresh that individual playlist if the stream is live.
> :information_source: **Future Work**
>
@ -129,26 +127,26 @@ MPC will also use `masterPlaylistLoader_` to select which media playlist is acti
### Media Source Extensions
The next thing MPC needs to do is set up a media source for [Media Source Extensions](https://www.w3.org/TR/media-source/). Specifically, it needs to create [this.mediaSource](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L210) and its associated [source buffers](https://github.com/videojs/http-streaming/blob/main/src/master-playlist-controller.js#L1818). These are where audio and video data will be appended, so that the browser has content to play. But those aren't used directly. Because source buffers can only handle one operation at a time, [this.sourceUpdater_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L234) is created. `sourceUpdater_` is a queue for operations performed on the source buffers. That's pretty much it. So all of the MSE pieces for appending get wrapped up in `sourceUpdater_`.
The next thing PC needs to do is set up a media source for [Media Source Extensions](https://www.w3.org/TR/media-source/). Specifically, it needs to create [this.mediaSource](/src/playlist-controller.js#L208) and its associated [source buffers](/src/playlist-controller.js#L1814). These are where audio and video data will be appended, so that the browser has content to play. But those aren't used directly. Because source buffers can only handle one operation at a time, [this.sourceUpdater_](/src/playlist-controller.js#L232) is created. `sourceUpdater_` is a queue for operations performed on the source buffers. That's pretty much it. So all of the MSE pieces for appending get wrapped up in `sourceUpdater_`.
## Segment Loaders
The SourceUpdater created for MSE above is passed to the segment loaders.
[this.mainSegmentLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L271-L275) is used for muxed content (audio and video in one segment) and for audio or video only streams.
[this.mainSegmentLoader_](/src/playlist-controller.js#L270-L274) is used for muxed content (audio and video in one segment) and for audio or video only streams.
[this.audioSegmentLoader_](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L278-L281) is used when the content is demuxed (audio and video in separate playlists).
[this.audioSegmentLoader_](/src/playlist-controller.js#L277-L280) is used when the content is demuxed (audio and video in separate playlists).
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
MasterPlaylistController --> SourceUpdater
MasterPlaylistController --> SegmentLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
PlaylistController --> SourceUpdater
PlaylistController --> SegmentLoader
```
Besides options and the `sourceUpdater_` from MPC, the segment loaders are given a [playlist](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/segment-loader.js#L988). This playlist is a media playlist from the `masterPlaylistLoader_`. So looking back at our parsed manifest object:
Besides options and the `sourceUpdater_` from PC, the segment loaders are given a [playlist](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/segment-loader.js#L988). This playlist is a media playlist from the `mainPlaylistLoader_`. So looking back at our parsed manifest object:
```
Manifest {
@ -174,19 +172,19 @@ VHS uses a strategy called `mediaIndex++` for choosing the next segment, see [he
If there are no seeks or rendition changes, `chooseNextRequest_` will rely on the `mediaIndex++` strategy.
If there are seeks or rendition changes, then `chooseNextRequest_` will look at segment timing values via the `SyncController` (created previously in MPC), the current time, and the buffer, to determine what the next segment should be, and what it's start time should be (to position it on the timeline).
If there are seeks or rendition changes, then `chooseNextRequest_` will look at segment timing values via the `SyncController` (created previously in PC), the current time, and the buffer, to determine what the next segment should be, and what it's start time should be (to position it on the timeline).
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
MasterPlaylistController --> SourceUpdater
MasterPlaylistController --> SegmentLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
PlaylistController --> SourceUpdater
PlaylistController --> SegmentLoader
SegmentLoader --> SyncController
```
The `SyncController` has various [strategies](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/sync-controller.js#L16) for ensuring that different renditions, which can have different media sequence and segment timing values, can be positioned on the playback timeline successfully. (It is also be [used by MPC](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/master-playlist-controller.js#L1477) to establish a `seekable` range.)
The `SyncController` has various [strategies](https://github.com/videojs/http-streaming/blob/0964cb4827d9e80aa36f2fa29e35dad92ca84111/src/sync-controller.js#L16) for ensuring that different renditions, which can have different media sequence and segment timing values, can be positioned on the playback timeline successfully. (It is also be [used by PC](/src/playlist-controller.js#L1472) to establish a `seekable` range.)
### Downloading and Appending Segments
@ -196,10 +194,10 @@ If the buffer is not full, and a segment was chosen, then `SegmentLoader` will d
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
MasterPlaylistController --> SourceUpdater
MasterPlaylistController --> SegmentLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
PlaylistController --> SourceUpdater
PlaylistController --> SegmentLoader
SegmentLoader --> SyncController
SegmentLoader --> mediaSegmentRequest
```
@ -213,10 +211,10 @@ When the `SegmentLoader` receives segment data events, it can append the data to
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
MasterPlaylistController --> SourceUpdater
MasterPlaylistController --> SegmentLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
PlaylistController --> SourceUpdater
PlaylistController --> SegmentLoader
SegmentLoader --> SyncController
SegmentLoader --> mediaSegmentRequest
SegmentLoader --> SourceUpdater
@ -231,10 +229,10 @@ Besides downloading segments, `mediaSegmentRequest` [decrypts](https://github.co
```mermaid
flowchart TD
VhsSourceHandler --> VhsHandler
VhsHandler --> MasterPlaylistController
MasterPlaylistController --> PlaylistLoader
MasterPlaylistController --> SourceUpdater
MasterPlaylistController --> SegmentLoader
VhsHandler --> PlaylistController
PlaylistController --> PlaylistLoader
PlaylistController --> SourceUpdater
PlaylistController --> SegmentLoader
SegmentLoader --> SyncController
SegmentLoader --> mediaSegmentRequest
SegmentLoader --> SourceUpdater
@ -247,4 +245,4 @@ The video can start playing as soon as there's enough audio and video (for muxed
But once `SegmentLoader` does finish, it starts the process again, looking for new content.
There are other modules, and other functions of the code (e.g., blacklisting logic, ABR, etc.), but this is the most critical path of VHS, the one that allows video to play in the browser.
There are other modules, and other functions of the code (e.g., excluding logic, ABR, etc.), but this is the most critical path of VHS, the one that allows video to play in the browser.

View file

@ -13,7 +13,7 @@ The [playlist loader](../src/playlist-loader.js) handles all of the details of r
During VOD playback, the loader will move quickly to the HAVE_METADATA state and then stay there unless a quality switch request sends it to SWITCHING_MEDIA while it fetches an alternate playlist. The loader enters the HAVE_CURRENT_METADATA when a live stream is detected and it's time to refresh the current media playlist to find out about new video segments.
### HLS Tech
Currently, the HLS project integrates with [video.js](http://www.videojs.com/) as a [tech](https://github.com/videojs/video.js/blob/master/docs/guides/tech.md). That means it's responsible for providing an interface that closely mirrors the `<video>` element. You can see that implementation in [videojs-http-streaming.js](../src/videojs-http-streaming.js), the primary entry point of the project.
Currently, the HLS project integrates with [video.js](http://www.videojs.com/) as a [tech](https://github.com/videojs/video.js/blob/main/docs/guides/tech.md). That means it's responsible for providing an interface that closely mirrors the `<video>` element. You can see that implementation in [videojs-http-streaming.js](../src/videojs-http-streaming.js), the primary entry point of the project.
### Transmuxing
Most browsers don't have support for the file type that HLS video segments are stored in. To get HLS playing back on those browsers, contrib-hls strings together a number of technologies:

View file

@ -34,7 +34,7 @@ gets used:
![Final selection](images/bitrate-switching-4.png)
If it turns out no rendition is acceptable based on the filtering
described above, the first encoding listed in the master playlist will
described above, the first encoding listed in the main playlist will
be used.
If you'd like your player to use a different set of priorities, it's

View file

@ -22,19 +22,19 @@ The [DPL] is written to be as similar as possible to the [PlaylistLoader][pl]. T
![DashPlaylistLoader States](images/dash-playlist-loader-states.nomnoml.svg)
- `HAVE_NOTHING` the state before the MPD is received and parsed.
- `HAVE_MASTER` the state before a media stream is setup but the MPD has been parsed.
- `HAVE_MAIN_MANIFEST` the state before a media stream is setup but the MPD has been parsed.
- `HAVE_METADATA` the state after a media stream is setup.
### API
- `load()` this will either start or kick the loader during playback.
- `start()` this will start the [DPL] and request the MPD.
- `parseMasterXml()` this will parse the MPD manifest and return the result.
- `parseMainXml()` this will parse the MPD manifest and return the result.
- `media()` this will return the currently active media stream or set a new active media stream.
### Events
- `loadedplaylist` signals the setup of a master playlist, representing the DASH source as a whole, from the MPD; or a media playlist, representing a media stream.
- `loadedplaylist` signals the setup of a main playlist, representing the DASH source as a whole, from the MPD; or a media playlist, representing a media stream.
- `loadedmetadata` signals initial setup of a media stream.
- `minimumUpdatePeriod` signals that a update period has ended and the MPD must be requested again.
- `playlistunchanged` signals that no changes have been made to a MPD.
@ -44,7 +44,7 @@ The [DPL] is written to be as similar as possible to the [PlaylistLoader][pl]. T
### Interaction with Other Modules
![DPL with MPC and MG](images/dash-playlist-loader-mpc-mg-sequence.plantuml.png)
![DPL with PC and MG](images/dash-playlist-loader-pc-mg-sequence.puml.png)
### Special Features
@ -64,17 +64,17 @@ To be filled out.
### Previous Behavior
Until version 1.9.0 of [VHS], we thought that [DPL] could skip the `HAVE_NOTHING` and `HAVE_MASTER` states, as no other XHR requests are needed once the MPD has been downloaded and parsed. However, this is incorrect as there are some Presentations that signal the use of a "Segment Index box" or `sidx`. This `sidx` references specific byte ranges in a file that could contain media or potentially other `sidx` boxes.
Until version 1.9.0 of [VHS], we thought that [DPL] could skip the `HAVE_NOTHING` and `HAVE_MAIN_MANIFEST` states, as no other XHR requests are needed once the MPD has been downloaded and parsed. However, this is incorrect as there are some Presentations that signal the use of a "Segment Index box" or `sidx`. This `sidx` references specific byte ranges in a file that could contain media or potentially other `sidx` boxes.
A DASH MPD that describes a `sidx` is therefore similar to an HLS master manifest, in that the MPD contains references to something that must be requested and parsed first before references to media segments can be obtained. With this in mind, it was necessary to update the initialization and state transitions of [DPL] to allow further XHR requests to be made after the initial request for the MPD.
A DASH MPD that describes a `sidx` is therefore similar to an HLS main manifest, in that the MPD contains references to something that must be requested and parsed first before references to media segments can be obtained. With this in mind, it was necessary to update the initialization and state transitions of [DPL] to allow further XHR requests to be made after the initial request for the MPD.
### Current Behavior
In [this PR](https://github.com/videojs/http-streaming/pull/386), the [DPL] was updated to go through the `HAVE_NOTHING` and `HAVE_MASTER` states before arriving at `HAVE_METADATA`. If the MPD does not contain `sidx` boxes, then this transition happens quickly after `load()` is called, spending little time in the `HAVE_MASTER` state.
In [this PR](https://github.com/videojs/http-streaming/pull/386), the [DPL] was updated to go through the `HAVE_NOTHING` and `HAVE_MAIN_MANIFEST` states before arriving at `HAVE_METADATA`. If the MPD does not contain `sidx` boxes, then this transition happens quickly after `load()` is called, spending little time in the `HAVE_MAIN_MANIFEST` state.
The initial media selection for `masterPlaylistLoader` is made in the `loadedplaylist` handler located in [MasterPlaylistController][mpc]. We now use `hasPendingRequest` to determine whether to automatically select a media playlist for the `masterPlaylistLoader` as a fallback in case one is not selected by [MPC]. The child [DPL]s are created with a media playlist passed in as an argument, so this fallback is not necessary for them. Instead, that media playlist is saved and auto-selected once we enter the `HAVE_MASTER` state.
The initial media selection for `mainPlaylistLoader` is made in the `loadedplaylist` handler located in [PlaylistController][pc]. We now use `hasPendingRequest` to determine whether to automatically select a media playlist for the `mainPlaylistLoader` as a fallback in case one is not selected by [PC]. The child [DPL]s are created with a media playlist passed in as an argument, so this fallback is not necessary for them. Instead, that media playlist is saved and auto-selected once we enter the `HAVE_MAIN_MANIFEST` state.
The `updateMaster` method will return `null` if no updates are found.
The `updateMain` method will return `null` if no updates are found.
The `selectinitialmedia` event is not triggered until an audioPlaylistLoader (which for DASH is always a child [DPL]) has a media playlist. This is signaled by triggering `loadedmetadata` on the respective [DPL]. This event is used to initialize the [Representations API][representations] and setup EME (see [contrib-eme]).
@ -82,6 +82,6 @@ The `selectinitialmedia` event is not triggered until an audioPlaylistLoader (wh
[sl]: ../src/segment-loader.js
[vhs]: intro.md
[pl]: ../src/playlist-loader.js
[mpc]: ../src/master-playlist-controller.js
[pc]: ../src/playlist-controller.js
[representations]: ../README.md#hlsrepresentations
[contrib-eme]: https://github.com/videojs/videojs-contrib-eme

View file

@ -4,9 +4,9 @@
**Media Playlist**: This is a manifest that represents a single rendition or media stream of the source.
**Master Playlist Controller**: This acts as the main controller for the playback engine. It interacts with the SegmentLoaders, PlaylistLoaders, PlaybackWatcher, etc.
**Playlist Controller**: This acts as the main controller for the playback engine. It interacts with the SegmentLoaders, PlaylistLoaders, PlaybackWatcher, etc.
**Playlist Loader**: This will request the source and load the master manifest. It is also instructed by the ABR algorithm to load a media playlist or wraps a media playlist if it is provided as the source. There are more details about the playlist loader [here](./arch.md).
**Playlist Loader**: This will request the source and load the main manifest. It is also instructed by the ABR algorithm to load a media playlist or wraps a media playlist if it is provided as the source. There are more details about the playlist loader [here](./arch.md).
**DASH Playlist Loader**: This will do as the PlaylistLoader does, but for DASH sources. It also handles DASH specific functionaltiy, such as refreshing the MPD according to the minimumRefreshPeriod and synchronizing to a server clock.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

View file

@ -1,12 +1,12 @@
<svg width="228" height="310" version="1.1" baseProfile="full" viewbox="0 0 228 310" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:10pt; font-family:'Arial', Helvetica, sans-serif;;stroke-width:2;stroke-linejoin:round;stroke-linecap:round"><text x="134" y="111" style="font-weight:normal;">load()</text>
<path d="M114 81 L114 105 L114 129 L114 129 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
<path d="M110.8 121 L114 125 L117.2 121 L114 129 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
<text x="134" y="211" style="font-weight:normal;">media()</text>
<path d="M114 181 L114 205 L114 229 L114 229 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
<path d="M110.8 221 L114 225 L117.2 221 L114 229 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
<rect x="37" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="57" y="60" style="">HAVE_NOTHING</text>
<rect x="40" y="130" height="50" width="149" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="60" y="160" style="">HAVE_MASTER</text>
<rect x="30" y="230" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="50" y="260" style="">HAVE_METADATA</text></svg>
<svg width="280" height="310" version="1.1" baseProfile="full" viewbox="0 0 280 310" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:10pt; font-family:'Arial', Helvetica, sans-serif;;stroke-width:2;stroke-linejoin:round;stroke-linecap:round"><text x="160" y="111" style="font-weight:normal;">load()</text>
<path d="M140 81 L140 105 L140 129 L140 129 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
<path d="M136.8 121 L140 125 L143.2 121 L140 129 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
<text x="160" y="211" style="font-weight:normal;">media()</text>
<path d="M140 181 L140 205 L140 229 L140 229 " style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
<path d="M136.8 221 L140 225 L143.2 221 L140 229 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
<rect x="63" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="83" y="60" style="">HAVE_NOTHING</text>
<rect x="30" y="130" height="50" width="220" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="50" y="160" style="">HAVE_MAIN_MANIFEST</text>
<rect x="56" y="230" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="76" y="260" style="">HAVE_METADATA</text></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 162 KiB

View file

@ -15,8 +15,8 @@
<path d="M155.2 489 L152 485 L148.8 489 L152 481 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
<rect x="75" y="30" height="50" width="154" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="95" y="60" style="">HAVE_NOTHING</text>
<rect x="78" y="130" height="50" width="149" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="98" y="160" style="">HAVE_MASTER</text>
<rect x="42" y="130" height="50" width="220" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="62" y="160" style="">HAVE_MAIN_MANIFEST</text>
<rect x="56" y="230" height="50" width="192" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
<text x="76.3" y="260" style="">SWITCHING_MEDIA</text>
<rect x="68" y="330" height="50" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

@ -1,119 +0,0 @@
@startuml
header DashPlaylistLoader sequences
title DashPlaylistLoader sequences: Master Manifest with Alternate Audio
Participant "MasterPlaylistController" as MPC #red
Participant "MasterDashPlaylistLoader" as MPL #blue
Participant "mainSegmentLoader" as SL #blue
Participant "AudioDashPlaylistLoader" as APL #green
Participant "audioSegmentLoader" as ASL #green
Participant "external server" as ext #brown
Participant "mpdParser" as parser #orange
Participant "mediaGroups" as MG #purple
Participant Tech #lightblue
== Initialization ==
MPC -> MPL : construct MasterPlaylistLoader
MPC -> MPL: load()
== Requesting Master Manifest ==
MPL -> MPL : start()
MPL -> ext: xhr request for master manifest
ext -> MPL : response with master manifest
MPL -> parser: parse manifest
parser -> MPL: object representing manifest
note over MPL #lightblue: trigger 'loadedplaylist'
== Requesting Video Manifest ==
note over MPL #lightblue: handling loadedplaylist
MPL -> MPL: media(x)
alt if no sidx
note over MPL #lightgray: zero delay to fake network request
else if sidx
break
MPL -> ext: request sidx
end
end
note over MPL #lightblue: trigger 'loadedmetadata' on master loader [T1]
note over MPL #lightblue: handling 'loadedmetadata'
opt vod and preload !== 'none'
MPL -> SL: playlist()
MPL -> SL: load()
end
== Initializing Media Groups, Choosing Active Tracks ==
MPL -> MG: setupMediaGroups()
MG -> MG: initialize()
== Initializing Alternate Audio Loader ==
MG -> APL: create child playlist loader for alt audio
MG -> MG: activeGroup and audio variant selected
MG -> MG: enable activeTrack, onTrackChanged()
MG -> ASL: reset audio segment loader
== Requesting Alternate Audio Manifest ==
MG -> MG: startLoaders()
MG -> APL: load()
APL -> APL: start()
APL -> APL: zero delay to fake network request
break finish pending tasks
MG -> Tech: add audioTrack
MPL -> MPC: setupSourceBuffers_()
MPL -> MPC: setupFirstPlay()
loop mainSegmentLoader.monitorBufferTick_()
SL -> ext: requests media segments
ext -> SL: response with media segment bytes
end
end
APL -> APL: zero delay over
APL -> APL: media(x)
alt if no sidx
note over APL #lightgray: zero delay to fake network request
else if sidx
break
MPL -> ext: request sidx
end
end
== Requesting Alternate Audio Segments ==
note over APL #lightblue: trigger 'loadedplaylist'
note over APL #lightblue: handling 'loadedplaylist'
APL -> ASL: playlist()
note over ASL #lightblue: trigger 'loadedmetadata' [T2]
note over APL #lightblue: handling 'loadedmetadata'
APL -> ASL: playlist()
APL -> ASL: load()
loop audioSegmentLoader.monitorBufferTick_()
ASL -> ext: requests media segments
ext -> ASL: response with media segment bytes
end
@enduml

View file

@ -17,5 +17,5 @@
#.label: align=center visual=none italic
[HAVE_NOTHING] load()-> [HAVE_MASTER]
[HAVE_MASTER] media()-> [HAVE_METADATA]
[HAVE_NOTHING] load()-> [HAVE_MAIN_MANIFEST]
[HAVE_MAIN_MANIFEST] media()-> [HAVE_METADATA]

View file

@ -1,246 +0,0 @@
@startuml
header PlaylistLoader sequences
title PlaylistLoader sequences: Master Manifest and Alternate Audio
Participant "MasterPlaylistController" as MPC #red
Participant "MasterPlaylistLoader" as MPL #blue
Participant "mainSegmentLoader" as SL #blue
Participant "AudioPlaylistLoader" as APL #green
Participant "audioSegmentLoader" as ASL #green
Participant "external server" as ext #brown
Participant "m3u8Parser" as parser #orange
Participant "mediaGroups" as MG #purple
Participant Tech #lightblue
== Initialization ==
group MasterPlaylistController.constructor()
MPC -> MPL : setting up MasterPlaylistLoader
note left #lightyellow
sets up mediaupdatetimeout
handler for live playlist staleness
end note
note over MPL #lightgray: state = 'HAVE_NOTHING'
MPC -> MPL: load()
end
group MasterPlaylistLoader.load()
MPL -> MPL : start()
note left #lightyellow: not started yet
== Requesting Master Manifest ==
group start()
note over MPL #lightgray: started = true
MPL -> ext: xhr request for master manifest
ext -> MPL : response with master manifest
MPL -> parser: parse master manifest
parser -> MPL: object representing manifest
MPL -> MPL: set loader's master playlist
note over MPL #lightgray: state = 'HAVE_MASTER'
note over MPL #lightblue: trigger 'loadedplaylist' on master loader
== Requesting Video Manifest ==
group 'loadedplaylist' handler
note over MPL #lightblue: handling loadedplaylist
MPL -> MPL : media()
note left #lightgray: select initial (video) playlist
note over MPL #lightyellow: state = 'SWITCHING_MEDIA'
group media()
MPL -> ext : request child manifest
ext -> MPL: child manifest returned
MPL -> MPL: haveMetadata()
note over MPL #lightyellow: state = 'HAVE_METADATA'
group haveMetadata()
MPL -> parser: parse child manifest
parser -> MPL: object representing the child manifest
note over MPL #lightyellow
update master and media playlists
end note
opt live
MPL -> MPL: setup mediaupdatetimeout
end
note over MPL #lightblue
trigger 'loadedplaylist' on master loader.
This does not end up requesting segments
at this point.
end note
group MasterPlaylistLoader 'loadedplaylist' handler
MPL -> MPL : setup durationchange handler
end
end
== Requesting Video Segments ==
note over MPL #lightblue: trigger 'loadedmetadata'
group 'loadedmetadata' handler
note over MPL #lightblue: handling 'loadedmetadata'
opt vod and preload !== 'none'
MPL -> SL: playlist()
note over SL #lightyellow: updates playlist
MPL -> SL: load()
note right #lightgray
This does nothing as mimeTypes
have not been set yet.
end note
end
MPL -> MG: setupMediaGroups()
== Initializing Media Groups, Choosing Active Tracks ==
group MediaGroups.setupMediaGroups()
group initialize()
MG -> APL: create child playlist loader for alt audio
note over APL #lightyellow: state = 'HAVE_NOTHING'
note left #lightgray
setup 'loadedmetadata' and 'loadedplaylist' listeners
on child alt audio playlist loader
end note
MG -> Tech: add audioTracks
end
MG -> MG: activeGroup and audio variant selected
MG -> MG: enable activeTrack, onTrackChanged()
note left #lightgray
There is no activePlaylistLoader at this point,
but there is an audio playlistLoader
end note
group onTrackChanged()
MG -> SL: reset mainSegmentLoader
note left #lightgray: Clears buffer, aborts all inflight requests
== Requesting Alternate Audio Manifest ==
MG -> MG: startLoaders()
group startLoaders()
note over MG #lightyellow
activePlaylistLoader = AudioPlaylistLoader
end note
MG -> APL: load()
end
group AudioPlaylistLoader.load()
APL -> APL: start()
group alt start()
note over APL #lightyellow: started = true
APL -> ext: request alt audio media manifest
break MasterPlaylistLoader 'loadedmetadata' handler
MPL -> MPC: setupSourceBuffers()
note left #lightgray
This will set mimeType.
Segments can be loaded from now on.
end note
MPL -> MPC: setupFirstPlay()
note left #lightgray
Immediate exit since the player
is paused
end note
end
ext -> APL: responds with child manifest
APL -> parser: parse child manifest
parser -> APL: object representing child manifest returned
note over APL #lightyellow: state = 'HAVE_MASTER'
note left #lightgray: Infer a master playlist
APL -> APL: haveMetadata()
note over APL #lightyellow: state = 'HAVE_METADATA'
group haveMetadata()
APL -> parser: parsing the child manifest again
parser -> APL: returning object representing child manifest
note over APL #lightyellow
update master and media references
end note
== Requesting Alternate Audio Segments ==
note over APL #lightblue: trigger 'loadedplaylist'
group 'loadedplaylist' handler
note over APL #lightblue: handling 'loadedplaylist'
APL -> ASL: playlist()
note over ASL #lightyellow: set playlist
end
end
note over APL #lightblue: trigger 'loadedmetadata'
group 'loadedmetadata' handler
note over APL #lightblue: handling 'loadedmetadata'
APL -> ASL: playlist()
APL -> ASL: load()
loop audioSegmentLoader.load()
ASL -> ext: requests media segments
ext -> ASL: response with media segment bytes
end
end
end
end
end
end
end
end
end
end
@enduml

View file

@ -1,114 +0,0 @@
@startuml
header PlaylistLoader sequences
title PlaylistLoader sequences: Master Manifest and Alternate Audio
Participant "MasterPlaylistController" as MPC #red
Participant "MasterPlaylistLoader" as MPL #blue
Participant "mainSegmentLoader" as SL #blue
Participant "AudioPlaylistLoader" as APL #green
Participant "audioSegmentLoader" as ASL #green
Participant "external server" as ext #brown
Participant "m3u8Parser" as parser #orange
Participant "mediaGroups" as MG #purple
Participant Tech #lightblue
== Initialization ==
MPC -> MPL : construct MasterPlaylistLoader
MPC -> MPL: load()
MPL -> MPL : start()
== Requesting Master Manifest ==
MPL -> ext: xhr request for master manifest
ext -> MPL : response with master manifest
MPL -> parser: parse master manifest
parser -> MPL: object representing manifest
note over MPL #lightblue: trigger 'loadedplaylist'
== Requesting Video Manifest ==
note over MPL #lightblue: handling loadedplaylist
MPL -> MPL : media()
MPL -> ext : request child manifest
ext -> MPL: child manifest returned
MPL -> parser: parse child manifest
parser -> MPL: object representing the child manifest
note over MPL #lightblue: trigger 'loadedplaylist'
note over MPL #lightblue: handleing 'loadedplaylist'
MPL -> SL: playlist()
MPL -> SL: load()
== Requesting Video Segments ==
note over MPL #lightblue: trigger 'loadedmetadata'
note over MPL #lightblue: handling 'loadedmetadata'
opt vod and preload !== 'none'
MPL -> SL: playlist()
MPL -> SL: load()
end
MPL -> MG: setupMediaGroups()
== Initializing Media Groups, Choosing Active Tracks ==
MG -> APL: create child playlist loader for alt audio
MG -> MG: activeGroup and audio variant selected
MG -> MG: enable activeTrack, onTrackChanged()
MG -> SL: reset mainSegmentLoader
== Requesting Alternate Audio Manifest ==
MG -> MG: startLoaders()
MG -> APL: load()
APL -> APL: start()
APL -> ext: request alt audio media manifest
break finish pending tasks
MG -> Tech: add audioTracks
MPL -> MPC: setupSourceBuffers()
MPL -> MPC: setupFirstPlay()
loop on monitorBufferTick
SL -> ext: requests media segments
ext -> SL: response with media segment bytes
end
end
ext -> APL: responds with child manifest
APL -> parser: parse child manifest
parser -> APL: object representing child manifest returned
== Requesting Alternate Audio Segments ==
note over APL #lightblue: trigger 'loadedplaylist'
note over APL #lightblue: handling 'loadedplaylist'
APL -> ASL: playlist()
note over APL #lightblue: trigger 'loadedmetadata'
note over APL #lightblue: handling 'loadedmetadata'
APL -> ASL: playlist()
APL -> ASL: load()
loop audioSegmentLoader.load()
ASL -> ext: requests media segments
ext -> ASL: response with media segment bytes
end
@enduml

View file

@ -17,8 +17,8 @@
#.label: align=center visual=none italic
[HAVE_NOTHING] load()-> [HAVE_MASTER]
[HAVE_MASTER] media()-> [SWITCHING_MEDIA]
[HAVE_NOTHING] load()-> [HAVE_MAIN_MANIFEST]
[HAVE_MAIN_MANIFEST] media()-> [SWITCHING_MEDIA]
[SWITCHING_MEDIA] media()/ start()-> [HAVE_METADATA]
[HAVE_METADATA] <--> [<label> mediaupdatetimeout]

View file

@ -53,7 +53,7 @@ The first change was to request pieces of a segment. There are a few approaches
*Plain text MIME type* was chosen because of its wide support. It provides a mechanism to access progressive bytes downloaded on [XMLHttpRequest progress events](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onprogress).
This change was made in [media-segment-request](https://github.com/videojs/http-streaming/blob/master/src/media-segment-request.js).
This change was made in [media-segment-request](/src/media-segment-request.js).
### Transmux and Append Segment Pieces
@ -93,7 +93,7 @@ The transmuxing logic moved to a new module called segment-transmuxer, which wra
##### mux.js
While most of the [mux.js pipeline](https://github.com/videojs/mux.js/blob/master/docs/diagram.png) supports pushing pieces of data (and should support LHLS by default), its "flushes" to send transmuxed data back to the caller expected full segments.
While most of the [mux.js pipeline](/docs/diagram.png) supports pushing pieces of data (and should support LHLS by default), its "flushes" to send transmuxed data back to the caller expected full segments.
Much of the pipeline was reused, however, the top level audio and video segment streams, as well as the entry point, were rewritten so that instead of providing a full segment on flushes, each frame of video was provided individually (audio frames still flush as a group). The new concept of partial flushes was added into the pipeline to handle this case.

View file

@ -33,4 +33,4 @@ With the transmux before append and LHLS changes, we only append video init segm
### Test Changes
Some tests were removed because they were no longer relevant after the change to creating source buffers later. For instance, `waits for both main and audio loaders to finish before calling endOfStream if main loader starting media is unknown` no longer can be tested by waiting for an audio loader response and checking for end of stream, as the test will time out since MasterPlaylistController will wait for track info from the main loader before the source buffers are created. That condition is checked elsewhere.
Some tests were removed because they were no longer relevant after the change to creating source buffers later. For instance, `waits for both main and audio loaders to finish before calling endOfStream if main loader starting media is unknown` no longer can be tested by waiting for an audio loader response and checking for end of stream, as the test will time out since PlaylistController will wait for track info from the main loader before the source buffers are created. That condition is checked elsewhere.

View file

@ -65,19 +65,19 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have
## Startup (how tracks are added and used)
> AudioTrack & AudioTrackList live in video.js
1. `HLS` creates a `MasterPlaylistController` and watches for the `loadedmetadata` event
1. `HLS` parses the m3u8 using the `MasterPlaylistController`
1. `MasterPlaylistController` creates a `PlaylistLoader` for the master m3u8
1. `MasterPlaylistController` creates `PlaylistLoader`s for every audio playlist
1. `MasterPlaylistController` creates a `SegmentLoader` for the main m3u8
1. `MasterPlaylistController` creates a `SegmentLoader` for a potential audio playlist
1. `HLS` creates a `PlaylistController` and watches for the `loadedmetadata` event
1. `HLS` parses the m3u8 using the `PlaylistController`
1. `PlaylistController` creates a `PlaylistLoader` for the main m3u8
1. `PlaylistController` creates `PlaylistLoader`s for every audio playlist
1. `PlaylistController` creates a `SegmentLoader` for the main m3u8
1. `PlaylistController` creates a `SegmentLoader` for a potential audio playlist
1. `HLS` sees the `loadedmetadata` and finds the currently selected MediaGroup and all the metadata
1. `HLS` removes all `AudioTrack`s from the `AudioTrackList`
1. `HLS` created `AudioTrack`s for the MediaGroup and adds them to the `AudioTrackList`
1. `HLS` calls `MasterPlaylistController`s `useAudio` with no arguments (causes it to use the currently enabled audio)
1. `MasterPlaylistController` turns off the current audio `PlaylistLoader` if it is on
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `MasterPlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (master or audio only)
1. `HLS` calls `PlaylistController`s `useAudio` with no arguments (causes it to use the currently enabled audio)
1. `PlaylistController` turns off the current audio `PlaylistLoader` if it is on
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `PlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (main or audio only)
1. `MediaSource`/`mux.js` determine how to mux
## How tracks are switched
@ -88,9 +88,9 @@ Corresponding AudioTrackList when media-group-1 is used (before any tracks have
1. `AudioTrackList` enables the new `Audiotrack` and disables all others
1. `AudioTrackList` triggers a `changed` event
1. `HLS` sees the `changed` event and finds the newly enabled `AudioTrack`
1. `HLS` sends the `label` for the new `AudioTrack` to `MasterPlaylistController`s `useAudio` function
1. `MasterPlaylistController` turns off the current audio `PlaylistLoader` if it is on
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `MasterPlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `MasterPlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (master or audio only)
1. `HLS` sends the `label` for the new `AudioTrack` to `PlaylistController`s `useAudio` function
1. `PlaylistController` turns off the current audio `PlaylistLoader` if it is on
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `PlaylistController` maps the `label` to the `PlaylistLoader` containing the audio
1. `PlaylistController` turns on that `PlaylistLoader` and the Corresponding `SegmentLoader` (main or audio only)
1. `MediaSource`/`mux.js` determine how to mux

View file

@ -9,7 +9,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
1. To request an m3u8.
2. To parse a m3u8 into a format [videojs-http-streaming][vhs] can understand.
3. To allow selection of a specific media stream.
4. To refresh a live master m3u8 for changes.
4. To refresh a live m3u8 for changes.
## Design
@ -18,7 +18,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
![PlaylistLoader States](images/playlist-loader-states.nomnoml.svg)
- `HAVE_NOTHING` the state before the m3u8 is received and parsed.
- `HAVE_MASTER` the state before a media manifest is parsed and setup but after the master manifest has been parsed and setup.
- `HAVE_MAIN_MANIFEST` the state before a media manifest is parsed and setup but after the main manifest has been parsed and setup.
- `HAVE_METADATA` the state after a media stream is setup.
- `SWITCHING_MEDIA` the intermediary state we go though while changing to a newly selected media playlist
- `HAVE_CURRENT_METADATA` a temporary state after requesting a refresh of the live manifest and before receiving the update
@ -31,7 +31,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
### Events
- `loadedplaylist` signals the setup of a master playlist, representing the HLS source as a whole, from the m3u8; or a media playlist, representing a media stream.
- `loadedplaylist` signals the setup of a main playlist, representing the HLS source as a whole, from the m3u8; or a media playlist, representing a media stream.
- `loadedmetadata` signals initial setup of a media stream.
- `playlistunchanged` signals that no changes have been made to a m3u8.
- `mediaupdatetimeout` signals that a live m3u8 and media stream must be refreshed.
@ -40,7 +40,7 @@ The [PlaylistLoader][pl] (PL) is responsible for requesting m3u8s, parsing them
### Interaction with Other Modules
![PL with MPC and MG](images/playlist-loader-mpc-mg-sequence.plantuml.png)
![PL with PC and MG](images/playlist-loader-pc-mg-sequence.puml.png)
[pl]: ../src/playlist-loader.js
[sl]: ../src/segment-loader.js

View file

@ -2,7 +2,7 @@
## Other troubleshooting guides
For issues around data embedded into media segments (e.g., 608 captions), see the [mux.js troubleshooting guide](https://github.com/videojs/mux.js/blob/master/docs/troubleshooting.md).
For issues around data embedded into media segments (e.g., 608 captions), see the [mux.js troubleshooting guide](https://github.com/videojs/mux.js/blob/main/docs/troubleshooting.md).
## Tools