3 * Copyright (c) 2012 Luca Barbato
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
27 * url syntax: sctp://host:port[?option=val...]
28 * option: 'listen' : listen for an incoming connection
29 * 'max_streams=n' : set the maximum number of streams
30 * 'reuse=1' : enable reusing the socket [TBD]
32 * by setting the maximum number of streams the protocol will use the
33 * first two bytes of the incoming/outgoing buffer to store the
34 * stream number of the packet being read/written.
40 #include <netinet/in.h>
41 #include <netinet/sctp.h>
49 #include "libavutil/intreadwrite.h"
50 #include "libavutil/parseutils.h"
51 #include "libavutil/opt.h"
55 #include "os_support.h"
59 * The sctp_recvmsg and sctp_sendmsg functions are part of the user
60 * library that offers support for the SCTP kernel Implementation.
61 * To avoid build-time clashes the functions sport an ff_-prefix here.
62 * The main purpose of this code is to provide the SCTP Socket API
63 * mappings for user applications to interface with SCTP in the kernel.
65 * This implementation is based on the Socket API Extensions for SCTP
66 * defined in <draft-ietf-tsvwg-sctpsocket-10.txt>
68 * Copyright (c) 2003 International Business Machines, Corp.
70 * Written or modified by:
71 * Ryan Layer <rmlayer@us.ibm.com>
74 static int ff_sctp_recvmsg(int s
, void *msg
, size_t len
, struct sockaddr
*from
,
75 socklen_t
*fromlen
, struct sctp_sndrcvinfo
*sinfo
,
80 char incmsg
[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo
))];
81 struct msghdr inmsg
= { 0 };
82 struct cmsghdr
*cmsg
= NULL
;
87 inmsg
.msg_name
= from
;
88 inmsg
.msg_namelen
= fromlen
? *fromlen
: 0;
91 inmsg
.msg_control
= incmsg
;
92 inmsg
.msg_controllen
= sizeof(incmsg
);
94 if ((recvb
= recvmsg(s
, &inmsg
, msg_flags
? *msg_flags
: 0)) < 0)
98 *fromlen
= inmsg
.msg_namelen
;
100 *msg_flags
= inmsg
.msg_flags
;
102 for (cmsg
= CMSG_FIRSTHDR(&inmsg
); cmsg
;
103 cmsg
= CMSG_NXTHDR(&inmsg
, cmsg
)) {
104 if ((IPPROTO_SCTP
== cmsg
->cmsg_level
) &&
105 (SCTP_SNDRCV
== cmsg
->cmsg_type
))
111 memcpy(sinfo
, CMSG_DATA(cmsg
), sizeof(struct sctp_sndrcvinfo
));
116 static int ff_sctp_send(int s
, const void *msg
, size_t len
,
117 const struct sctp_sndrcvinfo
*sinfo
, int flags
)
119 struct msghdr outmsg
= { 0 };
122 outmsg
.msg_name
= NULL
;
123 outmsg
.msg_namelen
= 0;
124 outmsg
.msg_iov
= &iov
;
125 iov
.iov_base
= (void*)msg
;
127 outmsg
.msg_iovlen
= 1;
128 outmsg
.msg_controllen
= 0;
131 char outcmsg
[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo
))];
132 struct cmsghdr
*cmsg
;
134 outmsg
.msg_control
= outcmsg
;
135 outmsg
.msg_controllen
= sizeof(outcmsg
);
136 outmsg
.msg_flags
= 0;
138 cmsg
= CMSG_FIRSTHDR(&outmsg
);
139 cmsg
->cmsg_level
= IPPROTO_SCTP
;
140 cmsg
->cmsg_type
= SCTP_SNDRCV
;
141 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct sctp_sndrcvinfo
));
143 outmsg
.msg_controllen
= cmsg
->cmsg_len
;
144 memcpy(CMSG_DATA(cmsg
), sinfo
, sizeof(struct sctp_sndrcvinfo
));
147 return sendmsg(s
, &outmsg
, flags
| MSG_NOSIGNAL
);
150 typedef struct SCTPContext
{
151 const AVClass
*class;
157 struct sockaddr_storage dest_addr
;
160 #define OFFSET(x) offsetof(SCTPContext, x)
161 #define D AV_OPT_FLAG_DECODING_PARAM
162 #define E AV_OPT_FLAG_ENCODING_PARAM
163 static const AVOption options
[] = {
164 { "listen", "Listen for incoming connections", OFFSET(listen
), AV_OPT_TYPE_BOOL
,{ .i64
= 0 }, 0, 1, .flags
= D
|E
},
165 { "timeout", "Connection timeout (in milliseconds)", OFFSET(timeout
), AV_OPT_TYPE_INT
, { .i64
= 10000 }, INT_MIN
, INT_MAX
, .flags
= D
|E
},
166 { "listen_timeout", "Bind timeout (in milliseconds)", OFFSET(listen_timeout
), AV_OPT_TYPE_INT
, { .i64
= -1 }, INT_MIN
, INT_MAX
, .flags
= D
|E
},
167 { "max_streams", "Max stream to allocate", OFFSET(max_streams
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT16_MAX
, .flags
= D
|E
},
171 static const AVClass sctp_class
= {
172 .class_name
= "sctp",
173 .item_name
= av_default_item_name
,
175 .version
= LIBAVUTIL_VERSION_INT
,
178 static int sctp_open(URLContext
*h
, const char *uri
, int flags
)
180 struct addrinfo
*ai
, *cur_ai
;
181 struct addrinfo hints
= { 0 };
182 struct sctp_event_subscribe event
= { 0 };
183 struct sctp_initmsg initparams
= { 0 };
186 SCTPContext
*s
= h
->priv_data
;
189 char hostname
[1024], proto
[1024], path
[1024];
192 av_url_split(proto
, sizeof(proto
), NULL
, 0, hostname
, sizeof(hostname
),
193 &port
, path
, sizeof(path
), uri
);
194 if (strcmp(proto
, "sctp"))
195 return AVERROR(EINVAL
);
196 if (port
<= 0 || port
>= 65536) {
197 av_log(s
, AV_LOG_ERROR
, "Port missing in uri\n");
198 return AVERROR(EINVAL
);
201 p
= strchr(uri
, '?');
203 ret
= ff_parse_opts_from_query_string(s
, p
, 0);
208 hints
.ai_family
= AF_UNSPEC
;
209 hints
.ai_socktype
= SOCK_STREAM
;
210 snprintf(portstr
, sizeof(portstr
), "%d", port
);
211 ret
= getaddrinfo(hostname
, portstr
, &hints
, &ai
);
213 av_log(h
, AV_LOG_ERROR
, "Failed to resolve hostname %s: %s\n",
214 hostname
, gai_strerror(ret
));
221 fd
= ff_socket(cur_ai
->ai_family
, SOCK_STREAM
, IPPROTO_SCTP
, h
);
228 if ((fd
= ff_listen_bind(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
229 s
->listen_timeout
, h
)) < 0) {
234 if ((ret
= ff_listen_connect(fd
, cur_ai
->ai_addr
, cur_ai
->ai_addrlen
,
235 s
->timeout
, h
, !!cur_ai
->ai_next
)) < 0) {
237 if (ret
== AVERROR_EXIT
)
244 event
.sctp_data_io_event
= 1;
245 /* TODO: Subscribe to more event types and handle them */
247 if (setsockopt(fd
, IPPROTO_SCTP
, SCTP_EVENTS
, &event
,
248 sizeof(event
)) != 0) {
249 av_log(h
, AV_LOG_ERROR
,
250 "SCTP ERROR: Unable to subscribe to events\n");
254 if (s
->max_streams
) {
255 initparams
.sinit_max_instreams
= s
->max_streams
;
256 initparams
.sinit_num_ostreams
= s
->max_streams
;
257 if (setsockopt(fd
, IPPROTO_SCTP
, SCTP_INITMSG
, &initparams
,
258 sizeof(initparams
)) < 0) {
259 av_log(h
, AV_LOG_ERROR
,
260 "SCTP ERROR: Unable to initialize socket max streams %d\n",
274 if (cur_ai
->ai_next
) {
275 /* Retry with the next sockaddr */
276 cur_ai
= cur_ai
->ai_next
;
290 static int sctp_wait_fd(int fd
, int write
)
292 int ev
= write
? POLLOUT
: POLLIN
;
293 struct pollfd p
= { .fd
= fd
, .events
= ev
, .revents
= 0 };
296 ret
= poll(&p
, 1, 100);
297 return ret
< 0 ? ff_neterrno() : p
.revents
& ev
? 0 : AVERROR(EAGAIN
);
300 static int sctp_read(URLContext
*h
, uint8_t *buf
, int size
)
302 SCTPContext
*s
= h
->priv_data
;
305 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
306 ret
= sctp_wait_fd(s
->fd
, 0);
311 if (s
->max_streams
) {
312 /*StreamId is introduced as a 2byte code into the stream*/
313 struct sctp_sndrcvinfo info
= { 0 };
314 ret
= ff_sctp_recvmsg(s
->fd
, buf
+ 2, size
- 2, NULL
, 0, &info
, 0);
315 AV_WB16(buf
, info
.sinfo_stream
);
316 ret
= ret
< 0 ? ret
: ret
+ 2;
318 ret
= recv(s
->fd
, buf
, size
, 0);
320 return ret
< 0 ? ff_neterrno() : ret
;
323 static int sctp_write(URLContext
*h
, const uint8_t *buf
, int size
)
325 SCTPContext
*s
= h
->priv_data
;
328 if (!(h
->flags
& AVIO_FLAG_NONBLOCK
)) {
329 ret
= sctp_wait_fd(s
->fd
, 1);
334 if (s
->max_streams
) {
336 return AVERROR(EINVAL
);
338 /*StreamId is introduced as a 2byte code into the stream*/
339 struct sctp_sndrcvinfo info
= { 0 };
340 info
.sinfo_stream
= AV_RB16(buf
);
341 if (info
.sinfo_stream
> s
->max_streams
) {
342 av_log(h
, AV_LOG_ERROR
, "bad input data\n");
345 ret
= ff_sctp_send(s
->fd
, buf
+ 2, size
- 2, &info
, MSG_EOR
);
347 ret
= send(s
->fd
, buf
, size
, MSG_NOSIGNAL
);
349 return ret
< 0 ? ff_neterrno() : ret
;
352 static int sctp_close(URLContext
*h
)
354 SCTPContext
*s
= h
->priv_data
;
359 static int sctp_get_file_handle(URLContext
*h
)
361 SCTPContext
*s
= h
->priv_data
;
365 const URLProtocol ff_sctp_protocol
= {
367 .url_open
= sctp_open
,
368 .url_read
= sctp_read
,
369 .url_write
= sctp_write
,
370 .url_close
= sctp_close
,
371 .url_get_file_handle
= sctp_get_file_handle
,
372 .priv_data_size
= sizeof(SCTPContext
),
373 .flags
= URL_PROTOCOL_FLAG_NETWORK
,
374 .priv_data_class
= &sctp_class
,