Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/src/stream_output/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 10 kB image not shown  

Quelle  sap.c   Sprache: C

 

/*****************************************************************************
 * sap.c : SAP announce handler
 *****************************************************************************
 * Copyright (C) 2002-2008 VLC authors and VideoLAN
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *          Rémi Denis-Courmont
 *
 * 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.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>

#include <stdlib.h>                                                /* free() */
#include <stdio.h>                                              /* sprintf() */
#include <string.h>
#include <assert.h>

#include <vlc_sout.h>
#include <vlc_network.h>
#include <vlc_memstream.h>

#include "stream_output.h"
#include "../libvlc.h"

/* SAP is always on that port */
#define IPPORT_SAP 9875

struct sap_address_t;

/* A SAP session descriptor, enqueued in the SAP handler queue */
struct session_descriptor_t
{
    struct vlc_list node;
    struct sap_address_t *addr;
    size_t length;
    char *data;
};

/* A SAP announce address. For each of these, we run the
 * control flow algorithm */

typedef struct sap_address_t
{
    struct vlc_list         node;

    vlc_thread_t            thread;
    vlc_cond_t              wait;

    char                    group[NI_MAXNUMERICHOST];
    vlc_sockaddr            orig;
    socklen_t               origlen;
    int                     fd;
    unsigned                interval;

    unsigned                session_count;
    struct vlc_list         sessions;
} sap_address_t;

static struct vlc_list sap_addrs = VLC_LIST_INITIALIZER(&sap_addrs);
static vlc_mutex_t sap_mutex = VLC_STATIC_MUTEX;

#define SAP_MAX_BUFFER 65534
#define MIN_INTERVAL 2
#define MAX_INTERVAL 300

static void *RunThread (void *);

static sap_address_t *AddressCreate (vlc_object_t *obj, const char *group)
{
    int fd = net_ConnectUDP (obj, group, IPPORT_SAP, 255);
    if (fd == -1)
        return NULL;

    sap_address_t *addr = malloc (sizeof (*addr));
    if (addr == NULL)
    {
        net_Close (fd);
        return NULL;
    }

    strlcpy (addr->group, group, sizeof (addr->group));
    addr->fd = fd;
    addr->origlen = sizeof (addr->orig);
    getsockname(fd, &addr->orig.sa, &addr->origlen);

    addr->interval = var_CreateGetInteger (obj, "sap-interval");
    vlc_cond_init (&addr->wait);
    addr->session_count = 0;
    vlc_list_init(&addr->sessions);
    return addr;
}

static void AddressDestroy (sap_address_t *addr)
{
    net_Close (addr->fd);
    free (addr);
}

/**
 * main SAP handler thread
 * \param self the SAP Handler object
 * \return nothing
 */

static void *RunThread (void *self)
{
    vlc_thread_set_name("vlc-sap");

    sap_address_t *addr = self;

    vlc_mutex_lock(&sap_mutex);

    while (!vlc_list_is_empty(&addr->sessions))
    {
        session_descriptor_t *p_session;
        vlc_tick_t deadline = vlc_tick_now();

        assert (addr->session_count > 0);

        vlc_list_foreach (p_session, &addr->sessions, node)
        {
            send (addr->fd, p_session->data, p_session->length, 0);
            deadline += vlc_tick_from_samples(addr->interval, addr->session_count);

            if (vlc_cond_timedwait(&addr->wait, &sap_mutex, deadline) == 0)
                break; /* list may have changed! */
        }
    }

    vlc_mutex_unlock(&sap_mutex);
    return NULL;
}

#undef sout_AnnounceRegisterSDP
/**
 *  Registers a new session with the announce handler, using a pregenerated SDP
 *
 * \param obj a VLC object
 * \param sdp the SDP to register
 * \param dst session address (needed for SAP address auto detection)
 * \return the new session descriptor structure
 */

session_descriptor_t *
sout_AnnounceRegisterSDP (vlc_object_t *obj, const char *sdp,
                          const char *dst)
{
    int i;
    char psz_addr[NI_MAXNUMERICHOST];
    vlc_sockaddr addr;
    socklen_t addrlen = 0;
    struct addrinfo *res;

    msg_Dbg (obj, "adding SAP session");

    if (vlc_getaddrinfo (dst, 0, NULL, &res) == 0)
    {
        if ((size_t)res->ai_addrlen <= sizeof (addr))
            memcpy (&addr, res->ai_addr, res->ai_addrlen);
        addrlen = res->ai_addrlen;
        freeaddrinfo (res);
    }

    if (addrlen == 0 || (size_t)addrlen > sizeof (addr))
    {
        msg_Err (obj, "No/invalid address specified for SAP announce" );
        return NULL;
    }

    /* Determine SAP multicast address automatically */
    switch (addr.sa.sa_family)
    {
#if defined (HAVE_INET_PTON) || defined (_WIN32)
        case AF_INET6:
        {
            /* See RFC3513 for list of valid IPv6 scopes */
            struct in6_addr *a6 = &addr.sin6.sin6_addr;

            memcpy( a6->s6_addr + 2, "\x00\x00\x00\x00\x00\x00"
                   "\x00\x00\x00\x00\x00\x02\x7f\xfe", 14 );
            if( IN6_IS_ADDR_MULTICAST( a6 ) )
                /* force flags to zero, preserve scope */
                a6->s6_addr[1] &= 0xf;
            else
                /* Unicast IPv6 - assume global scope */
                memcpy( a6->s6_addr, "\xff\x0e", 2 );
            break;
        }
#endif

        case AF_INET:
        {
            /* See RFC2365 for IPv4 scopes */
            uint32_t ipv4 = addr.sin.sin_addr.s_addr;

            /* 224.0.0.0/24 => 224.0.0.255 */
            if ((ipv4 & htonl (0xffffff00)) == htonl (0xe0000000))
                ipv4 =  htonl (0xe00000ff);
            else
            /* 239.255.0.0/16 => 239.255.255.255 */
            if ((ipv4 & htonl (0xffff0000)) == htonl (0xefff0000))
                ipv4 =  htonl (0xefffffff);
            else
            /* 239.192.0.0/14 => 239.195.255.255 */
            if ((ipv4 & htonl (0xfffc0000)) == htonl (0xefc00000))
                ipv4 =  htonl (0xefc3ffff);
            else
            if ((ipv4 & htonl (0xff000000)) == htonl (0xef000000))
                ipv4 = 0;
            else
            /* other addresses => 224.2.127.254 */
                ipv4 = htonl (0xe0027ffe);

            if( ipv4 == 0 )
            {
                msg_Err (obj, "Out-of-scope multicast address "
                         "not supported by SAP");
                return NULL;
            }

            addr.sin.sin_addr.s_addr = ipv4;
            break;
        }

        default:
            msg_Err (obj, "Address family %u not supported by SAP",
                     (unsigned)addr.sa.sa_family);
            return NULL;
    }

    i = vlc_getnameinfo( &addr.sa, addrlen,
                         psz_addr, sizeof( psz_addr ), NULL, NI_NUMERICHOST );

    if( i )
    {
        msg_Err (obj, "%s", gai_strerror (i));
        return NULL;
    }

    /* Find/create SAP address thread */
    sap_address_t *sap_addr;

    msg_Dbg (obj, "using SAP address: %s", psz_addr);
    vlc_mutex_lock (&sap_mutex);
    vlc_list_foreach (sap_addr, &sap_addrs, node)
        if (!strcmp (psz_addr, sap_addr->group))
            goto matched;

    sap_addr = AddressCreate (obj, psz_addr);
    if (sap_addr == NULL)
    {
        vlc_mutex_unlock(&sap_mutex);
        return NULL;
    }
matched:
    vlc_list_append(&sap_addr->node, &sap_addrs);

    session_descriptor_t *session = malloc(sizeof (*session));
    if (unlikely(session == NULL))
        goto out; /* NOTE: we should destroy the thread if left unused */

    /* Build the SAP Headers */
    struct vlc_memstream stream;
    vlc_memstream_open(&stream);

    /* SAPv1, not encrypted, not compressed */
    uint8_t flags = 0x20;
#ifdef AF_INET6
    if (sap_addr->orig.sa.sa_family == AF_INET6)
        flags |= 0x10;
#endif
    vlc_memstream_putc(&stream, flags);
    vlc_memstream_putc(&stream, 0x00); /* No authentication length */
    vlc_memstream_write(&stream, &(uint16_t){ vlc_tick_now() }, 2); /* ID hash */

    switch (sap_addr->orig.sa.sa_family)
    {
#ifdef AF_INET6
        case AF_INET6:
        {
            const struct in6_addr *a6 = &sap_addr->orig.sin6.sin6_addr;
            vlc_memstream_write(&stream, a6, 16);
            break;
        }
#endif
        case AF_INET:
        {
            const struct in_addr *a4 = &sap_addr->orig.sin.sin_addr;
            vlc_memstream_write(&stream, a4, 4);
            break;
        }
        default:
            vlc_assert_unreachable ();
    }

    vlc_memstream_puts(&stream, "application/sdp");
    vlc_memstream_putc(&stream, '\0');

    /* Build the final message */
    vlc_memstream_puts(&stream, sdp);

    if (vlc_memstream_close(&stream))
    {
        free(session);
        session = NULL;
        goto out;
    }

    session->addr = sap_addr;
    session->data = stream.ptr;
    session->length = stream.length;
    vlc_list_append(&session->node, &sap_addr->sessions);

    if (sap_addr->session_count++ == 0)
    {
        if (vlc_clone(&sap_addr->thread, RunThread, sap_addr))
        {
            msg_Err(obj, "unable to spawn SAP announce thread");
            AddressDestroy(sap_addr);
            free(session->data);
            free(session);
            session = NULL;
            goto out;
        }
    }
    else
        vlc_cond_signal(&sap_addr->wait);
out:
    vlc_mutex_unlock(&sap_mutex);
    return session;
}

#undef sout_AnnounceUnRegister
/**
 *  Unregisters an existing session
 *
 * \param obj a VLC object
 * \param session the session descriptor
 */

void sout_AnnounceUnRegister (vlc_object_t *obj, session_descriptor_t *session)
{
    sap_address_t *addr = session->addr;

    msg_Dbg (obj, "removing SAP session");
    vlc_mutex_lock (&sap_mutex);
    vlc_list_remove(&session->node);

    if (vlc_list_is_empty(&addr->sessions))
        /* Last session for this address -> unlink the address */
        vlc_list_remove(&addr->node);

    addr->session_count--;
    vlc_cond_signal(&addr->wait);
    vlc_mutex_unlock(&sap_mutex);

    if (vlc_list_is_empty(&addr->sessions))
    {
        vlc_join(addr->thread, NULL);
        AddressDestroy(addr);
    }

    free(session->data);
    free(session);
}

Messung
CHZG
85969190

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© 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.