@@ -7,39 +7,72 @@ defmodule Boombox.RTP do
7
7
alias Boombox.Pipeline.Ready
8
8
alias Membrane.RTP
9
9
10
- @ required_opts [ :port , :track_configs ]
10
+ @ required_opts % {
11
+ input: [ :port , :track_configs ] ,
12
+ output: [ :address , :port , :track_configs ]
13
+ }
14
+
11
15
@ required_encoding_specific_params % {
12
- AAC: [ bitrate_mode: [ require?: true ] , audio_specific_config: [ require?: true ] ] ,
13
- OPUS: [ ] ,
14
- H264: [ ppss: [ require?: false ] , spss: [ require?: false ] ] ,
15
- H265: [ ppss: [ require?: false ] , spss: [ require?: false ] ]
16
+ input: % {
17
+ AAC: [ bitrate_mode: [ require?: true ] , audio_specific_config: [ require?: true ] ] ,
18
+ H264: [ ppss: [ require?: false , default: [ ] ] , spss: [ require?: false , default: [ ] ] ] ,
19
+ H265: [
20
+ vpss: [ require?: false , default: [ ] ] ,
21
+ ppss: [ require?: false , default: [ ] ] ,
22
+ spss: [ require?: false , default: [ ] ]
23
+ ]
24
+ } ,
25
+ output: % {
26
+ AAC: [ bitrate_mode: [ require?: true ] ]
27
+ }
16
28
}
17
29
18
- @ type parsed_encoding_specific_params ::
30
+ @ type parsed_input_encoding_specific_params ::
19
31
% { bitrate_mode: RTP.AAC.Utils . mode ( ) , audio_specific_config: binary ( ) }
20
- | % { optional ( :ppss ) => [ binary ( ) ] , optional ( :spss ) => [ binary ( ) ] }
21
- | % {
22
- optional ( :vpss ) => [ binary ( ) ] ,
23
- optional ( :ppss ) => [ binary ( ) ] ,
24
- optional ( :spss ) => [ binary ( ) ]
25
- }
32
+ | % { :ppss => [ binary ( ) ] , :spss => [ binary ( ) ] }
33
+ | % { :vpss => [ binary ( ) ] , :ppss => [ binary ( ) ] , :spss => [ binary ( ) ] }
34
+ | % { }
35
+
36
+ @ type parsed_output_encoding_specific_params ::
37
+ % { bitrate_mode: RTP.AAC.Utils . mode ( ) }
26
38
| % { }
27
39
28
- @ type parsed_track_config :: % {
40
+ @ type parsed_input_track_config :: % {
41
+ encoding_name: RTP . encoding_name ( ) ,
42
+ encoding_specific_params: parsed_input_encoding_specific_params ( ) ,
43
+ payload_type: RTP . payload_type ( ) ,
44
+ clock_rate: RTP . clock_rate ( )
45
+ }
46
+
47
+ @ type parsed_output_track_config :: % {
29
48
encoding_name: RTP . encoding_name ( ) ,
30
- encoding_specific_params: parsed_encoding_specific_params ( ) ,
49
+ encoding_specific_params: parsed_output_encoding_specific_params ( ) ,
31
50
payload_type: RTP . payload_type ( ) ,
32
51
clock_rate: RTP . clock_rate ( )
33
52
}
34
53
35
54
@ type parsed_in_opts :: % {
36
55
port: :inet . port_number ( ) ,
37
- track_configs: % { audio: parsed_track_config ( ) , video: parsed_track_config ( ) }
56
+ track_configs: % {
57
+ optional ( :audio ) => parsed_input_track_config ( ) ,
58
+ optional ( :video ) => parsed_input_track_config ( )
59
+ }
38
60
}
39
61
62
+ @ type parsed_out_opts :: % {
63
+ address: :inet . ip_address ( ) ,
64
+ port: :inet . port_number ( ) ,
65
+ track_configs: % {
66
+ optional ( :audio ) => parsed_output_track_config ( ) ,
67
+ optional ( :video ) => parsed_output_track_config ( )
68
+ }
69
+ }
70
+
71
+ @ type parsed_track_config :: parsed_input_track_config ( ) | parsed_output_track_config ( )
72
+
40
73
@ spec create_input ( Boombox . in_rtp_opts ( ) ) :: Ready . t ( )
41
74
def create_input ( opts ) do
42
- parsed_options = validate_and_parse_options ( opts )
75
+ parsed_options = validate_and_parse_options ( :input , opts )
43
76
payload_type_mapping = get_payload_type_mapping ( parsed_options . track_configs )
44
77
45
78
spec =
@@ -51,8 +84,8 @@ defmodule Boombox.RTP do
51
84
{ depayloader , parser } =
52
85
case track_config . encoding_name do
53
86
:H264 ->
54
- ppss = Map . get ( track_config . encoding_specific_params , : ppss, [ ] )
55
- spss = Map . get ( track_config . encoding_specific_params , : spss, [ ] )
87
+ ppss = track_config . encoding_specific_params . ppss
88
+ spss = track_config . encoding_specific_params . spss
56
89
{ Membrane.RTP.H264.Depayloader , % Membrane.H264.Parser { ppss: ppss , spss: spss } }
57
90
58
91
:AAC ->
@@ -77,21 +110,86 @@ defmodule Boombox.RTP do
77
110
spec =
78
111
get_child ( :rtp_demuxer )
79
112
|> via_out ( :output , options: [ stream_id: { :encoding_name , track_config . encoding_name } ] )
80
- |> child ( { :jitter_buffer , track_config . encoding_name } , % Membrane.RTP.JitterBuffer {
113
+ |> child ( { :jitter_buffer , media_type } , % Membrane.RTP.JitterBuffer {
81
114
clock_rate: track_config . clock_rate
82
115
} )
83
- |> child ( { :rtp_depayloader , track_config . encoding_name } , depayloader )
84
- |> child ( { :rtp_in_parser , track_config . encoding_name } , parser )
116
+ |> child ( { :rtp_depayloader , media_type } , depayloader )
117
+ |> child ( { :rtp_in_parser , media_type } , parser )
85
118
86
119
{ media_type , spec }
87
120
end )
88
121
89
122
% Ready { spec_builder: spec , track_builders: track_builders }
90
123
end
91
124
92
- @ spec validate_and_parse_options ( Boombox . in_rtp_opts ( ) ) :: parsed_in_opts ( )
93
- defp validate_and_parse_options ( opts ) do
94
- Enum . each ( @ required_opts , fn required_option ->
125
+ @ spec link_output (
126
+ Boombox . out_rtp_opts ( ) ,
127
+ Boombox.Pipeline . track_builders ( ) ,
128
+ Membrane.ChildrenSpec . t ( )
129
+ ) :: Ready . t ( )
130
+ def link_output ( opts , track_builders , spec_builder ) do
131
+ parsed_opts = validate_and_parse_options ( :output , opts )
132
+
133
+ spec = [
134
+ spec_builder ,
135
+ child ( :rtp_muxer , Membrane.RTP.Muxer )
136
+ |> child ( :udp_rtp_sink , % Membrane.UDP.Sink {
137
+ destination_address: parsed_opts . address ,
138
+ destination_port_no: parsed_opts . port
139
+ } ) ,
140
+ Enum . map ( track_builders , fn { media_type , builder } ->
141
+ track_config = parsed_opts . track_configs [ media_type ]
142
+
143
+ { output_stream_format , parser , payloader } =
144
+ case track_config . encoding_name do
145
+ :H264 ->
146
+ { % Membrane.H264 { stream_structure: :annexb , alignment: :nalu } ,
147
+ % Membrane.H264.Parser { output_stream_structure: :annexb , output_alignment: :nalu } ,
148
+ Membrane.RTP.H264.Payloader }
149
+
150
+ :AAC ->
151
+ { % Membrane.AAC { encapsulation: :none } ,
152
+ % Membrane.AAC.Parser { out_encapsulation: :none } ,
153
+ % Membrane.RTP.AAC.Payloader {
154
+ mode: track_config . encoding_specific_params . bitrate_mode ,
155
+ frames_per_packet: 1
156
+ } }
157
+
158
+ :OPUS ->
159
+ { Membrane.Opus , % Membrane.Opus.Parser { delimitation: :undelimit } ,
160
+ Membrane.RTP.Opus.Payloader }
161
+
162
+ :H265 ->
163
+ { % Membrane.H265 { stream_structure: :annexb , alignment: :nalu } ,
164
+ % Membrane.H265.Parser { output_stream_structure: :annexb , output_alignment: :nalu } ,
165
+ Membrane.RTP.H265.Payloader }
166
+ end
167
+
168
+ builder
169
+ |> child ( { :rtp_transcoder , media_type } , % Boombox.Transcoder {
170
+ output_stream_format: output_stream_format
171
+ } )
172
+ |> child ( { :rtp_out_parser , media_type } , parser )
173
+ |> child ( { :rtp_payloader , media_type } , payloader )
174
+ |> child ( { :realtimer , media_type } , Membrane.Realtimer )
175
+ |> via_in ( :input ,
176
+ options: [
177
+ encoding: track_config . encoding_name ,
178
+ payload_type: track_config . payload_type ,
179
+ clock_rate: track_config . clock_rate
180
+ ]
181
+ )
182
+ |> get_child ( :rtp_muxer )
183
+ end )
184
+ ]
185
+
186
+ % Ready { actions: [ spec: spec ] }
187
+ end
188
+
189
+ @ spec validate_and_parse_options ( :input , Boombox . in_rtp_opts ( ) ) :: parsed_in_opts ( )
190
+ @ spec validate_and_parse_options ( :output , Boombox . out_rtp_opts ( ) ) :: parsed_out_opts ( )
191
+ defp validate_and_parse_options ( direction , opts ) do
192
+ Enum . each ( @ required_opts [ direction ] , fn required_option ->
95
193
unless Keyword . has_key? ( opts , required_option ) do
96
194
raise "Required option #{ inspect ( required_option ) } not present in passed RTP options"
97
195
end
@@ -103,16 +201,25 @@ defmodule Boombox.RTP do
103
201
104
202
parsed_track_configs =
105
203
Map . new ( opts [ :track_configs ] , fn { media_type , track_config } ->
106
- { media_type , validate_and_parse_track_config! ( track_config ) }
204
+ { media_type , validate_and_parse_track_config! ( direction , track_config ) }
107
205
end )
108
206
109
- % { port: opts [ :port ] , track_configs: parsed_track_configs }
207
+ case direction do
208
+ :input ->
209
+ % { port: opts [ :port ] , track_configs: parsed_track_configs }
210
+
211
+ :output ->
212
+ % { address: opts [ :address ] , port: opts [ :port ] , track_configs: parsed_track_configs }
213
+ end
110
214
end
111
215
112
- @ spec validate_and_parse_track_config! ( Boombox . rtp_track_config ( ) ) :: parsed_track_config ( )
113
- defp validate_and_parse_track_config! ( track_config ) do
216
+ @ spec validate_and_parse_track_config! ( :input , Boombox . rtp_track_config ( ) ) ::
217
+ parsed_input_track_config ( )
218
+ @ spec validate_and_parse_track_config! ( :output , Boombox . rtp_track_config ( ) ) ::
219
+ parsed_output_track_config ( )
220
+ defp validate_and_parse_track_config! ( direction , track_config ) do
114
221
{ encoding_name , encoding_specific_params } =
115
- validate_and_parse_encoding! ( track_config [ :encoding ] )
222
+ validate_and_parse_encoding! ( direction , track_config [ :encoding ] )
116
223
117
224
track_config = Keyword . put ( track_config , :encoding_name , encoding_name )
118
225
@@ -135,18 +242,24 @@ defmodule Boombox.RTP do
135
242
}
136
243
end
137
244
138
- @ spec validate_and_parse_encoding! ( RTP . encoding_name ( ) | Boombox . rtp_encoding_specific_params ( ) ) ::
139
- { RTP . encoding_name ( ) , % { } } | parsed_encoding_specific_params ( )
140
- defp validate_and_parse_encoding! ( encoding ) do
245
+ @ spec validate_and_parse_encoding! (
246
+ :input ,
247
+ RTP . encoding_name ( ) | Boombox . rtp_encoding_specific_params ( )
248
+ ) :: { RTP . encoding_name ( ) , parsed_input_encoding_specific_params ( ) }
249
+ @ spec validate_and_parse_encoding! (
250
+ :output ,
251
+ RTP . encoding_name ( ) | Boombox . rtp_encoding_specific_params ( )
252
+ ) :: { RTP . encoding_name ( ) , parsed_output_encoding_specific_params ( ) }
253
+ defp validate_and_parse_encoding! ( direction , encoding ) do
141
254
case encoding do
142
255
nil ->
143
256
raise "Encoding name not provided"
144
257
145
258
encoding when is_atom ( encoding ) ->
146
- validate_and_parse_encoding! ( { encoding , [ ] } )
259
+ validate_and_parse_encoding! ( direction , { encoding , [ ] } )
147
260
148
261
{ encoding , encoding_params } when is_atom ( encoding ) ->
149
- field_specs = Map . get ( @ required_encoding_specific_params , encoding , [ ] )
262
+ field_specs = Map . get ( @ required_encoding_specific_params [ direction ] , encoding , [ ] )
150
263
{ :ok , encoding_params } = Bunch.Config . parse ( encoding_params , field_specs )
151
264
{ encoding , encoding_params }
152
265
end
0 commit comments