Libav 0.7.1
|
00001 /* 00002 * Session Announcement Protocol (RFC 2974) muxer 00003 * Copyright (c) 2010 Martin Storsjo 00004 * 00005 * This file is part of Libav. 00006 * 00007 * Libav 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 * Libav 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 Libav; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "avformat.h" 00023 #include "libavutil/parseutils.h" 00024 #include "libavutil/random_seed.h" 00025 #include "libavutil/avstring.h" 00026 #include "libavutil/intreadwrite.h" 00027 #include "internal.h" 00028 #include "network.h" 00029 #include "os_support.h" 00030 #include "rtpenc_chain.h" 00031 #include "url.h" 00032 00033 struct SAPState { 00034 uint8_t *ann; 00035 int ann_size; 00036 URLContext *ann_fd; 00037 int64_t last_time; 00038 }; 00039 00040 static int sap_write_close(AVFormatContext *s) 00041 { 00042 struct SAPState *sap = s->priv_data; 00043 int i; 00044 00045 for (i = 0; i < s->nb_streams; i++) { 00046 AVFormatContext *rtpctx = s->streams[i]->priv_data; 00047 if (!rtpctx) 00048 continue; 00049 av_write_trailer(rtpctx); 00050 avio_close(rtpctx->pb); 00051 avformat_free_context(rtpctx); 00052 s->streams[i]->priv_data = NULL; 00053 } 00054 00055 if (sap->last_time && sap->ann && sap->ann_fd) { 00056 sap->ann[0] |= 4; /* Session deletion*/ 00057 ffurl_write(sap->ann_fd, sap->ann, sap->ann_size); 00058 } 00059 00060 av_freep(&sap->ann); 00061 if (sap->ann_fd) 00062 ffurl_close(sap->ann_fd); 00063 ff_network_close(); 00064 return 0; 00065 } 00066 00067 static int sap_write_header(AVFormatContext *s) 00068 { 00069 struct SAPState *sap = s->priv_data; 00070 char host[1024], path[1024], url[1024], announce_addr[50] = ""; 00071 char *option_list; 00072 int port = 9875, base_port = 5004, i, pos = 0, same_port = 0, ttl = 255; 00073 AVFormatContext **contexts = NULL; 00074 int ret = 0; 00075 struct sockaddr_storage localaddr; 00076 socklen_t addrlen = sizeof(localaddr); 00077 int udp_fd; 00078 00079 if (!ff_network_init()) 00080 return AVERROR(EIO); 00081 00082 /* extract hostname and port */ 00083 av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &base_port, 00084 path, sizeof(path), s->filename); 00085 if (base_port < 0) 00086 base_port = 5004; 00087 00088 /* search for options */ 00089 option_list = strrchr(path, '?'); 00090 if (option_list) { 00091 char buf[50]; 00092 if (av_find_info_tag(buf, sizeof(buf), "announce_port", option_list)) { 00093 port = strtol(buf, NULL, 10); 00094 } 00095 if (av_find_info_tag(buf, sizeof(buf), "same_port", option_list)) { 00096 same_port = strtol(buf, NULL, 10); 00097 } 00098 if (av_find_info_tag(buf, sizeof(buf), "ttl", option_list)) { 00099 ttl = strtol(buf, NULL, 10); 00100 } 00101 if (av_find_info_tag(buf, sizeof(buf), "announce_addr", option_list)) { 00102 av_strlcpy(announce_addr, buf, sizeof(announce_addr)); 00103 } 00104 } 00105 00106 if (!announce_addr[0]) { 00107 struct addrinfo hints, *ai = NULL; 00108 memset(&hints, 0, sizeof(hints)); 00109 hints.ai_family = AF_UNSPEC; 00110 if (getaddrinfo(host, NULL, &hints, &ai)) { 00111 av_log(s, AV_LOG_ERROR, "Unable to resolve %s\n", host); 00112 ret = AVERROR(EIO); 00113 goto fail; 00114 } 00115 if (ai->ai_family == AF_INET) { 00116 /* Also known as sap.mcast.net */ 00117 av_strlcpy(announce_addr, "224.2.127.254", sizeof(announce_addr)); 00118 #if HAVE_STRUCT_SOCKADDR_IN6 00119 } else if (ai->ai_family == AF_INET6) { 00120 /* With IPv6, you can use the same destination in many different 00121 * multicast subnets, to choose how far you want it routed. 00122 * This one is intended to be routed globally. */ 00123 av_strlcpy(announce_addr, "ff0e::2:7ffe", sizeof(announce_addr)); 00124 #endif 00125 } else { 00126 freeaddrinfo(ai); 00127 av_log(s, AV_LOG_ERROR, "Host %s resolved to unsupported " 00128 "address family\n", host); 00129 ret = AVERROR(EIO); 00130 goto fail; 00131 } 00132 freeaddrinfo(ai); 00133 } 00134 00135 contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams); 00136 if (!contexts) { 00137 ret = AVERROR(ENOMEM); 00138 goto fail; 00139 } 00140 00141 s->start_time_realtime = av_gettime(); 00142 for (i = 0; i < s->nb_streams; i++) { 00143 URLContext *fd; 00144 00145 ff_url_join(url, sizeof(url), "rtp", NULL, host, base_port, 00146 "?ttl=%d", ttl); 00147 if (!same_port) 00148 base_port += 2; 00149 ret = ffurl_open(&fd, url, AVIO_FLAG_WRITE); 00150 if (ret) { 00151 ret = AVERROR(EIO); 00152 goto fail; 00153 } 00154 s->streams[i]->priv_data = contexts[i] = 00155 ff_rtp_chain_mux_open(s, s->streams[i], fd, 0); 00156 av_strlcpy(contexts[i]->filename, url, sizeof(contexts[i]->filename)); 00157 } 00158 00159 ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port, 00160 "?ttl=%d&connect=1", ttl); 00161 ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_WRITE); 00162 if (ret) { 00163 ret = AVERROR(EIO); 00164 goto fail; 00165 } 00166 00167 udp_fd = ffurl_get_file_handle(sap->ann_fd); 00168 if (getsockname(udp_fd, (struct sockaddr*) &localaddr, &addrlen)) { 00169 ret = AVERROR(EIO); 00170 goto fail; 00171 } 00172 if (localaddr.ss_family != AF_INET 00173 #if HAVE_STRUCT_SOCKADDR_IN6 00174 && localaddr.ss_family != AF_INET6 00175 #endif 00176 ) { 00177 av_log(s, AV_LOG_ERROR, "Unsupported protocol family\n"); 00178 ret = AVERROR(EIO); 00179 goto fail; 00180 } 00181 sap->ann_size = 8192; 00182 sap->ann = av_mallocz(sap->ann_size); 00183 if (!sap->ann) { 00184 ret = AVERROR(EIO); 00185 goto fail; 00186 } 00187 sap->ann[pos] = (1 << 5); 00188 #if HAVE_STRUCT_SOCKADDR_IN6 00189 if (localaddr.ss_family == AF_INET6) 00190 sap->ann[pos] |= 0x10; 00191 #endif 00192 pos++; 00193 sap->ann[pos++] = 0; /* Authentication length */ 00194 AV_WB16(&sap->ann[pos], av_get_random_seed()); 00195 pos += 2; 00196 if (localaddr.ss_family == AF_INET) { 00197 memcpy(&sap->ann[pos], &((struct sockaddr_in*)&localaddr)->sin_addr, 00198 sizeof(struct in_addr)); 00199 pos += sizeof(struct in_addr); 00200 #if HAVE_STRUCT_SOCKADDR_IN6 00201 } else { 00202 memcpy(&sap->ann[pos], &((struct sockaddr_in6*)&localaddr)->sin6_addr, 00203 sizeof(struct in6_addr)); 00204 pos += sizeof(struct in6_addr); 00205 #endif 00206 } 00207 00208 av_strlcpy(&sap->ann[pos], "application/sdp", sap->ann_size - pos); 00209 pos += strlen(&sap->ann[pos]) + 1; 00210 00211 if (av_sdp_create(contexts, s->nb_streams, &sap->ann[pos], 00212 sap->ann_size - pos)) { 00213 ret = AVERROR_INVALIDDATA; 00214 goto fail; 00215 } 00216 av_freep(&contexts); 00217 av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", &sap->ann[pos]); 00218 pos += strlen(&sap->ann[pos]); 00219 sap->ann_size = pos; 00220 00221 if (sap->ann_size > sap->ann_fd->max_packet_size) { 00222 av_log(s, AV_LOG_ERROR, "Announcement too large to send in one " 00223 "packet\n"); 00224 goto fail; 00225 } 00226 00227 return 0; 00228 00229 fail: 00230 av_free(contexts); 00231 sap_write_close(s); 00232 return ret; 00233 } 00234 00235 static int sap_write_packet(AVFormatContext *s, AVPacket *pkt) 00236 { 00237 AVFormatContext *rtpctx; 00238 struct SAPState *sap = s->priv_data; 00239 int64_t now = av_gettime(); 00240 00241 if (!sap->last_time || now - sap->last_time > 5000000) { 00242 int ret = ffurl_write(sap->ann_fd, sap->ann, sap->ann_size); 00243 /* Don't abort even if we get "Destination unreachable" */ 00244 if (ret < 0 && ret != AVERROR(ECONNREFUSED)) 00245 return ret; 00246 sap->last_time = now; 00247 } 00248 rtpctx = s->streams[pkt->stream_index]->priv_data; 00249 return ff_write_chained(rtpctx, 0, pkt, s); 00250 } 00251 00252 AVOutputFormat ff_sap_muxer = { 00253 "sap", 00254 NULL_IF_CONFIG_SMALL("SAP output format"), 00255 NULL, 00256 NULL, 00257 sizeof(struct SAPState), 00258 CODEC_ID_AAC, 00259 CODEC_ID_MPEG4, 00260 sap_write_header, 00261 sap_write_packet, 00262 sap_write_close, 00263 .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER, 00264 }; 00265