HLS Receiver

MediaX can play an HTTP Live Streaming (HLS, RFC 8216) stream. mediax::hls::HlsDepayloader downloads the playlist and the MPEG-2 transport stream segments, demultiplexes the video, wraps each access unit in RTP (RFC 6184) and hands it to the H.264 decoder. It is a drop-in mediax::rtp::RtpDepayloader, so frames are received exactly as for the RTSP and SRT receivers.

Scope

This is a receiver (client) only. Audio is delivered to your application as data and is never played by MediaX, see Audio. AES-128 encrypted segments (EXT-X-KEY) are decrypted when MediaX is built with OpenSSL. SAMPLE-AES and DRM key formats, fragmented MP4 segments (EXT-X-MAP) and separate audio renditions (EXT-X-MEDIA) are not supported. The decoded video codec is H.264.

Requirements

  • BUILD_HLS=ON (the default). Not available on Windows.
  • libcurl (libcurl4-openssl-dev), see Installation.
  • OpenSSL (libssl-dev) to play AES-128 encrypted streams, such as the ABC TV feed.
  • An H.264 decoder backend. The OpenH264 software decoder (BUILD_OPENH264=ON) is used by default.
  • Optional, to receive audio as PCM: BUILD_HLS_AUDIO_DECODE=ON and FFmpeg libavcodec-dev libavutil-dev.

Features

Feature Behaviour
Playlists Master playlists (a variant is chosen) and media playlists
Variant choice preferred_height selects the closest height, max_bandwidth caps the bitrate, 0 selects the highest bandwidth
Live streams The playlist is refreshed, playback starts live_start_segments segments before the end (RFC 8216 6.3.3)
Finished streams Played to the end of the playlist (EXT-X-ENDLIST), then HasEnded() is true
Pacing Played out at the stream rate (realtime_pacing), or as fast as downloaded when false
Buffering At most max_buffer_ms of video is held between download and playout
Errors Redirects followed (max_redirects), the stream is abandoned after max_consecutive_errors failed requests
Transport http:// and https:// playlists and segments
Encryption AES-128 (EXT-X-KEY): the key is fetched once per URL, the IV is the IV attribute or the media sequence number

Usage

#include <iostream>
#include "hls/hls_depayloader.h"

int main() {
  mediax::hls::HlsClientConfig config;
  config.preferred_height = 576;  // Closest variant, 0 selects the highest bandwidth

  mediax::hls::HlsDepayloader rx;
  rx.SetClientConfig(config);
  rx.SetUrl("https://example.com/live/stream.m3u8");

  if (!rx.Open()) {
    std::cerr << rx.GetLastError() << "\n";
    return 1;
  }
  rx.Start();

  mediax::rtp::RtpFrameData frame;
  while (rx.Receive(&frame, 5000)) {
    std::cout << frame.resolution.width << "x" << frame.resolution.height << "\n";
  }
  rx.Close();
  return 0;
}

Register frame and error callbacks before calling Open(). Errors may be reported from a worker thread.

Audio

Audio muxed into the transport stream segments is passed to a callback. MediaX does not play it: the application owns the speaker output and sends the data to its audio device (ALSA, PulseAudio, SDL, Qt Multimedia, ...).

rx.RegisterAudioCallback([](const mediax::hls::HlsAudioFrame& audio) {
  if (audio.format == mediax::hls::HlsAudioFormat::kPcmS16) {
    // audio.pcm: interleaved 16 bit samples, audio.channels per sample time, at audio.sample_rate Hz
    PlayPcm(audio.pcm.data(), audio.SamplesPerChannel(), audio.channels, audio.sample_rate);
  } else {
    // audio.encoded: AAC (ADTS or LATM), MPEG audio, AC-3 or E-AC-3 exactly as in the stream
    DecodeAndPlay(audio.format, audio.encoded);
  }
});
rx.Open();  // Register the callback first
Behaviour Detail
Sources The first audio stream muxed in the segments: AAC (ADTS 0x0F, LATM 0x11), MPEG audio (0x03, 0x04), AC-3 (0x81), E-AC-3 (0x87)
Without a callback Audio is ignored and costs nothing
PCM Delivered when a decoder is available and decode_audio is true. Otherwise the encoded bytes are delivered
Decoder Built in with BUILD_HLS_AUDIO_DECODE=ON (libavcodec, LGPL), or supply your own with SetAudioDecoderFactory()
Timing pts is the 90 kHz stream timestamp. Blocks are delivered on the playout thread in step with the video
Threading The callback runs on the playout thread. Keep it short and hand the data to the audio device or another thread

Check HasBuiltInAudioDecoder() to see whether the library has the libavcodec decoder. A custom decoder derives from mediax::hls::HlsAudioDecoder:

rx.SetAudioDecoderFactory([](mediax::hls::HlsAudioFormat format) -> std::unique_ptr<mediax::hls::HlsAudioDecoder> {
  if (format != mediax::hls::HlsAudioFormat::kAacAdts) return nullptr;  // nullptr delivers this audio encoded
  return std::make_unique<MyAacDecoder>();
});

The factory is called on the download thread when the first audio of a stream arrives.

Configuration

mediax::hls::HlsClientConfig:

Field Default Description
connect_timeout_ms 5000 TCP connect timeout
request_timeout_ms 15000 Total time allowed for one HTTP request
max_redirects 5 HTTP redirects to follow
preferred_height 0 Preferred variant height in pixels, 0 selects the highest bandwidth
max_bandwidth 0 Ignore variants above this bits per second, 0 for no limit
live_start_segments 3 Segments before the end of a live playlist to start from
max_consecutive_errors 5 Failed requests before the stream is abandoned
max_buffer_ms 12000 Maximum video held between download and playout
realtime_pacing true Play out at the stream rate
decode_audio true Deliver audio as PCM when a decoder is available, otherwise encoded
user_agent MediaX HLS Client User-Agent request header

Status and Statistics

GetLastStatus() returns an HlsStatus: kOk, kInvalidArgument, kInvalidState, kNetworkError, kHttpError, kTimeout, kParseError, kUnsupported or kCancelled. Use HlsStatusToString() for text and GetLastError() for the detail.

GetStatistics() returns playlist requests, segments and bytes downloaded, frames delivered, RTP packets, audio blocks delivered, errors and the next segment sequence number. GetVariant() returns the playing variant (URI, bandwidth, size, codecs, frame rate).

Console Example

hls-receive --url=https://example.com/live/stream.m3u8 --height=576 --record=out.mkv
hls-receive --url=https://example.com/live/stream.m3u8 --audio_dump=audio.raw   # Receive the audio too
hls-player                                                                       # SDL2 player with sound, plays ABC TV

The Qt6 player examples can also show HLS streams, and check a playlist is live, see RTSP Receiver and Examples.