annotate ffmpeg/libavformat/sapenc.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents f445c3017523
children
rev   line source
yading@11 1 /*
yading@11 2 * Session Announcement Protocol (RFC 2974) muxer
yading@11 3 * Copyright (c) 2010 Martin Storsjo
yading@11 4 *
yading@11 5 * This file is part of FFmpeg.
yading@11 6 *
yading@11 7 * FFmpeg is free software; you can redistribute it and/or
yading@11 8 * modify it under the terms of the GNU Lesser General Public
yading@11 9 * License as published by the Free Software Foundation; either
yading@11 10 * version 2.1 of the License, or (at your option) any later version.
yading@11 11 *
yading@11 12 * FFmpeg is distributed in the hope that it will be useful,
yading@11 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@11 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@11 15 * Lesser General Public License for more details.
yading@11 16 *
yading@11 17 * You should have received a copy of the GNU Lesser General Public
yading@11 18 * License along with FFmpeg; if not, write to the Free Software
yading@11 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@11 20 */
yading@11 21
yading@11 22 #include "avformat.h"
yading@11 23 #include "libavutil/parseutils.h"
yading@11 24 #include "libavutil/random_seed.h"
yading@11 25 #include "libavutil/avstring.h"
yading@11 26 #include "libavutil/dict.h"
yading@11 27 #include "libavutil/intreadwrite.h"
yading@11 28 #include "libavutil/time.h"
yading@11 29 #include "libavutil/dict.h"
yading@11 30 #include "internal.h"
yading@11 31 #include "network.h"
yading@11 32 #include "os_support.h"
yading@11 33 #include "rtpenc_chain.h"
yading@11 34 #include "url.h"
yading@11 35
yading@11 36 struct SAPState {
yading@11 37 uint8_t *ann;
yading@11 38 int ann_size;
yading@11 39 URLContext *ann_fd;
yading@11 40 int64_t last_time;
yading@11 41 };
yading@11 42
yading@11 43 static int sap_write_close(AVFormatContext *s)
yading@11 44 {
yading@11 45 struct SAPState *sap = s->priv_data;
yading@11 46 int i;
yading@11 47
yading@11 48 for (i = 0; i < s->nb_streams; i++) {
yading@11 49 AVFormatContext *rtpctx = s->streams[i]->priv_data;
yading@11 50 if (!rtpctx)
yading@11 51 continue;
yading@11 52 av_write_trailer(rtpctx);
yading@11 53 avio_close(rtpctx->pb);
yading@11 54 avformat_free_context(rtpctx);
yading@11 55 s->streams[i]->priv_data = NULL;
yading@11 56 }
yading@11 57
yading@11 58 if (sap->last_time && sap->ann && sap->ann_fd) {
yading@11 59 sap->ann[0] |= 4; /* Session deletion*/
yading@11 60 ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
yading@11 61 }
yading@11 62
yading@11 63 av_freep(&sap->ann);
yading@11 64 if (sap->ann_fd)
yading@11 65 ffurl_close(sap->ann_fd);
yading@11 66 ff_network_close();
yading@11 67 return 0;
yading@11 68 }
yading@11 69
yading@11 70 static int sap_write_header(AVFormatContext *s)
yading@11 71 {
yading@11 72 struct SAPState *sap = s->priv_data;
yading@11 73 char host[1024], path[1024], url[1024], announce_addr[50] = "";
yading@11 74 char *option_list;
yading@11 75 int port = 9875, base_port = 5004, i, pos = 0, same_port = 0, ttl = 255;
yading@11 76 AVFormatContext **contexts = NULL;
yading@11 77 int ret = 0;
yading@11 78 struct sockaddr_storage localaddr;
yading@11 79 socklen_t addrlen = sizeof(localaddr);
yading@11 80 int udp_fd;
yading@11 81 AVDictionaryEntry* title = av_dict_get(s->metadata, "title", NULL, 0);
yading@11 82
yading@11 83 if (!ff_network_init())
yading@11 84 return AVERROR(EIO);
yading@11 85
yading@11 86 /* extract hostname and port */
yading@11 87 av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &base_port,
yading@11 88 path, sizeof(path), s->filename);
yading@11 89 if (base_port < 0)
yading@11 90 base_port = 5004;
yading@11 91
yading@11 92 /* search for options */
yading@11 93 option_list = strrchr(path, '?');
yading@11 94 if (option_list) {
yading@11 95 char buf[50];
yading@11 96 if (av_find_info_tag(buf, sizeof(buf), "announce_port", option_list)) {
yading@11 97 port = strtol(buf, NULL, 10);
yading@11 98 }
yading@11 99 if (av_find_info_tag(buf, sizeof(buf), "same_port", option_list)) {
yading@11 100 same_port = strtol(buf, NULL, 10);
yading@11 101 }
yading@11 102 if (av_find_info_tag(buf, sizeof(buf), "ttl", option_list)) {
yading@11 103 ttl = strtol(buf, NULL, 10);
yading@11 104 }
yading@11 105 if (av_find_info_tag(buf, sizeof(buf), "announce_addr", option_list)) {
yading@11 106 av_strlcpy(announce_addr, buf, sizeof(announce_addr));
yading@11 107 }
yading@11 108 }
yading@11 109
yading@11 110 if (!announce_addr[0]) {
yading@11 111 struct addrinfo hints = { 0 }, *ai = NULL;
yading@11 112 hints.ai_family = AF_UNSPEC;
yading@11 113 if (getaddrinfo(host, NULL, &hints, &ai)) {
yading@11 114 av_log(s, AV_LOG_ERROR, "Unable to resolve %s\n", host);
yading@11 115 ret = AVERROR(EIO);
yading@11 116 goto fail;
yading@11 117 }
yading@11 118 if (ai->ai_family == AF_INET) {
yading@11 119 /* Also known as sap.mcast.net */
yading@11 120 av_strlcpy(announce_addr, "224.2.127.254", sizeof(announce_addr));
yading@11 121 #if HAVE_STRUCT_SOCKADDR_IN6
yading@11 122 } else if (ai->ai_family == AF_INET6) {
yading@11 123 /* With IPv6, you can use the same destination in many different
yading@11 124 * multicast subnets, to choose how far you want it routed.
yading@11 125 * This one is intended to be routed globally. */
yading@11 126 av_strlcpy(announce_addr, "ff0e::2:7ffe", sizeof(announce_addr));
yading@11 127 #endif
yading@11 128 } else {
yading@11 129 freeaddrinfo(ai);
yading@11 130 av_log(s, AV_LOG_ERROR, "Host %s resolved to unsupported "
yading@11 131 "address family\n", host);
yading@11 132 ret = AVERROR(EIO);
yading@11 133 goto fail;
yading@11 134 }
yading@11 135 freeaddrinfo(ai);
yading@11 136 }
yading@11 137
yading@11 138 contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams);
yading@11 139 if (!contexts) {
yading@11 140 ret = AVERROR(ENOMEM);
yading@11 141 goto fail;
yading@11 142 }
yading@11 143
yading@11 144 s->start_time_realtime = av_gettime();
yading@11 145 for (i = 0; i < s->nb_streams; i++) {
yading@11 146 URLContext *fd;
yading@11 147
yading@11 148 ff_url_join(url, sizeof(url), "rtp", NULL, host, base_port,
yading@11 149 "?ttl=%d", ttl);
yading@11 150 if (!same_port)
yading@11 151 base_port += 2;
yading@11 152 ret = ffurl_open(&fd, url, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL);
yading@11 153 if (ret) {
yading@11 154 ret = AVERROR(EIO);
yading@11 155 goto fail;
yading@11 156 }
yading@11 157 ret = ff_rtp_chain_mux_open(&contexts[i], s, s->streams[i], fd, 0, i);
yading@11 158 if (ret < 0)
yading@11 159 goto fail;
yading@11 160 s->streams[i]->priv_data = contexts[i];
yading@11 161 av_strlcpy(contexts[i]->filename, url, sizeof(contexts[i]->filename));
yading@11 162 }
yading@11 163
yading@11 164 if (s->nb_streams > 0 && title)
yading@11 165 av_dict_set(&contexts[0]->metadata, "title", title->value, 0);
yading@11 166
yading@11 167 ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port,
yading@11 168 "?ttl=%d&connect=1", ttl);
yading@11 169 ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_WRITE,
yading@11 170 &s->interrupt_callback, NULL);
yading@11 171 if (ret) {
yading@11 172 ret = AVERROR(EIO);
yading@11 173 goto fail;
yading@11 174 }
yading@11 175
yading@11 176 udp_fd = ffurl_get_file_handle(sap->ann_fd);
yading@11 177 if (getsockname(udp_fd, (struct sockaddr*) &localaddr, &addrlen)) {
yading@11 178 ret = AVERROR(EIO);
yading@11 179 goto fail;
yading@11 180 }
yading@11 181 if (localaddr.ss_family != AF_INET
yading@11 182 #if HAVE_STRUCT_SOCKADDR_IN6
yading@11 183 && localaddr.ss_family != AF_INET6
yading@11 184 #endif
yading@11 185 ) {
yading@11 186 av_log(s, AV_LOG_ERROR, "Unsupported protocol family\n");
yading@11 187 ret = AVERROR(EIO);
yading@11 188 goto fail;
yading@11 189 }
yading@11 190 sap->ann_size = 8192;
yading@11 191 sap->ann = av_mallocz(sap->ann_size);
yading@11 192 if (!sap->ann) {
yading@11 193 ret = AVERROR(EIO);
yading@11 194 goto fail;
yading@11 195 }
yading@11 196 sap->ann[pos] = (1 << 5);
yading@11 197 #if HAVE_STRUCT_SOCKADDR_IN6
yading@11 198 if (localaddr.ss_family == AF_INET6)
yading@11 199 sap->ann[pos] |= 0x10;
yading@11 200 #endif
yading@11 201 pos++;
yading@11 202 sap->ann[pos++] = 0; /* Authentication length */
yading@11 203 AV_WB16(&sap->ann[pos], av_get_random_seed());
yading@11 204 pos += 2;
yading@11 205 if (localaddr.ss_family == AF_INET) {
yading@11 206 memcpy(&sap->ann[pos], &((struct sockaddr_in*)&localaddr)->sin_addr,
yading@11 207 sizeof(struct in_addr));
yading@11 208 pos += sizeof(struct in_addr);
yading@11 209 #if HAVE_STRUCT_SOCKADDR_IN6
yading@11 210 } else {
yading@11 211 memcpy(&sap->ann[pos], &((struct sockaddr_in6*)&localaddr)->sin6_addr,
yading@11 212 sizeof(struct in6_addr));
yading@11 213 pos += sizeof(struct in6_addr);
yading@11 214 #endif
yading@11 215 }
yading@11 216
yading@11 217 av_strlcpy(&sap->ann[pos], "application/sdp", sap->ann_size - pos);
yading@11 218 pos += strlen(&sap->ann[pos]) + 1;
yading@11 219
yading@11 220 if (av_sdp_create(contexts, s->nb_streams, &sap->ann[pos],
yading@11 221 sap->ann_size - pos)) {
yading@11 222 ret = AVERROR_INVALIDDATA;
yading@11 223 goto fail;
yading@11 224 }
yading@11 225 av_freep(&contexts);
yading@11 226 av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", &sap->ann[pos]);
yading@11 227 pos += strlen(&sap->ann[pos]);
yading@11 228 sap->ann_size = pos;
yading@11 229
yading@11 230 if (sap->ann_size > sap->ann_fd->max_packet_size) {
yading@11 231 av_log(s, AV_LOG_ERROR, "Announcement too large to send in one "
yading@11 232 "packet\n");
yading@11 233 goto fail;
yading@11 234 }
yading@11 235
yading@11 236 return 0;
yading@11 237
yading@11 238 fail:
yading@11 239 av_free(contexts);
yading@11 240 sap_write_close(s);
yading@11 241 return ret;
yading@11 242 }
yading@11 243
yading@11 244 static int sap_write_packet(AVFormatContext *s, AVPacket *pkt)
yading@11 245 {
yading@11 246 AVFormatContext *rtpctx;
yading@11 247 struct SAPState *sap = s->priv_data;
yading@11 248 int64_t now = av_gettime();
yading@11 249
yading@11 250 if (!sap->last_time || now - sap->last_time > 5000000) {
yading@11 251 int ret = ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
yading@11 252 /* Don't abort even if we get "Destination unreachable" */
yading@11 253 if (ret < 0 && ret != AVERROR(ECONNREFUSED))
yading@11 254 return ret;
yading@11 255 sap->last_time = now;
yading@11 256 }
yading@11 257 rtpctx = s->streams[pkt->stream_index]->priv_data;
yading@11 258 return ff_write_chained(rtpctx, 0, pkt, s);
yading@11 259 }
yading@11 260
yading@11 261 AVOutputFormat ff_sap_muxer = {
yading@11 262 .name = "sap",
yading@11 263 .long_name = NULL_IF_CONFIG_SMALL("SAP output"),
yading@11 264 .priv_data_size = sizeof(struct SAPState),
yading@11 265 .audio_codec = AV_CODEC_ID_AAC,
yading@11 266 .video_codec = AV_CODEC_ID_MPEG4,
yading@11 267 .write_header = sap_write_header,
yading@11 268 .write_packet = sap_write_packet,
yading@11 269 .write_trailer = sap_write_close,
yading@11 270 .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER,
yading@11 271 };