LCOV - code coverage report
Current view: top level - filters - out_rtp.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 477 560 85.2 %
Date: 2021-04-29 23:48:07 Functions: 14 14 100.0 %

          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) */

Generated by: LCOV version 1.13