Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  satip.c   Sprache: C

 

/*****************************************************************************
 * satip.c: SAT>IP input module
 *****************************************************************************
 * Copyright © 2016 VLC authors and VideoLAN
 * Copyright © 2016 jusst technologies GmbH
 * Copyright © 2016 Videolabs SAS
 * Copyright © 2016 Julian Scheel
 *
 * Authors: Julian Scheel <julian@jusst.de>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 *****************************************************************************/


#include "config.h"

#include <unistd.h>
#include <ctype.h>
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif

#include <vlc_common.h>
#include <vlc_threads.h>
#include <vlc_poll.h>
#include <vlc_plugin.h>
#include <vlc_access.h>
#include <vlc_network.h>
#include <vlc_block.h>
#include <vlc_queue.h>
#include <vlc_rand.h>
#include <vlc_url.h>
#include <vlc_interrupt.h>

#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <fcntl.h>
#include <errno.h>
#include <assert.h>

#define RTSP_DEFAULT_PORT 554
#define RTSP_RECEIVE_BUFFER 2048
#define RTP_HEADER_SIZE 12
#define VLEN 100
#define KEEPALIVE_INTERVAL 60
#define KEEPALIVE_MARGIN 5

#define SATIP_USERAGENT "LibVLC/" VERSION " (SatIP)"

static int satip_open(vlc_object_t *);
static void satip_close(vlc_object_t *);

#define MULTICAST_TEXT N_("Request multicast stream")
#define MULTICAST_LONGTEXT N_("Request server to send stream as multicast")

#define SATIP_HOST_TEXT N_("Host")

vlc_module_begin()
    set_shortname("satip")
    set_description( N_("SAT>IP Receiver Plugin") )
    set_capability("access", 201)
    set_callbacks(satip_open, satip_close)
    set_subcategory(SUBCAT_INPUT_ACCESS)
    add_obsolete_integer("satip-buffer") /* obsolete since 4.0.0 */
    add_bool("satip-multicast", false, MULTICAST_TEXT, MULTICAST_LONGTEXT)
    add_string("satip-host", "", SATIP_HOST_TEXT, NULL)
    change_safe()
    add_shortcut("rtsp", "satip")
vlc_module_end()

enum rtsp_state {
    RTSP_IDLE,
    RTSP_DESCRIBE,
    RTSP_SETUP,
    RTSP_PLAY,
    RTSP_RUNNING
};

enum rtsp_result {
    RTSP_RESULT_OK = 200,
};

#define UDP_ADDRESS_LEN 16
typedef struct
{
    char *content_base;
    char *control;
    char session_id[64];
    uint16_t stream_id;
    int keepalive_interval;

    char udp_address[UDP_ADDRESS_LEN];
    uint16_t udp_port;

    int tcp_sock;
    int udp_sock;
    int rtcp_sock;

    enum rtsp_state state;
    int cseq;

    vlc_queue_t queue;
    vlc_thread_t thread;
    uint16_t last_seq_nr;

    bool woken;
} access_sys_t;

VLC_FORMAT(3, 4)
static void net_Printf(stream_t *access, int fd, const char *fmt, ...)
{
    va_list ap;
    char *str;
    int val;

    va_start(ap, fmt);
    val = vasprintf(&str, fmt, ap);
    va_end(ap);

    if (val >= 0) {
        net_Write(access, fd, str, val);
        free(str);
    }
}

static void parse_session(char *request_line, char *session, unsigned max, int *timeout) {
    char *state;
    char *tok;

    tok = strtok_r(request_line, ";", &state);
    if (tok == NULL)
        return;
    memcpy(session, tok, __MIN(strlen(tok), max - 1));

    while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
        if (strncmp(tok, "timeout=", 8) == 0) {
            *timeout = atoi(tok + 8);
            if (*timeout > 5)
                *timeout -= KEEPALIVE_MARGIN;
            else if (*timeout > 0)
                *timeout = 1;
        }
    }
}

static int parse_port(char *str, uint16_t *port)
{
    int p = atoi(str);
    if (p < 0 || p > UINT16_MAX)
        return VLC_EINVAL;

    *port = p;

    return 0;
}

static int parse_transport(stream_t *access, char *request_line) {
    access_sys_t *sys = access->p_sys;
    char *state;
    char *tok;
    int err;

    tok = strtok_r(request_line, ";", &state);
    if (tok == NULL || strncmp(tok, "RTP/AVP", 7) != 0)
        return VLC_EGENERIC;

    tok = strtok_r(NULL, ";", &state);
    if (tok == NULL || strncmp(tok, "multicast", 9) != 0)
        return 0;

    while ((tok = strtok_r(NULL, ";", &state)) != NULL) {
        if (strncmp(tok, "destination=", 12) == 0) {
            memcpy(sys->udp_address, tok + 12, __MIN(strlen(tok + 12), UDP_ADDRESS_LEN - 1));
        } else if (strncmp(tok, "port=", 5) == 0) {
            char port[6];
            char *end;

            memset(port, 0x00, 6);
            memcpy(port, tok + 5, __MIN(strlen(tok + 5), 5));
            if ((end = strstr(port, "-")) != NULL)
                *end = '\0';
            err = parse_port(port, &sys->udp_port);
            if (err)
                return err;
        }
    }

    return 0;
}

/*
 * Semi-interruptible net_Gets replacement.
 * If an interruption is occurring it will fallback to non-interruptible read
 * with a given timeout before it returns.
 *
 * interrupted: Informs the caller whether an interrupt occurred or not
 */

static char *net_readln_timeout(vlc_object_t *obj, int fd, int timeout, bool *interrupted)
{
    char *buf = NULL;
    size_t size = 0, len = 0;
    bool intr = false;

    for (;;)
    {
        if (len == size)
        {
            if (unlikely(size >= (1 << 16)))
            {
                errno = EMSGSIZE;
                goto error; /* put sane buffer size limit */
            }

            char *newbuf = realloc(buf, size + 1024);
            if (unlikely(newbuf == NULL))
                goto error;
            buf = newbuf;
            size += 1024;
        }
        assert(len < size);

        ssize_t val = 0;
        if (!intr) {
            val = vlc_recv_i11e(fd, buf + len, size - len, MSG_PEEK);
            if (val <= 0 && errno == EINTR) {
                intr = true;
                if (interrupted)
                    *interrupted = true;
                continue;
            }
            if (val <= 0)
                goto error;
        } else {
            struct pollfd pfd = {
                .fd = fd,
                .events = POLLIN,
            };
            int ret;

            while((ret = poll(&pfd, 1, timeout)) < 0)
                ;

            val = recv(fd, buf + len, size - len, MSG_PEEK);
            if (val <= 0)
                goto error;
        }

        char *end = memchr(buf + len, '\n', val);
        if (end != NULL)
            val = (end + 1) - (buf + len);
        if (recv(fd, buf + len, val, 0) != val)
            goto error;
        len += val;
        if (end != NULL)
            break;
    }

    assert(len > 0);
    buf[--len] = '\0';
    if (len > 0 && buf[--len] == '\r')
        buf[len] = '\0';
    return buf;
error:
    msg_Err(obj, "read error: %s", vlc_strerror_c(errno));
    free(buf);
    return NULL;
}

#define skip_whitespace(x) while(*x == ' ') x++
static enum rtsp_result rtsp_handle(stream_t *access, bool *interrupted) {
    access_sys_t *sys = access->p_sys;
    uint8_t buffer[512];
    int rtsp_result = 0;
    bool have_header = false;
    size_t content_length = 0;
    size_t read = 0;
    char *in, *val;

    /* Parse header */
    while (!have_header) {
        in = net_readln_timeout(VLC_OBJECT(access), sys->tcp_sock, 5000,
                interrupted);
        if (in == NULL)
            break;

        if (strncmp(in, "RTSP/1.0 ", 9) == 0) {
            rtsp_result = atoi(in + 9);
        } else if (strncmp(in, "Content-Base:", 13) == 0) {
            free(sys->content_base);

            val = in + 13;
            skip_whitespace(val);

            sys->content_base = strdup(val);
        } else if (strncmp(in, "Content-Length:", 15) == 0) {
            val = in + 16;
            skip_whitespace(val);

            content_length = atoi(val);
        } else if (strncmp("Session:", in, 8) == 0) {
            val = in + 8;
            skip_whitespace(val);

            parse_session(val, sys->session_id, 64, &sys->keepalive_interval);
        } else if (strncmp("Transport:", in, 10) == 0) {
            val = in + 10;
            skip_whitespace(val);

            if (parse_transport(access, val) != 0) {
                rtsp_result = VLC_EGENERIC;
                break;
            }
        } else if (strncmp("com.ses.streamID:", in, 17) == 0) {
            val = in + 17;
            skip_whitespace(val);

            sys->stream_id = atoi(val);
        } else if (in[0] == '\0') {
            have_header = true;
        }

        free(in);
    }

    /* Discard further content */
    while (content_length > 0 &&
            (read = net_Read(access, sys->tcp_sock, buffer, __MIN(sizeof(buffer), content_length))))
        content_length -= read;

    return rtsp_result;
}

#ifdef HAVE_RECVMMSG
static void satip_cleanup_blocks(void *data)
{
    block_t **input_blocks = data;

    for (size_t i = 0; i < VLEN; i++)
        if (input_blocks[i] != NULL)
            block_Release(input_blocks[i]);
}
#endif

static int check_rtp_seq(stream_t *access, block_t *block)
{
    access_sys_t *sys = access->p_sys;
    uint16_t seq_nr = block->p_buffer[2] << 8 | block->p_buffer[3];

    if (seq_nr == sys->last_seq_nr) {
        msg_Warn(access, "Received duplicate packet (seq_nr=%"PRIu16")", seq_nr);
        return VLC_EGENERIC;
    } else if (seq_nr < (uint16_t)(sys->last_seq_nr + 1)) {
        msg_Warn(access, "Received out of order packet (seq_nr=%"PRIu16" < %"PRIu16")",
                seq_nr, sys->last_seq_nr);
        return VLC_EGENERIC;
    } else if (++sys->last_seq_nr > 1 && seq_nr > sys->last_seq_nr) {
        msg_Warn(access, "Gap in seq_nr (%"PRIu16" > %"PRIu16"), probably lost a packet",
                seq_nr, sys->last_seq_nr);
    }

    sys->last_seq_nr = seq_nr;
    return 0;
}

static void satip_teardown(void *data) {
    stream_t *access = data;
    access_sys_t *sys = access->p_sys;
    int ret;

    if (sys->tcp_sock > 0) {
        if (sys->session_id[0] > 0) {
            char discard_buf[32];
            struct pollfd pfd = {
                .fd = sys->tcp_sock,
                .events = POLLOUT,
            };
            char *msg;

            int len = asprintf(&msg, "TEARDOWN %s RTSP/1.0\r\n"
                    "CSeq: %d\r\n"
                    "Session: %s\r\n\r\n",
                    sys->control, sys->cseq++, sys->session_id);
            if (len < 0)
                return;

            /* make socket non-blocking, to avoid blocking when output buffer
             * has not enough space */

#ifndef _WIN32
            fcntl(sys->tcp_sock, F_SETFL, fcntl(sys->tcp_sock, F_GETFL) | O_NONBLOCK);
#else
            ioctlsocket(sys->tcp_sock, FIONBIO, &(unsigned long){ 1 });
#endif

            for (int sent = 0; sent < len;) {
                ret = poll(&pfd, 1, 5000);
                if (ret == 0) {
                    msg_Err(access, "Timed out sending RTSP teardown\n");
                    free(msg);
                    return;
                }

                ret = vlc_send(sys->tcp_sock, msg + sent, len - sent, 0);
                if (ret < 0) {
                    msg_Err(access, "Failed to send RTSP teardown: %d\n", ret);
                    free(msg);
                    return;
                }
                sent += ret;
            }
            free(msg);

            if (rtsp_handle(access, NULL) != RTSP_RESULT_OK) {
                msg_Err(access, "Failed to teardown RTSP session");
                return;
            }

            /* Some SATIP servers send a few empty extra bytes after TEARDOWN.
             * Try to read them, to avoid a TCP socket reset */

            while (recv(sys->tcp_sock, discard_buf, sizeof(discard_buf), 0) > 0);

            /* Extra sleep for compatibility with some satip servers, that
             * can't handle new sessions right after teardown */

            vlc_tick_sleep(VLC_TICK_FROM_MS(150));
        }
    }
}

#define RECV_TIMEOUT VLC_TICK_FROM_SEC(2)
static void *satip_thread(void *data) {
    stream_t *access = data;
    access_sys_t *sys = access->p_sys;
    int sock = sys->udp_sock;
    vlc_tick_t last_recv = vlc_tick_now();
    ssize_t len;
    vlc_tick_t next_keepalive = vlc_tick_now() + vlc_tick_from_sec(sys->keepalive_interval);

    vlc_thread_set_name("vlc-satip");

#ifdef HAVE_RECVMMSG
    struct mmsghdr msgs[VLEN];
    struct iovec iovecs[VLEN];
    block_t *input_blocks[VLEN];
    int retval;

    for (size_t i = 0; i < VLEN; i++) {
        memset(&msgs[i], 0, sizeof (msgs[i]));
        msgs[i].msg_hdr.msg_iov = &iovecs[i];
        msgs[i].msg_hdr.msg_iovlen = 1;
        iovecs[i].iov_base = NULL;
        iovecs[i].iov_len = RTSP_RECEIVE_BUFFER;
        input_blocks[i] = NULL;
    }
#else
    struct pollfd ufd;

    ufd.fd = sock;
    ufd.events = POLLIN;
#endif

    while (last_recv > vlc_tick_now() - RECV_TIMEOUT) {
#ifdef HAVE_RECVMMSG
        for (size_t i = 0; i < VLEN; i++) {
            if (input_blocks[i] != NULL)
                continue;

            input_blocks[i] = block_Alloc(RTSP_RECEIVE_BUFFER);
            if (unlikely(input_blocks[i] == NULL))
                break;

            iovecs[i].iov_base = input_blocks[i]->p_buffer;
        }

        vlc_cleanup_push(satip_cleanup_blocks, input_blocks);
        retval = recvmmsg(sock, msgs, VLEN, MSG_WAITFORONE, NULL);
        vlc_cleanup_pop();
        if (retval == -1)
            continue;

        last_recv = vlc_tick_now();
        for (int i = 0; i < retval; ++i) {
            block_t *block = input_blocks[i];

            len = msgs[i].msg_len;
            if (len < RTP_HEADER_SIZE || check_rtp_seq(access, block))
                continue;

            block->p_buffer += RTP_HEADER_SIZE;
            block->i_buffer = len - RTP_HEADER_SIZE;
            vlc_queue_Enqueue(&sys->queue, block);
            input_blocks[i] = NULL;
        }
#else
        if (poll(&ufd, 1, 20) == -1)
            continue;

        block_t *block = block_Alloc(RTSP_RECEIVE_BUFFER);
        if (block == NULL) {
            msg_Err(access, "Failed to allocate memory for input buffer");
            break;
        }

        block_cleanup_push(block);
        len = recv(sock, block->p_buffer, RTSP_RECEIVE_BUFFER, 0);
        vlc_cleanup_pop();

        if (len < RTP_HEADER_SIZE) {
            block_Release(block);
            continue;
        }

        if (check_rtp_seq(access, block)) {
            block_Release(block);
            continue;
        }
        last_recv = vlc_tick_now();
        block->p_buffer += RTP_HEADER_SIZE;
        block->i_buffer = len - RTP_HEADER_SIZE;
        vlc_queue_Enqueue(&sys->queue, block);
#endif

        if (sys->keepalive_interval > 0 && vlc_tick_now() > next_keepalive) {
            net_Printf(access, sys->tcp_sock,
                    "OPTIONS %s RTSP/1.0\r\n"
                    "CSeq: %d\r\n"
                    "Session: %s\r\n\r\n",
                    sys->control, sys->cseq++, sys->session_id);
            if (rtsp_handle(access, NULL) != RTSP_RESULT_OK)
                msg_Warn(access, "Failed to keepalive RTSP session");

            next_keepalive = vlc_tick_now() + vlc_tick_from_sec(sys->keepalive_interval);
        }
    }

#ifdef HAVE_RECVMMSG
    satip_cleanup_blocks(input_blocks);
#endif
    msg_Dbg(access, "timed out waiting for data...");
    vlc_queue_Kill(&sys->queue, &sys->woken);
    return NULL;
}

static block_t* satip_block(stream_t *access, bool *restrict eof) {
    access_sys_t *sys = access->p_sys;
    block_t *block = vlc_queue_DequeueKillable(&sys->queue, &sys->woken);

    if (block == NULL)
        *eof = true;

    return block;
}

static int satip_control(stream_t *access, int i_query, va_list args) {
    bool *pb_bool;

    switch(i_query)
    {
        case STREAM_CAN_CONTROL_PACE:
        case STREAM_CAN_SEEK:
        case STREAM_CAN_PAUSE:
            pb_bool = va_arg(args, bool *);
            *pb_bool = false;
            break;

        case STREAM_GET_PTS_DELAY:
            *va_arg(args, vlc_tick_t *) =
                VLC_TICK_FROM_MS(var_InheritInteger(access, "live-caching"));
            break;

        default:
            return VLC_EGENERIC;

    }
    return VLC_SUCCESS;
}

/* Bind two adjacent free ports, of which the first one is even (for RTP data)
 * and the second is odd (RTCP). This is a requirement of the satip
 * specification */

static int satip_bind_ports(stream_t *access)
{
    access_sys_t *sys = access->p_sys;
    uint8_t rnd;

    vlc_rand_bytes(&rnd, 1);
    sys->udp_port = 9000 + (rnd * 2); /* randomly chosen, even start point */
    while (sys->udp_sock < 0) {
        sys->udp_sock = net_OpenDgram(access, "0.0.0.0", sys->udp_port, NULL,
                0, IPPROTO_UDP);
        if (sys->udp_sock < 0) {
            if (sys->udp_port == 65534)
                break;

            sys->udp_port += 2;
            continue;
        }

        sys->rtcp_sock = net_OpenDgram(access, "0.0.0.0", sys->udp_port + 1, NULL,
                0, IPPROTO_UDP);
        if (sys->rtcp_sock < 0) {
            close(sys->udp_sock);
            sys->udp_port += 2;
            continue;
        }
    }

    if (sys->udp_sock < 0) {
        msg_Err(access, "Could not open two adjacent ports for RTP and RTCP data");
        return VLC_EGENERIC;
    }

    return 0;
}

static int satip_open(vlc_object_t *obj)
{
    stream_t *access = (stream_t *)obj;
    access_sys_t *sys;
    vlc_url_t url;

    bool multicast = var_InheritBool(access, "satip-multicast");

    access->p_sys = sys = vlc_obj_calloc(obj, 1, sizeof(*sys));
    if (sys == NULL)
        return VLC_ENOMEM;

    msg_Dbg(access, "try to open '%s'", access->psz_url);

    char *psz_host = var_InheritString(access, "satip-host");

    sys->udp_sock = -1;
    sys->rtcp_sock = -1;
    sys->tcp_sock = -1;

    /* convert url to lowercase, some famous m3u playlists for satip contain
     * uppercase parameters while most (all?) satip servers do only understand
     * parameters matching lowercase spelling as defined in the specification
     * */

    char *psz_lower_url = strdup(access->psz_url);
    if (psz_lower_url == NULL)
    {
        free( psz_host );
        return VLC_ENOMEM;
    }

    for (unsigned i = 0; i < strlen(psz_lower_url); i++)
        psz_lower_url[i] = tolower(psz_lower_url[i]);

    vlc_UrlParse(&url, psz_lower_url);
    if (url.i_port <= 0)
        url.i_port = RTSP_DEFAULT_PORT;
    if (psz_host == NULL && url.psz_host )
        psz_host = strdup(url.psz_host);
    if (psz_host == NULL )
        goto error;

    if (url.psz_host == NULL || url.psz_host[0] == '\0')
    {
        msg_Dbg(access, "malformed URL: %s", psz_lower_url);
        goto error;
    }

    msg_Dbg(access, "connect to host '%s'", psz_host);
    sys->tcp_sock = net_Connect(access, psz_host, url.i_port, SOCK_STREAM, 0);
    if (sys->tcp_sock < 0) {
        msg_Err(access, "Failed to connect to RTSP server %s:%d",
                psz_host, url.i_port);
        goto error;
    }
    setsockopt (sys->tcp_sock, SOL_SOCKET, SO_KEEPALIVE, &(int){ 1 }, sizeof (int));

    if (asprintf(&sys->content_base, "rtsp://%s:%d/", psz_host,
             url.i_port) < 0) {
        sys->content_base = NULL;
        goto error;
    }

    sys->last_seq_nr = 0;
    sys->keepalive_interval = (KEEPALIVE_INTERVAL - KEEPALIVE_MARGIN);

    vlc_url_t setup_url = url;

    // substitute "sat.ip" if present with an the host IP that was fetched during device discovery
    if( !strncasecmp( setup_url.psz_host, "sat.ip", 6 ) ) {
        setup_url.psz_host = psz_host;
    }

    // reverse the satip protocol trick, as SAT>IP believes to be RTSP
    if( setup_url.psz_protocol == NULL ||
        strncasecmp( setup_url.psz_protocol, "satip", 5 ) == 0 )
    {
        setup_url.psz_protocol = (char *)"rtsp";
    }

    char *psz_setup_url = vlc_uri_compose(&setup_url);
    if( psz_setup_url == NULL )
        goto error;

    if (multicast) {
        net_Printf(access, sys->tcp_sock,
                "SETUP %s RTSP/1.0\r\n"
                "CSeq: %d\r\n"
                "User-Agent: " SATIP_USERAGENT "\r\n"
                "Transport: RTP/AVP;multicast\r\n\r\n",
                psz_setup_url, sys->cseq++);
    } else {
        /* open UDP socket to acquire a free port to use */
        if (satip_bind_ports(access)) {
            free(psz_setup_url);
            goto error;
        }

        net_Printf(access, sys->tcp_sock,
                "SETUP %s RTSP/1.0\r\n"
                "CSeq: %d\r\n"
                "User-Agent: " SATIP_USERAGENT "\r\n"
                "Transport: RTP/AVP;unicast;client_port=%d-%d\r\n\r\n",
                psz_setup_url, sys->cseq++, sys->udp_port, sys->udp_port + 1);
    }
    free(psz_setup_url);

    bool interrupted = false;
    if (rtsp_handle(access, &interrupted) != RTSP_RESULT_OK) {
        msg_Err(access, "Failed to setup RTSP session");
        goto error;
    }

    if (asprintf(&sys->control, "%sstream=%d", sys->content_base, sys->stream_id) < 0) {
        sys->control = NULL;
        goto error;
    }

    if (interrupted) {
        msg_Warn(access, "SETUP was interrupted, abort startup");
        goto error;
    }

    /* Extra sleep for compatibility with some satip servers, that
     * can't handle PLAY right after SETUP */

    if (vlc_msleep_i11e(VLC_TICK_FROM_MS(50)) < 0)
        goto error;

    /* Open UDP socket for reading if not done */
    if (multicast) {
        sys->udp_sock = net_OpenDgram(access, sys->udp_address, sys->udp_port, "", sys->udp_port, IPPROTO_UDP);
        if (sys->udp_sock < 0) {
            msg_Err(access, "Failed to open UDP socket for listening.");
            goto error;
        }

        sys->rtcp_sock = net_OpenDgram(access, sys->udp_address, sys->udp_port + 1, "", sys->udp_port + 1, IPPROTO_UDP);
        if (sys->rtcp_sock < 0) {
            msg_Err(access, "Failed to open RTCP socket for listening.");
            goto error;
        }
    }

    net_Printf(access, sys->tcp_sock,
            "PLAY %s RTSP/1.0\r\n"
            "CSeq: %d\r\n"
            "User-Agent: " SATIP_USERAGENT "\r\n"
            "Session: %s\r\n\r\n",
            sys->control, sys->cseq++, sys->session_id);

    if (rtsp_handle(access, NULL) != RTSP_RESULT_OK) {
        msg_Err(access, "Failed to play RTSP session");
        goto error;
    }

    vlc_queue_Init(&sys->queue, offsetof (block_t, p_next));

    if (vlc_clone(&sys->thread, satip_thread, access)) {
        msg_Err(access, "Failed to create worker thread.");
        goto error;
    }

    access->pf_control = satip_control;
    access->pf_block = satip_block;

    free(psz_host);
    free(psz_lower_url);
    return VLC_SUCCESS;

error:
    free(psz_host);
    free(psz_lower_url);
    vlc_UrlClean(&url);

    satip_teardown(access);

    if (sys->udp_sock >= 0)
        net_Close(sys->udp_sock);
    if (sys->rtcp_sock >= 0)
        net_Close(sys->rtcp_sock);
    if (sys->tcp_sock >= 0)
        net_Close(sys->tcp_sock);

    free(sys->content_base);
    free(sys->control);
    return VLC_EGENERIC;
}

static void satip_close(vlc_object_t *obj)
{
    stream_t *access = (stream_t *)obj;
    access_sys_t *sys = access->p_sys;

    vlc_cancel(sys->thread);
    vlc_join(sys->thread, NULL);

    satip_teardown(access);

    block_ChainRelease(vlc_queue_DequeueAll(&sys->queue));
    net_Close(sys->udp_sock);
    net_Close(sys->rtcp_sock);
    net_Close(sys->tcp_sock);
    free(sys->content_base);
    free(sys->control);
}

Messung
CHZG
82919589

¤ Dauer der Verarbeitung: 0.4 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104852
#Domains=417162