rtpproto.c 8.58 KB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
1 2
/*
 * RTP network protocol
3
 * Copyright (c) 2002 Fabrice Bellard
Fabrice Bellard's avatar
Fabrice Bellard committed
4
 *
5 6 7
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
Fabrice Bellard's avatar
Fabrice Bellard committed
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
Fabrice Bellard's avatar
Fabrice Bellard committed
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Fabrice Bellard's avatar
Fabrice Bellard committed
13 14 15 16 17
 * 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
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Fabrice Bellard's avatar
Fabrice Bellard committed
20
 */
21 22

/**
23
 * @file libavformat/rtpproto.c
24 25 26
 * RTP protocol
 */

27
#include "libavutil/avstring.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
28 29 30
#include "avformat.h"

#include <unistd.h>
31
#include <stdarg.h>
32
#include "network.h"
33
#include "os_support.h"
Fabrice Bellard's avatar
Fabrice Bellard committed
34
#include <fcntl.h>
35
#if HAVE_SYS_SELECT_H
36
#include <sys/select.h>
37
#endif
Fabrice Bellard's avatar
Fabrice Bellard committed
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

#define RTP_TX_BUF_SIZE  (64 * 1024)
#define RTP_RX_BUF_SIZE  (128 * 1024)

typedef struct RTPContext {
    URLContext *rtp_hd, *rtcp_hd;
    int rtp_fd, rtcp_fd;
} RTPContext;

/**
 * If no filename is given to av_open_input_file because you want to
 * get the local port first, then you must call this function to set
 * the remote server address.
 *
 * @param s1 media file context
 * @param uri of the remote server
 * @return zero if no error.
 */
56

Fabrice Bellard's avatar
Fabrice Bellard committed
57 58 59 60 61
int rtp_set_remote_url(URLContext *h, const char *uri)
{
    RTPContext *s = h->priv_data;
    char hostname[256];
    int port;
62

Fabrice Bellard's avatar
Fabrice Bellard committed
63 64
    char buf[1024];
    char path[1024];
65 66

    url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
Fabrice Bellard's avatar
Fabrice Bellard committed
67 68 69 70 71 72 73 74 75 76 77
              path, sizeof(path), uri);

    snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port, path);
    udp_set_remote_url(s->rtp_hd, buf);

    snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port + 1, path);
    udp_set_remote_url(s->rtcp_hd, buf);
    return 0;
}


78 79 80 81 82
/**
 * add option to url of the form:
 * "http://host:port/path?option1=val1&option2=val2...
 */

83
static void url_add_option(char *buf, int buf_size, const char *fmt, ...)
84 85 86 87 88 89
{
    char buf1[1024];
    va_list ap;

    va_start(ap, fmt);
    if (strchr(buf, '?'))
90
        av_strlcat(buf, "&", buf_size);
91
    else
92
        av_strlcat(buf, "?", buf_size);
93
    vsnprintf(buf1, sizeof(buf1), fmt, ap);
94
    av_strlcat(buf, buf1, buf_size);
95 96 97
    va_end(ap);
}

98
static void build_udp_url(char *buf, int buf_size,
99
                          const char *hostname, int port,
100 101
                          int local_port, int ttl,
                          int max_packet_size)
102 103 104 105 106 107
{
    snprintf(buf, buf_size, "udp://%s:%d", hostname, port);
    if (local_port >= 0)
        url_add_option(buf, buf_size, "localport=%d", local_port);
    if (ttl >= 0)
        url_add_option(buf, buf_size, "ttl=%d", ttl);
108 109
    if (max_packet_size >=0)
        url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size);
110 111
}

112
/**
113
 * url syntax: rtp://host:port[?option=val...]
114
 * option: 'ttl=n'       : set the ttl value (for multicast only)
115
 *         'localport=n' : set the local port to n
116
 *         'pkt_size=n'  : set max packet size
117 118
 *
 */
119

Fabrice Bellard's avatar
Fabrice Bellard committed
120 121 122
static int rtp_open(URLContext *h, const char *uri, int flags)
{
    RTPContext *s;
123
    int port, is_output, ttl, local_port, max_packet_size;
Fabrice Bellard's avatar
Fabrice Bellard committed
124 125 126
    char hostname[256];
    char buf[1024];
    char path[1024];
127
    const char *p;
128

Fabrice Bellard's avatar
Fabrice Bellard committed
129 130 131 132
    is_output = (flags & URL_WRONLY);

    s = av_mallocz(sizeof(RTPContext));
    if (!s)
133
        return AVERROR(ENOMEM);
Fabrice Bellard's avatar
Fabrice Bellard committed
134
    h->priv_data = s;
135 136

    url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
Fabrice Bellard's avatar
Fabrice Bellard committed
137
              path, sizeof(path), uri);
138 139 140
    /* extract parameters */
    ttl = -1;
    local_port = -1;
141 142
    max_packet_size = -1;

143 144 145 146 147 148 149 150
    p = strchr(uri, '?');
    if (p) {
        if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
            ttl = strtol(buf, NULL, 10);
        }
        if (find_info_tag(buf, sizeof(buf), "localport", p)) {
            local_port = strtol(buf, NULL, 10);
        }
151 152 153
        if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
            max_packet_size = strtol(buf, NULL, 10);
        }
154
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
155

156
    build_udp_url(buf, sizeof(buf),
157
                  hostname, port, local_port, ttl, max_packet_size);
Fabrice Bellard's avatar
Fabrice Bellard committed
158 159 160
    if (url_open(&s->rtp_hd, buf, flags) < 0)
        goto fail;
    local_port = udp_get_local_port(s->rtp_hd);
Panagiotis Issaris's avatar
Panagiotis Issaris committed
161
    /* XXX: need to open another connection if the port is not even */
Fabrice Bellard's avatar
Fabrice Bellard committed
162 163

    /* well, should suppress localport in path */
164

165
    build_udp_url(buf, sizeof(buf),
166
                  hostname, port + 1, local_port + 1, ttl, max_packet_size);
Fabrice Bellard's avatar
Fabrice Bellard committed
167 168
    if (url_open(&s->rtcp_hd, buf, flags) < 0)
        goto fail;
169

Fabrice Bellard's avatar
Fabrice Bellard committed
170 171
    /* just to ease handle access. XXX: need to suppress direct handle
       access */
172 173
    s->rtp_fd = url_get_file_handle(s->rtp_hd);
    s->rtcp_fd = url_get_file_handle(s->rtcp_hd);
Fabrice Bellard's avatar
Fabrice Bellard committed
174

175
    h->max_packet_size = url_get_max_packet_size(s->rtp_hd);
Fabrice Bellard's avatar
Fabrice Bellard committed
176 177 178 179 180 181 182 183 184
    h->is_streamed = 1;
    return 0;

 fail:
    if (s->rtp_hd)
        url_close(s->rtp_hd);
    if (s->rtcp_hd)
        url_close(s->rtcp_hd);
    av_free(s);
185
    return AVERROR(EIO);
Fabrice Bellard's avatar
Fabrice Bellard committed
186 187
}

188
static int rtp_read(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
189 190 191
{
    RTPContext *s = h->priv_data;
    struct sockaddr_in from;
192 193
    socklen_t from_len;
    int len, fd_max, n;
Fabrice Bellard's avatar
Fabrice Bellard committed
194 195 196 197 198 199 200
    fd_set rfds;
#if 0
    for(;;) {
        from_len = sizeof(from);
        len = recvfrom (s->rtp_fd, buf, size, 0,
                        (struct sockaddr *)&from, &from_len);
        if (len < 0) {
201 202
            if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
                ff_neterrno() == FF_NETERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
203
                continue;
204
            return AVERROR(EIO);
Fabrice Bellard's avatar
Fabrice Bellard committed
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
        }
        break;
    }
#else
    for(;;) {
        /* build fdset to listen to RTP and RTCP packets */
        FD_ZERO(&rfds);
        fd_max = s->rtp_fd;
        FD_SET(s->rtp_fd, &rfds);
        if (s->rtcp_fd > fd_max)
            fd_max = s->rtcp_fd;
        FD_SET(s->rtcp_fd, &rfds);
        n = select(fd_max + 1, &rfds, NULL, NULL, NULL);
        if (n > 0) {
            /* first try RTCP */
            if (FD_ISSET(s->rtcp_fd, &rfds)) {
                from_len = sizeof(from);
                len = recvfrom (s->rtcp_fd, buf, size, 0,
                                (struct sockaddr *)&from, &from_len);
                if (len < 0) {
225 226
                    if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
                        ff_neterrno() == FF_NETERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
227
                        continue;
228
                    return AVERROR(EIO);
Fabrice Bellard's avatar
Fabrice Bellard committed
229 230 231 232 233 234 235 236 237
                }
                break;
            }
            /* then RTP */
            if (FD_ISSET(s->rtp_fd, &rfds)) {
                from_len = sizeof(from);
                len = recvfrom (s->rtp_fd, buf, size, 0,
                                (struct sockaddr *)&from, &from_len);
                if (len < 0) {
238 239
                    if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
                        ff_neterrno() == FF_NETERROR(EINTR))
Fabrice Bellard's avatar
Fabrice Bellard committed
240
                        continue;
241
                    return AVERROR(EIO);
Fabrice Bellard's avatar
Fabrice Bellard committed
242 243 244 245 246 247 248 249 250
                }
                break;
            }
        }
    }
#endif
    return len;
}

251
static int rtp_write(URLContext *h, uint8_t *buf, int size)
Fabrice Bellard's avatar
Fabrice Bellard committed
252 253
{
    RTPContext *s = h->priv_data;
254
    int ret;
Fabrice Bellard's avatar
Fabrice Bellard committed
255
    URLContext *hd;
256

Fabrice Bellard's avatar
Fabrice Bellard committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    if (buf[1] >= 200 && buf[1] <= 204) {
        /* RTCP payload type */
        hd = s->rtcp_hd;
    } else {
        /* RTP payload type */
        hd = s->rtp_hd;
    }

    ret = url_write(hd, buf, size);
#if 0
    {
        struct timespec ts;
        ts.tv_sec = 0;
        ts.tv_nsec = 10 * 1000000;
        nanosleep(&ts, NULL);
    }
#endif
    return ret;
}

static int rtp_close(URLContext *h)
{
    RTPContext *s = h->priv_data;

    url_close(s->rtp_hd);
    url_close(s->rtcp_hd);
    av_free(s);
    return 0;
}

/**
Panagiotis Issaris's avatar
Panagiotis Issaris committed
288
 * Return the local port used by the RTP connection
Fabrice Bellard's avatar
Fabrice Bellard committed
289 290 291
 * @param s1 media file context
 * @return the local port number
 */
292

Fabrice Bellard's avatar
Fabrice Bellard committed
293 294 295 296 297 298
int rtp_get_local_port(URLContext *h)
{
    RTPContext *s = h->priv_data;
    return udp_get_local_port(s->rtp_hd);
}

299
#if (LIBAVFORMAT_VERSION_MAJOR <= 52)
Fabrice Bellard's avatar
Fabrice Bellard committed
300
/**
301 302
 * Return the rtp and rtcp file handles for select() usage to wait for
 * several RTP streams at the same time.
Fabrice Bellard's avatar
Fabrice Bellard committed
303 304
 * @param h media file context
 */
305

Fabrice Bellard's avatar
Fabrice Bellard committed
306 307 308 309 310 311 312
void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd)
{
    RTPContext *s = h->priv_data;

    *prtp_fd = s->rtp_fd;
    *prtcp_fd = s->rtcp_fd;
}
313 314 315 316 317 318 319
#endif

static int rtp_get_file_handle(URLContext *h)
{
    RTPContext *s = h->priv_data;
    return s->rtp_fd;
}
Fabrice Bellard's avatar
Fabrice Bellard committed
320 321 322 323 324 325 326 327

URLProtocol rtp_protocol = {
    "rtp",
    rtp_open,
    rtp_read,
    rtp_write,
    NULL, /* seek */
    rtp_close,
328
    .url_get_file_handle = rtp_get_file_handle,
Fabrice Bellard's avatar
Fabrice Bellard committed
329
};