Statistics
| Branch: | Revision:

ffmpeg / libavformat / sapenc.c @ 22a3212e

History | View | Annotate | Download (8.03 KB)

1
/*
2
 * Session Announcement Protocol (RFC 2974) muxer
3
 * Copyright (c) 2010 Martin Storsjo
4
 *
5
 * This file is part of FFmpeg.
6
 *
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.
11
 *
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.
16
 *
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
20
 */
21

    
22
#include "avformat.h"
23
#include "libavutil/parseutils.h"
24
#include "libavutil/random_seed.h"
25
#include "libavutil/avstring.h"
26
#include "libavutil/intreadwrite.h"
27
#include "internal.h"
28
#include "network.h"
29
#include "os_support.h"
30
#include "rtpenc_chain.h"
31

    
32
struct SAPState {
33
    uint8_t    *ann;
34
    int         ann_size;
35
    URLContext *ann_fd;
36
    int64_t     last_time;
37
};
38

    
39
static int sap_write_close(AVFormatContext *s)
40
{
41
    struct SAPState *sap = s->priv_data;
42
    int i;
43

    
44
    for (i = 0; i < s->nb_streams; i++) {
45
        AVFormatContext *rtpctx = s->streams[i]->priv_data;
46
        if (!rtpctx)
47
            continue;
48
        av_write_trailer(rtpctx);
49
        avio_close(rtpctx->pb);
50
        avformat_free_context(rtpctx);
51
        s->streams[i]->priv_data = NULL;
52
    }
53

    
54
    if (sap->last_time && sap->ann && sap->ann_fd) {
55
        sap->ann[0] |= 4; /* Session deletion*/
56
        url_write(sap->ann_fd, sap->ann, sap->ann_size);
57
    }
58

    
59
    av_freep(&sap->ann);
60
    if (sap->ann_fd)
61
        url_close(sap->ann_fd);
62
    ff_network_close();
63
    return 0;
64
}
65

    
66
static int sap_write_header(AVFormatContext *s)
67
{
68
    struct SAPState *sap = s->priv_data;
69
    char host[1024], path[1024], url[1024], announce_addr[50] = "";
70
    char *option_list;
71
    int port = 9875, base_port = 5004, i, pos = 0, same_port = 0, ttl = 255;
72
    AVFormatContext **contexts = NULL;
73
    int ret = 0;
74
    struct sockaddr_storage localaddr;
75
    socklen_t addrlen = sizeof(localaddr);
76
    int udp_fd;
77

    
78
    if (!ff_network_init())
79
        return AVERROR(EIO);
80

    
81
    /* extract hostname and port */
82
    av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &base_port,
83
                 path, sizeof(path), s->filename);
84
    if (base_port < 0)
85
        base_port = 5004;
86

    
87
    /* search for options */
88
    option_list = strrchr(path, '?');
89
    if (option_list) {
90
        char buf[50];
91
        if (av_find_info_tag(buf, sizeof(buf), "announce_port", option_list)) {
92
            port = strtol(buf, NULL, 10);
93
        }
94
        if (av_find_info_tag(buf, sizeof(buf), "same_port", option_list)) {
95
            same_port = strtol(buf, NULL, 10);
96
        }
97
        if (av_find_info_tag(buf, sizeof(buf), "ttl", option_list)) {
98
            ttl = strtol(buf, NULL, 10);
99
        }
100
        if (av_find_info_tag(buf, sizeof(buf), "announce_addr", option_list)) {
101
            av_strlcpy(announce_addr, buf, sizeof(announce_addr));
102
        }
103
    }
104

    
105
    if (!announce_addr[0]) {
106
        struct addrinfo hints, *ai = NULL;
107
        memset(&hints, 0, sizeof(hints));
108
        hints.ai_family = AF_UNSPEC;
109
        if (getaddrinfo(host, NULL, &hints, &ai)) {
110
            av_log(s, AV_LOG_ERROR, "Unable to resolve %s\n", host);
111
            ret = AVERROR(EIO);
112
            goto fail;
113
        }
114
        if (ai->ai_family == AF_INET) {
115
            /* Also known as sap.mcast.net */
116
            av_strlcpy(announce_addr, "224.2.127.254", sizeof(announce_addr));
117
#if HAVE_STRUCT_SOCKADDR_IN6
118
        } else if (ai->ai_family == AF_INET6) {
119
            /* With IPv6, you can use the same destination in many different
120
             * multicast subnets, to choose how far you want it routed.
121
             * This one is intended to be routed globally. */
122
            av_strlcpy(announce_addr, "ff0e::2:7ffe", sizeof(announce_addr));
123
#endif
124
        } else {
125
            freeaddrinfo(ai);
126
            av_log(s, AV_LOG_ERROR, "Host %s resolved to unsupported "
127
                                    "address family\n", host);
128
            ret = AVERROR(EIO);
129
            goto fail;
130
        }
131
        freeaddrinfo(ai);
132
    }
133

    
134
    contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams);
135
    if (!contexts) {
136
        ret = AVERROR(ENOMEM);
137
        goto fail;
138
    }
139

    
140
    s->start_time_realtime = av_gettime();
141
    for (i = 0; i < s->nb_streams; i++) {
142
        URLContext *fd;
143

    
144
        ff_url_join(url, sizeof(url), "rtp", NULL, host, base_port,
145
                    "?ttl=%d", ttl);
146
        if (!same_port)
147
            base_port += 2;
148
        ret = url_open(&fd, url, URL_WRONLY);
149
        if (ret) {
150
            ret = AVERROR(EIO);
151
            goto fail;
152
        }
153
        s->streams[i]->priv_data = contexts[i] =
154
            ff_rtp_chain_mux_open(s, s->streams[i], fd, 0);
155
        av_strlcpy(contexts[i]->filename, url, sizeof(contexts[i]->filename));
156
    }
157

    
158
    ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port,
159
                "?ttl=%d&connect=1", ttl);
160
    ret = url_open(&sap->ann_fd, url, URL_WRONLY);
161
    if (ret) {
162
        ret = AVERROR(EIO);
163
        goto fail;
164
    }
165

    
166
    udp_fd = url_get_file_handle(sap->ann_fd);
167
    if (getsockname(udp_fd, (struct sockaddr*) &localaddr, &addrlen)) {
168
        ret = AVERROR(EIO);
169
        goto fail;
170
    }
171
    if (localaddr.ss_family != AF_INET
172
#if HAVE_STRUCT_SOCKADDR_IN6
173
        && localaddr.ss_family != AF_INET6
174
#endif
175
        ) {
176
        av_log(s, AV_LOG_ERROR, "Unsupported protocol family\n");
177
        ret = AVERROR(EIO);
178
        goto fail;
179
    }
180
    sap->ann_size = 8192;
181
    sap->ann = av_mallocz(sap->ann_size);
182
    if (!sap->ann) {
183
        ret = AVERROR(EIO);
184
        goto fail;
185
    }
186
    sap->ann[pos] = (1 << 5);
187
#if HAVE_STRUCT_SOCKADDR_IN6
188
    if (localaddr.ss_family == AF_INET6)
189
        sap->ann[pos] |= 0x10;
190
#endif
191
    pos++;
192
    sap->ann[pos++] = 0; /* Authentication length */
193
    AV_WB16(&sap->ann[pos], av_get_random_seed());
194
    pos += 2;
195
    if (localaddr.ss_family == AF_INET) {
196
        memcpy(&sap->ann[pos], &((struct sockaddr_in*)&localaddr)->sin_addr,
197
               sizeof(struct in_addr));
198
        pos += sizeof(struct in_addr);
199
#if HAVE_STRUCT_SOCKADDR_IN6
200
    } else {
201
        memcpy(&sap->ann[pos], &((struct sockaddr_in6*)&localaddr)->sin6_addr,
202
               sizeof(struct in6_addr));
203
        pos += sizeof(struct in6_addr);
204
#endif
205
    }
206

    
207
    av_strlcpy(&sap->ann[pos], "application/sdp", sap->ann_size - pos);
208
    pos += strlen(&sap->ann[pos]) + 1;
209

    
210
    if (avf_sdp_create(contexts, s->nb_streams, &sap->ann[pos],
211
                       sap->ann_size - pos)) {
212
        ret = AVERROR_INVALIDDATA;
213
        goto fail;
214
    }
215
    av_freep(&contexts);
216
    av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", &sap->ann[pos]);
217
    pos += strlen(&sap->ann[pos]);
218
    sap->ann_size = pos;
219

    
220
    if (sap->ann_size > url_get_max_packet_size(sap->ann_fd)) {
221
        av_log(s, AV_LOG_ERROR, "Announcement too large to send in one "
222
                                "packet\n");
223
        goto fail;
224
    }
225

    
226
    return 0;
227

    
228
fail:
229
    av_free(contexts);
230
    sap_write_close(s);
231
    return ret;
232
}
233

    
234
static int sap_write_packet(AVFormatContext *s, AVPacket *pkt)
235
{
236
    AVFormatContext *rtpctx;
237
    struct SAPState *sap = s->priv_data;
238
    int64_t now = av_gettime();
239

    
240
    if (!sap->last_time || now - sap->last_time > 5000000) {
241
        int ret = url_write(sap->ann_fd, sap->ann, sap->ann_size);
242
        /* Don't abort even if we get "Destination unreachable" */
243
        if (ret < 0 && ret != AVERROR(ECONNREFUSED))
244
            return ret;
245
        sap->last_time = now;
246
    }
247
    rtpctx = s->streams[pkt->stream_index]->priv_data;
248
    return ff_write_chained(rtpctx, 0, pkt, s);
249
}
250

    
251
AVOutputFormat ff_sap_muxer = {
252
    "sap",
253
    NULL_IF_CONFIG_SMALL("SAP output format"),
254
    NULL,
255
    NULL,
256
    sizeof(struct SAPState),
257
    CODEC_ID_AAC,
258
    CODEC_ID_MPEG4,
259
    sap_write_header,
260
    sap_write_packet,
261
    sap_write_close,
262
    .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER,
263
};
264