-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathav1_payloader.ex
123 lines (105 loc) · 3.57 KB
/
av1_payloader.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
defmodule ExWebRTC.RTP.AV1Payloader do
@moduledoc """
Encapsulates AV1 video frames into RTP packets.
https://norkin.org/research/av1_decoder_model/index.html
https://chromium.googlesource.com/external/webrtc/+/HEAD/modules/rtp_rtcp/source/video_rtp_depacketizer_av1.cc
AV1 format:
RTP payload syntax:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|Z|Y| W |N|-|-|-| (REQUIRED)
+=+=+=+=+=+=+=+=+ (REPEATED W-1 times, or any times if W = 0)
|1| |
+-+ OBU fragment|
|1| | (REQUIRED, leb128 encoded)
+-+ size |
|0| |
+-+-+-+-+-+-+-+-+
| OBU fragment |
| ... |
+=+=+=+=+=+=+=+=+
| ... |
+=+=+=+=+=+=+=+=+ if W > 0, last fragment MUST NOT have size field
| OBU fragment |
| ... |
+=+=+=+=+=+=+=+=+
OBU syntax:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|0| type |X|S|-| (REQUIRED)
+-+-+-+-+-+-+-+-+
X: | TID |SID|-|-|-| (OPTIONAL)
+-+-+-+-+-+-+-+-+
|1| |
+-+ OBU payload |
S: |1| | (OPTIONAL, variable length leb128 encoded)
+-+ size |
|0| |
+-+-+-+-+-+-+-+-+
| OBU payload |
| ... |
"""
import Bitwise
alias ExWebRTC.RTP.LEB128
@obu_sequence_header 1
@obu_temporal_delimiter 2
@opaque t() :: %__MODULE__{
max_payload_size: non_neg_integer()
}
defstruct [:max_payload_size]
@spec new(non_neg_integer()) :: t()
def new(max_payload_size \\ 1000) when max_payload_size > 100 do
%__MODULE__{max_payload_size: max_payload_size}
end
@doc """
Packs AV1 frame into one or more RTP packets.
Fields from RTP header like ssrc, timestamp etc. are set to 0.
"""
@spec payload(t(), frame :: binary()) :: {[ExRTP.Packet.t()], t()}
def payload(payloader, frame) when frame != <<>> do
# obus = parse_obus(frame)
# for obu <- obus do
# <<_::1, type::4, _::3, _rest::binary>> = obu
# dbg(type)
# end
# dbg(:end)
# remove temporal delimiter
obus =
frame
|> parse_obus()
|> Enum.reject(fn obu ->
<<_::1, type::4, _::3, _rest::binary>> = obu
type == @obu_temporal_delimiter
end)
rtp_packets =
Enum.map(obus, fn obu ->
<<_::1, type::4, _::3, _rest::binary>> = obu
n_bit = if type == @obu_sequence_header, do: 1, else: 0
# obu = LEB128.encode(byte_size(obu)) <> obu
payload = <<0::1, 0::1, 1::2, n_bit::1, 0::3>> <> obu
ExRTP.Packet.new(payload, 0, 0, 0, 0)
end)
last_rtp_packet = List.last(rtp_packets)
last_rtp_packet = %{last_rtp_packet | marker: true}
rtp_packets = List.insert_at(rtp_packets, -1, last_rtp_packet)
{rtp_packets, payloader}
end
defp parse_obus(data, obus \\ [])
defp parse_obus(<<>>, obus), do: Enum.reverse(obus)
# X and S bits set
defp parse_obus(<<_::5, 1::1, 1::1, _::1, _::8, rest::binary>> = data, obus) do
{leb128_size, obu_payload_size} = LEB128.read(rest)
<<obu::binary-size(2 + leb128_size + obu_payload_size), rest::binary>> = data
parse_obus(rest, [obu | obus])
end
# X bit unset but S bit set
defp parse_obus(<<_::5, 0::1, 1::1, _::1, rest::binary>> = data, obus) do
{leb128_size, obu_payload_size} = LEB128.read(rest)
<<obu::binary-size(1 + leb128_size + obu_payload_size), rest::binary>> = data
parse_obus(rest, [obu | obus])
end
# S bit unset
defp parse_obus(<<_::5, _::1, 0::1, _::1, _rest::binary>> = data, obus) do
parse_obus(<<>>, [data | obus])
end
end