Libav
|
00001 /* 00002 * RTP network protocol 00003 * Copyright (c) 2002 Fabrice Bellard 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00027 #include "libavutil/avstring.h" 00028 #include "avformat.h" 00029 #include "rtpdec.h" 00030 00031 #include <unistd.h> 00032 #include <stdarg.h> 00033 #include "internal.h" 00034 #include "network.h" 00035 #include "os_support.h" 00036 #include <fcntl.h> 00037 #if HAVE_SYS_SELECT_H 00038 #include <sys/select.h> 00039 #endif 00040 #include <sys/time.h> 00041 00042 #define RTP_TX_BUF_SIZE (64 * 1024) 00043 #define RTP_RX_BUF_SIZE (128 * 1024) 00044 00045 typedef struct RTPContext { 00046 URLContext *rtp_hd, *rtcp_hd; 00047 int rtp_fd, rtcp_fd; 00048 } RTPContext; 00049 00060 int rtp_set_remote_url(URLContext *h, const char *uri) 00061 { 00062 RTPContext *s = h->priv_data; 00063 char hostname[256]; 00064 int port; 00065 00066 char buf[1024]; 00067 char path[1024]; 00068 00069 ff_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, 00070 path, sizeof(path), uri); 00071 00072 ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, port, "%s", path); 00073 udp_set_remote_url(s->rtp_hd, buf); 00074 00075 ff_url_join(buf, sizeof(buf), "udp", NULL, hostname, port + 1, "%s", path); 00076 udp_set_remote_url(s->rtcp_hd, buf); 00077 return 0; 00078 } 00079 00080 00086 static void url_add_option(char *buf, int buf_size, const char *fmt, ...) 00087 { 00088 char buf1[1024]; 00089 va_list ap; 00090 00091 va_start(ap, fmt); 00092 if (strchr(buf, '?')) 00093 av_strlcat(buf, "&", buf_size); 00094 else 00095 av_strlcat(buf, "?", buf_size); 00096 vsnprintf(buf1, sizeof(buf1), fmt, ap); 00097 av_strlcat(buf, buf1, buf_size); 00098 va_end(ap); 00099 } 00100 00101 static void build_udp_url(char *buf, int buf_size, 00102 const char *hostname, int port, 00103 int local_port, int ttl, 00104 int max_packet_size) 00105 { 00106 ff_url_join(buf, buf_size, "udp", NULL, hostname, port, NULL); 00107 if (local_port >= 0) 00108 url_add_option(buf, buf_size, "localport=%d", local_port); 00109 if (ttl >= 0) 00110 url_add_option(buf, buf_size, "ttl=%d", ttl); 00111 if (max_packet_size >=0) 00112 url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size); 00113 } 00114 00131 static int rtp_open(URLContext *h, const char *uri, int flags) 00132 { 00133 RTPContext *s; 00134 int rtp_port, rtcp_port, 00135 is_output, ttl, 00136 local_rtp_port, local_rtcp_port, max_packet_size; 00137 char hostname[256]; 00138 char buf[1024]; 00139 char path[1024]; 00140 const char *p; 00141 00142 is_output = (flags & URL_WRONLY); 00143 00144 s = av_mallocz(sizeof(RTPContext)); 00145 if (!s) 00146 return AVERROR(ENOMEM); 00147 h->priv_data = s; 00148 00149 ff_url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &rtp_port, 00150 path, sizeof(path), uri); 00151 /* extract parameters */ 00152 ttl = -1; 00153 rtcp_port = rtp_port+1; 00154 local_rtp_port = -1; 00155 local_rtcp_port = -1; 00156 max_packet_size = -1; 00157 00158 p = strchr(uri, '?'); 00159 if (p) { 00160 if (find_info_tag(buf, sizeof(buf), "ttl", p)) { 00161 ttl = strtol(buf, NULL, 10); 00162 } 00163 if (find_info_tag(buf, sizeof(buf), "rtcpport", p)) { 00164 rtcp_port = strtol(buf, NULL, 10); 00165 } 00166 if (find_info_tag(buf, sizeof(buf), "localport", p)) { 00167 local_rtp_port = strtol(buf, NULL, 10); 00168 } 00169 if (find_info_tag(buf, sizeof(buf), "localrtpport", p)) { 00170 local_rtp_port = strtol(buf, NULL, 10); 00171 } 00172 if (find_info_tag(buf, sizeof(buf), "localrtcpport", p)) { 00173 local_rtcp_port = strtol(buf, NULL, 10); 00174 } 00175 if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) { 00176 max_packet_size = strtol(buf, NULL, 10); 00177 } 00178 } 00179 00180 build_udp_url(buf, sizeof(buf), 00181 hostname, rtp_port, local_rtp_port, ttl, max_packet_size); 00182 if (url_open(&s->rtp_hd, buf, flags) < 0) 00183 goto fail; 00184 if (local_rtp_port>=0 && local_rtcp_port<0) 00185 local_rtcp_port = udp_get_local_port(s->rtp_hd) + 1; 00186 00187 build_udp_url(buf, sizeof(buf), 00188 hostname, rtcp_port, local_rtcp_port, ttl, max_packet_size); 00189 if (url_open(&s->rtcp_hd, buf, flags) < 0) 00190 goto fail; 00191 00192 /* just to ease handle access. XXX: need to suppress direct handle 00193 access */ 00194 s->rtp_fd = url_get_file_handle(s->rtp_hd); 00195 s->rtcp_fd = url_get_file_handle(s->rtcp_hd); 00196 00197 h->max_packet_size = url_get_max_packet_size(s->rtp_hd); 00198 h->is_streamed = 1; 00199 return 0; 00200 00201 fail: 00202 if (s->rtp_hd) 00203 url_close(s->rtp_hd); 00204 if (s->rtcp_hd) 00205 url_close(s->rtcp_hd); 00206 av_free(s); 00207 return AVERROR(EIO); 00208 } 00209 00210 static int rtp_read(URLContext *h, uint8_t *buf, int size) 00211 { 00212 RTPContext *s = h->priv_data; 00213 struct sockaddr_in from; 00214 socklen_t from_len; 00215 int len, fd_max, n; 00216 fd_set rfds; 00217 struct timeval tv; 00218 #if 0 00219 for(;;) { 00220 from_len = sizeof(from); 00221 len = recvfrom (s->rtp_fd, buf, size, 0, 00222 (struct sockaddr *)&from, &from_len); 00223 if (len < 0) { 00224 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00225 ff_neterrno() == FF_NETERROR(EINTR)) 00226 continue; 00227 return AVERROR(EIO); 00228 } 00229 break; 00230 } 00231 #else 00232 for(;;) { 00233 if (url_interrupt_cb()) 00234 return AVERROR(EINTR); 00235 /* build fdset to listen to RTP and RTCP packets */ 00236 FD_ZERO(&rfds); 00237 fd_max = s->rtp_fd; 00238 FD_SET(s->rtp_fd, &rfds); 00239 if (s->rtcp_fd > fd_max) 00240 fd_max = s->rtcp_fd; 00241 FD_SET(s->rtcp_fd, &rfds); 00242 tv.tv_sec = 0; 00243 tv.tv_usec = 100 * 1000; 00244 n = select(fd_max + 1, &rfds, NULL, NULL, &tv); 00245 if (n > 0) { 00246 /* first try RTCP */ 00247 if (FD_ISSET(s->rtcp_fd, &rfds)) { 00248 from_len = sizeof(from); 00249 len = recvfrom (s->rtcp_fd, buf, size, 0, 00250 (struct sockaddr *)&from, &from_len); 00251 if (len < 0) { 00252 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00253 ff_neterrno() == FF_NETERROR(EINTR)) 00254 continue; 00255 return AVERROR(EIO); 00256 } 00257 break; 00258 } 00259 /* then RTP */ 00260 if (FD_ISSET(s->rtp_fd, &rfds)) { 00261 from_len = sizeof(from); 00262 len = recvfrom (s->rtp_fd, buf, size, 0, 00263 (struct sockaddr *)&from, &from_len); 00264 if (len < 0) { 00265 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00266 ff_neterrno() == FF_NETERROR(EINTR)) 00267 continue; 00268 return AVERROR(EIO); 00269 } 00270 break; 00271 } 00272 } else if (n < 0) { 00273 if (ff_neterrno() == FF_NETERROR(EINTR)) 00274 continue; 00275 return AVERROR(EIO); 00276 } 00277 } 00278 #endif 00279 return len; 00280 } 00281 00282 static int rtp_write(URLContext *h, uint8_t *buf, int size) 00283 { 00284 RTPContext *s = h->priv_data; 00285 int ret; 00286 URLContext *hd; 00287 00288 if (buf[1] >= 200 && buf[1] <= 204) { 00289 /* RTCP payload type */ 00290 hd = s->rtcp_hd; 00291 } else { 00292 /* RTP payload type */ 00293 hd = s->rtp_hd; 00294 } 00295 00296 ret = url_write(hd, buf, size); 00297 #if 0 00298 { 00299 struct timespec ts; 00300 ts.tv_sec = 0; 00301 ts.tv_nsec = 10 * 1000000; 00302 nanosleep(&ts, NULL); 00303 } 00304 #endif 00305 return ret; 00306 } 00307 00308 static int rtp_close(URLContext *h) 00309 { 00310 RTPContext *s = h->priv_data; 00311 00312 url_close(s->rtp_hd); 00313 url_close(s->rtcp_hd); 00314 av_free(s); 00315 return 0; 00316 } 00317 00324 int rtp_get_local_rtp_port(URLContext *h) 00325 { 00326 RTPContext *s = h->priv_data; 00327 return udp_get_local_port(s->rtp_hd); 00328 } 00329 00336 int rtp_get_local_port(URLContext *h) 00337 { 00338 RTPContext *s = h->priv_data; 00339 return udp_get_local_port(s->rtp_hd); 00340 } 00341 00348 int rtp_get_local_rtcp_port(URLContext *h) 00349 { 00350 RTPContext *s = h->priv_data; 00351 return udp_get_local_port(s->rtcp_hd); 00352 } 00353 00354 #if (LIBAVFORMAT_VERSION_MAJOR <= 52) 00355 00361 void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd) 00362 { 00363 RTPContext *s = h->priv_data; 00364 00365 *prtp_fd = s->rtp_fd; 00366 *prtcp_fd = s->rtcp_fd; 00367 } 00368 #endif 00369 00370 static int rtp_get_file_handle(URLContext *h) 00371 { 00372 RTPContext *s = h->priv_data; 00373 return s->rtp_fd; 00374 } 00375 00376 URLProtocol rtp_protocol = { 00377 "rtp", 00378 rtp_open, 00379 rtp_read, 00380 rtp_write, 00381 NULL, /* seek */ 00382 rtp_close, 00383 .url_get_file_handle = rtp_get_file_handle, 00384 };