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