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 00030 #include <unistd.h> 00031 #include <stdarg.h> 00032 #include "network.h" 00033 #include "os_support.h" 00034 #include <fcntl.h> 00035 #if HAVE_SYS_SELECT_H 00036 #include <sys/select.h> 00037 #endif 00038 00039 #define RTP_TX_BUF_SIZE (64 * 1024) 00040 #define RTP_RX_BUF_SIZE (128 * 1024) 00041 00042 typedef struct RTPContext { 00043 URLContext *rtp_hd, *rtcp_hd; 00044 int rtp_fd, rtcp_fd; 00045 } RTPContext; 00046 00057 int rtp_set_remote_url(URLContext *h, const char *uri) 00058 { 00059 RTPContext *s = h->priv_data; 00060 char hostname[256]; 00061 int port; 00062 00063 char buf[1024]; 00064 char path[1024]; 00065 00066 url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, 00067 path, sizeof(path), uri); 00068 00069 snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port, path); 00070 udp_set_remote_url(s->rtp_hd, buf); 00071 00072 snprintf(buf, sizeof(buf), "udp://%s:%d%s", hostname, port + 1, path); 00073 udp_set_remote_url(s->rtcp_hd, buf); 00074 return 0; 00075 } 00076 00077 00083 static void url_add_option(char *buf, int buf_size, const char *fmt, ...) 00084 { 00085 char buf1[1024]; 00086 va_list ap; 00087 00088 va_start(ap, fmt); 00089 if (strchr(buf, '?')) 00090 av_strlcat(buf, "&", buf_size); 00091 else 00092 av_strlcat(buf, "?", buf_size); 00093 vsnprintf(buf1, sizeof(buf1), fmt, ap); 00094 av_strlcat(buf, buf1, buf_size); 00095 va_end(ap); 00096 } 00097 00098 static void build_udp_url(char *buf, int buf_size, 00099 const char *hostname, int port, 00100 int local_port, int ttl, 00101 int max_packet_size) 00102 { 00103 snprintf(buf, buf_size, "udp://%s:%d", hostname, port); 00104 if (local_port >= 0) 00105 url_add_option(buf, buf_size, "localport=%d", local_port); 00106 if (ttl >= 0) 00107 url_add_option(buf, buf_size, "ttl=%d", ttl); 00108 if (max_packet_size >=0) 00109 url_add_option(buf, buf_size, "pkt_size=%d", max_packet_size); 00110 } 00111 00120 static int rtp_open(URLContext *h, const char *uri, int flags) 00121 { 00122 RTPContext *s; 00123 int port, is_output, ttl, local_port, max_packet_size; 00124 char hostname[256]; 00125 char buf[1024]; 00126 char path[1024]; 00127 const char *p; 00128 00129 is_output = (flags & URL_WRONLY); 00130 00131 s = av_mallocz(sizeof(RTPContext)); 00132 if (!s) 00133 return AVERROR(ENOMEM); 00134 h->priv_data = s; 00135 00136 url_split(NULL, 0, NULL, 0, hostname, sizeof(hostname), &port, 00137 path, sizeof(path), uri); 00138 /* extract parameters */ 00139 ttl = -1; 00140 local_port = -1; 00141 max_packet_size = -1; 00142 00143 p = strchr(uri, '?'); 00144 if (p) { 00145 if (find_info_tag(buf, sizeof(buf), "ttl", p)) { 00146 ttl = strtol(buf, NULL, 10); 00147 } 00148 if (find_info_tag(buf, sizeof(buf), "localport", p)) { 00149 local_port = strtol(buf, NULL, 10); 00150 } 00151 if (find_info_tag(buf, sizeof(buf), "pkt_size", p)) { 00152 max_packet_size = strtol(buf, NULL, 10); 00153 } 00154 } 00155 00156 build_udp_url(buf, sizeof(buf), 00157 hostname, port, local_port, ttl, max_packet_size); 00158 if (url_open(&s->rtp_hd, buf, flags) < 0) 00159 goto fail; 00160 local_port = udp_get_local_port(s->rtp_hd); 00161 /* XXX: need to open another connection if the port is not even */ 00162 00163 /* well, should suppress localport in path */ 00164 00165 build_udp_url(buf, sizeof(buf), 00166 hostname, port + 1, local_port + 1, ttl, max_packet_size); 00167 if (url_open(&s->rtcp_hd, buf, flags) < 0) 00168 goto fail; 00169 00170 /* just to ease handle access. XXX: need to suppress direct handle 00171 access */ 00172 s->rtp_fd = udp_get_file_handle(s->rtp_hd); 00173 s->rtcp_fd = udp_get_file_handle(s->rtcp_hd); 00174 00175 h->max_packet_size = url_get_max_packet_size(s->rtp_hd); 00176 h->is_streamed = 1; 00177 return 0; 00178 00179 fail: 00180 if (s->rtp_hd) 00181 url_close(s->rtp_hd); 00182 if (s->rtcp_hd) 00183 url_close(s->rtcp_hd); 00184 av_free(s); 00185 return AVERROR(EIO); 00186 } 00187 00188 static int rtp_read(URLContext *h, uint8_t *buf, int size) 00189 { 00190 RTPContext *s = h->priv_data; 00191 struct sockaddr_in from; 00192 socklen_t from_len; 00193 int len, fd_max, n; 00194 fd_set rfds; 00195 #if 0 00196 for(;;) { 00197 from_len = sizeof(from); 00198 len = recvfrom (s->rtp_fd, buf, size, 0, 00199 (struct sockaddr *)&from, &from_len); 00200 if (len < 0) { 00201 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00202 ff_neterrno() == FF_NETERROR(EINTR)) 00203 continue; 00204 return AVERROR(EIO); 00205 } 00206 break; 00207 } 00208 #else 00209 for(;;) { 00210 /* build fdset to listen to RTP and RTCP packets */ 00211 FD_ZERO(&rfds); 00212 fd_max = s->rtp_fd; 00213 FD_SET(s->rtp_fd, &rfds); 00214 if (s->rtcp_fd > fd_max) 00215 fd_max = s->rtcp_fd; 00216 FD_SET(s->rtcp_fd, &rfds); 00217 n = select(fd_max + 1, &rfds, NULL, NULL, NULL); 00218 if (n > 0) { 00219 /* first try RTCP */ 00220 if (FD_ISSET(s->rtcp_fd, &rfds)) { 00221 from_len = sizeof(from); 00222 len = recvfrom (s->rtcp_fd, buf, size, 0, 00223 (struct sockaddr *)&from, &from_len); 00224 if (len < 0) { 00225 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00226 ff_neterrno() == FF_NETERROR(EINTR)) 00227 continue; 00228 return AVERROR(EIO); 00229 } 00230 break; 00231 } 00232 /* then RTP */ 00233 if (FD_ISSET(s->rtp_fd, &rfds)) { 00234 from_len = sizeof(from); 00235 len = recvfrom (s->rtp_fd, buf, size, 0, 00236 (struct sockaddr *)&from, &from_len); 00237 if (len < 0) { 00238 if (ff_neterrno() == FF_NETERROR(EAGAIN) || 00239 ff_neterrno() == FF_NETERROR(EINTR)) 00240 continue; 00241 return AVERROR(EIO); 00242 } 00243 break; 00244 } 00245 } 00246 } 00247 #endif 00248 return len; 00249 } 00250 00251 static int rtp_write(URLContext *h, uint8_t *buf, int size) 00252 { 00253 RTPContext *s = h->priv_data; 00254 int ret; 00255 URLContext *hd; 00256 00257 if (buf[1] >= 200 && buf[1] <= 204) { 00258 /* RTCP payload type */ 00259 hd = s->rtcp_hd; 00260 } else { 00261 /* RTP payload type */ 00262 hd = s->rtp_hd; 00263 } 00264 00265 ret = url_write(hd, buf, size); 00266 #if 0 00267 { 00268 struct timespec ts; 00269 ts.tv_sec = 0; 00270 ts.tv_nsec = 10 * 1000000; 00271 nanosleep(&ts, NULL); 00272 } 00273 #endif 00274 return ret; 00275 } 00276 00277 static int rtp_close(URLContext *h) 00278 { 00279 RTPContext *s = h->priv_data; 00280 00281 url_close(s->rtp_hd); 00282 url_close(s->rtcp_hd); 00283 av_free(s); 00284 return 0; 00285 } 00286 00293 int rtp_get_local_port(URLContext *h) 00294 { 00295 RTPContext *s = h->priv_data; 00296 return udp_get_local_port(s->rtp_hd); 00297 } 00298 00305 void rtp_get_file_handles(URLContext *h, int *prtp_fd, int *prtcp_fd) 00306 { 00307 RTPContext *s = h->priv_data; 00308 00309 *prtp_fd = s->rtp_fd; 00310 *prtcp_fd = s->rtcp_fd; 00311 } 00312 00313 URLProtocol rtp_protocol = { 00314 "rtp", 00315 rtp_open, 00316 rtp_read, 00317 rtp_write, 00318 NULL, /* seek */ 00319 rtp_close, 00320 };