LCOV - code coverage report
Current view: top level - filters - out_sock.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 185 252 73.4 %
Date: 2021-04-29 23:48:07 Functions: 7 7 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
       6             :  *                                      All rights reserved
       7             :  *
       8             :  *  This file is part of GPAC / generic socket 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             : 
      27             : #include <gpac/filters.h>
      28             : #include <gpac/constants.h>
      29             : #include <gpac/network.h>
      30             : 
      31             : typedef struct
      32             : {
      33             :         GF_Socket *socket;
      34             :         Bool is_tuned;
      35             :         char address[GF_MAX_IP_NAME_LEN];
      36             :         Bool pck_pending;
      37             : } GF_SockOutClient;
      38             : 
      39             : typedef struct
      40             : {
      41             :         //options
      42             :         Double start, speed;
      43             :         char *dst, *mime, *ext, *ifce;
      44             :         Bool listen;
      45             :         u32 maxc, port, sockbuf, ka, kp, rate, ttl;
      46             :         GF_Fraction pckr, pckd;
      47             : 
      48             :         GF_Socket *socket;
      49             :         //only one output pid
      50             :         GF_FilterPid *pid;
      51             : 
      52             :         GF_List *clients;
      53             : 
      54             :         Bool pid_started;
      55             :         Bool had_clients;
      56             :         Bool pck_pending;
      57             : 
      58             :         GF_FilterCapability in_caps[2];
      59             :         char szExt[10];
      60             :         char szFileName[GF_MAX_PATH];
      61             : 
      62             :         u32 nb_pck_processed;
      63             :         u64 start_time;
      64             :         u64 nb_bytes_sent;
      65             : 
      66             :         GF_FilterPacket *rev_pck;
      67             :         u32 next_pckd_idx, next_pckr_idx;
      68             :         u32 nb_pckd_wnd, nb_pckr_wnd;
      69             : } GF_SockOutCtx;
      70             : 
      71             : 
      72          10 : static GF_Err sockout_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
      73             : {
      74             :         const GF_PropertyValue *p;
      75          10 :         GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
      76          10 :         if (is_remove) {
      77           0 :                 ctx->pid = NULL;
      78           0 :                 gf_sk_del(ctx->socket);
      79           0 :                 ctx->socket = NULL;
      80           0 :                 return GF_OK;
      81             :         }
      82          10 :         gf_filter_pid_check_caps(pid);
      83             : 
      84          10 :         if (!ctx->pid && (!ctx->listen || gf_list_count(ctx->clients) ) ) {
      85             :                 GF_FilterEvent evt;
      86           7 :                 gf_filter_pid_init_play_event(pid, &evt, ctx->start, ctx->speed, "SockOut");
      87           7 :                 gf_filter_pid_send_event(pid, &evt);
      88           7 :                 ctx->pid_started = GF_TRUE;
      89             :         }
      90          10 :         ctx->pid = pid;
      91             : 
      92          10 :         p = gf_filter_pid_get_property(pid, GF_PROP_PID_DISABLE_PROGRESSIVE);
      93          10 :         if (p && p->value.uint) {
      94           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Block patching is not supported by socket output\n"));
      95             :                 return GF_NOT_SUPPORTED;
      96             :         }
      97             :         return GF_OK;
      98             : }
      99             : 
     100          10 : static GF_Err sockout_initialize(GF_Filter *filter)
     101             : {
     102             :         GF_Err e;
     103             :         char *str, *url;
     104             :         u16 port;
     105             :         char *ext;
     106             :         u32 sock_type = 0;
     107          10 :         GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
     108             : 
     109          10 :         if (!ctx || !ctx->dst) return GF_OK;
     110             :         e = GF_NOT_SUPPORTED;
     111          10 :         if (!strncmp(ctx->dst, "tcp://", 6)) e = GF_OK;
     112           4 :         else if (!strncmp(ctx->dst, "udp://", 6)) e = GF_OK;
     113           0 :         else if (!strncmp(ctx->dst, "tcpu://", 7)) e = GF_OK;
     114           0 :         else if (!strncmp(ctx->dst, "udpu://", 7)) e = GF_OK;
     115             : 
     116             : 
     117             :         if (e)  {
     118           0 :                 gf_filter_setup_failure(filter, GF_NOT_SUPPORTED);
     119           0 :                 return GF_NOT_SUPPORTED;
     120             :         }
     121          10 :         if (ctx->ext) ext = ctx->ext;
     122             :         else {
     123           0 :                 ext = gf_file_ext_start(ctx->dst);
     124           0 :                 if (ext) ext++;
     125           0 :                 if (ext) {
     126           0 :                         const char *szport = strchr(ext, ':');
     127           0 :                         if (szport)
     128           0 :                                 ext = gf_file_ext_start(szport);
     129             :                 }
     130             :         }
     131             : 
     132          10 :         if (!ext && !ctx->mime) {
     133           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] No extension provided nor mime type for output file %s, cannot infer format\n", ctx->dst));
     134             :                 return GF_NOT_SUPPORTED;
     135             :         }
     136             : 
     137          10 :         if (ctx->listen) {
     138           3 :                 ctx->clients = gf_list_new();
     139           3 :                 if (!ctx->clients) return GF_OUT_OF_MEM;
     140             :         }
     141             : 
     142             :         //static cap, streamtype = file
     143          10 :         ctx->in_caps[0].code = GF_PROP_PID_STREAM_TYPE;
     144          10 :         ctx->in_caps[0].val = PROP_UINT(GF_STREAM_FILE);
     145          10 :         ctx->in_caps[0].flags = GF_CAPS_INPUT_STATIC;
     146             : 
     147          10 :         if (ctx->mime) {
     148           0 :                 ctx->in_caps[1].code = GF_PROP_PID_MIME;
     149           0 :                 ctx->in_caps[1].val = PROP_NAME( ctx->mime );
     150           0 :                 ctx->in_caps[1].flags = GF_CAPS_INPUT;
     151             :         } else {
     152          10 :                 strncpy(ctx->szExt, ext, 9);
     153          10 :                 ctx->szExt[9] = 0;
     154          10 :                 strlwr(ctx->szExt);
     155          10 :                 ctx->in_caps[1].code = GF_PROP_PID_FILE_EXT;
     156          10 :                 ctx->in_caps[1].val = PROP_NAME( ctx->szExt );
     157          10 :                 ctx->in_caps[1].flags = GF_CAPS_INPUT;
     158             :         }
     159          10 :         gf_filter_override_caps(filter, ctx->in_caps, 2);
     160             : 
     161             :         /*create our ourput socket*/
     162             : 
     163          10 :         if (!strnicmp(ctx->dst, "udp://", 6)) {
     164             :                 sock_type = GF_SOCK_TYPE_UDP;
     165           4 :                 ctx->listen = GF_FALSE;
     166           6 :         } else if (!strnicmp(ctx->dst, "tcp://", 6)) {
     167             :                 sock_type = GF_SOCK_TYPE_TCP;
     168             : #ifdef GPAC_HAS_SOCK_UN
     169           0 :         } else if (!strnicmp(ctx->dst, "udpu://", 7)) {
     170             :                 sock_type = GF_SOCK_TYPE_UDP_UN;
     171           0 :                 ctx->listen = GF_FALSE;
     172           0 :         } else if (!strnicmp(ctx->dst, "tcpu://", 7)) {
     173             :                 sock_type = GF_SOCK_TYPE_TCP_UN;
     174             : #endif
     175             :         } else {
     176             :                 return GF_NOT_SUPPORTED;
     177             :         }
     178             : 
     179             :         //skip ://
     180          10 :         url = strchr(ctx->dst, ':');
     181          10 :         url += 3;
     182             : 
     183          10 :         ctx->socket = gf_sk_new(sock_type);
     184          10 :         if (! ctx->socket ) {
     185           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Failed to open socket for %s\n", ctx->dst));
     186             :                 return GF_IO_ERR;
     187             :         }
     188             : 
     189             :         /*setup port and src*/
     190          10 :         port = ctx->port;
     191          10 :         str = strrchr(url, ':');
     192             :         /*take care of IPv6 address*/
     193          10 :         if (str && strchr(str, ']')) str = strchr(url, ':');
     194          10 :         if (str) {
     195          20 :                 port = atoi(str+1);
     196          10 :                 str[0] = 0;
     197             :         }
     198             : 
     199          10 :         if (gf_sk_is_multicast_address(url)) {
     200             :                 //server socket, do not bind
     201           1 :                 e = gf_sk_setup_multicast(ctx->socket, url, port, ctx->ttl, GF_TRUE, ctx->ifce);
     202           1 :                 ctx->listen = GF_FALSE;
     203          18 :         } else if ((sock_type == GF_SOCK_TYPE_UDP)
     204             : #ifdef GPAC_HAS_SOCK_UN
     205           9 :                 || (sock_type == GF_SOCK_TYPE_UDP_UN)
     206             : #endif
     207             :         ) {
     208           3 :                 e = gf_sk_bind(ctx->socket, ctx->ifce, port, url, port, GF_SOCK_REUSE_PORT | GF_SOCK_FAKE_BIND);
     209           3 :                 ctx->listen = GF_FALSE;
     210           6 :         } else if (ctx->listen) {
     211           3 :                 e = gf_sk_bind(ctx->socket, NULL, port, url, 0, GF_SOCK_REUSE_PORT);
     212           3 :                 if (!e) e = gf_sk_listen(ctx->socket, ctx->maxc);
     213           3 :                 if (!e) {
     214           3 :                         gf_filter_post_process_task(filter);
     215           3 :                         gf_sk_server_mode(ctx->socket, GF_TRUE);
     216             :                 }
     217             :         } else {
     218           3 :                 e = gf_sk_connect(ctx->socket, url, port, NULL);
     219             :         }
     220             : 
     221          10 :         if (str) str[0] = ':';
     222             : 
     223          10 :         if (e) {
     224           0 :                 gf_sk_del(ctx->socket);
     225           0 :                 ctx->socket = NULL;
     226           0 :                 return e;
     227             :         }
     228             : 
     229          10 :         gf_sk_set_buffer_size(ctx->socket, 0, ctx->sockbuf);
     230             : 
     231          10 :         return GF_OK;
     232             : }
     233             : 
     234          10 : static void sockout_finalize(GF_Filter *filter)
     235             : {
     236          10 :         GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
     237          10 :         if (ctx->clients) {
     238           6 :                 while (gf_list_count(ctx->clients)) {
     239           3 :                         GF_SockOutClient *sc = gf_list_pop_back(ctx->clients);
     240           3 :                         if (sc->socket) gf_sk_del(sc->socket);
     241           3 :                         gf_free(sc);
     242             :                 }
     243           3 :                 gf_list_del(ctx->clients);
     244             :         }
     245             : 
     246          10 :         if (ctx->socket) gf_sk_del(ctx->socket);
     247          10 : }
     248             : 
     249        1175 : static GF_Err sockout_send_packet(GF_SockOutCtx *ctx, GF_FilterPacket *pck, GF_Socket *dst_sock)
     250             : {
     251             :         GF_Err e;
     252             :         const GF_PropertyValue *p;
     253             :         u32 w, h, stride, stride_uv, pf;
     254             :         u32 nb_planes, uv_height;
     255             :         const char *pck_data;
     256             :         u32 pck_size;
     257             :         GF_FilterFrameInterface *hwf=NULL;
     258        1175 :         if (!dst_sock) return GF_OK;
     259             : 
     260        1175 :         pck_data = gf_filter_pck_get_data(pck, &pck_size);
     261        1175 :         if (pck_data) {
     262        1175 :                 e = gf_sk_send(dst_sock, pck_data, pck_size);
     263        1175 :                 if (e) {
     264           0 :                         GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Write error: %s\n", gf_error_to_string(e) ));
     265             :                 }
     266        1175 :                 ctx->nb_bytes_sent += pck_size;
     267             :                 return e;
     268             :         }
     269           0 :         hwf = gf_filter_pck_get_frame_interface(pck);
     270           0 :         if (!hwf) {
     271           0 :                 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] output file handle is not opened, discarding %d bytes\n", pck_size));
     272             :                 return GF_OK;
     273             :         }
     274             : 
     275           0 :         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_WIDTH);
     276           0 :         w = p ? p->value.uint : 0;
     277           0 :         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_HEIGHT);
     278           0 :         h = p ? p->value.uint : 0;
     279           0 :         p = gf_filter_pid_get_property(ctx->pid, GF_PROP_PID_PIXFMT);
     280           0 :         pf = p ? p->value.uint : 0;
     281             : 
     282             :         //get stride/stride_uv with no padding
     283           0 :         stride = stride_uv = 0;
     284           0 :         if (gf_pixel_get_size_info(pf, w, h, NULL, &stride, &stride_uv, &nb_planes, &uv_height) == GF_TRUE) {
     285             :                 u32 i;
     286           0 :                 for (i=0; i<nb_planes; i++) {
     287             :                         u32 j, write_h, lsize;
     288             :                         const u8 *out_ptr;
     289           0 :                         u32 out_stride = i ? stride_uv : stride;
     290           0 :                         e = hwf->get_plane(hwf, i, &out_ptr, &out_stride);
     291           0 :                         if (e) {
     292           0 :                                 GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Failed to fetch plane data from hardware frame, cannot write\n"));
     293           0 :                                 break;
     294             :                         }
     295           0 :                         if (i) {
     296           0 :                                 write_h = uv_height;
     297           0 :                                 lsize = stride_uv;
     298             :                         } else {
     299             :                                 write_h = h;
     300           0 :                                 lsize = stride;
     301             :                         }
     302           0 :                         for (j=0; j<write_h; j++) {
     303           0 :                                 e = gf_sk_send(dst_sock, out_ptr, lsize);
     304           0 :                                 if (e) {
     305           0 :                                         GF_LOG(GF_LOG_ERROR, GF_LOG_NETWORK, ("[SockOut] Write error: %s\n", gf_error_to_string(e) ));
     306             :                                 }
     307           0 :                                 out_ptr += out_stride;
     308           0 :                                 ctx->nb_bytes_sent += lsize;
     309             :                         }
     310             :                 }
     311             :         }
     312             :         return GF_OK;
     313             : }
     314             : 
     315             : 
     316        2545 : static GF_Err sockout_process(GF_Filter *filter)
     317             : {
     318             :         GF_Err e;
     319             :         Bool is_pck_ref = GF_FALSE;
     320             : 
     321             :         GF_FilterPacket *pck;
     322        2545 :         GF_SockOutCtx *ctx = (GF_SockOutCtx *) gf_filter_get_udta(filter);
     323             : 
     324        2545 :         if (!ctx->socket)
     325             :                 return GF_EOS;
     326             : 
     327        2540 :         if (ctx->rate) {
     328        1236 :                 if (!ctx->start_time) ctx->start_time = gf_sys_clock_high_res();
     329             :                 else {
     330        1232 :                         u64 now = gf_sys_clock_high_res() - ctx->start_time;
     331        1232 :                         if (ctx->nb_bytes_sent*8*1000000 > ctx->rate * now) {
     332         833 :                                 u64 diff = ctx->nb_bytes_sent*8*1000000 / ctx->rate - now;
     333         833 :                                 gf_filter_ask_rt_reschedule(filter, (u32) MAX(diff, 1000) );
     334         833 :                                 return GF_OK;
     335         399 :                         } else if (gf_filter_reporting_enabled(filter)) {
     336             :                                 char szMsg[200];
     337           0 :                                 snprintf(szMsg, 199, "Sending at "LLU" kbps\r", ctx->nb_bytes_sent*8*1000/now);
     338           0 :                                 szMsg[199] = 0;
     339           0 :                                 gf_filter_update_status(filter, 0, szMsg);
     340             :                         }
     341             :                 }
     342             :         }
     343             : 
     344        1707 :         if (ctx->listen) {
     345         915 :                 GF_Socket *new_conn=NULL;
     346         915 :                 e = gf_sk_accept(ctx->socket, &new_conn);
     347         915 :                 if ((e==GF_OK) && new_conn) {
     348             :                         GF_SockOutClient *sc;
     349           3 :                         GF_SAFEALLOC(sc, GF_SockOutClient);
     350           3 :                         if (!sc) return GF_OUT_OF_MEM;
     351             :                         
     352           3 :                         sc->socket = new_conn;
     353           3 :                         strcpy(sc->address, "unknown");
     354           3 :                         gf_sk_get_remote_address(new_conn, sc->address);
     355             : 
     356           3 :                         GF_LOG(GF_LOG_INFO, GF_LOG_NETWORK, ("[SockOut] Accepting new connection from %s\n", sc->address));
     357           3 :                         gf_list_add(ctx->clients, sc);
     358           3 :                         ctx->had_clients = GF_TRUE;
     359           3 :                         if (!ctx->pid_started && ctx->pid) {
     360             :                                 GF_FilterEvent evt;
     361           3 :                                 gf_filter_pid_init_play_event(ctx->pid, &evt, ctx->start, ctx->speed, "SockOut");
     362           3 :                                 gf_filter_pid_send_event(ctx->pid, &evt);
     363           3 :                                 ctx->pid_started = GF_TRUE;
     364             :                         }
     365           3 :                         sc->pck_pending = ctx->pck_pending;
     366           3 :                         if (!ctx->nb_pck_processed)
     367           3 :                                 sc->is_tuned = GF_TRUE;
     368             :                 }
     369             :         }
     370        1707 :         if (!ctx->pid) {
     371           6 :                 if (ctx->listen) gf_filter_post_process_task(filter);
     372             :                 return GF_OK;
     373             :         }
     374             : 
     375        1701 :         pck = gf_filter_pid_get_packet(ctx->pid);
     376        1701 :         if (!pck) {
     377          13 :                 if (gf_filter_pid_is_eos(ctx->pid)) {
     378          10 :                         if (ctx->rev_pck) {
     379             :                                 is_pck_ref = GF_TRUE;
     380             :                                 pck = ctx->rev_pck;
     381             :                         } else {
     382          10 :                                 if (!ctx->listen) {
     383           7 :                                         gf_sk_del(ctx->socket);
     384           7 :                                         ctx->socket = NULL;
     385           7 :                                         return GF_EOS;
     386             :                                 }
     387           3 :                                 if (!ctx->ka)
     388             :                                         return GF_EOS;
     389             :                                 //keep alive, ask for real-time reschedule of 100 ms - we should use socket groups and selects !
     390           0 :                                 gf_filter_ask_rt_reschedule(filter, 100000);
     391             :                         }
     392             :                 }
     393           3 :                 if (!pck)
     394             :                         return GF_OK;
     395             :         }
     396             : 
     397        1688 :         if (ctx->pckd.den && !ctx->rev_pck) {
     398         315 :                 u32 pck_idx = ctx->pckd.num;
     399         315 :                 u32 nb_pck = ctx->nb_pckd_wnd * ctx->pckd.den;
     400             : 
     401         315 :                 if (!pck_idx) {
     402         315 :                         if (!ctx->next_pckd_idx) ctx->next_pckd_idx = gf_rand() % ctx->pckd.den;
     403         315 :                         pck_idx = ctx->next_pckd_idx;
     404             :                 }
     405         315 :                 nb_pck += pck_idx;
     406             : 
     407         315 :                 if (nb_pck == 1+ctx->nb_pck_processed) {
     408           1 :                         ctx->nb_pck_processed++;
     409           1 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[SockOut] dropping packet %d per user request\r", ctx->nb_pck_processed));
     410           1 :                         gf_filter_pid_drop_packet(ctx->pid);
     411           1 :                         ctx->next_pckd_idx = 0;
     412           1 :                         ctx->nb_pckd_wnd ++;
     413           1 :                         return GF_OK;
     414             :                 }
     415             :         }
     416        1687 :         if (ctx->pckr.den && !ctx->rev_pck) {
     417         314 :                 u32 pck_idx = ctx->pckr.num;
     418         314 :                 u32 nb_pck = ctx->nb_pckr_wnd * ctx->pckr.den;
     419             : 
     420         314 :                 if (!pck_idx) {
     421         314 :                         if (!ctx->next_pckr_idx) ctx->next_pckr_idx = gf_rand() % ctx->pckr.den;
     422         314 :                         pck_idx = ctx->next_pckr_idx;
     423             :                 }
     424         314 :                 nb_pck += pck_idx;
     425             : 
     426         314 :                 if (nb_pck == 1+ctx->nb_pck_processed) {
     427          35 :                         ctx->rev_pck = pck;
     428          35 :                         gf_filter_pck_ref(&ctx->rev_pck);
     429          35 :                         GF_LOG(GF_LOG_WARNING, GF_LOG_NETWORK, ("[SockOut] Reverting packet %d per user request\r", ctx->nb_pck_processed));
     430          35 :                         ctx->nb_pck_processed++;
     431          35 :                         gf_filter_pid_drop_packet(ctx->pid);
     432          35 :                         pck = gf_filter_pid_get_packet(ctx->pid);
     433          35 :                         if (!pck) return GF_OK;
     434             :                 }
     435             :         }
     436             : 
     437        1657 :         if (ctx->listen) {
     438         903 :                 Bool had_pck_pending = ctx->pck_pending;
     439         903 :                 u32 i, nb_clients = gf_list_count(ctx->clients);
     440         903 :                 u8 dep_flags = gf_filter_pck_get_dependency_flags(pck);
     441             :                 if ((dep_flags & 0x3) == 1) {
     442             :                 }
     443         903 :                 ctx->pck_pending = GF_FALSE;
     444             : 
     445         903 :                 if (!nb_clients) {
     446             :                         //client disconnected, drop packet if needed
     447         517 :                         if (ctx->had_clients && !ctx->kp) {
     448           0 :                                 if (is_pck_ref) {
     449           0 :                                         gf_filter_pck_unref(pck);
     450           0 :                                         ctx->rev_pck = NULL;
     451             :                                 } else {
     452           0 :                                         gf_filter_pid_drop_packet(ctx->pid);
     453             :                                 }
     454             :                         }
     455             :                         return GF_OK;
     456             :                 }
     457         386 :                 for (i=0; i<nb_clients; i++) {
     458         386 :                         GF_SockOutClient *sc = gf_list_get(ctx->clients, i);
     459             :                         if (!sc->is_tuned) {
     460             : 
     461             :                         }
     462         386 :                         if (had_pck_pending && !sc->pck_pending) {
     463           0 :                                 continue;
     464             :                         }
     465         386 :                         sc->pck_pending = GF_FALSE;
     466             : 
     467         386 :                         e = sockout_send_packet(ctx, pck, sc->socket);
     468         386 :                         if (e == GF_BUFFER_TOO_SMALL) {
     469           0 :                                 sc->pck_pending = GF_TRUE;
     470           0 :                                 ctx->pck_pending = GF_TRUE;
     471             :                         }
     472             :                 }
     473         386 :                 if (ctx->pck_pending) return GF_OK;
     474             : 
     475             :         } else {
     476         754 :                 e = sockout_send_packet(ctx, pck, ctx->socket);
     477         754 :                 if (e == GF_BUFFER_TOO_SMALL) return GF_OK;
     478             :         }
     479             : 
     480        1140 :         ctx->nb_pck_processed++;
     481             : 
     482        1140 :         if (is_pck_ref) {
     483           0 :                 gf_filter_pck_unref(pck);
     484           0 :                 ctx->rev_pck = NULL;
     485             :         } else {
     486        1140 :                 gf_filter_pid_drop_packet(ctx->pid);
     487        1140 :                 if (ctx->rev_pck) {
     488          35 :                         e = sockout_send_packet(ctx, ctx->rev_pck, ctx->socket);
     489          35 :                         if (e == GF_BUFFER_TOO_SMALL) return GF_OK;
     490          35 :                         gf_filter_pck_unref(ctx->rev_pck);
     491          35 :                         ctx->rev_pck = NULL;
     492          35 :                         ctx->next_pckr_idx=0;
     493          35 :                         ctx->nb_pckr_wnd++;
     494             :                 }
     495             :         }
     496             :         return GF_OK;
     497             : }
     498             : 
     499        2198 : static GF_FilterProbeScore sockout_probe_url(const char *url, const char *mime)
     500             : {
     501        2198 :         if (!strnicmp(url, "tcp://", 6)) return GF_FPROBE_SUPPORTED;
     502        2192 :         if (!strnicmp(url, "udp://", 6)) return GF_FPROBE_SUPPORTED;
     503             : #ifdef GPAC_HAS_SOCK_UN
     504        2188 :         if (!strnicmp(url, "tcpu://", 7)) return GF_FPROBE_SUPPORTED;
     505        2188 :         if (!strnicmp(url, "udpu://", 7)) return GF_FPROBE_SUPPORTED;
     506             : #endif
     507        2188 :         return GF_FPROBE_NOT_SUPPORTED;
     508             : }
     509             : 
     510             : 
     511             : #define OFFS(_n)        #_n, offsetof(GF_SockOutCtx, _n)
     512             : 
     513             : static const GF_FilterArgs SockOutArgs[] =
     514             : {
     515             :         { OFFS(dst), "URL of destination - see filter help", GF_PROP_NAME, NULL, NULL, 0},
     516             :         { OFFS(sockbuf), "block size used to read file", GF_PROP_UINT, "65536", NULL, GF_FS_ARG_HINT_ADVANCED},
     517             :         { OFFS(port), "default port if not specified", GF_PROP_UINT, "1234", NULL, 0},
     518             :         { OFFS(ifce), "default multicast interface", GF_PROP_NAME, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
     519             :         { OFFS(ext), "file extension of pipe data - see filter help", GF_PROP_STRING, NULL, NULL, 0},
     520             :         { OFFS(mime), "mime type of pipe data - see filter help", GF_PROP_STRING, NULL, NULL, 0},
     521             :         { OFFS(listen), "indicate the output socket works in server mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     522             :         { OFFS(maxc), "max number of concurrent connections", GF_PROP_UINT, "+I", NULL, GF_FS_ARG_HINT_ADVANCED},
     523             :         { OFFS(ka), "keep socket alive if no more connections", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
     524             :         { OFFS(kp), "keep packets in queue if no more clients", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
     525             :         { OFFS(start), "set playback start offset. Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, 0},
     526             :         { OFFS(speed), "set playback speed. If speed is negative and start is 0, start is set to -1", GF_PROP_DOUBLE, "1.0", NULL, 0},
     527             :         { OFFS(rate), "set send rate in bps, disabled by default (as fast as possible)", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
     528             :         { OFFS(pckr), "reverse packet every N - see filter help", GF_PROP_FRACTION, "0/0", NULL, GF_FS_ARG_HINT_EXPERT},
     529             :         { OFFS(pckd), "drop packet every N - see filter help", GF_PROP_FRACTION, "0/0", NULL, GF_FS_ARG_HINT_EXPERT},
     530             :         { OFFS(ttl), "multicast TTL", GF_PROP_UINT, "0", "0-127", GF_FS_ARG_HINT_EXPERT},
     531             :         {0}
     532             : };
     533             : 
     534             : static const GF_FilterCapability SockOutCaps[] =
     535             : {
     536             :         CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
     537             :         CAP_STRING(GF_CAPS_INPUT,GF_PROP_PID_FILE_EXT, "*"),
     538             :         CAP_STRING(GF_CAPS_INPUT,GF_PROP_PID_MIME, "*"),
     539             : };
     540             : 
     541             : 
     542             : GF_FilterRegister SockOutRegister = {
     543             :         .name = "sockout",
     544             :         GF_FS_SET_DESCRIPTION("UDP/TCP output")
     545             : #ifndef GPAC_DISABLE_DOC
     546             :         .help = "This filter handles generic output sockets (mono-directional) in blocking mode only.\n"
     547             :                 "The filter can work in server mode, waiting for source connections, or or in client mode, directly connecting.\n"
     548             :                 "In server mode, the filter can be instructed to keep running at the end of the stream.\n"
     549             :                 "In server mode, the default behavior is to keep input packets when no more clients are connected; "
     550             :                 "this can be adjusted though the [-kp]() option, however there is no realtime regulation of how fast packets are dropped.\n"
     551             :                 "If your sources are not real time, consider adding a real-time scheduler in the chain (cf reframer filter), or set the send [-rate]() option.\n"
     552             :                 "\n"
     553             :                 "- UDP sockets are used for destinations URLs formatted as `udp://NAME`\n"
     554             :                 "- TCP sockets are used for destinations URLs formatted as `tcp://NAME`\n"
     555             : #ifdef GPAC_HAS_SOCK_UN
     556             :                 "- UDP unix domain sockets are used for destinations URLs formatted as `udpu://NAME`\n"
     557             :                 "- TCP unix domain sockets are used for destinations URLs formatted as `tcpu://NAME`\n"
     558             : #else
     559             :                 "Your platform does not supports unix domain sockets"
     560             : #endif
     561             :                 "\n"
     562             :                 "When ports are specified in the URL and the default option separators are used (see `gpac -h doc`), the URL must either:\n"
     563             :                 "- have a trailing '/', eg `udp://localhost:1234/[:opts]`\n"
     564             :                 "- use `gpac` '/', eg `udp://localhost:1234[:gpac:opts]`\n"
     565             :                 "\n"
     566             :                 "The socket output can be configured to drop or revert packet order for test purposes.\n"
     567             :                 "For both mode, a window size in packets is specified as the drop/revert fraction denominator, and the index of the packet to drop/revert is given as the numerator/\n"
     568             :                 "If the numerator is 0, a packet is randomly chosen in that window.\n"
     569             :                 "EX :pckd=4/10\n"\
     570             :                 "This drops every 4th packet of each 10 packet window.\n"
     571             :                 "EX :pckr=0/100\n"\
     572             :                 "This reverts the send order of one random packet in each 100 packet window.\n"
     573             :                 "\n",
     574             : #endif //GPAC_DISABLE_DOC
     575             :         .private_size = sizeof(GF_SockOutCtx),
     576             :         .args = SockOutArgs,
     577             :         SETCAPS(SockOutCaps),
     578             :         .probe_url = sockout_probe_url,
     579             :         .initialize = sockout_initialize,
     580             :         .finalize = sockout_finalize,
     581             :         .configure_pid = sockout_configure_pid,
     582             :         .process = sockout_process
     583             : };
     584             : 
     585             : 
     586        2877 : const GF_FilterRegister *sockout_register(GF_FilterSession *session)
     587             : {
     588        2877 :         return &SockOutRegister;
     589             : }
     590             : 

Generated by: LCOV version 1.13