Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2019-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / rtp output filter
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 : #include <gpac/internal/media_dev.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/maths.h>
29 :
30 : #if !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING)
31 :
32 : #include <gpac/filters.h>
33 : #include <gpac/ietf.h>
34 : #include <gpac/config_file.h>
35 : #include <gpac/base_coding.h>
36 : #include <gpac/rtp_streamer.h>
37 :
38 : #include "out_rtp.h"
39 :
40 :
41 : typedef struct
42 : {
43 : //options
44 : char *ip;
45 : char *dst, *ext, *mime;
46 : u32 port;
47 : Bool loop, xps;
48 : Bool mpeg4;
49 : u32 mtu;
50 : u32 ttl;
51 : char *ifce;
52 : u32 payt, tt;
53 : s32 delay;
54 : char *info, *url, *email;
55 : s32 runfor, tso;
56 : Bool latm;
57 :
58 : /*timeline origin of our session (all tracks) in microseconds*/
59 : u64 sys_clock_at_init;
60 :
61 : /*list of streams in session*/
62 : GF_List *streams;
63 :
64 : /*base stream if this stream contains a media decoding dependency, 0 otherwise*/
65 : u32 base_pid_id;
66 :
67 : Bool first_RTCP_sent;
68 :
69 : GF_RTPOutStream *active_stream;
70 : u32 active_stream_idx;
71 : u64 active_min_ts_microsec;
72 :
73 : GF_FilterPid *opid;
74 :
75 : Bool wait_for_loop;
76 : u64 microsec_ts_init;
77 : //0: not single stream, 1: input is raw media, 2: input is TS
78 : u32 single_stream;
79 : GF_FilterCapability in_caps[2];
80 : char szExt[10];
81 : } GF_RTPOutCtx;
82 :
83 :
84 34 : GF_Err rtpout_create_sdp(GF_List *streams, Bool is_rtsp, const char *ip, const char *info, const char *sess_name, const char *url, const char *email, u32 base_pid_id, FILE **sdp_tmp, u64 *session_id)
85 : {
86 : FILE *sdp_out;
87 : u32 i, count;
88 : u64 session_version;
89 34 : sdp_out = gf_file_temp(NULL);
90 34 : if (!sdp_out) return GF_IO_ERR;
91 34 : *sdp_tmp = sdp_out;
92 :
93 :
94 34 : count = gf_list_count(streams);
95 :
96 34 : gf_fprintf(sdp_out, "v=0\n");
97 34 : if (gf_sys_is_test_mode()) {
98 34 : *session_id = 0;
99 : session_version = 0;
100 : } else {
101 0 : if (! *session_id) *session_id = gf_net_get_ntp_ts();
102 0 : session_version = gf_net_get_ntp_ts();
103 : }
104 34 : gf_fprintf(sdp_out, "o=gpac "LLU" "LLU" IN IP%d %s\n", *session_id, session_version, gf_net_is_ipv6(ip) ? 6 : 4, ip);
105 34 : gf_fprintf(sdp_out, "s=%s\n", sess_name);
106 :
107 34 : if (info) {
108 0 : gf_fprintf(sdp_out, "i=%s\n", info);
109 : } else {
110 34 : GF_RTPOutStream *stream = gf_list_get(streams, 0);
111 34 : const char *src = gf_filter_pid_orig_src_args(stream->pid, GF_FALSE);
112 34 : if (!src) src = gf_filter_pid_get_source_filter_name(stream->pid);
113 : else {
114 34 : src = gf_file_basename(src);
115 : }
116 34 : if (src)
117 34 : gf_fprintf(sdp_out, "i=%s\n", src);
118 : }
119 34 : gf_fprintf(sdp_out, "u=%s\n", url ? url : "http://gpac.io");
120 34 : if (email) {
121 0 : gf_fprintf(sdp_out, "e=%s\n", email);
122 : }
123 34 : if (is_rtsp) {
124 5 : gf_fprintf(sdp_out, "c=IN IP4 0.0.0.0\n");
125 : } else {
126 29 : gf_fprintf(sdp_out, "c=IN IP%d %s\n", gf_net_is_ipv6(ip) ? 6 : 4, ip);
127 : }
128 34 : gf_fprintf(sdp_out, "t=0 0\n");
129 :
130 34 : if (is_rtsp) {
131 5 : gf_fprintf(sdp_out, "a=control=*\n");
132 : }
133 :
134 34 : if (gf_sys_is_test_mode()) {
135 34 : gf_fprintf(sdp_out, "a=x-copyright: Streamed with GPAC - http://gpac.io\n");
136 : } else {
137 0 : gf_fprintf(sdp_out, "a=x-copyright: Streamed with GPAC %s - %s\n", gf_gpac_version(), gf_gpac_copyright() );
138 : }
139 :
140 34 : if (is_rtsp) {
141 : Double max_dur=0;
142 : Bool disable_seek = GF_FALSE;
143 6 : for (i=0; i<count; i++) {
144 : const GF_PropertyValue *p;
145 6 : GF_RTPOutStream *stream = gf_list_get(streams, i);
146 6 : if (!stream->rtp) continue;
147 :
148 6 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DURATION);
149 6 : if (p) {
150 6 : Double dur = (Double) p->value.lfrac.num;
151 6 : dur /= p->value.lfrac.den;
152 6 : if (dur>max_dur) max_dur = dur;
153 : }
154 6 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_PLAYBACK_MODE);
155 6 : if (!p || (p->value.uint<GF_PLAYBACK_MODE_FASTFORWARD))
156 : disable_seek = GF_TRUE;
157 : }
158 :
159 5 : if (!disable_seek && max_dur) {
160 5 : gf_fprintf(sdp_out, "a=range:npt=0-%g\n", max_dur);
161 : }
162 : }
163 :
164 34 : if (base_pid_id) {
165 0 : gf_fprintf(sdp_out, "a=group:DDP L%d", base_pid_id);
166 0 : for (i = 0; i < count; i++) {
167 0 : GF_RTPOutStream *st = gf_list_get(streams, i);
168 0 : if (st->depends_on == base_pid_id) {
169 0 : gf_fprintf(sdp_out, " L%d", i+1);
170 : }
171 : }
172 0 : gf_fprintf(sdp_out, "\n");
173 : }
174 :
175 35 : for (i=0; i<count; i++) {
176 35 : char *sdp_media=NULL;
177 : const char *KMS = NULL;
178 : char *dsi = NULL;
179 : char *dsi_enh = NULL;
180 : u32 w, h, tw, th;
181 : s32 tx, ty;
182 : s16 tl;
183 : u32 dsi_len = 0;
184 : u32 dsi_enh_len = 0;
185 : const GF_PropertyValue *p;
186 35 : GF_RTPOutStream *stream = gf_list_get(streams, i);
187 35 : if (!stream->rtp) continue;
188 :
189 35 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DECODER_CONFIG);
190 35 : if (p && p->value.data.ptr) {
191 : dsi = p->value.data.ptr;
192 27 : dsi_len = p->value.data.size;
193 : }
194 :
195 35 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
196 35 : if (p && p->value.data.ptr) {
197 : dsi_enh = p->value.data.ptr;
198 2 : dsi_enh_len = p->value.data.size;
199 : }
200 :
201 : w = h = tw = th = 0;
202 : tx = ty = 0;
203 : tl = 0;
204 :
205 35 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_WIDTH);
206 35 : if (p) w = p->value.uint;
207 :
208 35 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_HEIGHT);
209 35 : if (p) h = p->value.uint;
210 :
211 35 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_PROTECTION_KMS_URI);
212 35 : if (p) KMS = p->value.string;
213 :
214 35 : if (stream->codecid == GF_CODECID_TX3G) {
215 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_TRANS_X);
216 2 : if (p) tx = p->value.sint;
217 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_TRANS_Y);
218 2 : if (p) ty = p->value.sint;
219 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ZORDER);
220 2 : if (p) tl = p->value.sint;
221 : tw = w;
222 : th = h;
223 :
224 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_WIDTH_MAX);
225 2 : if (p) w = p->value.uint;
226 :
227 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_HEIGHT_MAX);
228 2 : if (p) h = p->value.uint;
229 : }
230 :
231 35 : gf_rtp_streamer_append_sdp_extended(stream->rtp, stream->id, dsi, dsi_len, dsi_enh, dsi_enh_len, (char *)KMS, w, h, tw, th, tx, ty, tl, is_rtsp, &sdp_media);
232 :
233 35 : if (sdp_media) {
234 35 : gf_fprintf(sdp_out, "%s", sdp_media);
235 35 : gf_free(sdp_media);
236 : }
237 35 : if (base_pid_id) {
238 : u32 j;
239 :
240 0 : gf_fprintf(sdp_out, "a=mid:L%d\n", i+1);
241 0 : gf_fprintf(sdp_out, "a=depend:%d lay", gf_rtp_streamer_get_payload_type(stream->rtp) );
242 :
243 0 : for (j=0; j<count; j++) {
244 0 : GF_RTPOutStream *tk = gf_list_get(streams, j);
245 0 : if (tk == stream) continue;
246 0 : if (tk->depends_on == stream->id) {
247 0 : gf_fprintf(sdp_out, " L%d:%d", j+1, gf_rtp_streamer_get_payload_type(tk->rtp) );
248 : }
249 : }
250 0 : gf_fprintf(sdp_out, "\n");
251 : }
252 :
253 35 : if (is_rtsp) {
254 6 : gf_fprintf(sdp_out, "a=control:trackID=%d\n", stream->ctrl_id);
255 : }
256 : }
257 34 : gf_fprintf(sdp_out, "\n");
258 34 : return GF_OK;
259 : }
260 :
261 36 : GF_Err rtpout_init_streamer(GF_RTPOutStream *stream, const char *ipdest, Bool inject_xps, Bool use_mpeg4_signaling, Bool use_latm, u32 payt, u32 mtu, u32 ttl, const char *ifce, Bool is_rtsp, u32 *base_pid_id, u32 file_mode)
262 : {
263 : Bool disable_mpeg4 = GF_FALSE;
264 : u32 flags, average_size, max_size, max_tsdelta, codecid, const_dur, nb_ch, samplerate, max_cts_offset, bandwidth, IV_length, KI_length, dsi_len, max_ptime, au_sn_len;
265 : char *dsi;
266 : Bool is_crypted;
267 : const GF_PropertyValue *p;
268 :
269 36 : *base_pid_id = 0;
270 :
271 : dsi_len = samplerate = nb_ch = IV_length = KI_length = 0;
272 : is_crypted = 0;
273 : dsi = NULL;
274 : flags = 0;
275 : max_ptime = au_sn_len = 0;
276 :
277 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_CODECID);
278 36 : codecid = p ? p->value.uint : 0;
279 36 : if (stream->codecid && (stream->codecid != codecid)) {
280 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Dynamic change of codec in RTP session not supported !\n"));
281 : return GF_FILTER_NOT_SUPPORTED;
282 : }
283 36 : stream->codecid = codecid;
284 :
285 36 : stream->is_encrypted = GF_FALSE;
286 36 : if (stream->streamtype == GF_STREAM_ENCRYPTED) {
287 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ORIG_STREAM_TYPE);
288 2 : if (p) stream->streamtype = p->value.uint;
289 2 : stream->is_encrypted = GF_TRUE;
290 :
291 2 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
292 2 : if (!p || (p->value.uint != GF_ISOM_ISMACRYP_SCHEME)) {
293 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Protected track with scheme type %s, cannot stream (only ISMA over RTP is supported !\n", p ? gf_4cc_to_str(p->value.uint) : "unknwon" ));
294 : return GF_FILTER_NOT_SUPPORTED;
295 : }
296 : }
297 :
298 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_TIMESCALE);
299 36 : stream->timescale = p ? p->value.uint : 1000;
300 :
301 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_CONFIG_IDX);
302 36 : stream->sample_desc_index = p ? p->value.uint : 0;
303 :
304 :
305 : u32 cfg_crc=0;
306 : dsi = NULL;
307 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DECODER_CONFIG);
308 36 : if (p) {
309 27 : dsi = p->value.data.ptr;
310 27 : dsi_len = p->value.data.size;
311 27 : cfg_crc = gf_crc_32(dsi, dsi_len);
312 : }
313 36 : if (stream->rtp && (cfg_crc==stream->cfg_crc))
314 : return GF_OK;
315 :
316 36 : if (inject_xps)
317 1 : stream->inject_ps = GF_TRUE;
318 35 : else if (stream->cfg_crc)
319 0 : stream->inject_ps = GF_TRUE;
320 36 : stream->cfg_crc = cfg_crc;
321 :
322 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_NB_FRAMES);
323 36 : stream->nb_aus = p ? p->value.uint : 0;
324 :
325 36 : switch (stream->streamtype) {
326 : case GF_STREAM_OD:
327 : case GF_STREAM_SCENE:
328 : //todo, check if sync shadow is used
329 : // if (gf_isom_has_sync_shadows(ctx->isom, stream->track_num) || gf_isom_has_sample_dependency(ctx->isom, stream->track_num))
330 : // flags |= GP_RTP_PCK_SYSTEMS_CAROUSEL;
331 : break;
332 19 : case GF_STREAM_AUDIO:
333 19 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_SAMPLE_RATE);
334 19 : if (p) samplerate = p->value.uint;
335 19 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_NUM_CHANNELS);
336 19 : if (p) nb_ch = p->value.uint;
337 : break;
338 : case GF_STREAM_VISUAL:
339 : break;
340 1 : case GF_STREAM_FILE:
341 1 : if (file_mode==2) {
342 1 : stream->codecid = codecid = GF_CODECID_FAKE_MP2T;
343 1 : stream->timescale = 90000;
344 : }
345 : break;
346 : default:
347 : break;
348 : }
349 :
350 36 : gf_filter_pid_set_framing_mode(stream->pid, (stream->streamtype==GF_STREAM_FILE) ? GF_FALSE : GF_TRUE);
351 :
352 : /*get sample info*/
353 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_MAX_FRAME_SIZE);
354 36 : max_size = p ? p->value.uint : 0;
355 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_AVG_FRAME_SIZE);
356 36 : average_size = p ? p->value.uint : 0;
357 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_MAX_TS_DELTA);
358 36 : max_tsdelta = p ? p->value.uint : 0;
359 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_MAX_CTS_OFFSET);
360 36 : max_cts_offset = p ? p->value.uint : (u32) -1;
361 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_CONSTANT_DURATION);
362 36 : const_dur = p ? p->value.uint : 0;
363 :
364 :
365 36 : if (stream->avcc) gf_odf_avc_cfg_del(stream->avcc);
366 36 : stream->avcc = NULL;
367 36 : if (stream->hvcc) gf_odf_hevc_cfg_del(stream->hvcc);
368 36 : stream->hvcc = NULL;
369 :
370 36 : stream->avc_nalu_size = 0;
371 36 : switch (codecid) {
372 6 : case GF_CODECID_AVC:
373 : case GF_CODECID_SVC:
374 : case GF_CODECID_MVC:
375 6 : if (dsi) {
376 6 : GF_AVCConfig *avcc = gf_odf_avc_cfg_read(dsi, dsi_len);
377 6 : if (avcc) {
378 6 : stream->avc_nalu_size = avcc->nal_unit_size;
379 6 : if (stream->inject_ps)
380 1 : stream->avcc = avcc;
381 : else
382 5 : gf_odf_avc_cfg_del(avcc);
383 : }
384 : }
385 : break;
386 2 : case GF_CODECID_HEVC:
387 : case GF_CODECID_LHVC:
388 2 : if (dsi) {
389 2 : GF_HEVCConfig *hvcc = gf_odf_hevc_cfg_read(dsi, dsi_len, (codecid==GF_CODECID_LHVC) ? GF_TRUE : GF_FALSE );
390 2 : if (hvcc) {
391 2 : stream->avc_nalu_size = hvcc->nal_unit_size;
392 2 : if (stream->inject_ps) {
393 0 : u32 i, count = gf_list_count(hvcc->param_array);
394 : GF_NALUFFParamArray *vpsa=NULL, *spsa=NULL;
395 0 : stream->hvcc = hvcc;
396 0 : for (i=0; i<count; i++) {
397 0 : GF_NALUFFParamArray *pa = gf_list_get(hvcc->param_array, i);
398 0 : if (!vpsa && (pa->type == GF_HEVC_NALU_VID_PARAM)) {
399 : vpsa = pa;
400 0 : gf_list_rem(hvcc->param_array, i);
401 0 : count--;
402 : }
403 0 : else if (!spsa && (pa->type == GF_HEVC_NALU_SEQ_PARAM)) {
404 : spsa = pa;
405 0 : gf_list_rem(hvcc->param_array, i);
406 0 : count--;
407 : }
408 : }
409 : //insert SPS at beginning
410 0 : gf_list_insert(hvcc->param_array, spsa, 0);
411 : //insert VPS at beginning - we now have VPS, SPS and other (PPS, SEI...)
412 0 : gf_list_insert(hvcc->param_array, vpsa, 0);
413 : } else
414 2 : gf_odf_hevc_cfg_del(hvcc);
415 : }
416 : }
417 : break;
418 11 : case GF_CODECID_AAC_MPEG4:
419 : case GF_CODECID_AAC_MPEG2_MP:
420 : case GF_CODECID_AAC_MPEG2_LCP:
421 : case GF_CODECID_AAC_MPEG2_SSRP:
422 : //we cannot disable mpeg4 payload type, compute default values !!
423 11 : if (!const_dur || !average_size || !max_tsdelta || !max_size) {
424 : const_dur = 1024;
425 5 : const_dur *= stream->timescale;
426 5 : const_dur /= samplerate;
427 : max_tsdelta = const_dur;
428 : average_size = 500;
429 : max_size = 1000;
430 5 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTPOut] AAC stream detected but not information available on average size/tsdelta/duration, assuming const dur %d max_tsdelta %d average size %d max size %d\n", const_dur, max_tsdelta, average_size, max_size));
431 : }
432 11 : if (use_latm)
433 : flags |= GP_RTP_PCK_USE_LATM_AAC;
434 : break;
435 : }
436 :
437 36 : if (max_cts_offset==(u32)-1) {
438 27 : if (stream->streamtype==GF_STREAM_VISUAL) {
439 : disable_mpeg4 = GF_TRUE;
440 : }
441 : max_cts_offset = 0;
442 : }
443 :
444 36 : if (!disable_mpeg4 && use_mpeg4_signaling)
445 : flags = GP_RTP_PCK_SIGNAL_RAP | GP_RTP_PCK_FORCE_MPEG4;
446 :
447 :
448 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ID);
449 36 : stream->id = p ? p->value.uint : 0;
450 :
451 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_BITRATE);
452 36 : bandwidth = p ? p->value.uint : 0;
453 :
454 : if (codecid==GF_CODECID_AAC_MPEG4)
455 :
456 : if (is_crypted) {
457 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ISMA_SELECTIVE_ENC);
458 : if (p->value.boolean) {
459 : flags |= GP_RTP_PCK_SELECTIVE_ENCRYPTION;
460 : }
461 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ISMA_IV_LENGTH);
462 : IV_length = p ? p->value.uint : 0;
463 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_ISMA_KI_LENGTH);
464 : KI_length = p ? p->value.uint : 0;
465 : }
466 :
467 :
468 : /*init packetizer*/
469 72 : stream->rtp = gf_rtp_streamer_new(stream->streamtype, codecid, stream->timescale,
470 36 : (char *) ipdest, stream->port, mtu, ttl, ifce,
471 : flags, dsi, dsi_len,
472 : payt, samplerate, nb_ch,
473 : is_crypted, IV_length, KI_length,
474 : average_size, max_size, max_tsdelta, max_cts_offset, const_dur, bandwidth, max_ptime, au_sn_len, is_rtsp);
475 :
476 36 : if (!stream->rtp) {
477 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Could not initialize RTP for stream %s: not supported\n", gf_filter_pid_get_name(stream->pid) ));
478 : return GF_NOT_SUPPORTED;
479 : }
480 :
481 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DELAY);
482 36 : stream->ts_delay = p ? p->value.longsint : 0;
483 :
484 : payt++;
485 36 : stream->microsec_ts_scale_frac.num = 1000000;
486 36 : stream->microsec_ts_scale_frac.den = stream->timescale;
487 :
488 185 : while (! (stream->microsec_ts_scale_frac.num % 10) && ! (stream->microsec_ts_scale_frac.den % 10)) {
489 113 : stream->microsec_ts_scale_frac.num /= 10;
490 113 : stream->microsec_ts_scale_frac.den /= 10;
491 : }
492 :
493 36 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_PID_DEPENDENCY_ID);
494 36 : if (p) {
495 0 : *base_pid_id = p->value.uint;
496 0 : gf_rtp_streamer_disable_auto_rtcp(stream->rtp);
497 : }
498 : return GF_OK;
499 : }
500 :
501 30 : static GF_Err rtpout_setup_sdp(GF_RTPOutCtx *ctx)
502 : {
503 : FILE *sdp_out;
504 : u32 fsize;
505 : GF_Err e;
506 30 : u64 sess_id=0;
507 : u8 *output;
508 30 : const char *ip = ctx->ip;
509 30 : if (!ip) ip = "127.0.0.1";
510 :
511 30 : if (ctx->single_stream) return GF_OK;
512 :
513 29 : e = rtpout_create_sdp(ctx->streams, GF_FALSE, ip, ctx->info, "livesession", ctx->url, ctx->email, ctx->base_pid_id, &sdp_out, &sess_id);
514 29 : if (e) return e;
515 :
516 29 : fsize = (u32) gf_ftell(sdp_out);
517 29 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(ctx->opid, fsize, &output);
518 29 : if (pck) {
519 29 : gf_fseek(sdp_out, 0, SEEK_SET);
520 29 : u32 read = (u32) gf_fread(output, fsize, sdp_out);
521 29 : if (read != fsize) {
522 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Failed to read SDP from temp file, got %d bytes but expecting %d\n", read, fsize));
523 0 : gf_filter_pck_discard(pck);
524 : e = GF_IO_ERR;
525 : } else {
526 29 : char c = output[fsize-1];
527 29 : output[fsize-1] = 0;
528 29 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTPOut] SDP file generated: %s\n", output));
529 29 : output[fsize-1] = c;
530 29 : gf_filter_pck_set_framing(pck, GF_TRUE, GF_TRUE);
531 29 : gf_filter_pck_send(pck);
532 : e = GF_OK;
533 : }
534 : } else {
535 : e = GF_OUT_OF_MEM;
536 : }
537 29 : gf_fclose(sdp_out);
538 29 : return e;
539 : }
540 :
541 30 : static u16 rtpout_check_next_port(GF_RTPOutCtx *ctx, u16 first_port)
542 : {
543 30 : u32 i, count = gf_list_count(ctx->streams);
544 60 : for (i=0;i<count; i++) {
545 30 : GF_RTPOutStream *stream = gf_list_get(ctx->streams, i);
546 30 : if (stream->port==first_port) {
547 0 : return rtpout_check_next_port(ctx, (u16) (first_port+2) );
548 : }
549 : }
550 : return first_port;
551 : }
552 :
553 30 : static void rtpout_del_stream(GF_RTPOutStream *st)
554 : {
555 30 : if (st->rtp) gf_rtp_streamer_del(st->rtp);
556 30 : if (st->pck) gf_filter_pid_drop_packet(st->pid);
557 30 : if (st->avcc)
558 1 : gf_odf_avc_cfg_del(st->avcc);
559 30 : if (st->hvcc)
560 0 : gf_odf_hevc_cfg_del(st->hvcc);
561 30 : gf_free(st);
562 30 : }
563 :
564 30 : static GF_Err rtpout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
565 : {
566 30 : GF_RTPOutCtx *ctx = (GF_RTPOutCtx *) gf_filter_get_udta(filter);
567 : GF_Err e = GF_OK;
568 : GF_RTPOutStream *stream;
569 : u16 first_port;
570 : u32 streamType, payt;
571 : const GF_PropertyValue *p;
572 :
573 30 : first_port = ctx->port;
574 :
575 30 : if (is_remove) {
576 0 : GF_RTPOutStream *t =gf_filter_pid_get_udta(pid);
577 0 : if (t) {
578 0 : if (ctx->active_stream==t) ctx->active_stream = NULL;
579 0 : gf_list_del_item(ctx->streams, t);
580 0 : rtpout_del_stream(t);
581 : }
582 0 : if (!gf_list_count(ctx->streams)) {
583 0 : if (ctx->opid) gf_filter_pid_set_eos(ctx->opid);
584 : return GF_EOS;
585 : }
586 : return GF_OK;
587 : }
588 30 : stream = gf_filter_pid_get_udta(pid);
589 :
590 30 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
591 30 : streamType = p ? p->value.uint : 0;
592 :
593 30 : switch (streamType) {
594 : case GF_STREAM_VISUAL:
595 : case GF_STREAM_AUDIO:
596 : break;
597 1 : case GF_STREAM_FILE:
598 : case GF_STREAM_UNKNOWN:
599 1 : if (stream) {
600 0 : if (ctx->active_stream==stream) ctx->active_stream = NULL;
601 0 : gf_list_del_item(ctx->streams, stream);
602 0 : rtpout_del_stream(stream);
603 : }
604 1 : if (!ctx->dst)
605 : return GF_FILTER_NOT_SUPPORTED;
606 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_MIME);
607 1 : if (p && p->value.string && !strcmp(p->value.string, "video/mpeg-2")) {
608 0 : ctx->single_stream = 2;
609 : } else {
610 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FILE_EXT);
611 1 : if (p && p->value.string && !strcmp(p->value.string, "ts")) {
612 1 : ctx->single_stream = 2;
613 : } else {
614 : return GF_FILTER_NOT_SUPPORTED;
615 : }
616 : }
617 :
618 : break;
619 : default:
620 : break;
621 : }
622 30 : if (!stream) {
623 30 : GF_SAFEALLOC(stream, GF_RTPOutStream);
624 30 : if (!stream) return GF_OUT_OF_MEM;
625 30 : gf_list_add(ctx->streams, stream);
626 30 : stream->pid = pid;
627 30 : stream->streamtype = streamType;
628 30 : stream->min_dts = GF_FILTER_NO_TS;
629 30 : gf_filter_pid_set_udta(pid, stream);
630 30 : if (ctx->single_stream) {
631 : GF_FilterEvent evt;
632 1 : gf_filter_pid_init_play_event(pid, &evt, 0, 1.0, "RTPOut");
633 1 : gf_filter_pid_send_event(pid, &evt);
634 : }
635 : }
636 30 : if (!ctx->single_stream) {
637 29 : if (!ctx->opid) {
638 29 : ctx->opid = gf_filter_pid_new(filter);
639 : }
640 29 : gf_filter_pid_copy_properties(ctx->opid, pid);
641 29 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
642 29 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL );
643 29 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, NULL );
644 29 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_FILE_EXT, &PROP_STRING("sdp") );
645 29 : gf_filter_pid_set_name(ctx->opid, "SDP");
646 : } else {
647 1 : char *dst = ctx->dst+6;
648 1 : char *sep = strchr(dst, ':');
649 1 : if (sep) {
650 2 : first_port = ctx->port = atoi(sep+1);
651 1 : sep[0] = 0;
652 1 : if (ctx->ip) gf_free(ctx->ip);
653 1 : ctx->ip = gf_strdup(dst);
654 1 : sep[0] = ':';
655 : }
656 : }
657 :
658 30 : stream->port = rtpout_check_next_port(ctx, first_port);
659 :
660 30 : payt = ctx->payt + gf_list_find(ctx->streams, stream);
661 : //init rtp
662 30 : e = rtpout_init_streamer(stream, ctx->ip ? ctx->ip : "127.0.0.1", ctx->xps, ctx->mpeg4, ctx->latm, payt, ctx->mtu, ctx->ttl, ctx->ifce, GF_FALSE, &ctx->base_pid_id, ctx->single_stream);
663 30 : if (e) return e;
664 :
665 30 : stream->selected = GF_TRUE;
666 :
667 30 : if (ctx->loop) {
668 14 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PLAYBACK_MODE);
669 14 : if (!p || (p->value.uint<GF_PLAYBACK_MODE_FASTFORWARD)) {
670 0 : ctx->loop = GF_FALSE;
671 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] PID %s cannot be seek, disabling loop\n", gf_filter_pid_get_name(pid) ));
672 : }
673 : }
674 : return GF_OK;
675 : }
676 :
677 30 : static GF_Err rtpout_initialize(GF_Filter *filter)
678 : {
679 30 : GF_RTPOutCtx *ctx = (GF_RTPOutCtx *) gf_filter_get_udta(filter);
680 30 : if (!ctx->payt) ctx->payt = 96;
681 30 : if (!ctx->port) ctx->port = 7000;
682 30 : if (!ctx->mtu) ctx->mtu = 1450;
683 30 : if (ctx->payt<96) ctx->payt = 96;
684 30 : if (ctx->payt>127) ctx->payt = 127;
685 30 : ctx->streams = gf_list_new();
686 :
687 30 : if (ctx->dst && (ctx->ext || ctx->mime) ) {
688 : //static cap, streamtype = file
689 1 : ctx->in_caps[0].code = GF_PROP_PID_STREAM_TYPE;
690 1 : ctx->in_caps[0].val = PROP_UINT(GF_STREAM_FILE);
691 1 : ctx->in_caps[0].flags = GF_CAPS_INPUT_STATIC;
692 :
693 1 : if (ctx->mime) {
694 0 : ctx->in_caps[1].code = GF_PROP_PID_MIME;
695 0 : ctx->in_caps[1].val = PROP_NAME( ctx->mime );
696 0 : ctx->in_caps[1].flags = GF_CAPS_INPUT;
697 : } else {
698 1 : strncpy(ctx->szExt, ctx->ext, 9);
699 1 : ctx->szExt[9] = 0;
700 1 : strlwr(ctx->szExt);
701 1 : ctx->in_caps[1].code = GF_PROP_PID_FILE_EXT;
702 1 : ctx->in_caps[1].val = PROP_NAME( ctx->szExt );
703 1 : ctx->in_caps[1].flags = GF_CAPS_INPUT;
704 : }
705 1 : gf_filter_override_caps(filter, ctx->in_caps, 2);
706 1 : gf_filter_set_max_extra_input_pids(filter, 0);
707 1 : ctx->single_stream = GF_TRUE;
708 : }
709 30 : return GF_OK;
710 : }
711 :
712 30 : static void rtpout_finalize(GF_Filter *filter)
713 : {
714 30 : GF_RTPOutCtx *ctx = (GF_RTPOutCtx *) gf_filter_get_udta(filter);
715 :
716 90 : while (gf_list_count(ctx->streams)) {
717 30 : GF_RTPOutStream *tmp = gf_list_pop_back(ctx->streams);
718 30 : rtpout_del_stream(tmp);
719 : }
720 30 : gf_list_del(ctx->streams);
721 :
722 30 : }
723 :
724 3 : static GF_Err rtpout_send_xps(GF_RTPOutStream *stream, GF_List *pslist, Bool *au_start, u32 pck_size, u32 cts, u32 dts, u32 duration)
725 : {
726 : GF_Err e;
727 3 : u32 i, count = gf_list_count(pslist);
728 5 : for (i=0; i<count; i++) {
729 2 : GF_NALUFFParam *sl = gf_list_get(pslist, i);
730 2 : e = gf_rtp_streamer_send_data(stream->rtp, (char *) sl->data, sl->size, pck_size, cts, dts, stream->current_sap ? 1 : 0, *au_start, GF_FALSE, stream->pck_num, duration, stream->sample_desc_index);
731 2 : if (e) return e;
732 2 : *au_start = GF_FALSE;
733 : }
734 : return GF_OK;
735 : }
736 :
737 30 : static Bool rtpout_init_clock(GF_RTPOutCtx *ctx)
738 : {
739 : GF_Err e;
740 : u64 min_dts = GF_FILTER_NO_TS;
741 30 : u32 i, count = gf_list_count(ctx->streams);
742 :
743 60 : for (i=0; i<count; i++) {
744 : u64 dts;
745 30 : GF_RTPOutStream *stream = gf_list_get(ctx->streams, i);
746 30 : GF_FilterPacket *pck = gf_filter_pid_get_packet(stream->pid);
747 30 : if (!pck) return GF_FALSE;
748 :
749 30 : dts = gf_filter_pck_get_dts(pck);
750 30 : if (dts==GF_FILTER_NO_TS)
751 0 : dts = gf_filter_pck_get_cts(pck);
752 :
753 30 : if (dts==GF_FILTER_NO_TS) dts=0;
754 :
755 30 : dts *= 1000000;
756 30 : dts /= stream->timescale;
757 30 : if (min_dts > dts)
758 : min_dts = dts;
759 :
760 30 : if (ctx->tso>0) {
761 15 : u64 offset = ctx->tso;
762 15 : offset *= stream->timescale;
763 15 : offset /= 1000000;
764 15 : stream->rtp_ts_offset = (u32) offset;
765 : }
766 : }
767 30 : ctx->sys_clock_at_init = gf_sys_clock_high_res();
768 30 : ctx->microsec_ts_init = min_dts;
769 30 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTPOut] RTP clock initialized - time origin set to "LLU" us (sys clock) / "LLU" us (media clock)\n", ctx->sys_clock_at_init, ctx->microsec_ts_init));
770 30 : if (ctx->tso<0) {
771 15 : gf_rand_init(GF_FALSE);
772 30 : for (i=0; i<count; i++) {
773 15 : GF_RTPOutStream *stream = gf_list_get(ctx->streams, i);
774 15 : stream->rtp_ts_offset = gf_rand();
775 15 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTPOut] RTP stream %d initial RTP TS set to %d\n", i+1, stream->rtp_ts_offset));
776 : }
777 : }
778 :
779 30 : e = rtpout_setup_sdp(ctx);
780 30 : if (e) return e;
781 :
782 30 : if (ctx->runfor==0) {
783 14 : for (i=0; i<count; i++) {
784 14 : GF_RTPOutStream *stream = gf_list_get(ctx->streams, i);
785 14 : gf_filter_pid_set_discard(stream->pid, GF_TRUE);
786 : }
787 : }
788 :
789 : return GF_TRUE;
790 : }
791 :
792 2471985 : GF_Err rtpout_process_rtp(GF_List *streams, GF_RTPOutStream **active_stream, Bool loop, s32 delay, u32 *active_stream_idx, u64 sys_clock_at_init, u64 *active_min_ts_microsec, u64 microsec_ts_init, Bool *wait_for_loop, u32 *repost_delay_us, Bool *first_RTCP_sent, u32 base_pid_id)
793 : {
794 : GF_Err e = GF_OK;
795 : GF_RTPOutStream *stream;
796 : u32 duration, i, count;
797 : s64 diff;
798 : u64 clock;
799 : const char *pck_data;
800 : u32 pck_size;
801 : u32 dts, cts;
802 :
803 : /*browse all inputs and locate most mature stream*/
804 2471985 : if (! *active_stream) {
805 : u32 nb_eos = 0;
806 1004 : count = gf_list_count(streams);
807 :
808 1004 : *active_min_ts_microsec = (u64) -1;
809 2118 : for (i=0; i<count; i++) {
810 1114 : stream = gf_list_get(streams, i);
811 1114 : if (!stream->rtp) continue;
812 :
813 : /*load next AU*/
814 1114 : if (!stream->pck) {
815 : u64 ts;
816 1008 : stream->pck = gf_filter_pid_get_packet(stream->pid);
817 :
818 1008 : if (!stream->pck) {
819 62 : if (gf_filter_pid_is_eos(stream->pid)) {
820 : //flush stream
821 45 : if (!stream->bye_sent) {
822 30 : stream->bye_sent = GF_TRUE;
823 30 : gf_rtp_streamer_send_au(stream->rtp, NULL, 0, 0, 0, GF_FALSE);
824 30 : gf_rtp_streamer_send_bye(stream->rtp);
825 : }
826 45 : nb_eos++;
827 : }
828 62 : continue;
829 : }
830 946 : stream->current_dts = gf_filter_pck_get_dts(stream->pck);
831 : //if CTS is not set, use prev packet CTS
832 946 : ts = gf_filter_pck_get_cts(stream->pck);
833 946 : if (ts==GF_FILTER_NO_TS) ts = stream->current_cts;
834 946 : stream->current_cts = ts;
835 :
836 946 : stream->current_sap = gf_filter_pck_get_sap(stream->pck);
837 946 : duration = gf_filter_pck_get_duration(stream->pck);
838 946 : if (duration) stream->current_duration = duration;
839 946 : if (stream->current_dts==GF_FILTER_NO_TS)
840 0 : stream->current_dts = stream->current_cts;
841 :
842 946 : if (stream->min_dts==GF_FILTER_NO_TS) {
843 22 : stream->min_dts = stream->current_dts;
844 : }
845 :
846 946 : stream->microsec_dts = (u64) (stream->microsec_ts_scale_frac.num * (s64) stream->current_dts);
847 946 : stream->microsec_dts /= stream->microsec_ts_scale_frac.den;
848 946 : stream->microsec_dts += stream->microsec_ts_offset + sys_clock_at_init;
849 :
850 946 : if (stream->microsec_dts < microsec_ts_init) {
851 0 : stream->microsec_dts = 0;
852 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTPOut] next RTP packet (stream %d) has a timestamp "LLU" less than initial timestamp "LLU" - forcing to 0\n", i+1, stream->microsec_dts, microsec_ts_init));
853 : } else {
854 946 : stream->microsec_dts -= microsec_ts_init;
855 : }
856 :
857 946 : if (stream->current_sap>GF_FILTER_SAP_3) stream->current_sap = 0;
858 946 : stream->pck_num++;
859 946 : *wait_for_loop = GF_FALSE;
860 : }
861 :
862 : /*check timing*/
863 1052 : if (stream->pck) {
864 1052 : if (*active_min_ts_microsec > stream->microsec_dts) {
865 983 : *active_min_ts_microsec = stream->microsec_dts;
866 983 : *active_stream = stream;
867 983 : *active_stream_idx = i+1;
868 : }
869 : }
870 : }
871 :
872 : /*no input data ...*/
873 1004 : if (! *active_stream) {
874 59 : if (nb_eos==count) {
875 : u64 max_dur = 0;
876 45 : if (!loop)
877 : return GF_EOS;
878 :
879 14 : for (i=0; i<count; i++) {
880 14 : stream = gf_list_get(streams, i);
881 14 : u64 dur = stream->current_dts + stream->current_duration - stream->min_dts;
882 :
883 14 : dur *= 1000000;
884 14 : dur /= stream->timescale;
885 :
886 14 : if (max_dur < dur) {
887 : max_dur = dur;
888 : }
889 : }
890 14 : if (*wait_for_loop)
891 : return GF_OK;
892 :
893 14 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTPOut] RTP session done, looping source\n"));
894 14 : *wait_for_loop = GF_TRUE;
895 28 : for (i=0; i<count; i++) {
896 : GF_FilterEvent evt;
897 : const GF_PropertyValue *p;
898 14 : stream = gf_list_get(streams, i);
899 14 : p = gf_filter_pid_get_property(stream->pid, GF_PROP_NO_TS_LOOP);
900 14 : if (!p || !p->value.boolean) {
901 : u64 new_ts;
902 : new_ts = max_dur;
903 14 : new_ts *= stream->timescale;
904 14 : new_ts /= 1000000;
905 14 : stream->ts_offset += new_ts;
906 14 : stream->microsec_ts_offset = (u64) (stream->ts_offset*(1000000.0/stream->timescale) + sys_clock_at_init);
907 : }
908 :
909 : //loop pid: stop and play
910 14 : GF_FEVT_INIT(evt, GF_FEVT_STOP, stream->pid);
911 14 : gf_filter_pid_send_event(stream->pid, &evt);
912 :
913 14 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, stream->pid);
914 14 : gf_filter_pid_send_event(stream->pid, &evt);
915 : }
916 : }
917 : return GF_OK;
918 : }
919 : }
920 :
921 2471926 : stream = *active_stream;
922 2471926 : clock = gf_sys_clock_high_res();
923 2471926 : diff = (s64) *active_min_ts_microsec;
924 2471926 : diff += ((s64) delay) * 1000;
925 2471926 : diff -= (s64) clock;
926 :
927 2471926 : if (diff > 1000) {
928 : u64 repost_in;
929 : //if more than 11 secs ahead of time, ask for delay minus one second, otherwise ask for half the delay
930 2470985 : if (diff<=11000) repost_in = diff/3;
931 685229 : else repost_in = diff - 10000;
932 2470985 : *repost_delay_us = (u32) repost_in;
933 2470985 : GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTPOut] next RTP packet (stream %d DTS "LLU") scheduled in "LLU" us, requesting filter reschedule in "LLU" us - clock "LLU" us\n", *active_stream_idx, stream->current_dts, diff, repost_in, clock));
934 : return GF_OK;
935 941 : } else if (diff<=-1000) {
936 2 : GF_LOG(GF_LOG_WARNING, GF_LOG_RTP, ("[RTPOut] RTP session stream %d - sending packet %d (DTS "LLU") too late by %d us - clock "LLU" us\n", *active_stream_idx, stream->pck_num, stream->current_dts, -diff, clock));
937 939 : } else if (diff>0){
938 892 : GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTPOut] RTP session stream %d - sending packet %d (DTS "LLU") ahead of %d us - clock "LLU" us\n", *active_stream_idx, stream->pck_num, stream->current_dts, diff, clock));
939 : }
940 :
941 : /*send packets*/
942 941 : pck_data = gf_filter_pck_get_data(stream->pck, &pck_size);
943 941 : if (!pck_size) {
944 0 : gf_filter_pid_drop_packet(stream->pid);
945 0 : stream->pck = NULL;
946 0 : *active_stream = NULL;
947 0 : return GF_OK;
948 : }
949 :
950 941 : dts = (u32) (stream->current_dts + stream->ts_offset);
951 941 : cts = (u32) (stream->current_cts + stream->ts_offset);
952 941 : duration = stream->current_duration;
953 :
954 941 : dts += stream->rtp_ts_offset;
955 941 : cts += stream->rtp_ts_offset;
956 941 : if (stream->ts_delay>=0) {
957 806 : dts += (u32) stream->ts_delay;
958 806 : cts += (u32) stream->ts_delay;
959 : } else {
960 135 : if ((s32) dts >= -stream->ts_delay)
961 135 : dts += (s32) stream->ts_delay;
962 : else
963 : dts = 0;
964 :
965 135 : if ((s32) cts >= -stream->ts_delay )
966 135 : cts += (s32) stream->ts_delay;
967 : else
968 : cts = 0;
969 : }
970 :
971 : #ifndef GPAC_DISABLE_LOG
972 941 : if (gf_log_tool_level_on(GF_LOG_RTP, GF_LOG_DEBUG)) {
973 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_RTP, ("[RTPOut] Sending RTP packets for stream %d pck %d/%d DTS "LLU" - CTS "LLU" - RTP TS "LLU" - size %d - SAP %d - clock "LLU" us\n", *active_stream_idx, stream->pck_num, stream->nb_aus, stream->current_dts, stream->current_dts, cts, pck_size, stream->current_sap, clock) );
974 : } else {
975 941 : GF_LOG(GF_LOG_INFO, GF_LOG_RTP, ("[RTPOut] Runtime %08u ms send stream %d pck %08u/%08u DTS %08u CTS %08u RTP-TS %08u size %08u SAP %d\r", (u32) (clock - sys_clock_at_init)/1000, *active_stream_idx, stream->pck_num, stream->nb_aus, (u32) stream->current_dts, (u32) stream->current_dts, (u32) cts, pck_size, stream->current_sap) );
976 : }
977 : #endif
978 :
979 : /*we are about to send scalable base: trigger RTCP reports with the same NTP. This avoids
980 : NTP drift due to system clock precision which could break sync decoding*/
981 941 : if (! *first_RTCP_sent || (base_pid_id && (base_pid_id== stream->id) )) {
982 : u32 ntp_sec, ntp_frac;
983 : /*force sending RTCP SR every RAP ? - not really compliant but we cannot perform scalable tuning otherwise*/
984 21 : u32 ntp_type = stream->current_sap ? 2 : 1;
985 21 : gf_net_get_ntp(&ntp_sec, &ntp_frac);
986 21 : count = gf_list_count(streams);
987 :
988 42 : for (i=0; i<count; i++) {
989 22 : GF_RTPOutStream *astream = gf_list_get(streams, i);
990 22 : if (!astream->pck) break;
991 :
992 21 : u32 ts = (u32) (astream->current_cts + astream->ts_offset + astream->rtp_ts_offset);
993 21 : gf_rtp_streamer_send_rtcp(stream->rtp, GF_TRUE, ts, ntp_type, ntp_sec, ntp_frac);
994 : }
995 21 : *first_RTCP_sent = GF_TRUE;
996 : }
997 :
998 : /*unpack nal units*/
999 941 : if (stream->avc_nalu_size) {
1000 : Bool au_start, au_end;
1001 : u32 v, size;
1002 146 : u32 remain = pck_size;
1003 : const char *ptr = pck_data;
1004 :
1005 146 : au_start = 1;
1006 : e = GF_OK;
1007 :
1008 146 : if (stream->avcc && stream->current_sap) {
1009 1 : e = rtpout_send_xps(stream, stream->avcc->sequenceParameterSets, &au_start, pck_size, cts, dts, duration);
1010 1 : if (!e)
1011 1 : e = rtpout_send_xps(stream, stream->avcc->sequenceParameterSetExtensions, &au_start, pck_size, cts, dts, duration);
1012 :
1013 1 : if (!e)
1014 1 : e = rtpout_send_xps(stream, stream->avcc->pictureParameterSets, &au_start, pck_size, cts, dts, duration);
1015 : }
1016 145 : else if (stream->hvcc && stream->current_sap) {
1017 0 : u32 nbps = gf_list_count(stream->hvcc->param_array);
1018 0 : for (i=0; i<nbps; i++) {
1019 0 : GF_NALUFFParamArray *pa = gf_list_get(stream->hvcc->param_array, i);
1020 :
1021 0 : if (!e)
1022 0 : e = rtpout_send_xps(stream, pa->nalus, &au_start, pck_size, cts, dts, duration);
1023 : }
1024 : }
1025 :
1026 541 : while (!e && remain) {
1027 : size = 0;
1028 395 : v = (*active_stream)->avc_nalu_size;
1029 2370 : while (v) {
1030 1580 : size |= (u8) *ptr;
1031 1580 : ptr++;
1032 1580 : remain--;
1033 1580 : v-=1;
1034 1580 : if (v) size<<=8;
1035 : }
1036 395 : if (remain < size) {
1037 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Broken AVC nalu encapsulation: NALU size is %d but only %d bytes left in sample %d\n", size, remain, (*active_stream)->pck_num));
1038 : break;
1039 : }
1040 395 : remain -= size;
1041 395 : au_end = remain ? 0 : 1;
1042 :
1043 395 : e = gf_rtp_streamer_send_data(stream->rtp, (char *) ptr, size, pck_size, cts, dts, stream->current_sap ? 1 : 0, au_start, au_end, stream->pck_num, duration, stream->sample_desc_index);
1044 395 : ptr += size;
1045 395 : au_start = 0;
1046 : }
1047 : } else {
1048 795 : e = gf_rtp_streamer_send_data(stream->rtp, (char *) pck_data, pck_size, pck_size, cts, dts, stream->current_sap ? 1 : 0, 1, 1, stream->pck_num, duration, stream->sample_desc_index);
1049 : }
1050 941 : gf_filter_pid_drop_packet(stream->pid);
1051 941 : stream->pck = NULL;
1052 :
1053 941 : if (e) {
1054 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_RTP, ("[RTPOut] Error sending RTP packet %d: %s\n", stream->pck_num, gf_error_to_string(e) ));
1055 : }
1056 :
1057 941 : *active_stream = NULL;
1058 941 : return e;
1059 :
1060 : }
1061 :
1062 2469288 : static GF_Err rtpout_process(GF_Filter *filter)
1063 : {
1064 : GF_Err e = GF_OK;
1065 2469288 : u32 repost_delay_us=0;
1066 2469288 : GF_RTPOutCtx *ctx = gf_filter_get_udta(filter);
1067 :
1068 : /*init session timeline - all sessions are sync'ed for packet scheduling purposes*/
1069 2469288 : if (!ctx->sys_clock_at_init) {
1070 30 : if (!rtpout_init_clock(ctx)) return GF_OK;
1071 : }
1072 :
1073 2469288 : if (ctx->runfor>0) {
1074 0 : s64 diff = gf_sys_clock_high_res();
1075 0 : diff -= ctx->sys_clock_at_init;
1076 0 : diff /= 1000;
1077 0 : if ((s32) diff > ctx->runfor) {
1078 0 : u32 i, count = gf_list_count(ctx->streams);
1079 0 : for (i=0; i<count; i++) {
1080 0 : GF_RTPOutStream *stream = gf_list_get(ctx->streams, i);
1081 0 : gf_filter_pid_set_discard(stream->pid, GF_TRUE);
1082 0 : stream->pck = NULL;
1083 : }
1084 0 : if (ctx->opid) gf_filter_pid_set_eos(ctx->opid);
1085 : return GF_EOS;
1086 : }
1087 : }
1088 :
1089 2469288 : e = rtpout_process_rtp(ctx->streams, &ctx->active_stream, ctx->loop, ctx->delay, &ctx->active_stream_idx, ctx->sys_clock_at_init, &ctx->active_min_ts_microsec, ctx->microsec_ts_init, &ctx->wait_for_loop, &repost_delay_us, &ctx->first_RTCP_sent, ctx->base_pid_id);
1090 2469288 : if (e) return e;
1091 :
1092 2469257 : if (repost_delay_us)
1093 2468683 : gf_filter_ask_rt_reschedule(filter, repost_delay_us);
1094 :
1095 : return GF_OK;
1096 : }
1097 :
1098 2198 : static GF_FilterProbeScore rtpout_probe_url(const char *url, const char *mime)
1099 : {
1100 2198 : if (!strnicmp(url, "rtp://", 6)) return GF_FPROBE_SUPPORTED;
1101 2197 : return GF_FPROBE_NOT_SUPPORTED;
1102 : }
1103 :
1104 : static const GF_FilterCapability RTPOutCaps[] =
1105 : {
1106 : //anything else (not file and framed) result in manifest PID
1107 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1108 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
1109 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
1110 :
1111 : CAP_UINT(GF_CAPS_OUTPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1112 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_FILE_EXT, "sdp"),
1113 : CAP_STRING(GF_CAPS_OUTPUT, GF_PROP_PID_MIME, "application/sdp"),
1114 : {0},
1115 : //anything else (not file and framed) result in media pids not file
1116 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1117 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED | GF_CAPFLAG_LOADED_FILTER, GF_PROP_PID_UNFRAMED, GF_TRUE),
1118 : {0},
1119 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1120 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_FILE_EXT, "ts|m2t|mts|dmb|trp"),
1121 : CAP_STRING(GF_CAPS_INPUT, GF_PROP_PID_MIME, "video/mpeg-2|video/mp2t|video/mpeg"),
1122 : };
1123 :
1124 :
1125 : #define OFFS(_n) #_n, offsetof(GF_RTPOutCtx, _n)
1126 : static const GF_FilterArgs RTPOutArgs[] =
1127 : {
1128 : { OFFS(ip), "destination IP address (NULL is 127.0.0.1)", GF_PROP_STRING, NULL, NULL, 0},
1129 : { OFFS(port), "port for first stream in session", GF_PROP_UINT, "7000", NULL, 0},
1130 : { OFFS(loop), "loop all streams in session (not always possible depending on source type)", GF_PROP_BOOL, "true", NULL, 0},
1131 : { OFFS(mpeg4), "send all streams using MPEG-4 generic payload format if posible", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1132 : { OFFS(mtu), "size of RTP MTU in bytes", GF_PROP_UINT, "1460", NULL, 0},
1133 : { OFFS(ttl), "time-to-live for multicast packets", GF_PROP_UINT, "2", NULL, GF_FS_ARG_HINT_ADVANCED},
1134 : { OFFS(ifce), "default network interface to use", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1135 : { OFFS(payt), "payload type to use for dynamic configs", GF_PROP_UINT, "96", "96-127", GF_FS_ARG_HINT_EXPERT},
1136 : { OFFS(delay), "send delay for packet (negative means send earlier)", GF_PROP_SINT, "0", NULL, 0},
1137 : { OFFS(tt), "time tolerance in microseconds. Whenever schedule time minus realtime is below this value, the packet is sent right away", GF_PROP_UINT, "1000", NULL, GF_FS_ARG_HINT_EXPERT},
1138 : { OFFS(runfor), "run for the given time in ms. Negative value means run for ever (if loop) or source duration, 0 only outputs the sdp", GF_PROP_SINT, "-1", NULL, 0},
1139 : { OFFS(tso), "set timestamp offset in microseconds. Negative value means random initial timestamp", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_EXPERT},
1140 : { OFFS(xps), "force parameter set injection at each SAP. If not set, only inject if different from SDP ones", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1141 : { OFFS(latm), "use latm for AAC payload format", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1142 : { OFFS(dst), "URL for direct RTP mode - see filter help", GF_PROP_NAME, NULL, NULL, 0},
1143 : { OFFS(ext), "file extension for direct RTP mode - see filter help", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1144 : { OFFS(mime), "set mime type for direct RTP mode - see filter help", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1145 : {0}
1146 : };
1147 :
1148 :
1149 : GF_FilterRegister RTPOutRegister = {
1150 : .name = "rtpout",
1151 : GF_FS_SET_DESCRIPTION("RTP Streamer")
1152 : GF_FS_SET_HELP("The RTP streamer handles SDP/RTP output streaming.\n"
1153 : "# SDP mode\n"
1154 : "When the destination url is an SDP, the filter outputs an SDP on a file pid and streams RTP packets over UDP, starting from the indicated [-port]().\n"
1155 : "# Direct RTP mode\n"
1156 : "When the destination url uses the protocol scheme `rtp://IP:PORT`, the filter does not output any SDP and streams a single input over RTP, using PORT indicated in the destination URL, or the first [-port]() configured.\n"
1157 : "In this mode, it is usually needed to specify the desired format using [-ext]() or [-mime]().\n"
1158 : "EX gpac -i src -o rtp://localhost:1234/:ext=ts\n"
1159 : "This will indicate that the RTP streamer expects a MPEG-2 TS mux as an input.\n"
1160 : "# RTP Packets\n"
1161 : "The RTP packets produced have a maximum payload set by the [-mtu]() option (IP packet will be MTU + 40 bytes of IP+UDP+RTP headers).\n"
1162 : "The real-time scheduling algorithm works as follows:\n"
1163 : "- first initialize the clock by:\n"
1164 : " - computing the smallest timestamp for all input pids\n"
1165 : " - mapping this media time to the system clock\n"
1166 : "- determine the earliest packet to send next on each input pid, adding [-delay]() if any\n"
1167 : "- finally compare the packet mapped timestamp __TS__ to the system clock __SC__. When __TS__ - __SC__ is less than [-tt](), the RTP packets for the source packet are sent\n"
1168 : )
1169 : .private_size = sizeof(GF_RTPOutCtx),
1170 : .max_extra_pids = -1,
1171 : .args = RTPOutArgs,
1172 : //dynamic redirect since RTP may be dynamically loaded when solving .sdp destinations
1173 : .flags = GF_FS_REG_DYNAMIC_REDIRECT,
1174 : .initialize = rtpout_initialize,
1175 : .finalize = rtpout_finalize,
1176 : SETCAPS(RTPOutCaps),
1177 : .configure_pid = rtpout_configure_pid,
1178 : .probe_url = rtpout_probe_url,
1179 : .process = rtpout_process
1180 : };
1181 :
1182 :
1183 2877 : const GF_FilterRegister *rtpout_register(GF_FilterSession *session)
1184 : {
1185 2877 : return &RTPOutRegister;
1186 : }
1187 :
1188 : #else
1189 :
1190 : const GF_FilterRegister *rtpout_register(GF_FilterSession *session)
1191 : {
1192 : return NULL;
1193 : }
1194 :
1195 : #endif /* !defined(GPAC_DISABLE_ISOM) && !defined(GPAC_DISABLE_STREAMING) */
|