added rtp protocol
[libav.git] / libav / rtpproto.c
CommitLineData
3b50d2ad
FB
1/*
2 * RTP network protocol
3 * Copyright (c) 2002 Fabrice Bellard.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "avformat.h"
20
21#include <unistd.h>
22#include <sys/types.h>
23#include <sys/socket.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <netdb.h>
27#include <fcntl.h>
28
29#define RTP_TX_BUF_SIZE (64 * 1024)
30#define RTP_RX_BUF_SIZE (128 * 1024)
31
32typedef struct RTPContext {
33 URLContext *rtp_hd, *rtcp_hd;
34 int rtp_fd, rtcp_fd;
35} RTPContext;
36
37/**
38 * If no filename is given to av_open_input_file because you want to
39 * get the local port first, then you must call this function to set
40 * the remote server address.
41 *
42 * url syntax: rtp://host:port[?option=val...]
43 * option: 'multicast=1' : enable multicast
44 * 'ttl=n' : set the ttl value (for multicast only)
45 *
46 * @param s1 media file context
47 * @param uri of the remote server
48 * @return zero if no error.
49 */
50int rtp_set_remote_url(URLContext *h, const char *uri)
51{
52 RTPContext *s = h->priv_data;
53 char hostname[256];
54 int port;
55 char buf[1024];
56 char path[1024];
57
58 url_split(NULL, 0, hostname, sizeof(hostname), &port,
59 path, sizeof(path), uri);
60
61 snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port, path);
62 udp_set_remote_url(s->rtp_hd, buf);
63
64 snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port + 1, path);
65 udp_set_remote_url(s->rtcp_hd, buf);
66 return 0;
67}
68
69
70static int rtp_open(URLContext *h, const char *uri, int flags)
71{
72 RTPContext *s;
73 int port, is_output, local_port;
74 char hostname[256];
75 char buf[1024];
76 char path[1024];
77 URLContext *tmp_hd;
78
79 is_output = (flags & URL_WRONLY);
80
81 s = av_mallocz(sizeof(RTPContext));
82 if (!s)
83 return -ENOMEM;
84 h->priv_data = s;
85
86 url_split(NULL, 0, hostname, sizeof(hostname), &port,
87 path, sizeof(path), uri);
88
89 snprintf(buf, sizeof(buf), "udp://%s:%d%s",
90 hostname, port, path);
91 if (url_open(&s->rtp_hd, buf, flags) < 0)
92 goto fail;
93 local_port = udp_get_local_port(s->rtp_hd);
94 /* XXX: need to open another connexion if the port is not even */
95
96 /* well, should suppress localport in path */
97
98 snprintf(buf, sizeof(buf), "udp://%s:%d%s%clocalport=%d",
99 hostname, port + 1, path,
100 strchr(path, '?') != NULL ? '&' : '?',
101 local_port + 1);
102 if (url_open(&s->rtcp_hd, buf, flags) < 0)
103 goto fail;
104
105 /* just to ease handle access. XXX: need to suppress direct handle
106 access */
107 s->rtp_fd = udp_get_file_handle(s->rtp_hd);
108 s->rtcp_fd = udp_get_file_handle(s->rtcp_hd);
109
110 h->max_packet_size = url_get_max_packet_size(s->rtp_hd);
111 h->is_streamed = 1;
112 return 0;
113
114 fail:
115 if (s->rtp_hd)
116 url_close(s->rtp_hd);
117 if (s->rtcp_hd)
118 url_close(s->rtcp_hd);
119 av_free(s);
120 return -EIO;
121}
122
123static int rtp_read(URLContext *h, UINT8 *buf, int size)
124{
125 RTPContext *s = h->priv_data;
126 struct sockaddr_in from;
127 int from_len, len, fd_max, n;
128 fd_set rfds;
129#if 0
130 for(;;) {
131 from_len = sizeof(from);
132 len = recvfrom (s->rtp_fd, buf, size, 0,
133 (struct sockaddr *)&from, &from_len);
134 if (len < 0) {
135 if (errno == EAGAIN || errno == EINTR)
136 continue;
137 return -EIO;
138 }
139 break;
140 }
141#else
142 for(;;) {
143 /* build fdset to listen to RTP and RTCP packets */
144 FD_ZERO(&rfds);
145 fd_max = s->rtp_fd;
146 FD_SET(s->rtp_fd, &rfds);
147 if (s->rtcp_fd > fd_max)
148 fd_max = s->rtcp_fd;
149 FD_SET(s->rtcp_fd, &rfds);
150 n = select(fd_max + 1, &rfds, NULL, NULL, NULL);
151 if (n > 0) {
152 /* first try RTCP */
153 if (FD_ISSET(s->rtcp_fd, &rfds)) {
154 from_len = sizeof(from);
155 len = recvfrom (s->rtcp_fd, buf, size, 0,
156 (struct sockaddr *)&from, &from_len);
157 if (len < 0) {
158 if (errno == EAGAIN || errno == EINTR)
159 continue;
160 return -EIO;
161 }
162 break;
163 }
164 /* then RTP */
165 if (FD_ISSET(s->rtp_fd, &rfds)) {
166 from_len = sizeof(from);
167 len = recvfrom (s->rtp_fd, buf, size, 0,
168 (struct sockaddr *)&from, &from_len);
169 if (len < 0) {
170 if (errno == EAGAIN || errno == EINTR)
171 continue;
172 return -EIO;
173 }
174 break;
175 }
176 }
177 }
178#endif
179 return len;
180}
181
182static int rtp_write(URLContext *h, UINT8 *buf, int size)
183{
184 RTPContext *s = h->priv_data;
185 int ret, fd;
186 URLContext *hd;
187
188 if (buf[1] >= 200 && buf[1] <= 204) {
189 /* RTCP payload type */
190 hd = s->rtcp_hd;
191 } else {
192 /* RTP payload type */
193 hd = s->rtp_hd;
194 }
195
196 ret = url_write(hd, buf, size);
197#if 0
198 {
199 struct timespec ts;
200 ts.tv_sec = 0;
201 ts.tv_nsec = 10 * 1000000;
202 nanosleep(&ts, NULL);
203 }
204#endif
205 return ret;
206}
207
208static int rtp_close(URLContext *h)
209{
210 RTPContext *s = h->priv_data;
211
212 url_close(s->rtp_hd);
213 url_close(s->rtcp_hd);
214 av_free(s);
215 return 0;
216}
217
218/**
219 * Return the local port used by the RTP connexion
220 * @param s1 media file context
221 * @return the local port number
222 */
223int rtp_get_local_port(URLContext *h)
224{
225 RTPContext *s = h->priv_data;
226 return udp_get_local_port(s->rtp_hd);
227}
228
229/**
230 * Return the rtp and rtcp file handles for select() usage to wait for several RTP
231 * streams at the same time.
232 * @param h media file context
233 */
234void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd)
235{
236 RTPContext *s = h->priv_data;
237
238 *prtp_fd = s->rtp_fd;
239 *prtcp_fd = s->rtcp_fd;
240}
241
242URLProtocol rtp_protocol = {
243 "rtp",
244 rtp_open,
245 rtp_read,
246 rtp_write,
247 NULL, /* seek */
248 rtp_close,
249};