[multicat-devel] [Git][videolan/multicat][master] Add new smooThS program to minimize jitter.

Christophe Massiot (@cmassiot) gitlab at videolan.org
Mon Sep 11 07:05:45 UTC 2023



Christophe Massiot pushed to branch master at VideoLAN / multicat


Commits:
5e784d4b by Christophe Massiot at 2023-09-11T09:05:00+02:00
Add new smooThS program to minimize jitter.

- - - - -


9 changed files:

- .gitignore
- Makefile
- NEWS
- README
- multilive.c
- + smooths.c
- + ulist.h
- util.c
- util.h


Changes:

=====================================
.gitignore
=====================================
@@ -8,3 +8,4 @@
 /multilive
 /offsets
 /reordertp
+/smooths


=====================================
Makefile
=====================================
@@ -16,12 +16,13 @@ OBJ_OFFSETS = offsets.o util.o
 OBJ_LASTS = lasts.o
 OBJ_MULTICAT_VALIDATE = multicat_validate.o util.o
 OBJ_MULTILIVE = multilive.o util.o
+OBJ_SMOOTHS = smooths.o util.o
 
 PREFIX ?= /usr/local
 BIN = $(DESTDIR)/$(PREFIX)/bin
 MAN = $(DESTDIR)/$(PREFIX)/share/man/man1
 
-all: multicat ingests aggregartp reordertp offsets lasts multicat_validate multilive
+all: multicat ingests aggregartp reordertp offsets lasts multicat_validate multilive smooths
 
 $(OBJ_MULTICAT): Makefile util.h
 $(OBJ_INGESTS): Makefile util.h
@@ -31,6 +32,7 @@ $(OBJ_OFFSETS): Makefile util.h
 $(OBJ_LASTS): Makefile
 $(OBJ_MULTICAT_VALIDATE): Makefile util.h
 $(OBJ_MULTILIVE): Makefile util.h
+$(OBJ_SMOOTHS): Makefile util.h ulist.h
 
 multicat: $(OBJ_MULTICAT)
 	$(CC) $(LDFLAGS) -o $@ $(OBJ_MULTICAT) $(LDLIBS)
@@ -56,17 +58,20 @@ multicat_validate: $(OBJ_MULTICAT_VALIDATE)
 multilive: $(OBJ_MULTILIVE)
 	$(CC) $(LDFLAGS) -o $@ $(OBJ_MULTILIVE) $(LDLIBS)
 
+smooths: $(OBJ_SMOOTHS)
+	$(CC) $(LDFLAGS) -o $@ $(OBJ_SMOOTHS) $(LDLIBS)
+
 clean:
-	-rm -f multicat $(OBJ_MULTICAT) ingests $(OBJ_INGESTS) aggregartp $(OBJ_AGGREGARTP) reordertp $(OBJ_REORDERTP) offsets $(OBJ_OFFSETS) lasts $(OBJ_LASTS) multicat_validate $(OBJ_MULTICAT_VALIDATE) multilive $(OBJ_MULTILIVE)
+	-rm -f multicat $(OBJ_MULTICAT) ingests $(OBJ_INGESTS) aggregartp $(OBJ_AGGREGARTP) reordertp $(OBJ_REORDERTP) offsets $(OBJ_OFFSETS) lasts $(OBJ_LASTS) multicat_validate $(OBJ_MULTICAT_VALIDATE) multilive $(OBJ_MULTILIVE) smooths $(OBJ_SMOOTHS)
 
 install: all
 	@install -d $(BIN)
 	@install -d $(MAN)
-	@install multicat ingests aggregartp reordertp offsets lasts multicat_validate multilive $(BIN)
+	@install multicat ingests aggregartp reordertp offsets lasts multicat_validate multilive smooths $(BIN)
 	@install multicat.1 ingests.1 aggregartp.1 reordertp.1 offsets.1 lasts.1 $(MAN)
 
 uninstall:
-	@rm $(BIN)/multicat $(BIN)/ingests $(BIN)/aggregartp $(BIN)/reordertp $(BIN)/offsets $(BIN)/lasts $(BIN)/multicat_validate $(BIN)/multilive
+	@rm $(BIN)/multicat $(BIN)/ingests $(BIN)/aggregartp $(BIN)/reordertp $(BIN)/offsets $(BIN)/lasts $(BIN)/multicat_validate $(BIN)/multilive $(BIN)/smooths
 	@rm $(MAN)/multicat.1 $(MAN)/ingests.1 $(MAN)/aggregartp.1 $(MAN)/reordertp.1 $(MAN)/offsets.1 $(MAN)/lasts.1
 
 dist:


=====================================
NEWS
=====================================
@@ -1,6 +1,7 @@
 Changes between 2.3 and 2.4:
 ----------------------------
   * Fix multilive
+  * Add new smooths program
 
 Changes between 2.2 and 2.3:
 ----------------------------


=====================================
README
=====================================
@@ -35,6 +35,10 @@ of lost packets via an additional UDP or TCP connection. ReordeRTP can also
 smooth up the reception of a stream from a link that is known to reorder
 and add jitter to packets.
 
+To minimize jitter and please IAT analysers, you can also use smooThS.
+SmooThS reads the RTP timestamp and actively waits for the proper time
+to send the packet.
+
 The multicat suite of applications is very lightweight and designed to
 operate in tight environments. Memory and CPU usages are kept to a minimum,
 and they feature only one thread of execution.
@@ -231,3 +235,15 @@ Running another master on a different machine at a higher priority (who will
 preempt the other master):
 
 multilive -y 1001 @239.255.255.255:1025 239.255.255.255:1025
+
+
+Using smooThS
+=============
+
+SmooThS command line is close to multicat's:
+
+smooths -c /etc/smooths.conf @239.255.255.255:5004
+
+where smooths.conf contains a list of destinations such as:
+
+239.255.255.254:5004


=====================================
multilive.c
=====================================
@@ -40,7 +40,6 @@
 
 #include "util.h"
 
-#define CLOCK_FREQ              UINT64_C(27000000)
 #define DEFAULT_PRIORITY        1
 #define DEFAULT_PERIOD          (CLOCK_FREQ / 5)
 #define DEFAULT_DEAD            5


=====================================
smooths.c
=====================================
@@ -0,0 +1,532 @@
+/*****************************************************************************
+ * smooths.c: smooth a transport stream
+ *****************************************************************************
+ * Copyright (C) 2023 VideoLAN
+ *
+ * Authors: Christophe Massiot <cmassiot at upipe.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/* POLLRDHUP */
+#define _GNU_SOURCE 1
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <syslog.h>
+#include <sys/uio.h>
+#include <limits.h>
+#include <ctype.h>
+
+#ifndef POLLRDHUP
+#   define POLLRDHUP 0
+#endif
+
+#include <bitstream/ietf/rtp.h>
+#include <bitstream/mpeg/ts.h>
+#include <bitstream/mpeg/pes.h>
+
+#include "util.h"
+#include "ulist.h"
+
+#define POLL_TIMEOUT 0 /* non-blocking mode */
+#define DEFAULT_LATENCY (CLOCK_FREQ / 5)
+#define WARN_JITTER (CLOCK_FREQ / 1000)
+#define PACKET_SIZE 1328
+
+/*****************************************************************************
+ * Local declarations
+ *****************************************************************************/
+struct packet {
+    struct uchain uchain;
+    uint8_t p_buffer[PACKET_SIZE];
+};
+
+UBASE_FROM_TO(packet, uchain, uchain, uchain)
+
+struct output {
+    struct uchain uchain;
+    int i_fd;
+    char *psz_uri;
+    bool b_raw_packets;
+    bool b_udp;
+    bool b_found;
+    struct udprawpkt pktheader;
+};
+
+UBASE_FROM_TO(output, uchain, uchain, uchain)
+
+static struct uchain output_list;
+static int i_input_fd;
+
+static volatile sig_atomic_t b_die = 0, b_error = 0, b_reload = 1;
+
+static void usage(void)
+{
+    msg_Raw( NULL, "Usage: smooths [-i <RT priority>] [-l <syslogtag>] [-L <latency>] -c <conf file> <input item>" );
+    msg_Raw( NULL, "    item format: [<connect addr>[:<connect port>]][@[<bind addr][:<bind port>]]" );
+    msg_Raw( NULL, "    latency in 27000000 MHz units" );
+    exit(EXIT_FAILURE);
+}
+
+/*****************************************************************************
+ * udp_*: UDP socket handlers
+ *****************************************************************************/
+static ssize_t udp_Read( void *p_buf, size_t i_len )
+{
+    ssize_t i_ret;
+
+    if ( (i_ret = recv( i_input_fd, p_buf, i_len, MSG_DONTWAIT )) < 0 &&
+         errno != EAGAIN && errno != EWOULDBLOCK )
+    {
+        msg_Err( NULL, "recv error (%s)", strerror(errno) );
+        b_die = b_error = 1;
+        return 0;
+    }
+
+    return i_ret > 0 ? i_ret : 0;
+}
+
+static void udp_ExitRead(void)
+{
+    close( i_input_fd );
+}
+
+static int udp_InitRead( const char *psz_arg )
+{
+    if ( (i_input_fd = OpenSocket( psz_arg, 0, DEFAULT_PORT, 0,
+                                   NULL, NULL, NULL )) < 0 )
+        return -1;
+
+    return 0;
+}
+
+static ssize_t raw_Write( struct output *p_output,
+                          const void *p_buf, size_t i_len )
+{
+#ifndef __APPLE__
+    ssize_t i_ret;
+    struct iovec iov[2];
+
+    #if defined(__FreeBSD__)
+    p_output->pktheader.udph.uh_ulen
+    #else
+    p_output->pktheader.udph.len
+    #endif
+    = htons(sizeof(struct udphdr) + i_len);
+
+    #if defined(__FreeBSD__)
+    p_output->pktheader.iph.ip_len = htons(sizeof(struct udprawpkt) + i_len);
+    #endif
+
+    iov[0].iov_base = &p_output->pktheader;
+    iov[0].iov_len = sizeof(struct udprawpkt);
+
+    iov[1].iov_base = (void *) p_buf;
+    iov[1].iov_len = i_len;
+
+    if ( (i_ret = writev( p_output->i_fd, iov, 2 )) < 0 )
+    {
+        if ( errno == EBADF || errno == ECONNRESET || errno == EPIPE )
+        {
+            msg_Err( NULL, "write error (%s) on output %s", strerror(errno),
+                     p_output->psz_uri );
+            b_die = b_error = 1;
+        }
+        /* otherwise do not set b_die because these errors can be transient */
+        return 0;
+    }
+
+    return i_ret;
+#else
+    return -1;
+#endif
+}
+
+static ssize_t udp_Write( struct output *p_output,
+                          const void *p_buf, size_t i_len )
+{
+    if ( p_output->b_udp )
+    {
+        p_buf += RTP_HEADER_SIZE;
+        i_len -= RTP_HEADER_SIZE;
+    }
+
+    if ( p_output->b_raw_packets )
+        return raw_Write( p_output, p_buf, i_len );
+
+    ssize_t i_ret;
+    if ( (i_ret = send( p_output->i_fd, p_buf, i_len, 0 )) < 0 )
+    {
+        if ( errno == EBADF || errno == ECONNRESET || errno == EPIPE )
+        {
+            msg_Err( NULL, "write error (%s) on output %s", strerror(errno),
+                     p_output->psz_uri );
+            b_die = b_error = 1;
+        }
+        /* otherwise do not set b_die because these errors can be transient */
+        return 0;
+    }
+
+    return i_ret;
+}
+
+static void udp_ExitWrite( struct output *p_output )
+{
+    msg_Info( NULL, "closing %s", p_output->psz_uri );
+    close( p_output->i_fd );
+    free( p_output->psz_uri );
+    free( p_output );
+}
+
+static struct output *udp_InitWrite( const char *psz_arg )
+{
+    struct output *p_output = malloc(sizeof(struct output));
+    struct opensocket_opt opt;
+
+    msg_Info( NULL, "opening %s", psz_arg );
+    memset(&opt, 0, sizeof(struct opensocket_opt));
+    opt.p_raw_pktheader = &p_output->pktheader;
+    opt.pb_raw_packets = &p_output->b_raw_packets;
+    opt.pb_udp = &p_output->b_udp;
+    if ( (p_output->i_fd = OpenSocket( psz_arg, 0, 0, DEFAULT_PORT,
+                                       NULL, NULL, &opt )) > 0 )
+    {
+        p_output->psz_uri = strdup(psz_arg);
+        return p_output;
+    }
+    free(p_output);
+    return NULL;
+}
+
+/*****************************************************************************
+ * config_*: configuration file related functions
+ *****************************************************************************/
+static void config_ReadFile( const char *psz_conf_file )
+{
+    FILE *p_file;
+    char psz_line[2048];
+
+    if ( (p_file = fopen( psz_conf_file, "r" )) == NULL )
+    {
+        msg_Err( NULL, "can't fopen config file %s", psz_conf_file );
+        return;
+    }
+
+    while ( fgets( psz_line, sizeof(psz_line), p_file ) != NULL )
+    {
+        struct uchain *p_uchain;
+        char *psz_parser;
+
+        psz_parser = strpbrk( psz_line, " #\n" );
+        if ( psz_parser != NULL )
+            *psz_parser-- = '\0';
+        while ( psz_parser >= psz_line && isblank( *psz_parser ) )
+            *psz_parser-- = '\0';
+        if ( psz_line[0] == '\0' )
+            continue;
+
+        /* Find out if we already have this output */
+        ulist_foreach (&output_list, p_uchain)
+        {
+            struct output *p_output = output_from_uchain(p_uchain);
+            if (!strcmp(p_output->psz_uri, psz_line))
+            {
+                p_output->b_found = true;
+                break;
+            }
+        }
+        if ( p_uchain != &output_list )
+            continue;
+
+        /* Not found, open it */
+        struct output *p_output = udp_InitWrite( psz_line );
+        if ( p_output == NULL )
+        {
+            msg_Warn( NULL, "couldn't parse %s", psz_line );
+            continue;
+        }
+        p_output->b_found = true;
+        ulist_add(&output_list, output_to_uchain(p_output));
+    }
+
+    fclose( p_file );
+
+    /* Now close outputs that were not in the file, and reset b_found flag */
+    struct uchain *p_uchain, *p_tmp;
+    ulist_delete_foreach (&output_list, p_uchain, p_tmp)
+    {
+        struct output *p_output = output_from_uchain(p_uchain);
+        if (!p_output->b_found)
+        {
+            ulist_delete(p_uchain);
+            udp_ExitWrite(p_output);
+        }
+        else
+            p_output->b_found = false;
+    }
+}
+
+static void config_Free(void)
+{
+    struct uchain *p_uchain, *p_tmp;
+    ulist_delete_foreach (&output_list, p_uchain, p_tmp)
+    {
+        udp_ExitWrite(output_from_uchain(p_uchain));
+    }
+}
+
+/*****************************************************************************
+ * CompareSequences: Compare the sequence numbers from 2 RTP packets
+ *****************************************************************************/
+static int CompareSequences( struct uchain *p_uchain1,
+                             struct uchain *p_uchain2 )
+{
+    struct packet *p_packet1 = packet_from_uchain(p_uchain1);
+    struct packet *p_packet2 = packet_from_uchain(p_uchain2);
+    uint16_t i_seqnum1 = rtp_get_seqnum(p_packet1->p_buffer);
+    uint16_t i_seqnum2 = rtp_get_seqnum(p_packet2->p_buffer);
+
+    int i_diff = i_seqnum1 - i_seqnum2;
+    if (i_diff > 0)
+        return (i_diff < 0x8000) ? i_diff : -i_diff;
+    else if (i_diff < 0)
+        return (i_diff > -0x8000) ? i_diff : -i_diff;
+    else
+        return 0;
+}
+
+/*****************************************************************************
+ * Signal Handler
+ *****************************************************************************/
+static void SigHandler( int i_signal )
+{
+    if ( i_signal != SIGHUP )
+        b_die = b_error = 1;
+    else
+        b_reload = 1;
+}
+
+/*****************************************************************************
+ * Entry point
+ *****************************************************************************/
+int main( int i_argc, char **pp_argv )
+{
+    int i_priority = -1;
+    const char *psz_syslog_tag = NULL;
+    uint64_t i_latency = DEFAULT_LATENCY;
+    const char *psz_conf_file = NULL;
+    int c;
+    struct sigaction sa;
+    sigset_t set;
+    struct uchain packet_list;
+    struct packet *p_packet;
+    uint64_t i_next_stc = UINT64_MAX;
+
+    /* Parse options */
+    while ( (c = getopt( i_argc, pp_argv, "i:l:L:c:h" )) != -1 )
+    {
+        switch ( c )
+        {
+        case 'i':
+            i_priority = strtol( optarg, NULL, 0 );
+            break;
+
+        case 'l':
+            psz_syslog_tag = optarg;
+            break;
+
+        case 'L':
+            i_latency = strtol( optarg, NULL, 0 );
+            break;
+
+        case 'c':
+            psz_conf_file = optarg;
+            break;
+
+        case 'h':
+        default:
+            usage();
+            break;
+        }
+    }
+    if ( optind >= i_argc || psz_conf_file == NULL )
+        usage();
+
+    if ( psz_syslog_tag != NULL )
+        msg_Openlog( psz_syslog_tag, LOG_NDELAY, LOG_USER );
+
+    /* Open sockets */
+    if ( udp_InitRead( pp_argv[optind] ) < 0 )
+    {
+        msg_Err( NULL, "input not found, exiting" );
+        exit(EXIT_FAILURE);
+    }
+    optind++;
+
+    ulist_init(&output_list);
+
+    /* Real-time priority */
+    if ( i_priority > 0 )
+    {
+        struct sched_param param;
+        int i_error;
+
+        memset( &param, 0, sizeof(struct sched_param) );
+        param.sched_priority = i_priority;
+        if ( (i_error = pthread_setschedparam( pthread_self(), SCHED_FIFO,
+                                               &param )) )
+        {
+            msg_Warn( NULL, "couldn't set thread priority: %s",
+                      strerror(i_error) );
+        }
+    }
+
+    /* Set signal handlers */
+    memset( &sa, 0, sizeof(struct sigaction) );
+    sa.sa_handler = SigHandler;
+    sigfillset( &set );
+
+    if ( sigaction( SIGTERM, &sa, NULL ) == -1 ||
+         sigaction( SIGHUP, &sa, NULL ) == -1 ||
+         sigaction( SIGINT, &sa, NULL ) == -1 ||
+         sigaction( SIGPIPE, &sa, NULL ) == -1 )
+    {
+        msg_Err( NULL, "couldn't set signal handler: %s", strerror(errno) );
+        exit(EXIT_FAILURE);
+    }
+
+    /* Main loop */
+    ulist_init(&packet_list);
+    p_packet = malloc( sizeof(struct packet) );
+    uchain_init(packet_to_uchain(p_packet));
+    while ( !b_die )
+    {
+        if ( b_reload )
+        {
+            config_ReadFile( psz_conf_file );
+            b_reload = 0;
+        }
+
+        uint64_t i_stc = wall_Date();
+
+        if ( i_next_stc <= i_stc )
+        {
+            /* Output packet */
+            struct uchain *p_uchain;
+            struct packet *p_current = packet_from_uchain(ulist_pop(&packet_list));
+            ulist_foreach (&output_list, p_uchain)
+            {
+                struct output *output = output_from_uchain(p_uchain);
+                udp_Write( output, p_current->p_buffer, PACKET_SIZE );
+            }
+
+            if ( i_next_stc <= i_stc - WARN_JITTER )
+            {
+                msg_Warn( NULL, "CR was missed by %"PRIu64" us",
+                          (i_stc - i_next_stc) / 27 );
+            }
+
+            /* Now calculate the date of the next packet */
+            if ( ulist_empty(&packet_list) )
+            {
+                i_next_stc = UINT64_MAX;
+            }
+            else
+            {
+                uint32_t i_current_timestamp =
+                    rtp_get_timestamp( p_current->p_buffer );
+                struct packet *p_next =
+                    packet_from_uchain(ulist_peek(&packet_list));
+                uint32_t i_next_timestamp =
+                    rtp_get_timestamp( p_next->p_buffer );
+                uint64_t i_diff =
+                    ((UINT32_MAX + 1 + (uint64_t)i_next_timestamp -
+                      i_current_timestamp) & UINT32_MAX) * 300;
+
+                if ( i_diff > i_latency )
+                {
+                    i_next_stc += i_latency;
+                    msg_Warn( NULL, "resetting CR due to too long delay (%"PRIu64" ms)",
+                              i_diff * 1000 / CLOCK_FREQ );
+                }
+                else
+                {
+                    i_next_stc += i_diff;
+                }
+
+                free(p_current);
+            }
+
+            /* Check if there is not another packet to send */
+            continue;
+        }
+
+        /* Read and queue */
+        ssize_t i_read_size = udp_Read( p_packet->p_buffer, PACKET_SIZE );
+        if ( i_read_size > 0 )
+        {
+            if ( !rtp_check_hdr( p_packet->p_buffer ) )
+            {
+                msg_Warn( NULL, "invalid RTP packet received" );
+                continue;
+            }
+
+            /* Reorder packet if needed */
+            ulist_bubble_reverse(&packet_list, packet_to_uchain(p_packet),
+                                 CompareSequences);
+
+            if ( i_next_stc == UINT64_MAX )
+            {
+                i_next_stc = i_stc + i_latency;
+                msg_Warn( NULL, "resetting CR due to empty buffer" );
+            }
+
+            p_packet = malloc( sizeof(struct packet) );
+            uchain_init(packet_to_uchain(p_packet));
+        }
+    }
+
+    free(p_packet);
+    struct uchain *p_uchain, *p_tmp;
+    ulist_delete_foreach (&packet_list, p_uchain, p_tmp)
+    {
+        free(packet_from_uchain(p_uchain));
+    }
+
+    udp_ExitRead();
+    config_Free();
+
+    if ( psz_syslog_tag != NULL )
+        msg_Closelog();
+
+    return b_error ? EXIT_FAILURE : EXIT_SUCCESS;
+}


=====================================
ulist.h
=====================================
@@ -0,0 +1,406 @@
+/* part of upipe/include/upipe/ubase.h */
+
+/*
+ * Copyright (C) 2012-2019 OpenHeadend S.A.R.L.
+ * Copyright (C) 2020 EasyTools
+ *
+ * Authors: Christophe Massiot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stddef.h>
+
+/** @This marks a function or variable as possibly unused (suppresses compiler
+ * warnings). */
+#define UBASE_UNUSED __attribute__ ((unused))
+
+#ifndef container_of
+/** @This is used to retrieve the private portion of a structure. */
+#   define container_of(ptr, type, member) ({                               \
+        const __typeof__( ((type *)0)->member ) *_mptr = (ptr);             \
+        (type *)( (char *)_mptr - offsetof(type,member) );})
+#endif
+
+/** @This declares two functions dealing with substructures included into a
+ * larger structure.
+ *
+ * @param STRUCTURE name of the larger structure
+ * @param SUBSTRUCT name of the smaller substructure
+ * @param SUBNAME name to use for the functions
+ * (STRUCTURE##_{to,from}_##SUBNAME)
+ * @param SUB name of the @tt{struct SUBSTRUCT} field of @tt{struct STRUCTURE}
+ */
+#define UBASE_FROM_TO(STRUCTURE, SUBSTRUCT, SUBNAME, SUB)                   \
+/** @internal @This returns a pointer to SUBNAME.                           \
+ *                                                                          \
+ * @param STRUCTURE pointer to struct STRUCTURE                             \
+ * @return pointer to struct SUBSTRUCT                                      \
+ */                                                                         \
+static UBASE_UNUSED inline struct SUBSTRUCT *                               \
+    STRUCTURE##_to_##SUBNAME(struct STRUCTURE *s)                           \
+{                                                                           \
+    return &s->SUB;                                                         \
+}                                                                           \
+/** @internal @This returns a pointer to SUBNAME.                           \
+ *                                                                          \
+ * @param sub pointer to struct SUBSTRUCT                                   \
+ * @return pointer to struct STRUCTURE                                      \
+ */                                                                         \
+static UBASE_UNUSED inline struct STRUCTURE *                               \
+    STRUCTURE##_from_##SUBNAME(struct SUBSTRUCT *sub)                       \
+{                                                                           \
+    return container_of(sub, struct STRUCTURE, SUB);                        \
+}
+
+/** @This is designed to chain uref and ubuf in a list. */
+struct uchain {
+    /** pointer to next element */
+    struct uchain *next;
+    /** pointer to previous element */
+    struct uchain *prev;
+};
+
+/** @This initializes a uchain.
+ *
+ * @param uchain pointer to a uchain structure
+ */
+static inline void uchain_init(struct uchain *uchain)
+{
+    uchain->next = uchain->prev = NULL;
+}
+
+
+/* part of upipe/include/upipe/ulist.h */
+
+/*
+ * Copyright (C) 2012-2016 OpenHeadend S.A.R.L.
+ * Copyright (C) 2020 EasyTools
+ *
+ * Authors: Christophe Massiot
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject
+ * to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/** @file
+ * @short Upipe implementation of lists of structures (NOT thread-safe)
+ *
+ * Please note that ulists cannot be assigned, as in:
+ * struct uchain mylist = mystruct->mylist;
+ */
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+/** @This initializes a ulist.
+ *
+ * @param uchain pointer to a ulist
+ */
+static inline void ulist_init(struct uchain *ulist)
+{
+    ulist->next = ulist->prev = ulist;
+}
+
+/** @This checks if the element is the first of the list.
+ *
+ * @param ulist pointer to a ulist
+ * @param element pointer to element
+ * @return true if the element is the first
+ */
+static inline bool ulist_is_first(struct uchain *ulist, struct uchain *element)
+{
+    return element->prev == ulist;
+}
+
+/** @This checks if the element is the last of the list.
+ *
+ * @param ulist pointer to a ulist
+ * @param element pointer to element
+ * @return true if the element is the last
+ */
+static inline bool ulist_is_last(struct uchain *ulist, struct uchain *element)
+{
+    return element->next == ulist;
+}
+
+/** @This checks if the element is in a list.
+ *
+ * @param element pointer to element
+ * @return true if the element is in the list
+ */
+static inline bool ulist_is_in(struct uchain *element)
+{
+    return !(element->next == NULL);
+}
+
+/** @This checks if the list is empty.
+ *
+ * @param ulist pointer to a ulist
+ * @return true if the list is empty
+ */
+static inline bool ulist_empty(struct uchain *ulist)
+{
+    return ulist_is_last(ulist, ulist);
+}
+
+/** @This calculates the depth of the list (suboptimal, only for debug).
+ *
+ * @param ulist pointer to a ulist
+ * @return the depth of the list
+ */
+static inline size_t ulist_depth(struct uchain *ulist)
+{
+    struct uchain *uchain = ulist->next;
+    size_t depth = 0;
+    while (uchain != ulist) {
+        depth++;
+        uchain = uchain->next;
+    }
+    return depth;
+}
+
+/** @This adds a new element to a ulist at the given position.
+ *
+ * @param element pointer to element to add
+ * @param prev pointer to previous element
+ * @param next pointer to next element
+ */
+static inline void ulist_insert(struct uchain *prev, struct uchain *next,
+                                struct uchain *element)
+{
+    next->prev = element;
+    element->next = next;
+    element->prev = prev;
+    prev->next = element;
+}
+
+/** @This deletes an element from a ulist.
+ *
+ * @param element pointer to element to delete
+ */
+static inline void ulist_delete(struct uchain *element)
+{
+    element->prev->next = element->next;
+    element->next->prev = element->prev;
+    uchain_init(element);
+}
+
+/** @This adds a new element at the end.
+ *
+ * @param ulist pointer to a ulist structure
+ * @param element pointer to element to add
+ */
+static inline void ulist_add(struct uchain *ulist, struct uchain *element)
+{
+    ulist_insert(ulist->prev, ulist, element);
+}
+
+/** @This adds a new element at the beginning.
+ *
+ * @param ulist pointer to a ulist
+ * @param element pointer to the first element to add
+ */
+static inline void ulist_unshift(struct uchain *ulist, struct uchain *element)
+{
+    ulist_insert(ulist, ulist->next, element);
+}
+
+/** @This returns a pointer to the first element of the list (without
+ * removing it).
+ *
+ * @param ulist pointer to a ulist
+ * @return pointer to the first element
+ */
+static inline struct uchain *ulist_peek(struct uchain *ulist)
+{
+    if (ulist_empty(ulist))
+        return NULL;
+    return ulist->next;
+}
+
+/** @This returns a pointer to the last element of the list (without
+ * removing it).
+ *
+ * @param ulist pointer to a ulist
+ * @return pointer to the last element or NULL if the list is empty
+ */
+static inline struct uchain *ulist_peek_last(struct uchain *ulist)
+{
+    if (ulist_empty(ulist))
+        return NULL;
+    return ulist->prev;
+}
+
+/** @This returns a pointer to the first element of the list and removes
+ * it.
+ *
+ * @param ulist pointer to a ulist
+ * @return pointer to the first element
+ */
+static inline struct uchain *ulist_pop(struct uchain *ulist)
+{
+    if (ulist_empty(ulist))
+        return NULL;
+    struct uchain *element = ulist->next;
+    ulist->next = element->next;
+    ulist->next->prev = ulist;
+    uchain_init(element);
+    return element;
+}
+
+/** @This return a pointer to the element at the given index.
+ *
+ * @param ulist pointer to a ulist
+ * @param index the index in the list
+ * @return pointer to the element at index
+ */
+static inline struct uchain *ulist_at(struct uchain *ulist, unsigned index)
+{
+    struct uchain *uchain;
+    for (uchain = ulist->next; uchain != ulist; uchain = uchain->next)
+        if (!index--)
+            return uchain;
+    return NULL;
+}
+
+/** @This sorts through a list using a comparison function.
+ *
+ * @param ulist pointer to a ulist
+ * @param compar comparison function accepting two uchains as arguments
+ */
+static inline void ulist_sort(struct uchain *ulist,
+        int (*compar)(struct uchain **, struct uchain **))
+{
+    size_t depth = ulist_depth(ulist);
+    size_t i;
+    if (!depth)
+        return;
+
+    struct uchain *array[depth];
+    for (i = 0; i < depth; i++)
+        array[i] = ulist_pop(ulist);
+
+    qsort(array, depth, sizeof(struct uchain *),
+            (int (*)(const void *, const void *))compar);
+
+    for (i = 0; i < depth; i++)
+        ulist_add(ulist, array[i]);
+}
+
+/** @This walks through a ulist. Please note that the list may not be altered
+ * during the walk (see @ref #ulist_delete_foreach).
+ *
+ * @param ulist pointer to a ulist
+ * @param uchain iterator
+ */
+#define ulist_foreach(ulist, uchain)                                        \
+    for ((uchain) = (ulist)->next; (uchain) != (ulist);                     \
+         (uchain) = (uchain)->next)
+
+/** @This walks through a ulist in reverse. Please note that the list may not be altered
+ * during the walk (see @ref #ulist_delete_foreach_reverse).
+ *
+ * @param ulist pointer to a ulist
+ * @param uchain iterator
+ */
+#define ulist_foreach_reverse(ulist, uchain)                                \
+    for ((uchain) = (ulist)->prev; (uchain) != (ulist);                     \
+         (uchain) = (uchain)->prev)
+
+/** @This walks through a ulist. This variant allows to remove the current
+ * element safely.
+ *
+ * @param ulist pointer to a ulist
+ * @param uchain iterator
+ * @param uchain_tmp uchain to use for temporary storage
+ */
+#define ulist_delete_foreach(ulist, uchain, uchain_tmp)                     \
+    for ((uchain) = (ulist)->next, (uchain_tmp) = (uchain)->next;           \
+         (uchain) != (ulist);                                               \
+         (uchain) = (uchain_tmp), (uchain_tmp) = (uchain)->next)
+
+/** @This walks through a ulist in reverse. This variant allows to remove the current
+ * element safely.
+ *
+ * @param ulist pointer to a ulist
+ * @param uchain iterator
+ * @param uchain_tmp uchain to use for temporary storage
+ */
+#define ulist_delete_foreach_reverse(ulist, uchain, uchain_tmp)             \
+    for ((uchain) = (ulist)->prev, (uchain_tmp) = (uchain)->prev;           \
+         (uchain) != (ulist);                                               \
+         (uchain) = (uchain_tmp), (uchain_tmp) = (uchain)->prev)
+
+/** @This inserts a new element into a list using a comparison function.
+ *
+ * @param ulist pointer to a ulist
+ * @param element element to insert
+ * @param compar comparison function accepting two uchains as arguments
+ */
+static inline void ulist_bubble(struct uchain *ulist, struct uchain *element,
+        int (*compar)(struct uchain *, struct uchain *))
+{
+    struct uchain *uchain;
+    ulist_foreach (ulist, uchain) {
+        if (compar(element, uchain) < 0) {
+            ulist_insert(uchain->prev, uchain, element);
+            return;
+        }
+    }
+    ulist_insert(ulist->prev, ulist, element);
+}
+
+/** @This inserts a new element into a list using a comparison function, from
+ * the end.
+ *
+ * @param ulist pointer to a ulist
+ * @param element element to insert
+ * @param compar comparison function accepting two uchains as arguments
+ */
+static inline void ulist_bubble_reverse(struct uchain *ulist,
+        struct uchain *element,
+        int (*compar)(struct uchain *, struct uchain *))
+{
+    struct uchain *uchain;
+    ulist_foreach_reverse (ulist, uchain) {
+        if (compar(element, uchain) > 0) {
+            ulist_insert(uchain, uchain->next, element);
+            return;
+        }
+    }
+    ulist_insert(ulist, ulist->next, element);
+}


=====================================
util.c
=====================================
@@ -569,8 +569,15 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
         pb_tcp = &b_tcp;
     *pb_tcp = false;
 
-    if ( p_opt != NULL && p_opt->pb_multicast != NULL )
-        *p_opt->pb_multicast = false;
+    if ( p_opt )
+    {
+        if ( p_opt->pb_multicast )
+            *p_opt->pb_multicast = false;
+        if ( p_opt->pb_raw_packets )
+            *p_opt->pb_raw_packets = false;
+        if ( p_opt->pb_udp )
+            *p_opt->pb_udp = false;
+    }
 
     psz_token2 = strrchr( psz_arg, ',' );
     if ( psz_token2 )
@@ -659,6 +666,11 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
                 i_tos = strtol( ARG_OPTION("tos="), NULL, 0 );
             else if ( IS_OPTION("tcp") )
                 *pb_tcp = true;
+            else if ( IS_OPTION("udp") )
+            {
+                if ( p_opt && p_opt->pb_udp )
+                    *p_opt->pb_udp = true;
+            }
             else if ( IS_OPTION("srcaddr=") )
             {
                 char *option = config_stropt( ARG_OPTION("srcaddr=") );
@@ -712,12 +724,14 @@ int OpenSocket( const char *_psz_arg, int i_ttl, uint16_t i_bind_port,
     /* Socket configuration */
     if ( i_fd < 0 )
     {
-        if (b_raw_packets && b_host)
+        if (b_raw_packets && i_raw_srcaddr != INADDR_ANY && b_host)
         { 
             RawFillHeaders(p_opt->p_raw_pktheader,
                 i_raw_srcaddr, connect_addr.sin.sin_addr.s_addr, i_raw_srcport,
                 ntohs(connect_addr.sin.sin_port), i_ttl, i_tos, 0);
             i_fd = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
+            if ( p_opt->pb_raw_packets != NULL )
+                *p_opt->pb_raw_packets = true;
 #ifdef __FreeBSD__
             if ( setsockopt( i_fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1 )
             {


=====================================
util.h
=====================================
@@ -40,6 +40,7 @@
 #define DEFAULT_ROTATE_OFFSET UINT64_C(0)
 #define TS_SIZE 188
 #define RTP_HEADER_SIZE 12
+#define CLOCK_FREQ UINT64_C(27000000)
 
 #define VERB_DBG  3
 #define VERB_INFO 2
@@ -76,6 +77,8 @@ struct udprawpkt {
  struct opensocket_opt {
     struct udprawpkt *p_raw_pktheader;
     bool *pb_multicast;
+    bool *pb_raw_packets;
+    bool *pb_udp;
  };
 
 



View it on GitLab: https://code.videolan.org/videolan/multicat/-/commit/5e784d4b0436143ae63f1f2c38f30f027fa9a776

-- 
View it on GitLab: https://code.videolan.org/videolan/multicat/-/commit/5e784d4b0436143ae63f1f2c38f30f027fa9a776
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the multicat-devel mailing list