Repository navigation
How It Works
A provider plugin writes its channels into a bouquet as services of type 4097 (played by GStreamer). With the stream proxy on, the stream of such a service is a lazy address instead of the address of the stream:
4097:0:1:<sid>:<tsid>:...:pluto%3a//<channel id>:<channel name>
Nothing has to be fetched while the bouquet is written, and an address that expires cannot be stale in the bouquet. The real address is looked up when the channel is played or recorded.
Enigma2 calls two service extensions of StreamProxyCockpit for every playService() and recordService():
- The extension recognises the scheme (
pluto,rakutentv,samsungtv, ...) and hands the channel to the plugin that registered it. - The plugin starts the proxy if it is not running, registers the channel with it and returns the address of the proxy for the channel.
- The extension replaces the stream of the service by that address, and Enigma2 plays or records from there.
The extensions are installed as soon as a plugin has registered. This happens when the plugin is imported, which is before the first session exists, so a record timer that is resumed while Enigma2 starts finds them in place.
An instant recording is a special case: Enigma2 hands the recorder the service that is playing, whose stream was already replaced by the proxy's address. The extension recognises that address again, for channels a plugin resolved before.
The proxy is an HTTP server on 127.0.0.1:7654. It is only reachable from the receiver itself. Its endpoints:
| Path | Used for |
|---|---|
/auto/<channel id>.m3u8 |
Where playback starts. It fetches the master playlist of the channel and decides how to serve it (see below) |
/master/<id>.m3u8, /pl/<id>/<n>.m3u8, /seg/<id>/<n>.ts
|
Playlists and segments of a stream with a separate audio stream (unmuxed) |
/mpl/<id>.m3u8, /mseg/<id>/<n>.ts
|
Playlist and segments of a stream that is already muxed |
/rec/<id>.ts |
One continuous transport stream for a recording |
A channel can have request headers (for example a user agent or a token) which the proxy sends to the server with every request, and an address that is renewed when it no longer works.
Some channels deliver video and audio as two streams (#EXT-X-MEDIA:TYPE=AUDIO in the master playlist). The decoder of the receiver cannot play that as it is. For such a channel the proxy fetches the video and audio segments, decrypts them if they are AES-128 encrypted, and muxes them into single transport stream segments that it serves as a normal HLS stream. Audio that arrives late is muxed into its window when it is there, and replaced by silence only when it does not arrive in time.
A stream that already carries video and audio together can still be made of independently encoded pieces: at every #EXT-X-DISCONTINUITY (an ad starts or ends) the timestamps jump, the continuity counters start again and PIDs or codec parameters may change. The decoder of some receivers shows distorted video for a few seconds then.
For these channels the proxy runs one poller per channel. It follows the media playlist, fetches every segment once and remuxes it with ffmpeg (stream copy), so that the PIDs and the PAT/PMT are the same wherever a piece came from. After a discontinuity the timeline continues where the previous segment ended, and the continuity counters run on over all segments. The result is a playlist without discontinuities, served at /mpl and /mseg.
A stream that cannot be processed this way (fMP4 or byte-range segments, SAMPLE-AES) is not touched: /auto redirects to the stream itself, the way it was played before the proxy existed.
Enigma2's recorder writes whatever the source delivers into the file. A stream with ad breaks would make the recording end or become unplayable at the first break. A recording is therefore not made from the HLS playlist but from /rec/<channel id>.ts: the proxy concatenates the segments of the channel into one transport stream from the live edge on, with a continuous timeline. If the source stops for a moment (the stream is renewed, the network drops), the proxy keeps the connection to the recorder open and carries on when the stream is back, so the recorder does not end the recording and start another file.
Everything runs through the same pipeline as live playback. A channel that you watch while it is recorded is downloaded once, and several recordings and timeshifts of the same channel share it.
The channel of the live slot stays open while you watch it. When you zap to another service, the proxy closes it, so that it does not keep polling the server. The exception is a channel that a recorder still uses: a running record timer, or any recorder connected to /rec/ such as a timeshift of TimeshiftCockpit. It is closed when the last recorder is done.
StreamProxyCockpit