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