Statistics
| Branch: | Revision:

ffmpeg / libavformat / rtpproto.c @ f3bfe388

History | View | Annotate | Download (10.3 KB)

1
/*
2
 * RTP network protocol
3
 * Copyright (c) 2002 Fabrice Bellard
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * 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
20
 */
21

    
22
/**
23
 * @file
24
 * RTP protocol
25
 */
26

    
27
#include "libavutil/avstring.h"
28
#include "avformat.h"
29
#include "rtpdec.h"
30

    
31
#include <unistd.h>
32
#include <stdarg.h>
33
#include "internal.h"
34
#include "network.h"
35
#include "os_support.h"
36
#include <fcntl.h>
37
#if HAVE_SYS_SELECT_H
38
#include <sys/select.h>
39
#endif
40
#include <sys/time.h>
41

    
42
#define RTP_TX_BUF_SIZE  (64 * 1024)
43
#define RTP_RX_BUF_SIZE  (128 * 1024)
44

    
45
typedef struct RTPContext {
46
    URLContext *rtp_hd, *rtcp_hd;
47
    int rtp_fd, rtcp_fd;
48
} RTPContext;
49

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

    
60
int rtp_set_remote_url(URLContext *h, const char *uri)
61
{
62
    RTPContext *s = h->priv_data;
63
    char hostname[256];
64
    int port;
65

    
66
    char buf[1024];
67
    char path[1024];
68

    
69
    av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port,
70
                 path, sizeof(path), uri);
71

    
72
    ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, port, "%s", path);
73
    udp_set_remote_url(s->rtp_hd, buf);
74

    
75
    ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, port + 1, "%s", path);
76
    udp_set_remote_url(s->rtcp_hd, buf);
77
    return 0;
78
}
79

    
80

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

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

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

    
101
static void build_udp_url(char *buf, int buf_size,
102
                          const char *hostname, int port,
103
                          int local_port, int ttl,
104
                          int max_packet_size)
105
{
106
    ff_url_join(buf, buf_size, "udp", NULL, hostname, port, NULL);
107
    if (local_port >= 0)
108
        url_add_option(buf, buf_size, "localport=%d", local_port);
109
    if (ttl >= 0)
110
        url_add_option(buf, buf_size, "ttl=%d", ttl);
111
    if (max_packet_size >=0)
112
        url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size);
113
}
114

    
115
/**
116
 * url syntax: rtp://host:port[?option=val...]
117
 * option: 'ttl=n'            : set the ttl value (for multicast only)
118
 *         'rtcpport=n'       : set the remote rtcp port to n
119
 *         'localrtpport=n'   : set the local rtp port to n
120
 *         'localrtcpport=n'  : set the local rtcp port to n
121
 *         'pkt_size=n'       : set max packet size
122
 * deprecated option:
123
 *         'localport=n'      : set the local port to n
124
 *
125
 * if rtcpport isn't set the rtcp port will be the rtp port + 1
126
 * if local rtp port isn't set any available port will be used for the local
127
 * rtp and rtcp ports
128
 * if the local rtcp port is not set it will be the local rtp port + 1
129
 */
130

    
131
static int rtp_open(URLContext *h, const char *uri, int flags)
132
{
133
    RTPContext *s;
134
    int rtp_port, rtcp_port,
135
        is_output, ttl,
136
        local_rtp_port, local_rtcp_port, max_packet_size;
137
    char hostname[256];
138
    char buf[1024];
139
    char path[1024];
140
    const char *p;
141

    
142
    is_output = (flags & URL_WRONLY);
143

    
144
    s = av_mallocz(sizeof(RTPContext));
145
    if (!s)
146
        return AVERROR(ENOMEM);
147
    h->priv_data = s;
148

    
149
    av_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port,
150
                 path, sizeof(path), uri);
151
    /* extract parameters */
152
    ttl = -1;
153
    rtcp_port = rtp_port+1;
154
    local_rtp_port = -1;
155
    local_rtcp_port = -1;
156
    max_packet_size = -1;
157

    
158
    p = strchr(uri, '?');
159
    if (p) {
160
        if (find_info_tag(buf, sizeof(buf), "ttl", p)) {
161
            ttl = strtol(buf, NULL, 10);
162
        }
163
        if (find_info_tag(buf, sizeof(buf), "rtcpport", p)) {
164
            rtcp_port = strtol(buf, NULL, 10);
165
        }
166
        if (find_info_tag(buf, sizeof(buf), "localport", p)) {
167
            local_rtp_port = strtol(buf, NULL, 10);
168
        }
169
        if (find_info_tag(buf, sizeof(buf), "localrtpport", p)) {
170
            local_rtp_port = strtol(buf, NULL, 10);
171
        }
172
        if (find_info_tag(buf, sizeof(buf), "localrtcpport", p)) {
173
            local_rtcp_port = strtol(buf, NULL, 10);
174
        }
175
        if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) {
176
            max_packet_size = strtol(buf, NULL, 10);
177
        }
178
    }
179

    
180
    build_udp_url(buf, sizeof(buf),
181
                  hostname, rtp_port, local_rtp_port, ttl, max_packet_size);
182
    if (url_open(&s->rtp_hd, buf, flags) < 0)
183
        goto fail;
184
    if (local_rtp_port>=0 && local_rtcp_port<0)
185
        local_rtcp_port = udp_get_local_port(s->rtp_hd) + 1;
186

    
187
    build_udp_url(buf, sizeof(buf),
188
                  hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size);
189
    if (url_open(&s->rtcp_hd, buf, flags) < 0)
190
        goto fail;
191

    
192
    /* just to ease handle access. XXX: need to suppress direct handle
193
       access */
194
    s->rtp_fd = url_get_file_handle(s->rtp_hd);
195
    s->rtcp_fd = url_get_file_handle(s->rtcp_hd);
196

    
197
    h->max_packet_size = url_get_max_packet_size(s->rtp_hd);
198
    h->is_streamed = 1;
199
    return 0;
200

    
201
 fail:
202
    if (s->rtp_hd)
203
        url_close(s->rtp_hd);
204
    if (s->rtcp_hd)
205
        url_close(s->rtcp_hd);
206
    av_free(s);
207
    return AVERROR(EIO);
208
}
209

    
210
static int rtp_read(URLContext *h, uint8_t *buf, int size)
211
{
212
    RTPContext *s = h->priv_data;
213
    struct sockaddr_in from;
214
    socklen_t from_len;
215
    int len, fd_max, n;
216
    fd_set rfds;
217
    struct timeval tv;
218
#if 0
219
    for(;;) {
220
        from_len = sizeof(from);
221
        len = recvfrom (s->rtp_fd, buf, size, 0,
222
                        (struct sockaddr *)&from, &from_len);
223
        if (len < 0) {
224
            if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
225
                ff_neterrno() == FF_NETERROR(EINTR))
226
                continue;
227
            return AVERROR(EIO);
228
        }
229
        break;
230
    }
231
#else
232
    for(;;) {
233
        if (url_interrupt_cb())
234
            return AVERROR(EINTR);
235
        /* build fdset to listen to RTP and RTCP packets */
236
        FD_ZERO(&rfds);
237
        fd_max = s->rtp_fd;
238
        FD_SET(s->rtp_fd, &rfds);
239
        if (s->rtcp_fd > fd_max)
240
            fd_max = s->rtcp_fd;
241
        FD_SET(s->rtcp_fd, &rfds);
242
        tv.tv_sec = 0;
243
        tv.tv_usec = 100 * 1000;
244
        n = select(fd_max + 1, &rfds, NULL, NULL, &tv);
245
        if (n > 0) {
246
            /* first try RTCP */
247
            if (FD_ISSET(s->rtcp_fd, &rfds)) {
248
                from_len = sizeof(from);
249
                len = recvfrom (s->rtcp_fd, buf, size, 0,
250
                                (struct sockaddr *)&from, &from_len);
251
                if (len < 0) {
252
                    if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
253
                        ff_neterrno() == FF_NETERROR(EINTR))
254
                        continue;
255
                    return AVERROR(EIO);
256
                }
257
                break;
258
            }
259
            /* then RTP */
260
            if (FD_ISSET(s->rtp_fd, &rfds)) {
261
                from_len = sizeof(from);
262
                len = recvfrom (s->rtp_fd, buf, size, 0,
263
                                (struct sockaddr *)&from, &from_len);
264
                if (len < 0) {
265
                    if (ff_neterrno() == FF_NETERROR(EAGAIN) ||
266
                        ff_neterrno() == FF_NETERROR(EINTR))
267
                        continue;
268
                    return AVERROR(EIO);
269
                }
270
                break;
271
            }
272
        } else if (n < 0) {
273
            if (ff_neterrno() == FF_NETERROR(EINTR))
274
                continue;
275
            return AVERROR(EIO);
276
        }
277
    }
278
#endif
279
    return len;
280
}
281

    
282
static int rtp_write(URLContext *h, const uint8_t *buf, int size)
283
{
284
    RTPContext *s = h->priv_data;
285
    int ret;
286
    URLContext *hd;
287

    
288
    if (buf[1] >= 200 && buf[1] <= 204) {
289
        /* RTCP payload type */
290
        hd = s->rtcp_hd;
291
    } else {
292
        /* RTP payload type */
293
        hd = s->rtp_hd;
294
    }
295

    
296
    ret = url_write(hd, buf, size);
297
#if 0
298
    {
299
        struct timespec ts;
300
        ts.tv_sec = 0;
301
        ts.tv_nsec = 10 * 1000000;
302
        nanosleep(&ts, NULL);
303
    }
304
#endif
305
    return ret;
306
}
307

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

    
312
    url_close(s->rtp_hd);
313
    url_close(s->rtcp_hd);
314
    av_free(s);
315
    return 0;
316
}
317

    
318
/**
319
 * Return the local rtp port used by the RTP connection
320
 * @param s1 media file context
321
 * @return the local port number
322
 */
323

    
324
int rtp_get_local_rtp_port(URLContext *h)
325
{
326
    RTPContext *s = h->priv_data;
327
    return udp_get_local_port(s->rtp_hd);
328
}
329

    
330
/**
331
 * Return the local rtp port used by the RTP connection
332
 * @param s1 media file context
333
 * @return the local port number
334
 */
335

    
336
int rtp_get_local_port(URLContext *h)
337
{
338
    RTPContext *s = h->priv_data;
339
    return udp_get_local_port(s->rtp_hd);
340
}
341

    
342
/**
343
 * Return the local rtcp port used by the RTP connection
344
 * @param s1 media file context
345
 * @return the local port number
346
 */
347

    
348
int rtp_get_local_rtcp_port(URLContext *h)
349
{
350
    RTPContext *s = h->priv_data;
351
    return udp_get_local_port(s->rtcp_hd);
352
}
353

    
354
#if (LIBAVFORMAT_VERSION_MAJOR <= 52)
355
/**
356
 * Return the rtp and rtcp file handles for select() usage to wait for
357
 * several RTP streams at the same time.
358
 * @param h media file context
359
 */
360

    
361
void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd)
362
{
363
    RTPContext *s = h->priv_data;
364

    
365
    *prtp_fd = s->rtp_fd;
366
    *prtcp_fd = s->rtcp_fd;
367
}
368
#endif
369

    
370
static int rtp_get_file_handle(URLContext *h)
371
{
372
    RTPContext *s = h->priv_data;
373
    return s->rtp_fd;
374
}
375

    
376
URLProtocol rtp_protocol = {
377
    "rtp",
378
    rtp_open,
379
    rtp_read,
380
    rtp_write,
381
    NULL, /* seek */
382
    rtp_close,
383
    .url_get_file_handle = rtp_get_file_handle,
384
};