/src/gpac/src/ietf/rtp_streamer.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2024 |
6 | | * All rights reserved |
7 | | * |
8 | | * This file is part of GPAC / IETF RTP/RTSP/SDP sub-project |
9 | | * |
10 | | * GPAC is free software; you can redistribute it and/or modify |
11 | | * it under the terms of the GNU Lesser General Public License as published by |
12 | | * the Free Software Foundation; either version 2, or (at your option) |
13 | | * any later version. |
14 | | * |
15 | | * GPAC is distributed in the hope that it will be useful, |
16 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | * GNU Lesser General Public License for more details. |
19 | | * |
20 | | * You should have received a copy of the GNU Lesser General Public |
21 | | * License along with this library; see the file COPYING. If not, write to |
22 | | * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. |
23 | | * |
24 | | */ |
25 | | |
26 | | |
27 | | #include <gpac/rtp_streamer.h> |
28 | | #include <gpac/constants.h> |
29 | | #include <gpac/base_coding.h> |
30 | | #ifndef GPAC_DISABLE_AV_PARSERS |
31 | | #include <gpac/avparse.h> |
32 | | #endif |
33 | | #include <gpac/internal/ietf_dev.h> |
34 | | |
35 | | #if !defined(GPAC_DISABLE_STREAMING) |
36 | | |
37 | | /*for ISOBMFF subtypes*/ |
38 | | #include <gpac/isomedia.h> |
39 | | |
40 | 0 | #define RTCP_BUF_SIZE 10000 |
41 | | struct __rtp_streamer |
42 | | { |
43 | | GP_RTPPacketizer *packetizer; |
44 | | GF_RTPChannel *channel; |
45 | | |
46 | | /* The current packet being formed */ |
47 | | char *buffer; |
48 | | u32 payload_len, buffer_alloc; |
49 | | |
50 | | u32 in_timescale; |
51 | | char rtcp_buf[RTCP_BUF_SIZE]; |
52 | | |
53 | | const char *netcap_id; |
54 | | GF_Err last_err; |
55 | | }; |
56 | | |
57 | | |
58 | | /*callbacks from packetizer to channel*/ |
59 | | |
60 | | static void rtp_stream_on_new_packet(void *cbk, GF_RTPHeader *header) |
61 | 0 | { |
62 | 0 | } |
63 | | |
64 | | static void rtp_stream_on_packet_done(void *cbk, GF_RTPHeader *header) |
65 | 0 | { |
66 | 0 | GF_RTPStreamer *rtp = (GF_RTPStreamer*)cbk; |
67 | 0 | GF_Err e = gf_rtp_send_packet(rtp->channel, header, rtp->buffer+12, rtp->payload_len, GF_TRUE); |
68 | |
|
69 | 0 | #ifndef GPAC_DISABLE_LOG |
70 | 0 | if (e) { |
71 | 0 | rtp->last_err = e; |
72 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Error %s sending RTP packet SN %u - TS %u\n", gf_error_to_string(e), header->SequenceNumber, header->TimeStamp)); |
73 | 0 | } else { |
74 | 0 | GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("RTP SN %u - TS %u - M %u - Size %u\n", header->SequenceNumber, header->TimeStamp, header->Marker, rtp->payload_len + 12)); |
75 | 0 | } |
76 | | #else |
77 | | if (e) { |
78 | | fprintf(stderr, "Error %s sending RTP packet SN %u - TS %u\n", gf_error_to_string(e), header->SequenceNumber, header->TimeStamp); |
79 | | } |
80 | | #endif |
81 | 0 | rtp->payload_len = 0; |
82 | 0 | } |
83 | | |
84 | | static void rtp_stream_on_data(void *cbk, u8 *data, u32 data_size, Bool is_head) |
85 | 0 | { |
86 | 0 | GF_RTPStreamer *rtp = (GF_RTPStreamer*)cbk; |
87 | 0 | if (!data ||!data_size) return; |
88 | | |
89 | 0 | if (rtp->payload_len+data_size+12 > rtp->buffer_alloc) { |
90 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP] Packet size %d bigger than MTU size %d - discarding\n", rtp->payload_len+data_size+12, rtp->buffer_alloc)); |
91 | 0 | rtp->payload_len += data_size; |
92 | 0 | return; |
93 | 0 | } |
94 | 0 | if (!is_head) { |
95 | 0 | memcpy(rtp->buffer + rtp->payload_len + 12, data, data_size); |
96 | 0 | } else { |
97 | 0 | memmove(rtp->buffer + data_size + 12, rtp->buffer + 12, rtp->payload_len); |
98 | 0 | memcpy(rtp->buffer + 12, data, data_size); |
99 | 0 | } |
100 | 0 | rtp->payload_len += data_size; |
101 | 0 | } |
102 | | |
103 | | |
104 | | GF_Err gf_rtp_streamer_init_rtsp(GF_RTPStreamer *rtp, u32 path_mtu, GF_RTSPTransport *tr, const char *ifce_addr) |
105 | 0 | { |
106 | 0 | GF_Err res; |
107 | |
|
108 | 0 | if (!rtp->channel) { |
109 | 0 | rtp->channel = gf_rtp_new_ex(rtp->netcap_id); |
110 | 0 | if (!rtp->channel) return GF_OUT_OF_MEM; |
111 | 0 | rtp->channel->TimeScale = rtp->packetizer->sl_config.timestampResolution; |
112 | 0 | } |
113 | 0 | res = gf_rtp_setup_transport(rtp->channel, tr, tr->destination); |
114 | 0 | if (res !=0) { |
115 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot setup RTP transport info: %s\n", gf_error_to_string(res) )); |
116 | 0 | return res; |
117 | 0 | } |
118 | | |
119 | 0 | res = gf_rtp_initialize(rtp->channel, 0, GF_TRUE, path_mtu, 0, 0, (char *)ifce_addr); |
120 | 0 | if (res !=0) { |
121 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot initialize RTP sockets: %s\n", gf_error_to_string(res) )); |
122 | 0 | return res; |
123 | 0 | } |
124 | 0 | return GF_OK; |
125 | 0 | } |
126 | | static GF_Err rtp_stream_init_channel(GF_RTPStreamer *rtp, u32 path_mtu, const char * dest, int port, int ttl, const char *ifce_addr, const char *netcap_id) |
127 | 0 | { |
128 | 0 | GF_RTSPTransport tr; |
129 | 0 | GF_Err res; |
130 | |
|
131 | 0 | rtp->channel = gf_rtp_new_ex(netcap_id); |
132 | 0 | if (!rtp->channel) return GF_OUT_OF_MEM; |
133 | 0 | rtp->channel->TimeScale = rtp->packetizer->sl_config.timestampResolution; |
134 | | |
135 | | //gf_rtp_set_ports(rtp->channel, 0); |
136 | 0 | memset(&tr, 0, sizeof(GF_RTSPTransport)); |
137 | |
|
138 | 0 | tr.IsUnicast = gf_sk_is_multicast_address(dest) ? GF_FALSE : GF_TRUE; |
139 | 0 | tr.Profile="RTP/AVP"; |
140 | 0 | tr.destination = (char *)dest; |
141 | 0 | tr.source = "0.0.0.0"; |
142 | 0 | tr.IsRecord = GF_FALSE; |
143 | 0 | tr.Append = GF_FALSE; |
144 | 0 | tr.SSRC = rand(); |
145 | 0 | tr.TTL = ttl; |
146 | |
|
147 | 0 | tr.port_first = port; |
148 | 0 | tr.port_last = port+1; |
149 | 0 | if (tr.IsUnicast) { |
150 | 0 | tr.client_port_first = port; |
151 | 0 | tr.client_port_last = port+1; |
152 | 0 | } else { |
153 | 0 | tr.source = (char *)dest; |
154 | 0 | } |
155 | |
|
156 | 0 | res = gf_rtp_setup_transport(rtp->channel, &tr, dest); |
157 | 0 | if (res !=0) { |
158 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot setup RTP transport info: %s\n", gf_error_to_string(res) )); |
159 | 0 | return res; |
160 | 0 | } |
161 | | |
162 | 0 | res = gf_rtp_initialize(rtp->channel, 0, GF_TRUE, path_mtu, 0, 0, (char *)ifce_addr); |
163 | 0 | if (res !=0) { |
164 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("Cannot initialize RTP sockets: %s\n", gf_error_to_string(res) )); |
165 | 0 | return res; |
166 | 0 | } |
167 | 0 | return GF_OK; |
168 | 0 | } |
169 | | |
170 | | GF_EXPORT |
171 | | GF_RTPStreamer *gf_rtp_streamer_new_ex(const GF_RTPStreamerConfig *cfg, Bool for_rtsp) |
172 | 0 | { |
173 | 0 | GF_SLConfig slc; |
174 | 0 | GF_RTPStreamer *stream; |
175 | 0 | u32 rtp_type, default_rtp_rate; |
176 | 0 | u8 OfficialPayloadType; |
177 | 0 | u32 required_rate, force_dts_delta, PL_ID; |
178 | 0 | char *mpeg4mode; |
179 | 0 | Bool has_mpeg4_mapping; |
180 | 0 | GF_Err e; |
181 | |
|
182 | 0 | u32 timeScale = cfg->timeScale ? cfg->timeScale : 1000; |
183 | 0 | u32 flags = cfg->flags; |
184 | 0 | u32 streamType = cfg->streamType; |
185 | 0 | u32 codecid = cfg->codecid; |
186 | 0 | u32 PayloadType = cfg->PayloadType; |
187 | 0 | u32 maxDTSDelta = cfg->maxDTSDelta; |
188 | 0 | u32 max_ptime = cfg->max_ptime; |
189 | |
|
190 | 0 | GF_SAFEALLOC(stream, GF_RTPStreamer); |
191 | 0 | if (!stream) return NULL; |
192 | | |
193 | | |
194 | | /*by default NO PL signaled*/ |
195 | 0 | PL_ID = 0; |
196 | 0 | OfficialPayloadType = 0; |
197 | 0 | force_dts_delta = 0; |
198 | 0 | mpeg4mode = NULL; |
199 | 0 | required_rate = 0; |
200 | 0 | has_mpeg4_mapping = GF_TRUE; |
201 | 0 | rtp_type = 0; |
202 | | |
203 | | /*for max compatibility with QT*/ |
204 | 0 | default_rtp_rate = 90000; |
205 | | |
206 | | /*timed-text is a bit special, we support multiple stream descriptions & co*/ |
207 | 0 | switch (cfg->streamType) { |
208 | 0 | case GF_STREAM_AUDIO: |
209 | 0 | required_rate = cfg->sample_rate; |
210 | 0 | break; |
211 | 0 | case GF_STREAM_VISUAL: |
212 | 0 | rtp_type = GF_RTP_PAYT_MPEG4; |
213 | 0 | required_rate = default_rtp_rate; |
214 | 0 | if (cfg->is_crypted) { |
215 | | /*that's another pain with ISMACryp, even if no B-frames the DTS is signaled...*/ |
216 | 0 | if (cfg->codecid==GF_CODECID_MPEG4_PART2) force_dts_delta = 22; |
217 | 0 | flags |= GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_SIGNAL_TS; |
218 | 0 | } |
219 | 0 | break; |
220 | 0 | case GF_STREAM_SCENE: |
221 | 0 | case GF_STREAM_OD: |
222 | 0 | if (cfg->codecid == GF_CODECID_DIMS) { |
223 | | #if GPAC_ENABLE_3GPP_DIMS_RTP |
224 | | rtp_type = GF_RTP_PAYT_3GPP_DIMS; |
225 | | has_mpeg4_mapping = GF_FALSE; |
226 | | #else |
227 | 0 | gf_rtp_streamer_del(stream); |
228 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] 3GPP DIMS over RTP disabled in build\n", cfg->streamType)); |
229 | 0 | return NULL; |
230 | 0 | #endif |
231 | 0 | } else { |
232 | 0 | rtp_type = GF_RTP_PAYT_MPEG4; |
233 | 0 | } |
234 | 0 | break; |
235 | 0 | } |
236 | | |
237 | 0 | switch (cfg->codecid) { |
238 | | /*AAC*/ |
239 | 0 | case GF_CODECID_AAC_MPEG4: |
240 | 0 | case GF_CODECID_AAC_MPEG2_MP: |
241 | 0 | case GF_CODECID_AAC_MPEG2_LCP: |
242 | 0 | case GF_CODECID_AAC_MPEG2_SSRP: |
243 | 0 | PL_ID = 0x01; |
244 | 0 | mpeg4mode = "AAC"; |
245 | 0 | rtp_type = GF_RTP_PAYT_MPEG4; |
246 | 0 | required_rate = cfg->sample_rate; |
247 | |
|
248 | 0 | #ifndef GPAC_DISABLE_AV_PARSERS |
249 | 0 | if (cfg->dsi) { |
250 | 0 | GF_M4ADecSpecInfo a_cfg; |
251 | 0 | gf_m4a_get_config((u8 *)cfg->dsi, cfg->dsi_len, &a_cfg); |
252 | | //nb_ch = a_cfg.nb_chan; |
253 | | //sample_rate = a_cfg.base_sr; |
254 | 0 | PL_ID = a_cfg.audioPL; |
255 | 0 | switch (a_cfg.base_object_type) { |
256 | 0 | case GF_M4A_AAC_MAIN: |
257 | 0 | case GF_M4A_AAC_LC: |
258 | 0 | if (flags & GP_RTP_PCK_USE_LATM_AAC) { |
259 | 0 | rtp_type = GF_RTP_PAYT_LATM; |
260 | 0 | break; |
261 | 0 | } |
262 | 0 | case GF_M4A_AAC_SBR: |
263 | 0 | case GF_M4A_AAC_PS: |
264 | 0 | case GF_M4A_AAC_LTP: |
265 | 0 | case GF_M4A_AAC_SCALABLE: |
266 | 0 | case GF_M4A_ER_AAC_LC: |
267 | 0 | case GF_M4A_ER_AAC_LTP: |
268 | 0 | case GF_M4A_ER_AAC_SCALABLE: |
269 | 0 | mpeg4mode = "AAC"; |
270 | 0 | break; |
271 | 0 | case GF_M4A_CELP: |
272 | 0 | case GF_M4A_ER_CELP: |
273 | 0 | mpeg4mode = "CELP"; |
274 | 0 | break; |
275 | 0 | } |
276 | 0 | } |
277 | 0 | #endif |
278 | 0 | break; |
279 | | |
280 | | /*MPEG1/2 audio*/ |
281 | 0 | case GF_CODECID_MPEG2_PART3: |
282 | 0 | case GF_CODECID_MPEG_AUDIO: |
283 | 0 | case GF_CODECID_MPEG_AUDIO_L1: |
284 | 0 | if (!cfg->is_crypted) { |
285 | 0 | rtp_type = GF_RTP_PAYT_MPEG12_AUDIO; |
286 | | /*use official RTP/AVP payload type*/ |
287 | 0 | OfficialPayloadType = 14; |
288 | 0 | required_rate = 90000; |
289 | 0 | } |
290 | | /*encrypted MP3 must be sent through MPEG-4 generic to signal all ISMACryp stuff*/ |
291 | 0 | else { |
292 | 0 | rtp_type = GF_RTP_PAYT_MPEG4; |
293 | 0 | } |
294 | 0 | break; |
295 | | |
296 | | /*ISO/IEC 14496-2*/ |
297 | 0 | case GF_CODECID_MPEG4_PART2: |
298 | 0 | PL_ID = 1; |
299 | 0 | #ifndef GPAC_DISABLE_AV_PARSERS |
300 | 0 | if (cfg->dsi) { |
301 | 0 | GF_M4VDecSpecInfo vhdr; |
302 | 0 | gf_m4v_get_config((u8 *)cfg->dsi, cfg->dsi_len, &vhdr); |
303 | 0 | PL_ID = vhdr.VideoPL; |
304 | 0 | } |
305 | 0 | #endif |
306 | 0 | break; |
307 | | |
308 | | /*MPEG1/2 video*/ |
309 | 0 | case GF_CODECID_MPEG1: |
310 | 0 | case GF_CODECID_MPEG2_SIMPLE: |
311 | 0 | case GF_CODECID_MPEG2_MAIN: |
312 | 0 | case GF_CODECID_MPEG2_SNR: |
313 | 0 | case GF_CODECID_MPEG2_SPATIAL: |
314 | 0 | case GF_CODECID_MPEG2_HIGH: |
315 | 0 | case GF_CODECID_MPEG2_422: |
316 | 0 | if (!cfg->is_crypted) { |
317 | 0 | rtp_type = GF_RTP_PAYT_MPEG12_VIDEO; |
318 | 0 | OfficialPayloadType = 32; |
319 | 0 | } |
320 | 0 | break; |
321 | | /*AVC/H.264*/ |
322 | 0 | case GF_CODECID_AVC: |
323 | 0 | required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ |
324 | 0 | rtp_type = GF_RTP_PAYT_H264_AVC; |
325 | 0 | PL_ID = 0x0F; |
326 | 0 | break; |
327 | | /*H264-SVC*/ |
328 | 0 | case GF_CODECID_SVC: |
329 | 0 | case GF_CODECID_MVC: |
330 | 0 | required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ |
331 | 0 | rtp_type = GF_RTP_PAYT_H264_SVC; |
332 | 0 | PL_ID = 0x0F; |
333 | 0 | break; |
334 | | |
335 | | /*HEVC*/ |
336 | 0 | case GF_CODECID_HEVC: |
337 | 0 | required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ |
338 | 0 | rtp_type = GF_RTP_PAYT_HEVC; |
339 | 0 | PL_ID = 0x0F; |
340 | 0 | break; |
341 | | /*LHVC*/ |
342 | 0 | case GF_CODECID_LHVC: |
343 | 0 | required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ |
344 | 0 | rtp_type = GF_RTP_PAYT_LHVC; |
345 | 0 | PL_ID = 0x0F; |
346 | 0 | break; |
347 | | /*VVC*/ |
348 | 0 | case GF_CODECID_VVC: |
349 | 0 | required_rate = 90000; /* "90 kHz clock rate MUST be used"*/ |
350 | 0 | rtp_type = GF_RTP_PAYT_VVC; |
351 | 0 | PL_ID = 0x0F; |
352 | 0 | break; |
353 | 0 | case GF_CODECID_H263: |
354 | 0 | rtp_type = GF_RTP_PAYT_H263; |
355 | 0 | required_rate = 90000; |
356 | 0 | streamType = GF_STREAM_VISUAL; |
357 | 0 | OfficialPayloadType = 34; |
358 | | /*not 100% compliant (short header is missing) but should still work*/ |
359 | 0 | codecid = GF_CODECID_MPEG4_PART2; |
360 | 0 | PL_ID = 0x01; |
361 | 0 | break; |
362 | 0 | case GF_CODECID_AMR: |
363 | 0 | required_rate = 8000; |
364 | 0 | rtp_type = GF_RTP_PAYT_AMR; |
365 | 0 | streamType = GF_STREAM_AUDIO; |
366 | 0 | has_mpeg4_mapping = GF_FALSE; |
367 | 0 | break; |
368 | 0 | case GF_CODECID_AMR_WB: |
369 | 0 | required_rate = 16000; |
370 | 0 | rtp_type = GF_RTP_PAYT_AMR_WB; |
371 | 0 | streamType = GF_STREAM_AUDIO; |
372 | 0 | has_mpeg4_mapping = GF_FALSE; |
373 | 0 | break; |
374 | 0 | case GF_CODECID_AC3: |
375 | 0 | rtp_type = GF_RTP_PAYT_AC3; |
376 | 0 | streamType = GF_STREAM_AUDIO; |
377 | 0 | has_mpeg4_mapping = GF_TRUE; |
378 | 0 | break; |
379 | 0 | case GF_CODECID_EAC3: |
380 | 0 | rtp_type = GF_RTP_PAYT_EAC3; |
381 | 0 | streamType = GF_STREAM_AUDIO; |
382 | 0 | has_mpeg4_mapping = GF_FALSE; |
383 | 0 | break; |
384 | | |
385 | 0 | case GF_CODECID_QCELP: |
386 | 0 | required_rate = 8000; |
387 | 0 | rtp_type = GF_RTP_PAYT_QCELP; |
388 | 0 | streamType = GF_STREAM_AUDIO; |
389 | 0 | codecid = GF_CODECID_QCELP; |
390 | 0 | OfficialPayloadType = 12; |
391 | | // nb_ch = 1; |
392 | 0 | break; |
393 | 0 | case GF_CODECID_EVRC: |
394 | 0 | case GF_CODECID_SMV: |
395 | 0 | required_rate = 8000; |
396 | 0 | rtp_type = GF_RTP_PAYT_EVRC_SMV; |
397 | 0 | streamType = GF_STREAM_AUDIO; |
398 | 0 | codecid = (codecid==GF_ISOM_SUBTYPE_3GP_EVRC) ? GF_CODECID_EVRC : GF_CODECID_SMV; |
399 | | // nb_ch = 1; |
400 | 0 | break; |
401 | 0 | case GF_CODECID_TX3G: |
402 | 0 | rtp_type = GF_RTP_PAYT_3GPP_TEXT; |
403 | | /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/ |
404 | 0 | PL_ID = 0x10; |
405 | 0 | break; |
406 | 0 | case GF_CODECID_TEXT_MPEG4: |
407 | 0 | rtp_type = GF_RTP_PAYT_3GPP_TEXT; |
408 | | /*fixme - this works cos there's only one PL for text in mpeg4 at the current time*/ |
409 | 0 | PL_ID = 0x10; |
410 | 0 | break; |
411 | 0 | case GF_CODECID_FAKE_MP2T: |
412 | 0 | rtp_type = GF_RTP_PAYT_MP2T; |
413 | 0 | PayloadType = OfficialPayloadType = GF_RTP_PAYT_MP2T; |
414 | 0 | required_rate = 90000; |
415 | 0 | break; |
416 | 0 | case GF_CODECID_OPUS: |
417 | 0 | rtp_type = GF_RTP_PAYT_OPUS; |
418 | 0 | streamType = GF_STREAM_AUDIO; |
419 | 0 | has_mpeg4_mapping = GF_FALSE; |
420 | 0 | break; |
421 | | |
422 | 0 | default: |
423 | 0 | if (!rtp_type) { |
424 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] Unsupported stream type %x\n", streamType)); |
425 | 0 | gf_rtp_streamer_del(stream); |
426 | 0 | return NULL; |
427 | 0 | } |
428 | 0 | break; |
429 | 0 | } |
430 | | |
431 | 0 | if (flags & GP_RTP_PCK_FORCE_STATIC_ID) { |
432 | 0 | if (!OfficialPayloadType) { |
433 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] Codec type %s requires SDP output\n", gf_codecid_name(codecid) )); |
434 | 0 | gf_rtp_streamer_del(stream); |
435 | 0 | return NULL; |
436 | 0 | } |
437 | 0 | PayloadType = OfficialPayloadType; |
438 | 0 | } |
439 | | /*override hinter type if requested and possible*/ |
440 | 0 | else if (has_mpeg4_mapping && (flags & GP_RTP_PCK_FORCE_MPEG4)) { |
441 | 0 | rtp_type = GF_RTP_PAYT_MPEG4; |
442 | 0 | } |
443 | | /*use static payload ID if enabled*/ |
444 | 0 | else if (OfficialPayloadType && (flags & GP_RTP_PCK_USE_STATIC_ID) ) { |
445 | 0 | PayloadType = OfficialPayloadType; |
446 | 0 | } |
447 | | |
448 | | /*systems carousel: we need at least IDX and RAP signaling*/ |
449 | 0 | if (flags & GP_RTP_PCK_SYSTEMS_CAROUSEL) { |
450 | 0 | flags |= GP_RTP_PCK_SIGNAL_RAP; |
451 | 0 | } |
452 | | |
453 | | /*update flags in MultiSL*/ |
454 | 0 | if (flags & GP_RTP_PCK_USE_MULTI) { |
455 | 0 | if (cfg->MinSize != cfg->MaxSize) flags |= GP_RTP_PCK_SIGNAL_SIZE; |
456 | 0 | if (!cfg->const_dur) flags |= GP_RTP_PCK_SIGNAL_TS; |
457 | 0 | } |
458 | | |
459 | | /*default SL for RTP */ |
460 | 0 | memset(&slc, 0, sizeof(GF_SLConfig)); |
461 | 0 | slc.tag = GF_ODF_SLC_TAG; |
462 | 0 | slc.useTimestampsFlag = 1; |
463 | 0 | slc.timestampLength = 32; |
464 | 0 | slc.timestampResolution = timeScale; |
465 | | |
466 | | /*override clockrate if set*/ |
467 | 0 | if (required_rate) { |
468 | 0 | Double sc = required_rate; |
469 | 0 | sc /= slc.timestampResolution; |
470 | 0 | maxDTSDelta = (u32) (maxDTSDelta*sc); |
471 | 0 | slc.timestampResolution = required_rate; |
472 | 0 | } |
473 | | /*switch to RTP TS*/ |
474 | 0 | max_ptime = (u32) (max_ptime * slc.timestampResolution / 1000); |
475 | |
|
476 | 0 | slc.AUSeqNumLength = cfg->au_sn_len; |
477 | 0 | slc.CUDuration = cfg->const_dur; |
478 | |
|
479 | 0 | if (flags & GP_RTP_PCK_SIGNAL_RAP) { |
480 | 0 | slc.useRandomAccessPointFlag = 1; |
481 | 0 | } else { |
482 | 0 | slc.useRandomAccessPointFlag = 0; |
483 | 0 | slc.hasRandomAccessUnitsOnlyFlag = 1; |
484 | 0 | } |
485 | |
|
486 | 0 | stream->packetizer = gf_rtp_builder_new(rtp_type, &slc, flags, |
487 | 0 | stream, |
488 | 0 | rtp_stream_on_new_packet, rtp_stream_on_packet_done, |
489 | 0 | NULL, rtp_stream_on_data); |
490 | |
|
491 | 0 | if (!stream->packetizer) { |
492 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] Failed to create packetizer\n")); |
493 | 0 | gf_rtp_streamer_del(stream); |
494 | 0 | return NULL; |
495 | 0 | } |
496 | | |
497 | 0 | gf_rtp_builder_init(stream->packetizer, (u8) PayloadType, cfg->MTU, max_ptime, |
498 | 0 | streamType, codecid, PL_ID, cfg->MinSize, cfg->MaxSize, cfg->avgTS, maxDTSDelta, cfg->IV_length, cfg->KI_length, mpeg4mode); |
499 | | |
500 | |
|
501 | 0 | if (force_dts_delta) stream->packetizer->slMap.DTSDeltaLength = force_dts_delta; |
502 | |
|
503 | 0 | if (!for_rtsp) { |
504 | 0 | e = rtp_stream_init_channel(stream, cfg->MTU + 12, cfg->ip_dest, cfg->port, cfg->TTL, cfg->ifce_addr, cfg->netcap_id); |
505 | 0 | if (e) { |
506 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTP Packetizer] Failed to create RTP channel - error %s\n", gf_error_to_string(e) )); |
507 | 0 | gf_rtp_streamer_del(stream); |
508 | 0 | return NULL; |
509 | 0 | } |
510 | 0 | } |
511 | | |
512 | 0 | stream->in_timescale = timeScale; |
513 | 0 | stream->netcap_id = cfg->netcap_id; |
514 | |
|
515 | 0 | stream->buffer_alloc = cfg->MTU+12; |
516 | 0 | stream->buffer = (char*)gf_malloc(sizeof(char) * stream->buffer_alloc); |
517 | |
|
518 | 0 | return stream; |
519 | 0 | } |
520 | | |
521 | | GF_EXPORT |
522 | | GF_RTPStreamer *gf_rtp_streamer_new(u32 streamType, u32 codecid, u32 timeScale, |
523 | | const char *ip_dest, u16 port, u32 MTU, u8 TTL, const char *ifce_addr, |
524 | | u32 flags, const u8 *dsi, u32 dsi_len, |
525 | | u32 PayloadType, u32 sample_rate, u32 nb_ch, |
526 | | Bool is_crypted, u32 IV_length, u32 KI_length, |
527 | | u32 MinSize, u32 MaxSize, u32 avgTS, u32 maxDTSDelta, u32 const_dur, u32 bandwidth, u32 max_ptime, |
528 | | u32 au_sn_len, Bool for_rtsp) |
529 | 0 | { |
530 | 0 | GF_RTPStreamerConfig cfg; |
531 | 0 | memset(&cfg, 0, sizeof(GF_RTPStreamerConfig)); |
532 | 0 | #define RTCFG_SET(_t) cfg._t = _t |
533 | |
|
534 | 0 | RTCFG_SET(streamType); |
535 | 0 | RTCFG_SET(codecid); |
536 | 0 | RTCFG_SET(timeScale); |
537 | 0 | RTCFG_SET(ip_dest); |
538 | 0 | RTCFG_SET(port); |
539 | 0 | RTCFG_SET(MTU); |
540 | 0 | RTCFG_SET(TTL); |
541 | 0 | RTCFG_SET(ifce_addr); |
542 | 0 | RTCFG_SET(flags); |
543 | 0 | RTCFG_SET(dsi); |
544 | 0 | RTCFG_SET(dsi_len); |
545 | 0 | RTCFG_SET(PayloadType); |
546 | 0 | RTCFG_SET(sample_rate); |
547 | 0 | RTCFG_SET(nb_ch); |
548 | 0 | RTCFG_SET(is_crypted); |
549 | 0 | RTCFG_SET(IV_length); |
550 | 0 | RTCFG_SET(KI_length); |
551 | 0 | RTCFG_SET(MinSize); |
552 | 0 | RTCFG_SET(MaxSize); |
553 | 0 | RTCFG_SET(avgTS); |
554 | 0 | RTCFG_SET(maxDTSDelta); |
555 | 0 | RTCFG_SET(const_dur); |
556 | 0 | RTCFG_SET(bandwidth); |
557 | 0 | RTCFG_SET(max_ptime); |
558 | 0 | RTCFG_SET(au_sn_len); |
559 | 0 | #undef RTCFG_SET |
560 | |
|
561 | 0 | return gf_rtp_streamer_new_ex(&cfg, for_rtsp); |
562 | 0 | } |
563 | | |
564 | | |
565 | | GF_EXPORT |
566 | | void gf_rtp_streamer_del(GF_RTPStreamer *streamer) |
567 | 0 | { |
568 | 0 | if (streamer) { |
569 | 0 | if (streamer->channel) gf_rtp_del(streamer->channel); |
570 | 0 | if (streamer->packetizer) gf_rtp_builder_del(streamer->packetizer); |
571 | 0 | if (streamer->buffer) gf_free(streamer->buffer); |
572 | 0 | gf_free(streamer); |
573 | 0 | } |
574 | 0 | } |
575 | | |
576 | | #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING) |
577 | | |
578 | | void gf_media_format_ttxt_sdp(GP_RTPPacketizer *builder, char *payload_name, char **out_sdp_line, u32 w, u32 h, s32 tx, s32 ty, s16 l, u32 max_w, u32 max_h, char *tx3g_base64) |
579 | 0 | { |
580 | 0 | char tmp_buf[101]; |
581 | 0 | if (!out_sdp_line) return; |
582 | 0 | if (*out_sdp_line) |
583 | 0 | (*out_sdp_line)[0] = 0; |
584 | |
|
585 | 0 | tmp_buf[100] = 0; |
586 | 0 | snprintf(tmp_buf, 100, "a=fmtp:%d sver=60; ", builder->PayloadType); |
587 | 0 | gf_dynstrcat(out_sdp_line, tmp_buf, NULL); |
588 | |
|
589 | 0 | snprintf(tmp_buf, 100, "width=%d; height=%d; tx=%d; ty=%d; layer=%d; ", w, h, tx, ty, l); |
590 | 0 | gf_dynstrcat(out_sdp_line, tmp_buf, NULL); |
591 | |
|
592 | 0 | snprintf(tmp_buf, 100, "max-w=%d; max-h=%d", max_w, max_h); |
593 | 0 | gf_dynstrcat(out_sdp_line, tmp_buf, NULL); |
594 | |
|
595 | 0 | if (tx3g_base64) { |
596 | 0 | gf_dynstrcat(out_sdp_line, "; tx3g=", NULL); |
597 | 0 | gf_dynstrcat(out_sdp_line, tx3g_base64, NULL); |
598 | 0 | } |
599 | 0 | } |
600 | | |
601 | | #endif /*!defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING)*/ |
602 | | |
603 | | |
604 | | GF_EXPORT |
605 | | GF_Err gf_rtp_streamer_append_sdp_extended(GF_RTPStreamer *rtp, u16 ESID, const u8 *dsi, u32 dsi_len, const u8 *dsi_enh, u32 dsi_enh_len, char *KMS_URI, u32 width, u32 height, u32 tw, u32 th, s32 tx, s32 ty, s16 tl, u32 nb_channels, Bool for_rtsp, char **out_sdp_buffer) |
606 | 0 | { |
607 | 0 | u16 port=0; |
608 | 0 | char mediaName[30], payloadName[30]; |
609 | 0 | char tmp_buf[101]; |
610 | |
|
611 | 0 | tmp_buf[100]=0; |
612 | 0 | if (!out_sdp_buffer) return GF_BAD_PARAM; |
613 | | |
614 | 0 | gf_rtp_builder_get_payload_name(rtp->packetizer, payloadName, mediaName); |
615 | 0 | if (!for_rtsp) { |
616 | | //this can happen when forwarding a multicast SDP from RTSP, where only a subset of streams have been setup |
617 | 0 | if (!rtp->channel) return GF_OK; |
618 | 0 | gf_rtp_get_ports(rtp->channel, &port, NULL); |
619 | 0 | } |
620 | | |
621 | 0 | snprintf(tmp_buf, 100, "m=%s %d RTP/%s %u\n", mediaName, for_rtsp ? 0 : port, rtp->packetizer->slMap.IV_length ? "SAVP" : "AVP", rtp->packetizer->PayloadType); |
622 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
623 | 0 | if (nb_channels > 1) |
624 | 0 | snprintf(tmp_buf, 100, "a=rtpmap:%u %s/%u/%u\n", rtp->packetizer->PayloadType, payloadName, rtp->packetizer->sl_config.timestampResolution, nb_channels); |
625 | 0 | else |
626 | 0 | snprintf(tmp_buf, 100, "a=rtpmap:%u %s/%u\n", rtp->packetizer->PayloadType, payloadName, rtp->packetizer->sl_config.timestampResolution); |
627 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
628 | |
|
629 | 0 | if (ESID |
630 | | #if GPAC_ENABLE_3GPP_DIMS_RTP |
631 | | && (rtp->packetizer->rtp_payt != GF_RTP_PAYT_3GPP_DIMS) |
632 | | #endif |
633 | 0 | && (rtp->packetizer->rtp_payt != GF_RTP_PAYT_OPUS) |
634 | 0 | ) { |
635 | 0 | snprintf(tmp_buf, 100, "a=mpeg4-esid:%d\n", ESID); |
636 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
637 | 0 | } |
638 | |
|
639 | 0 | if (width && height) { |
640 | 0 | if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H263) { |
641 | 0 | snprintf(tmp_buf, 100, "a=cliprect:0,0,%d,%d\n", height, width); |
642 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
643 | 0 | } |
644 | | /*extensions for some mobile phones*/ |
645 | 0 | snprintf(tmp_buf, 100, "a=framesize:%d %d-%d\n", rtp->packetizer->PayloadType, width, height); |
646 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
647 | 0 | } |
648 | | |
649 | | /*AMR*/ |
650 | 0 | if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR) || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_AMR_WB)) { |
651 | 0 | snprintf(tmp_buf, 100, "a=fmtp:%d octet-align=1\n", rtp->packetizer->PayloadType); |
652 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
653 | 0 | } |
654 | 0 | #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING) |
655 | | /*Text*/ |
656 | 0 | else if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_3GPP_TEXT) { |
657 | 0 | char *sdp = NULL; |
658 | 0 | gf_media_format_ttxt_sdp(rtp->packetizer, payloadName, &sdp, tw, th, tx, ty, tl, width, height, (u8 *)dsi_enh); |
659 | 0 | gf_dynstrcat(out_sdp_buffer, sdp, NULL); |
660 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
661 | 0 | if (sdp) gf_free(sdp); |
662 | 0 | } |
663 | 0 | #endif |
664 | | /*EVRC/SMV in non header-free mode*/ |
665 | 0 | else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_EVRC_SMV) && (rtp->packetizer->auh_size>1)) { |
666 | 0 | snprintf(tmp_buf, 100, "a=fmtp:%d maxptime=%d\n", rtp->packetizer->PayloadType, rtp->packetizer->auh_size*20); |
667 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
668 | 0 | } |
669 | | /*H264/AVC*/ |
670 | 0 | else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_H264_AVC) || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_H264_SVC)) { |
671 | 0 | GF_AVCConfig *avcc = dsi ? gf_odf_avc_cfg_read((u8*)dsi, dsi_len) : NULL; |
672 | |
|
673 | 0 | if (avcc) { |
674 | 0 | snprintf(tmp_buf, 100, "a=fmtp:%d profile-level-id=%02X%02X%02X; packetization-mode=1", rtp->packetizer->PayloadType, avcc->AVCProfileIndication, avcc->profile_compatibility, avcc->AVCLevelIndication); |
675 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
676 | |
|
677 | 0 | if (gf_list_count(avcc->pictureParameterSets) || gf_list_count(avcc->sequenceParameterSets)) { |
678 | 0 | u32 i, count, b64s; |
679 | 0 | char b64[200]; |
680 | 0 | gf_dynstrcat(out_sdp_buffer, "; sprop-parameter-sets=", NULL); |
681 | |
|
682 | 0 | count = gf_list_count(avcc->sequenceParameterSets); |
683 | 0 | for (i=0; i<count; i++) { |
684 | 0 | GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(avcc->sequenceParameterSets, i); |
685 | 0 | b64s = gf_base64_encode(sl->data, sl->size, b64, 200); |
686 | 0 | b64[b64s]=0; |
687 | 0 | gf_dynstrcat(out_sdp_buffer, b64, NULL); |
688 | 0 | if (i+1<count) gf_dynstrcat(out_sdp_buffer, ",", NULL); |
689 | 0 | } |
690 | 0 | if (i) gf_dynstrcat(out_sdp_buffer, ",", NULL); |
691 | 0 | count = gf_list_count(avcc->pictureParameterSets); |
692 | 0 | for (i=0; i<count; i++) { |
693 | 0 | GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(avcc->pictureParameterSets, i); |
694 | 0 | b64s = gf_base64_encode(sl->data, sl->size, b64, 200); |
695 | 0 | b64[b64s]=0; |
696 | 0 | gf_dynstrcat(out_sdp_buffer, b64, NULL); |
697 | 0 | if (i+1<count) gf_dynstrcat(out_sdp_buffer, ",", NULL); |
698 | 0 | } |
699 | 0 | } |
700 | 0 | gf_odf_avc_cfg_del(avcc); |
701 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
702 | 0 | } |
703 | 0 | } |
704 | 0 | else if ((rtp->packetizer->rtp_payt == GF_RTP_PAYT_HEVC) |
705 | 0 | || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_LHVC) |
706 | 0 | || (rtp->packetizer->rtp_payt == GF_RTP_PAYT_VVC) |
707 | 0 | ) { |
708 | 0 | GF_VVCConfig *vvcc = NULL; |
709 | 0 | GF_HEVCConfig *hvcc = NULL; |
710 | 0 | GF_List *param_array = NULL; |
711 | 0 | u8 sps_nut=0, pps_nut=0, vps_nut=0; |
712 | 0 | if (rtp->packetizer->rtp_payt == GF_RTP_PAYT_VVC) { |
713 | 0 | vvcc = dsi ? gf_odf_vvc_cfg_read((u8*)dsi, dsi_len) : NULL; |
714 | 0 | param_array = vvcc ? vvcc->param_array : NULL; |
715 | 0 | sps_nut = GF_VVC_NALU_SEQ_PARAM; |
716 | 0 | pps_nut = GF_VVC_NALU_PIC_PARAM; |
717 | 0 | vps_nut = GF_VVC_NALU_VID_PARAM; |
718 | 0 | } else { |
719 | 0 | if (dsi) { |
720 | 0 | hvcc = gf_odf_hevc_cfg_read((u8*)dsi, dsi_len, GF_FALSE); |
721 | 0 | } else if (dsi_enh) { |
722 | 0 | hvcc = gf_odf_hevc_cfg_read((u8*)dsi_enh, dsi_enh_len, GF_TRUE); |
723 | 0 | } |
724 | 0 | param_array = hvcc ? hvcc->param_array : NULL; |
725 | 0 | sps_nut = GF_HEVC_NALU_SEQ_PARAM; |
726 | 0 | pps_nut = GF_HEVC_NALU_PIC_PARAM; |
727 | 0 | vps_nut = GF_HEVC_NALU_VID_PARAM; |
728 | 0 | } |
729 | |
|
730 | 0 | if (param_array) { |
731 | 0 | u32 count, i, j, b64s; |
732 | 0 | char b64[200]; |
733 | 0 | snprintf(tmp_buf, 100, "a=fmtp:%d", rtp->packetizer->PayloadType); |
734 | 0 | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
735 | |
|
736 | 0 | count = gf_list_count(param_array); |
737 | 0 | for (i = 0; i < count; i++) { |
738 | 0 | GF_NALUFFParamArray *ar = (GF_NALUFFParamArray *)gf_list_get(param_array, i); |
739 | 0 | if (ar->type==sps_nut) { |
740 | 0 | gf_dynstrcat(out_sdp_buffer, "; sprop-sps=", NULL); |
741 | 0 | } else if (ar->type==pps_nut) { |
742 | 0 | gf_dynstrcat(out_sdp_buffer, "; sprop-pps=", NULL); |
743 | 0 | } else if (ar->type==vps_nut) { |
744 | 0 | gf_dynstrcat(out_sdp_buffer, "; sprop-vps=", NULL); |
745 | 0 | } |
746 | 0 | for (j = 0; j < gf_list_count(ar->nalus); j++) { |
747 | 0 | GF_NALUFFParam *sl = (GF_NALUFFParam *)gf_list_get(ar->nalus, j); |
748 | 0 | b64s = gf_base64_encode(sl->data, sl->size, b64, 200); |
749 | 0 | b64[b64s]=0; |
750 | 0 | if (j) gf_dynstrcat(out_sdp_buffer, ", ", NULL); |
751 | 0 | gf_dynstrcat(out_sdp_buffer, b64, NULL); |
752 | 0 | } |
753 | 0 | } |
754 | 0 | if (vvcc) gf_odf_vvc_cfg_del(vvcc); |
755 | 0 | if (hvcc) gf_odf_hevc_cfg_del(hvcc); |
756 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
757 | 0 | } |
758 | 0 | } |
759 | | /*MPEG-4 decoder config*/ |
760 | 0 | else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_MPEG4) { |
761 | 0 | char *sdp = NULL; |
762 | 0 | gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, &sdp, (u8*)dsi, dsi_len); |
763 | 0 | gf_dynstrcat(out_sdp_buffer, sdp, NULL); |
764 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
765 | 0 | if (sdp) gf_free(sdp); |
766 | |
|
767 | 0 | if (rtp->packetizer->slMap.IV_length && KMS_URI) { |
768 | 0 | if (!strnicmp(KMS_URI, "(key)", 5) || !strnicmp(KMS_URI, "(ipmp)", 6) || !strnicmp(KMS_URI, "(uri)", 5)) { |
769 | 0 | gf_dynstrcat(out_sdp_buffer, "; ISMACrypKey=", NULL); |
770 | 0 | } else { |
771 | 0 | gf_dynstrcat(out_sdp_buffer, "; ISMACrypKey=(uri)", NULL); |
772 | 0 | } |
773 | 0 | gf_dynstrcat(out_sdp_buffer, KMS_URI, NULL); |
774 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
775 | 0 | } |
776 | 0 | } |
777 | | #if GPAC_ENABLE_3GPP_DIMS_RTP |
778 | | /*DIMS decoder config*/ |
779 | | else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_3GPP_DIMS) { |
780 | | snprintf(tmp_buf, 100, "a=fmtp:%d Version-profile=%d", rtp->packetizer->PayloadType, 10); |
781 | | gf_dynstrcat(out_sdp_buffer, tmp_buf, NULL); |
782 | | if (rtp->packetizer->flags & GP_RTP_DIMS_COMPRESSED) { |
783 | | gf_dynstrcat(out_sdp_buffer, ";content-coding=deflate", NULL); |
784 | | } |
785 | | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
786 | | } |
787 | | #endif |
788 | | /*MPEG-4 Audio LATM*/ |
789 | 0 | else if (rtp->packetizer->rtp_payt==GF_RTP_PAYT_LATM) { |
790 | 0 | GF_BitStream *bs; |
791 | 0 | u8 *config_bytes; |
792 | 0 | u32 config_size; |
793 | | |
794 | | /* form config string */ |
795 | 0 | bs = gf_bs_new(NULL, 32, GF_BITSTREAM_WRITE); |
796 | 0 | gf_bs_write_int(bs, 0, 1); /* AudioMuxVersion */ |
797 | 0 | gf_bs_write_int(bs, 1, 1); /* all streams same time */ |
798 | 0 | gf_bs_write_int(bs, 0, 6); /* numSubFrames */ |
799 | 0 | gf_bs_write_int(bs, 0, 4); /* numPrograms */ |
800 | 0 | gf_bs_write_int(bs, 0, 3); /* numLayer */ |
801 | | |
802 | | /* audio-specific config - PacketVideo patch: don't signal SBR and PS stuff, not allowed in LATM with audioMuxVersion=0*/ |
803 | 0 | if (dsi) gf_bs_write_data(bs, dsi, MIN(dsi_len, 2) ); |
804 | | |
805 | | /* other data */ |
806 | 0 | gf_bs_write_int(bs, 0, 3); /* frameLengthType */ |
807 | 0 | gf_bs_write_int(bs, 0xff, 8); /* latmBufferFullness */ |
808 | 0 | gf_bs_write_int(bs, 0, 1); /* otherDataPresent */ |
809 | 0 | gf_bs_write_int(bs, 0, 1); /* crcCheckPresent */ |
810 | 0 | gf_bs_get_content(bs, &config_bytes, &config_size); |
811 | 0 | gf_bs_del(bs); |
812 | |
|
813 | 0 | char *sdp = NULL; |
814 | 0 | gf_rtp_builder_format_sdp(rtp->packetizer, payloadName, &sdp, config_bytes, config_size); |
815 | 0 | gf_free(config_bytes); |
816 | 0 | gf_dynstrcat(out_sdp_buffer, sdp, NULL); |
817 | 0 | gf_dynstrcat(out_sdp_buffer, "\n", NULL); |
818 | 0 | if (sdp) gf_free(sdp); |
819 | 0 | } |
820 | 0 | return GF_OK; |
821 | 0 | } |
822 | | |
823 | | |
824 | | |
825 | | GF_EXPORT |
826 | | char *gf_rtp_streamer_format_sdp_header(char *app_name, char *ip_dest, char *session_name, char *iod64) |
827 | 0 | { |
828 | 0 | u64 size; |
829 | 0 | char *sdp, *tmp_fn = NULL; |
830 | 0 | FILE *tmp = gf_file_temp(&tmp_fn); |
831 | 0 | if (!tmp) return NULL; |
832 | | |
833 | | /* write SDP header*/ |
834 | 0 | gf_fprintf(tmp, "v=0\n"); |
835 | 0 | gf_fprintf(tmp, "o=%s 3326096807 1117107880000 IN IP%d %s\n", app_name, gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); |
836 | 0 | gf_fprintf(tmp, "s=%s\n", (session_name ? session_name : "GPAC Scene Streaming Session")); |
837 | 0 | gf_fprintf(tmp, "c=IN IP%d %s\n", gf_net_is_ipv6(ip_dest) ? 6 : 4, ip_dest); |
838 | 0 | gf_fprintf(tmp, "t=0 0\n"); |
839 | |
|
840 | 0 | if (iod64) |
841 | 0 | gf_fprintf(tmp, "a=mpeg4-iod:\"data:application/mpeg4-iod;base64,%s\"\n", iod64); |
842 | |
|
843 | 0 | size = gf_fsize(tmp); |
844 | 0 | sdp = (char*)gf_malloc(sizeof(char) * (size_t)(size+1)); |
845 | 0 | size = gf_fread(sdp, (size_t)size, tmp); |
846 | 0 | sdp[size] = 0; |
847 | 0 | gf_fclose(tmp); |
848 | 0 | gf_file_delete(tmp_fn); |
849 | 0 | gf_free(tmp_fn); |
850 | 0 | return sdp; |
851 | 0 | } |
852 | | |
853 | | GF_EXPORT |
854 | | GF_Err gf_rtp_streamer_append_sdp(GF_RTPStreamer *rtp, u16 ESID, const u8 *dsi, u32 dsi_len, char *KMS_URI, char **out_sdp_buffer) |
855 | 0 | { |
856 | 0 | return gf_rtp_streamer_append_sdp_extended(rtp, ESID, dsi, dsi_len, NULL, 0, KMS_URI, 0, 0, 0, 0, 0, 0, 0, 0, GF_FALSE, out_sdp_buffer); |
857 | 0 | } |
858 | | |
859 | | GF_EXPORT |
860 | | GF_Err gf_rtp_streamer_send_data(GF_RTPStreamer *rtp, u8 *data, u32 size, u32 fullsize, u64 cts, u64 dts, Bool is_rap, Bool au_start, Bool au_end, u32 au_sn, u32 sampleDuration, u32 sampleDescIndex) |
861 | 0 | { |
862 | 0 | GF_Err e; |
863 | 0 | if (!rtp->channel) return data ? GF_BAD_PARAM : GF_EOS; |
864 | | |
865 | 0 | rtp->packetizer->sl_header.compositionTimeStamp = gf_timestamp_rescale(cts, rtp->in_timescale, rtp->channel->TimeScale); |
866 | 0 | rtp->packetizer->sl_header.decodingTimeStamp = gf_timestamp_rescale(dts, rtp->in_timescale, rtp->channel->TimeScale); |
867 | 0 | rtp->packetizer->sl_header.randomAccessPointFlag = is_rap; |
868 | 0 | rtp->packetizer->sl_header.accessUnitStartFlag = au_start; |
869 | 0 | rtp->packetizer->sl_header.accessUnitEndFlag = au_end; |
870 | 0 | rtp->packetizer->sl_header.AU_sequenceNumber = au_sn; |
871 | 0 | sampleDuration = (u32) gf_timestamp_rescale(sampleDuration, rtp->in_timescale, rtp->channel->TimeScale); |
872 | 0 | if (au_start && size) rtp->packetizer->nb_aus++; |
873 | |
|
874 | 0 | rtp->last_err = GF_OK; |
875 | 0 | e = gf_rtp_builder_process(rtp->packetizer, data, size, (u8) au_end, fullsize, sampleDuration, sampleDescIndex); |
876 | 0 | if (e) return e; |
877 | 0 | return rtp->last_err; |
878 | 0 | } |
879 | | |
880 | | GF_EXPORT |
881 | | GF_Err gf_rtp_streamer_send_au(GF_RTPStreamer *rtp, u8 *data, u32 size, u64 cts, u64 dts, Bool is_rap) |
882 | 0 | { |
883 | 0 | return gf_rtp_streamer_send_data(rtp, data, size, size, cts, dts, is_rap, GF_TRUE, GF_TRUE, 0, 0, 0); |
884 | 0 | } |
885 | | |
886 | | GF_EXPORT |
887 | | GF_Err gf_rtp_streamer_send_au_with_sn(GF_RTPStreamer *rtp, u8 *data, u32 size, u64 cts, u64 dts, Bool is_rap, u32 inc_au_sn) |
888 | 0 | { |
889 | 0 | if (inc_au_sn) rtp->packetizer->sl_header.AU_sequenceNumber += inc_au_sn; |
890 | 0 | return gf_rtp_streamer_send_data(rtp, data, size, size, cts, dts, is_rap, GF_TRUE, GF_TRUE, rtp->packetizer->sl_header.AU_sequenceNumber, 0, 0); |
891 | 0 | } |
892 | | |
893 | | GF_EXPORT |
894 | | void gf_rtp_streamer_disable_auto_rtcp(GF_RTPStreamer *streamer) |
895 | 0 | { |
896 | 0 | streamer->channel->no_auto_rtcp = GF_TRUE; |
897 | 0 | } |
898 | | |
899 | | GF_EXPORT |
900 | | GF_Err gf_rtp_streamer_send_rtcp(GF_RTPStreamer *streamer, Bool force_ts, u32 rtp_ts, u32 force_ntp_type, u32 ntp_sec, u32 ntp_frac) |
901 | 0 | { |
902 | 0 | if (force_ts) streamer->channel->last_pck_ts = rtp_ts; |
903 | 0 | if (force_ntp_type) { |
904 | 0 | streamer->channel->forced_ntp_sec = ntp_sec; |
905 | 0 | streamer->channel->forced_ntp_frac = ntp_frac; |
906 | 0 | if (force_ntp_type==2) { |
907 | 0 | streamer->channel->next_report_time = 0; |
908 | 0 | } |
909 | | //we are sendind RTCP before first packet was sent, set sent time to same values |
910 | 0 | if (!streamer->channel->last_pck_ntp_sec) { |
911 | 0 | streamer->channel->last_pck_ntp_sec = ntp_sec; |
912 | 0 | streamer->channel->last_pck_ntp_frac = ntp_frac; |
913 | 0 | } |
914 | 0 | } else { |
915 | 0 | streamer->channel->forced_ntp_sec = 0; |
916 | 0 | streamer->channel->forced_ntp_frac = 0; |
917 | 0 | } |
918 | |
|
919 | 0 | GF_Err e = gf_rtp_send_rtcp_report(streamer->channel); |
920 | 0 | if (force_ntp_type) { |
921 | 0 | streamer->channel->forced_ntp_sec = 0; |
922 | 0 | streamer->channel->forced_ntp_frac = 0; |
923 | 0 | } |
924 | 0 | return e; |
925 | 0 | } |
926 | | |
927 | | GF_EXPORT |
928 | | GF_Err gf_rtp_streamer_send_bye(GF_RTPStreamer *streamer) |
929 | 0 | { |
930 | 0 | if (!streamer->channel) return GF_OK; |
931 | 0 | return gf_rtp_send_bye(streamer->channel); |
932 | 0 | } |
933 | | |
934 | | GF_EXPORT |
935 | | u8 gf_rtp_streamer_get_payload_type(GF_RTPStreamer *streamer) |
936 | 0 | { |
937 | 0 | return streamer ? streamer->packetizer->PayloadType : 0; |
938 | 0 | } |
939 | | |
940 | | GF_EXPORT |
941 | | u16 gf_rtp_streamer_get_next_rtp_sn(GF_RTPStreamer *streamer) |
942 | 0 | { |
943 | 0 | return streamer->packetizer->rtp_header.SequenceNumber+1; |
944 | 0 | } |
945 | | |
946 | | GF_EXPORT |
947 | | GF_Err gf_rtp_streamer_set_interleave_callbacks(GF_RTPStreamer *streamer, gf_rtp_tcp_callback RTP_TCPCallback, void *cbk1, void *cbk2) |
948 | 0 | { |
949 | |
|
950 | 0 | return gf_rtp_set_interleave_callbacks(streamer->channel, RTP_TCPCallback, cbk1, cbk2); |
951 | 0 | } |
952 | | |
953 | | GF_EXPORT |
954 | | GF_Err gf_rtp_streamer_read_rtcp(GF_RTPStreamer *streamer, gf_rtcp_rr_callback rtcp_cbk, void *udta) |
955 | 0 | { |
956 | 0 | u32 i, frac, sec; |
957 | 0 | u32 size = gf_rtp_read_rtcp(streamer->channel, streamer->rtcp_buf, RTCP_BUF_SIZE); |
958 | 0 | if (!size || !rtcp_cbk) return GF_EOS; |
959 | | |
960 | 0 | gf_net_get_ntp(&sec, &frac); |
961 | 0 | GF_Err e = gf_rtp_decode_rtcp(streamer->channel, streamer->rtcp_buf, size, NULL); |
962 | 0 | if (e<0) return e; |
963 | | |
964 | 0 | for (i=0; i<streamer->channel->nb_rctp_rr; i++) { |
965 | 0 | GF_RTCP_Report *rr = &streamer->channel->rtcp_rr[i]; |
966 | 0 | u32 ssrc = (rr->ssrc==streamer->channel->SSRC) ? 0 : rr->ssrc; |
967 | 0 | u32 lsr_sec = rr->last_sr>>16; |
968 | 0 | u32 lsr_frac = (rr->last_sr&0xFFFF)<<16; |
969 | 0 | u64 dlsr = rr->delay_last_sr * 1000; |
970 | 0 | dlsr /= 65536; |
971 | 0 | s64 diff = (sec&0x0000FFFF) * 1000; |
972 | 0 | diff -= lsr_sec*1000; |
973 | 0 | diff += ((frac>>16)*1000)/65536; |
974 | 0 | diff -= (lsr_frac*1000)/65536; |
975 | 0 | diff -= dlsr; |
976 | |
|
977 | 0 | u32 rtt_ms = (diff>=0) ? (u32) diff : 0; |
978 | 0 | u32 loss_rate = (rr->frac_lost*1000)/255; |
979 | |
|
980 | 0 | rtcp_cbk(udta, ssrc, rtt_ms, rr->jitter, loss_rate); |
981 | 0 | } |
982 | 0 | return GF_OK; |
983 | 0 | } |
984 | | |
985 | | GF_EXPORT |
986 | | u32 gf_rtp_streamer_get_ssrc(GF_RTPStreamer *streamer) |
987 | 0 | { |
988 | 0 | return (streamer && streamer->channel) ? streamer->channel->SSRC : 0; |
989 | 0 | } |
990 | | |
991 | | GF_EXPORT |
992 | | u32 gf_rtp_streamer_get_timescale(GF_RTPStreamer *streamer) |
993 | 0 | { |
994 | 0 | return (streamer && streamer->packetizer) ? streamer->packetizer->sl_config.timestampResolution : 0; |
995 | 0 | } |
996 | | |
997 | | GF_EXPORT |
998 | | u32 gf_rtp_streamer_get_codecid(GF_RTPStreamer *streamer) |
999 | 0 | { |
1000 | 0 | return (streamer && streamer->packetizer) ? streamer->packetizer->slMap.CodecID : 0 ; |
1001 | 0 | } |
1002 | | |
1003 | | #endif /*GPAC_DISABLE_STREAMING && GPAC_DISABLE_ISOM*/ |
1004 | | |