sapenc.c
Go to the documentation of this file.
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/dict.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/time.h"
29 #include "libavutil/dict.h"
30 #include "internal.h"
31 #include "network.h"
32 #include "os_support.h"
33 #include "rtpenc_chain.h"
34 #include "url.h"
35 
36 struct SAPState {
38  int ann_size;
40  int64_t last_time;
41 };
42 
44 {
45  struct SAPState *sap = s->priv_data;
46  int i;
47 
48  for (i = 0; i < s->nb_streams; i++) {
49  AVFormatContext *rtpctx = s->streams[i]->priv_data;
50  if (!rtpctx)
51  continue;
52  av_write_trailer(rtpctx);
53  avio_close(rtpctx->pb);
54  avformat_free_context(rtpctx);
55  s->streams[i]->priv_data = NULL;
56  }
57 
58  if (sap->last_time && sap->ann && sap->ann_fd) {
59  sap->ann[0] |= 4; /* Session deletion*/
60  ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
61  }
62 
63  av_freep(&sap->ann);
64  if (sap->ann_fd)
65  ffurl_close(sap->ann_fd);
67  return 0;
68 }
69 
71 {
72  struct SAPState *sap = s->priv_data;
73  char host[1024], path[1024], url[1024], announce_addr[50] = "";
74  char *option_list;
75  int port = 9875, base_port = 5004, i, pos = 0, same_port = 0, ttl = 255;
76  AVFormatContext **contexts = NULL;
77  int ret = 0;
78  struct sockaddr_storage localaddr;
79  socklen_t addrlen = sizeof(localaddr);
80  int udp_fd;
81  AVDictionaryEntry* title = av_dict_get(s->metadata, "title", NULL, 0);
82 
83  if (!ff_network_init())
84  return AVERROR(EIO);
85 
86  /* extract hostname and port */
87  av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &base_port,
88  path, sizeof(path), s->filename);
89  if (base_port < 0)
90  base_port = 5004;
91 
92  /* search for options */
93  option_list = strrchr(path, '?');
94  if (option_list) {
95  char buf[50];
96  if (av_find_info_tag(buf, sizeof(buf), "announce_port", option_list)) {
97  port = strtol(buf, NULL, 10);
98  }
99  if (av_find_info_tag(buf, sizeof(buf), "same_port", option_list)) {
100  same_port = strtol(buf, NULL, 10);
101  }
102  if (av_find_info_tag(buf, sizeof(buf), "ttl", option_list)) {
103  ttl = strtol(buf, NULL, 10);
104  }
105  if (av_find_info_tag(buf, sizeof(buf), "announce_addr", option_list)) {
106  av_strlcpy(announce_addr, buf, sizeof(announce_addr));
107  }
108  }
109 
110  if (!announce_addr[0]) {
111  struct addrinfo hints = { 0 }, *ai = NULL;
112  hints.ai_family = AF_UNSPEC;
113  if (getaddrinfo(host, NULL, &hints, &ai)) {
114  av_log(s, AV_LOG_ERROR, "Unable to resolve %s\n", host);
115  ret = AVERROR(EIO);
116  goto fail;
117  }
118  if (ai->ai_family == AF_INET) {
119  /* Also known as sap.mcast.net */
120  av_strlcpy(announce_addr, "224.2.127.254", sizeof(announce_addr));
121 #if HAVE_STRUCT_SOCKADDR_IN6
122  } else if (ai->ai_family == AF_INET6) {
123  /* With IPv6, you can use the same destination in many different
124  * multicast subnets, to choose how far you want it routed.
125  * This one is intended to be routed globally. */
126  av_strlcpy(announce_addr, "ff0e::2:7ffe", sizeof(announce_addr));
127 #endif
128  } else {
129  freeaddrinfo(ai);
130  av_log(s, AV_LOG_ERROR, "Host %s resolved to unsupported "
131  "address family\n", host);
132  ret = AVERROR(EIO);
133  goto fail;
134  }
135  freeaddrinfo(ai);
136  }
137 
138  contexts = av_mallocz(sizeof(AVFormatContext*) * s->nb_streams);
139  if (!contexts) {
140  ret = AVERROR(ENOMEM);
141  goto fail;
142  }
143 
145  for (i = 0; i < s->nb_streams; i++) {
146  URLContext *fd;
147 
148  ff_url_join(url, sizeof(url), "rtp", NULL, host, base_port,
149  "?ttl=%d", ttl);
150  if (!same_port)
151  base_port += 2;
152  ret = ffurl_open(&fd, url, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL);
153  if (ret) {
154  ret = AVERROR(EIO);
155  goto fail;
156  }
157  ret = ff_rtp_chain_mux_open(&contexts[i], s, s->streams[i], fd, 0, i);
158  if (ret < 0)
159  goto fail;
160  s->streams[i]->priv_data = contexts[i];
161  av_strlcpy(contexts[i]->filename, url, sizeof(contexts[i]->filename));
162  }
163 
164  if (s->nb_streams > 0 && title)
165  av_dict_set(&contexts[0]->metadata, "title", title->value, 0);
166 
167  ff_url_join(url, sizeof(url), "udp", NULL, announce_addr, port,
168  "?ttl=%d&connect=1", ttl);
169  ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_WRITE,
170  &s->interrupt_callback, NULL);
171  if (ret) {
172  ret = AVERROR(EIO);
173  goto fail;
174  }
175 
176  udp_fd = ffurl_get_file_handle(sap->ann_fd);
177  if (getsockname(udp_fd, (struct sockaddr*) &localaddr, &addrlen)) {
178  ret = AVERROR(EIO);
179  goto fail;
180  }
181  if (localaddr.ss_family != AF_INET
183  && localaddr.ss_family != AF_INET6
184 #endif
185  ) {
186  av_log(s, AV_LOG_ERROR, "Unsupported protocol family\n");
187  ret = AVERROR(EIO);
188  goto fail;
189  }
190  sap->ann_size = 8192;
191  sap->ann = av_mallocz(sap->ann_size);
192  if (!sap->ann) {
193  ret = AVERROR(EIO);
194  goto fail;
195  }
196  sap->ann[pos] = (1 << 5);
197 #if HAVE_STRUCT_SOCKADDR_IN6
198  if (localaddr.ss_family == AF_INET6)
199  sap->ann[pos] |= 0x10;
200 #endif
201  pos++;
202  sap->ann[pos++] = 0; /* Authentication length */
203  AV_WB16(&sap->ann[pos], av_get_random_seed());
204  pos += 2;
205  if (localaddr.ss_family == AF_INET) {
206  memcpy(&sap->ann[pos], &((struct sockaddr_in*)&localaddr)->sin_addr,
207  sizeof(struct in_addr));
208  pos += sizeof(struct in_addr);
209 #if HAVE_STRUCT_SOCKADDR_IN6
210  } else {
211  memcpy(&sap->ann[pos], &((struct sockaddr_in6*)&localaddr)->sin6_addr,
212  sizeof(struct in6_addr));
213  pos += sizeof(struct in6_addr);
214 #endif
215  }
216 
217  av_strlcpy(&sap->ann[pos], "application/sdp", sap->ann_size - pos);
218  pos += strlen(&sap->ann[pos]) + 1;
219 
220  if (av_sdp_create(contexts, s->nb_streams, &sap->ann[pos],
221  sap->ann_size - pos)) {
222  ret = AVERROR_INVALIDDATA;
223  goto fail;
224  }
225  av_freep(&contexts);
226  av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", &sap->ann[pos]);
227  pos += strlen(&sap->ann[pos]);
228  sap->ann_size = pos;
229 
230  if (sap->ann_size > sap->ann_fd->max_packet_size) {
231  av_log(s, AV_LOG_ERROR, "Announcement too large to send in one "
232  "packet\n");
233  goto fail;
234  }
235 
236  return 0;
237 
238 fail:
239  av_free(contexts);
240  sap_write_close(s);
241  return ret;
242 }
243 
245 {
246  AVFormatContext *rtpctx;
247  struct SAPState *sap = s->priv_data;
248  int64_t now = av_gettime();
249 
250  if (!sap->last_time || now - sap->last_time > 5000000) {
251  int ret = ffurl_write(sap->ann_fd, sap->ann, sap->ann_size);
252  /* Don't abort even if we get "Destination unreachable" */
253  if (ret < 0 && ret != AVERROR(ECONNREFUSED))
254  return ret;
255  sap->last_time = now;
256  }
257  rtpctx = s->streams[pkt->stream_index]->priv_data;
258  return ff_write_chained(rtpctx, 0, pkt, s);
259 }
260 
262  .name = "sap",
263  .long_name = NULL_IF_CONFIG_SMALL("SAP output"),
264  .priv_data_size = sizeof(struct SAPState),
265  .audio_codec = AV_CODEC_ID_AAC,
266  .video_codec = AV_CODEC_ID_MPEG4,
267  .write_header = sap_write_header,
268  .write_packet = sap_write_packet,
269  .write_trailer = sap_write_close,
270  .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER,
271 };
void av_url_split(char *proto, int proto_size, char *authorization, int authorization_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, const char *url)
Split a URL string into components.
void * av_mallocz(size_t size)
Allocate a block of size bytes with alignment suitable for all memory accesses (including vectors if ...
Definition: mem.c:205
const char * s
Definition: avisynth_c.h:668
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition: error.h:59
int64_t start_time_realtime
Start time of the stream in real world time, in microseconds since the unix epoch (00:00 1st January ...
Definition: avformat.h:1101
AVIOInterruptCB interrupt_callback
Custom interrupt callbacks for the I/O layer.
Definition: avformat.h:1125
int ffurl_write(URLContext *h, const unsigned char *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: avio.c:317
int64_t last_time
Definition: sapenc.c:40
title('Sinusoid at 1/4 the Spampling Rate')
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:333
void ff_network_close(void)
Definition: network.c:173
void * priv_data
Definition: avformat.h:663
#define freeaddrinfo
Definition: network.h:195
AVOutputFormat ff_sap_muxer
Definition: sapenc.c:261
AVDictionaryEntry * av_dict_get(AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition: dict.c:39
int ff_url_join(char *str, int size, const char *proto, const char *authorization, const char *hostname, int port, const char *fmt,...) av_printf_format(7
Assemble a URL string from components.
void av_freep(void *arg)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc() and set the pointer ...
Definition: mem.c:198
Format I/O context.
Definition: avformat.h:944
static int sap_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition: sapenc.c:244
Public dictionary API.
uint8_t
int ff_network_init(void)
Definition: network.c:126
miscellaneous OS support macros and functions.
uint16_t ss_family
Definition: network.h:105
static AVPacket pkt
Definition: demuxing.c:56
AVStream ** streams
Definition: avformat.h:992
int av_sdp_create(AVFormatContext *ac[], int n_files, char *buf, int size)
Generate an SDP for an RTP session.
Definition: sdp.c:703
int av_find_info_tag(char *arg, int arg_size, const char *tag1, const char *info)
Attempt to find a specific tag in a URL.
Definition: parseutils.c:647
AVDictionary * metadata
Definition: avformat.h:1092
void av_free(void *ptr)
Free a memory block which has been allocated with av_malloc(z)() or av_realloc(). ...
Definition: mem.c:183
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification. ...
int avio_close(AVIOContext *s)
Close the resource accessed by the AVIOContext s and free it.
Definition: aviobuf.c:821
void av_log(void *avcl, int level, const char *fmt,...)
Definition: log.c:246
size_t av_strlcpy(char *dst, const char *src, size_t size)
Copy the string src to dst, but no more than size - 1 bytes, and null-terminate dst.
Definition: avstring.c:82
#define HAVE_STRUCT_SOCKADDR_IN6
Definition: config.h:256
unsigned int nb_streams
A list of all streams in the file.
Definition: avformat.h:991
#define AV_LOG_VERBOSE
Definition: log.h:157
char filename[1024]
input or output filename
Definition: avformat.h:994
URLContext * ann_fd
Definition: sapdec.c:36
ret
Definition: avfilter.c:821
uint8_t * ann
Definition: sapenc.c:37
#define AVFMT_GLOBALHEADER
Format wants global header.
Definition: avformat.h:351
const char * name
Definition: avformat.h:378
static int sap_write_close(AVFormatContext *s)
Definition: sapenc.c:43
int ffurl_get_file_handle(URLContext *h)
Return the file descriptor associated with this URL.
Definition: avio.c:399
int64_t av_gettime(void)
Get the current time in microseconds.
Definition: time.c:39
NULL
Definition: eval.c:55
static int sap_write_header(AVFormatContext *s)
Definition: sapenc.c:70
int ann_size
Definition: sapenc.c:38
AVIOContext * pb
I/O context.
Definition: avformat.h:977
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:148
void * buf
Definition: avisynth_c.h:594
Definition: url.h:41
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:62
synthesis window for stochastic i
#define AV_WB16(p, darg)
Definition: intreadwrite.h:237
void avformat_free_context(AVFormatContext *s)
Free an AVFormatContext and all its streams.
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFilterBuffer structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Buffer references ownership and permissions
misc parsing utilities
int ffurl_close(URLContext *h)
Definition: avio.c:359
#define getaddrinfo
Definition: network.h:194
Main libavformat public API header.
int ff_rtp_chain_mux_open(AVFormatContext **out, AVFormatContext *s, AVStream *st, URLContext *handle, int packet_size, int idx)
Definition: rtpenc_chain.c:29
#define AVFMT_NOFILE
Demuxer will use avio_open, no opened file should be provided by the caller.
Definition: avformat.h:345
int ffurl_open(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options)
Create an URLContext for accessing to the resource indicated by url, and open it. ...
Definition: avio.c:247
int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt, AVFormatContext *src)
Write a packet to another muxer than the one the user originally intended.
char * value
Definition: dict.h:82
void * priv_data
Format private data.
Definition: avformat.h:964
int av_write_trailer(AVFormatContext *s)
Write the stream trailer to an output media file and free the file private data.
Definition: mux.c:769
int max_packet_size
if non zero, the stream is packetized with this max packet size
Definition: url.h:47
unbuffered private I/O API
uint32_t av_get_random_seed(void)
Get a seed to use in conjunction with random functions.
Definition: random_seed.c:105
This structure stores compressed data.
int ai_family
Definition: network.h:116