2 * Session Announcement Protocol (RFC 2974) muxer
3 * Copyright (c) 2010 Martin Storsjo
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "libavutil/parseutils.h"
24 #include "libavutil/random_seed.h"
25 #include "libavutil/avstring.h"
26 #include "libavutil/dict.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/time.h"
31 #include "os_support.h"
32 #include "rtpenc_chain.h"
42 static int sap_write_close(AVFormatContext
*s
)
44 struct SAPState
*sap
= s
->priv_data
;
47 for (i
= 0; i
< s
->nb_streams
; i
++) {
48 AVFormatContext
*rtpctx
= s
->streams
[i
]->priv_data
;
51 av_write_trailer(rtpctx
);
52 avio_close(rtpctx
->pb
);
53 avformat_free_context(rtpctx
);
54 s
->streams
[i
]->priv_data
= NULL
;
57 if (sap
->last_time
&& sap
->ann
&& sap
->ann_fd
) {
58 sap
->ann
[0] |= 4; /* Session deletion*/
59 ffurl_write(sap
->ann_fd
, sap
->ann
, sap
->ann_size
);
64 ffurl_close(sap
->ann_fd
);
69 static int sap_write_header(AVFormatContext
*s
)
71 struct SAPState
*sap
= s
->priv_data
;
72 char host
[1024], path
[1024], url
[1024], announce_addr
[50] = "";
74 int port
= 9875, base_port
= 5004, i
, pos
= 0, same_port
= 0, ttl
= 255;
75 AVFormatContext
**contexts
= NULL
;
77 struct sockaddr_storage localaddr
;
78 socklen_t addrlen
= sizeof(localaddr
);
80 AVDictionaryEntry
* title
= av_dict_get(s
->metadata
, "title", NULL
, 0);
82 if (!ff_network_init())
85 /* extract hostname and port */
86 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &base_port
,
87 path
, sizeof(path
), s
->filename
);
91 /* search for options */
92 option_list
= strrchr(path
, '?');
95 if (av_find_info_tag(buf
, sizeof(buf
), "announce_port", option_list
)) {
96 port
= strtol(buf
, NULL
, 10);
98 if (av_find_info_tag(buf
, sizeof(buf
), "same_port", option_list
)) {
99 same_port
= strtol(buf
, NULL
, 10);
101 if (av_find_info_tag(buf
, sizeof(buf
), "ttl", option_list
)) {
102 ttl
= strtol(buf
, NULL
, 10);
104 if (av_find_info_tag(buf
, sizeof(buf
), "announce_addr", option_list
)) {
105 av_strlcpy(announce_addr
, buf
, sizeof(announce_addr
));
109 if (!announce_addr
[0]) {
110 struct addrinfo hints
= { 0 }, *ai
= NULL
;
111 hints
.ai_family
= AF_UNSPEC
;
112 if (getaddrinfo(host
, NULL
, &hints
, &ai
)) {
113 av_log(s
, AV_LOG_ERROR
, "Unable to resolve %s\n", host
);
117 if (ai
->ai_family
== AF_INET
) {
118 /* Also known as sap.mcast.net */
119 av_strlcpy(announce_addr
, "224.2.127.254", sizeof(announce_addr
));
120 #if HAVE_STRUCT_SOCKADDR_IN6
121 } else if (ai
->ai_family
== AF_INET6
) {
122 /* With IPv6, you can use the same destination in many different
123 * multicast subnets, to choose how far you want it routed.
124 * This one is intended to be routed globally. */
125 av_strlcpy(announce_addr
, "ff0e::2:7ffe", sizeof(announce_addr
));
129 av_log(s
, AV_LOG_ERROR
, "Host %s resolved to unsupported "
130 "address family\n", host
);
137 contexts
= av_mallocz_array(s
->nb_streams
, sizeof(AVFormatContext
*));
139 ret
= AVERROR(ENOMEM
);
143 if (s
->start_time_realtime
== 0 || s
->start_time_realtime
== AV_NOPTS_VALUE
)
144 s
->start_time_realtime
= av_gettime();
145 for (i
= 0; i
< s
->nb_streams
; i
++) {
148 ff_url_join(url
, sizeof(url
), "rtp", NULL
, host
, base_port
,
152 ret
= ffurl_open(&fd
, url
, AVIO_FLAG_WRITE
, &s
->interrupt_callback
, NULL
);
157 ret
= ff_rtp_chain_mux_open(&contexts
[i
], s
, s
->streams
[i
], fd
, 0, i
);
160 s
->streams
[i
]->priv_data
= contexts
[i
];
161 s
->streams
[i
]->time_base
= contexts
[i
]->streams
[0]->time_base
;
162 av_strlcpy(contexts
[i
]->filename
, url
, sizeof(contexts
[i
]->filename
));
165 if (s
->nb_streams
> 0 && title
)
166 av_dict_set(&contexts
[0]->metadata
, "title", title
->value
, 0);
168 ff_url_join(url
, sizeof(url
), "udp", NULL
, announce_addr
, port
,
169 "?ttl=%d&connect=1", ttl
);
170 ret
= ffurl_open(&sap
->ann_fd
, url
, AVIO_FLAG_WRITE
,
171 &s
->interrupt_callback
, NULL
);
177 udp_fd
= ffurl_get_file_handle(sap
->ann_fd
);
178 if (getsockname(udp_fd
, (struct sockaddr
*) &localaddr
, &addrlen
)) {
182 if (localaddr
.ss_family
!= AF_INET
183 #if HAVE_STRUCT_SOCKADDR_IN6
184 && localaddr
.ss_family
!= AF_INET6
187 av_log(s
, AV_LOG_ERROR
, "Unsupported protocol family\n");
191 sap
->ann_size
= 8192;
192 sap
->ann
= av_mallocz(sap
->ann_size
);
197 sap
->ann
[pos
] = (1 << 5);
198 #if HAVE_STRUCT_SOCKADDR_IN6
199 if (localaddr
.ss_family
== AF_INET6
)
200 sap
->ann
[pos
] |= 0x10;
203 sap
->ann
[pos
++] = 0; /* Authentication length */
204 AV_WB16(&sap
->ann
[pos
], av_get_random_seed());
206 if (localaddr
.ss_family
== AF_INET
) {
207 memcpy(&sap
->ann
[pos
], &((struct sockaddr_in
*)&localaddr
)->sin_addr
,
208 sizeof(struct in_addr
));
209 pos
+= sizeof(struct in_addr
);
210 #if HAVE_STRUCT_SOCKADDR_IN6
212 memcpy(&sap
->ann
[pos
], &((struct sockaddr_in6
*)&localaddr
)->sin6_addr
,
213 sizeof(struct in6_addr
));
214 pos
+= sizeof(struct in6_addr
);
218 av_strlcpy(&sap
->ann
[pos
], "application/sdp", sap
->ann_size
- pos
);
219 pos
+= strlen(&sap
->ann
[pos
]) + 1;
221 if (av_sdp_create(contexts
, s
->nb_streams
, &sap
->ann
[pos
],
222 sap
->ann_size
- pos
)) {
223 ret
= AVERROR_INVALIDDATA
;
227 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", &sap
->ann
[pos
]);
228 pos
+= strlen(&sap
->ann
[pos
]);
231 if (sap
->ann_size
> sap
->ann_fd
->max_packet_size
) {
232 av_log(s
, AV_LOG_ERROR
, "Announcement too large to send in one "
245 static int sap_write_packet(AVFormatContext
*s
, AVPacket
*pkt
)
247 AVFormatContext
*rtpctx
;
248 struct SAPState
*sap
= s
->priv_data
;
249 int64_t now
= av_gettime_relative();
251 if (!sap
->last_time
|| now
- sap
->last_time
> 5000000) {
252 int ret
= ffurl_write(sap
->ann_fd
, sap
->ann
, sap
->ann_size
);
253 /* Don't abort even if we get "Destination unreachable" */
254 if (ret
< 0 && ret
!= AVERROR(ECONNREFUSED
))
256 sap
->last_time
= now
;
258 rtpctx
= s
->streams
[pkt
->stream_index
]->priv_data
;
259 return ff_write_chained(rtpctx
, 0, pkt
, s
, 0);
262 AVOutputFormat ff_sap_muxer
= {
264 .long_name
= NULL_IF_CONFIG_SMALL("SAP output"),
265 .priv_data_size
= sizeof(struct SAPState
),
266 .audio_codec
= AV_CODEC_ID_AAC
,
267 .video_codec
= AV_CODEC_ID_MPEG4
,
268 .write_header
= sap_write_header
,
269 .write_packet
= sap_write_packet
,
270 .write_trailer
= sap_write_close
,
271 .flags
= AVFMT_NOFILE
| AVFMT_GLOBALHEADER
,