input.c 5.8 KB
Newer Older
1 2 3 4 5 6 7 8
/**
 * @file input.c
 * @brief RTP packet input
 */
/*****************************************************************************
 * Copyright © 2008 Rémi Denis-Courmont
 *
 * This library is free software; you can redistribute it and/or
9 10
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1
11 12 13 14
 * of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Lesser General Public License for more details.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <vlc_common.h>
#include <vlc_demux.h>
#include <vlc_block.h>
#include <vlc_network.h>

32
#include <limits.h>
33
#include <errno.h>
34 35 36 37
#include <unistd.h>
#ifdef HAVE_POLL
# include <poll.h>
#endif
38 39 40
#ifdef HAVE_SYS_UIO_H
# include <sys/uio.h>
#endif
41 42

#include "rtp.h"
43 44 45
#ifdef HAVE_SRTP
# include <srtp.h>
#endif
46

47 48
#define DEFAULT_MRU (1500u - (20 + 8))

49
/**
50
 * Processes a packet received from the RTP socket.
51
 */
52
static void rtp_process (demux_t *demux, block_t *block)
53
{
54
    demux_sys_t *sys = demux->p_sys;
55

56 57 58 59 60
    if (block->i_buffer < 2)
        goto drop;
    const uint8_t ptype = rtp_ptype (block);
    if (ptype >= 72 && ptype <= 76)
        goto drop; /* Muxed RTCP, ignore for now FIXME */
61

62 63 64 65 66 67 68 69
#ifdef HAVE_SRTP
    if (sys->srtp != NULL)
    {
        size_t len = block->i_buffer;
        if (srtp_recv (sys->srtp, block->p_buffer, &len))
        {
            msg_Dbg (demux, "SRTP authentication/decryption failed");
            goto drop;
70
        }
71
        block->i_buffer = len;
72
    }
73
#endif
74

75 76 77
    /* TODO: use SDP and get rid of this hack */
    if (unlikely(sys->autodetect))
    {   /* Autodetect payload type, _before_ rtp_queue() */
78
        rtp_autodetect (demux, sys->session, block);
79
        sys->autodetect = false;
80 81
    }

82 83 84 85 86
    rtp_queue (demux, sys->session, block);
    return;
drop:
    block_Release (block);
}
87

Steve Lhomme's avatar
Steve Lhomme committed
88
static int rtp_timeout (vlc_tick_t deadline)
89 90 91
{
    if (deadline == VLC_TS_INVALID)
        return -1; /* infinite */
92

93
    vlc_tick_t t = vlc_tick_now ();
94 95
    if (t >= deadline)
        return 0;
96

97 98 99 100
    t = (deadline - t) / (CLOCK_FREQ / INT64_C(1000));
    if (unlikely(t > INT_MAX))
        return INT_MAX;
    return t;
101 102
}

103 104 105 106
/**
 * RTP/RTCP session thread for datagram sockets
 */
void *rtp_dgram_thread (void *opaque)
107
{
108 109
    demux_t *demux = opaque;
    demux_sys_t *sys = demux->p_sys;
Steve Lhomme's avatar
Steve Lhomme committed
110
    vlc_tick_t deadline = VLC_TS_INVALID;
111
    int rtp_fd = sys->fd;
112 113 114 115 116 117 118 119 120
    struct iovec iov =
    {
        .iov_len = DEFAULT_MRU,
    };
    struct msghdr msg =
    {
        .msg_iov = &iov,
        .msg_iovlen = 1,
    };
121

122 123 124
    struct pollfd ufd[1];
    ufd[0].fd = rtp_fd;
    ufd[0].events = POLLIN;
125

126 127 128 129
    for (;;)
    {
        int n = poll (ufd, 1, rtp_timeout (deadline));
        if (n == -1)
130 131
            continue;

132 133 134 135 136
        int canc = vlc_savecancel ();
        if (n == 0)
            goto dequeue;

        if (ufd[0].revents)
137
        {
138 139 140 141
            n--;
            if (unlikely(ufd[0].revents & POLLHUP))
                break; /* RTP socket dead (DCCP only) */

142
            block_t *block = block_Alloc (iov.iov_len);
143
            if (unlikely(block == NULL))
144 145 146 147 148 149
            {
                if (iov.iov_len == DEFAULT_MRU)
                    break; /* we are totallly screwed */
                iov.iov_len = DEFAULT_MRU;
                continue; /* retry with shrunk MRU */
            }
150

151 152 153 154 155 156 157 158
            iov.iov_base = block->p_buffer;
#ifdef __linux__
            msg.msg_flags = MSG_TRUNC;
#else
            msg.msg_flags = 0;
#endif

            ssize_t len = recvmsg (rtp_fd, &msg, 0);
159 160
            if (len != -1)
            {
161 162 163 164 165 166 167 168 169 170 171 172
#ifdef MSG_TRUNC
                if (msg.msg_flags & MSG_TRUNC)
                {
                    msg_Err(demux, "%zd bytes packet truncated (MRU was %zu)",
                            len, iov.iov_len);
                    block->i_flags |= BLOCK_FLAG_CORRUPTED;
                    iov.iov_len = len;
                }
                else
#endif
                    block->i_buffer = len;

173 174 175
                rtp_process (demux, block);
            }
            else
176
            {
177 178
                msg_Warn (demux, "RTP network error: %s",
                          vlc_strerror_c(errno));
179
                block_Release (block);
180 181
            }
        }
182 183 184 185 186

    dequeue:
        if (!rtp_dequeue (demux, sys->session, &deadline))
            deadline = VLC_TS_INVALID;
        vlc_restorecancel (canc);
187 188 189 190
    }
    return NULL;
}

191 192 193 194
/**
 * RTP/RTCP session thread for stream sockets (framed RTP)
 */
void *rtp_stream_thread (void *opaque)
195
{
196
#ifndef _WIN32
197 198 199
    demux_t *demux = opaque;
    demux_sys_t *sys = demux->p_sys;
    int fd = sys->fd;
200 201

    for (;;)
202
    {
203 204 205 206 207
        /* There is no reordering on stream sockets, so no timeout. */
        ssize_t val;

        uint16_t frame_len;
        if (recv (fd, &frame_len, 2, MSG_WAITALL) != 2)
208 209
            break;

210 211 212 213 214 215 216 217 218 219 220 221
        block_t *block = block_Alloc (ntohs (frame_len));
        if (unlikely(block == NULL))
            break;

        block_cleanup_push (block);
        val = recv (fd, block->p_buffer, block->i_buffer, MSG_WAITALL);
        vlc_cleanup_pop ();

        if (val != (ssize_t)block->i_buffer)
        {
            block_Release (block);
            break;
222
        }
223

224
        int canc = vlc_savecancel ();
225
        rtp_process (demux, block);
226
        rtp_dequeue_force (demux, sys->session);
227
        vlc_restorecancel (canc);
228
    }
229 230 231
#else
    (void) opaque;
#endif
232 233
    return NULL;
}