Current section

Files

Jump to
membrane_http_adaptive_stream_plugin lib membrane_http_adaptive_stream hls.ex
Raw

lib/membrane_http_adaptive_stream/hls.ex

defmodule Membrane.HTTPAdaptiveStream.HLS do
@moduledoc """
`Membrane.HTTPAdaptiveStream.Manifest` implementation for HLS.
Currently supports up to one audio and video stream.
"""
alias Membrane.HTTPAdaptiveStream.Manifest
alias Membrane.HTTPAdaptiveStream.Manifest.Track
alias Membrane.Time
@behaviour Manifest
@version 7
@av_manifest """
#EXTM3U
#EXT-X-VERSION:#{@version}
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="avc1.42e00a",AUDIO="a"
video.m3u8
#EXT-X-MEDIA:TYPE=AUDIO,NAME="a",GROUP-ID="a",AUTOSELECT=YES,DEFAULT=YES,URI="audio.m3u8"
"""
@impl true
def serialize(%Manifest{} = manifest) do
tracks_by_content = manifest.tracks |> Map.values() |> Enum.group_by(& &1.content_type)
main_manifest_name = "#{manifest.name}.m3u8"
case {tracks_by_content[:audio], tracks_by_content[:video]} do
{[audio], [video]} ->
[
{main_manifest_name, @av_manifest},
{"audio.m3u8", serialize_track(audio)},
{"video.m3u8", serialize_track(video)}
]
{[audio], nil} ->
[{main_manifest_name, serialize_track(audio)}]
{nil, [video]} ->
[{main_manifest_name, serialize_track(video)}]
end
end
defp serialize_track(%Track{} = track) do
use Ratio
target_duration = Ratio.ceil(track.target_segment_duration / Time.second()) |> trunc
media_sequence = track.current_seq_num - Enum.count(track.segments)
"""
#EXTM3U
#EXT-X-VERSION:#{@version}
#EXT-X-TARGETDURATION:#{target_duration}
#EXT-X-MEDIA-SEQUENCE:#{media_sequence}
#EXT-X-MAP:URI="#{track.header_name}"
#{
track.segments
|> Enum.flat_map(&["#EXTINF:#{Ratio.to_float(&1.duration / Time.second())},", &1.name])
|> Enum.join("\n")
}
#{if track.finished?, do: "#EXT-X-ENDLIST", else: ""}
"""
end
end