/
manifest.ex
131 lines (111 loc) · 4.1 KB
/
manifest.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
defmodule Membrane.HTTPAdaptiveStream.Manifest do
@moduledoc """
Behaviour for manifest serialization.
"""
use Bunch.Access
alias __MODULE__.{Changeset, Track}
@type serialized_manifest_t :: {manifest_name :: String.t(), manifest_content :: String.t()}
@type serialized_manifests_t :: %{
master_manifest: serialized_manifest_t(),
manifest_per_track: %{
optional(track_id :: any()) => serialized_manifest_t()
}
}
@callback serialize(t) :: serialized_manifests_t()
@type t :: %__MODULE__{
name: String.t(),
module: module,
tracks: %{(id :: any) => Track.t()}
}
@enforce_keys [:name, :module]
defstruct @enforce_keys ++ [tracks: %{}]
@doc """
Add a track to the manifest.
Returns the name under which the header file should be stored.
"""
@spec add_track(t, Track.Config.t()) :: {header_name :: String.t(), t}
def add_track(manifest, %Track.Config{} = config) do
track = Track.new(config)
manifest = %__MODULE__{manifest | tracks: Map.put(manifest.tracks, config.id, track)}
{track.header_name, manifest}
end
@doc """
Adds segment to the manifest. In case of ll-hls it will add partial segment, and also full segment if needed.
Returns `Membrane.HTTPAdaptiveStream.Manifest.Track.Changeset`.
"""
@spec add_chunk(
t,
track_id :: Track.id_t(),
Membrane.Buffer.t()
) ::
{Changeset.t(), t}
def add_chunk(%__MODULE__{} = manifest, track_id, buffer) do
opts = %{
payload: buffer.payload,
size: byte_size(buffer.payload),
independent?: Map.get(buffer.metadata, :independent?, true),
last_chunk?: Map.fetch!(buffer.metadata, :last_chunk?),
duration: buffer.metadata.duration,
complete?: true
}
get_and_update_in(
manifest,
[:tracks, track_id],
&Track.add_chunk(&1, opts)
)
end
@spec serialize(t) :: serialized_manifests_t()
def serialize(%__MODULE__{module: module} = manifest) do
module.serialize(manifest)
end
@spec has_track?(t(), Track.id_t()) :: boolean()
def has_track?(%__MODULE__{tracks: tracks}, track_id), do: Map.has_key?(tracks, track_id)
@spec persisted?(t(), Track.id_t()) :: boolean()
def persisted?(%__MODULE__{tracks: tracks}, track_id),
do: Track.persisted?(Map.get(tracks, track_id))
@doc """
Append a discontinuity to the track.
This will inform the player that eg. the parameters of the encoder changed and allow you to provide a new MP4 header.
For details on discontinuities refer to [RFC 8216](https://datatracker.ietf.org/doc/html/rfc8216).
"""
@spec discontinue_track(t(), Track.id_t()) :: {header_name :: String.t(), t()}
def discontinue_track(%__MODULE__{} = manifest, track_id) do
get_and_update_in(
manifest,
[:tracks, track_id],
&Track.discontinue/1
)
end
@spec finish(t, Track.id_t()) :: {Changeset.t(), t}
def finish(%__MODULE__{} = manifest, track_id) do
get_and_update_in(manifest, [:tracks, track_id], &Track.finish/1)
end
@doc """
Filter all tracks that have option `:persisted?` set to true, then
restores all the stale segments in those tracks.
"""
@spec from_beginning(t()) :: t
def from_beginning(%__MODULE__{} = manifest) do
tracks =
manifest.tracks
|> Enum.filter(fn {_track_id, track} -> Track.persisted?(track) end)
|> Map.new(fn {track_id, track} -> {track_id, Track.from_beginning(track)} end)
%__MODULE__{manifest | tracks: tracks}
end
@doc """
Returns all segments grouped by the track id.
"""
@spec segments_per_track(t()) :: %{
optional(track_id :: term()) => [segment_name :: String.t()]
}
def segments_per_track(%__MODULE__{} = manifest) do
Map.new(manifest.tracks, fn {track_id, track} -> {track_id, Track.all_segments(track)} end)
end
@doc """
Returns one header per track
"""
@spec header_per_track(t()) :: %{optional(track_id :: term()) => String.t()}
def header_per_track(%__MODULE__{} = manifest) do
Map.new(manifest.tracks, fn {track_id, track} -> {track_id, Track.header(track)} end)
end
end