Originally committed as revision 25463 to svn://svn.ffmpeg.org/ffmpeg/trunk
Martin Storsjö authored on 2010/10/13 18:06:59... | ... |
@@ -44,7 +44,7 @@ version <next>: |
44 | 44 |
- blackframe filter |
45 | 45 |
- Demuxer for Leitch/Harris' VR native stream format (LXF) |
46 | 46 |
- RTP depacketization of the X-QT QuickTime format |
47 |
-- SAP (Session Announcement Protocol, RFC 2974) muxer |
|
47 |
+- SAP (Session Announcement Protocol, RFC 2974) muxer and demuxer |
|
48 | 48 |
- cropdetect filter |
49 | 49 |
|
50 | 50 |
|
... | ... |
@@ -1363,6 +1363,7 @@ ogg_demuxer_select="golomb" |
1363 | 1363 |
psp_muxer_select="mov_muxer" |
1364 | 1364 |
rtsp_demuxer_select="http_protocol sdp_demuxer" |
1365 | 1365 |
rtsp_muxer_select="rtp_muxer http_protocol sdp_demuxer" |
1366 |
+sap_demuxer_select="sdp_demuxer" |
|
1366 | 1367 |
sap_muxer_select="rtp_muxer rtp_protocol" |
1367 | 1368 |
sdp_demuxer_select="asf_demuxer rm_demuxer rtp_protocol mpegts_demuxer mov_demuxer" |
1368 | 1369 |
spdif_muxer_select="aac_parser" |
... | ... |
@@ -217,7 +217,7 @@ library: |
217 | 217 |
@tab Output is performed by publishing stream to RTMP server |
218 | 218 |
@item RTP @tab X @tab |
219 | 219 |
@item RTSP @tab X @tab X |
220 |
-@item SAP @tab X @tab |
|
220 |
+@item SAP @tab X @tab X |
|
221 | 221 |
@item SDP @tab @tab X |
222 | 222 |
@item Sega FILM/CPK @tab @tab X |
223 | 223 |
@tab Used in many Sega Saturn console games. |
... | ... |
@@ -290,10 +290,12 @@ ffmpeg -re -i @var{input} -f rtsp -muxdelay 0.1 rtsp://server/live.sdp |
290 | 290 |
@section sap |
291 | 291 |
|
292 | 292 |
Session Announcement Protocol (RFC 2974). This is not technically a |
293 |
-protocol handler in libavformat, it is a muxer. |
|
293 |
+protocol handler in libavformat, it is a muxer and demuxer. |
|
294 | 294 |
It is used for signalling of RTP streams, by announcing the SDP for the |
295 | 295 |
streams regularly on a separate port. |
296 | 296 |
|
297 |
+@subsection Muxer |
|
298 |
+ |
|
297 | 299 |
The syntax for a SAP url given to the muxer is: |
298 | 300 |
@example |
299 | 301 |
sap://@var{destination}[:@var{port}][?@var{options}] |
... | ... |
@@ -325,6 +327,8 @@ If set to 1, send all RTP streams on the same port pair. If zero (the |
325 | 325 |
default), all streams are sent on unique ports, with each stream on a |
326 | 326 |
port 2 numbers higher than the previous. |
327 | 327 |
VLC/Live555 requires this to be set to 1, to be able to receive the stream. |
328 |
+The RTP stack in libavformat for receiving requires all streams to be sent |
|
329 |
+on unique ports. |
|
328 | 330 |
@end table |
329 | 331 |
|
330 | 332 |
Example command lines follow. |
... | ... |
@@ -335,6 +339,46 @@ To broadcast a stream on the local subnet, for watching in VLC: |
335 | 335 |
ffmpeg -re -i @var{input} -f sap sap://224.0.0.255?same_port=1 |
336 | 336 |
@end example |
337 | 337 |
|
338 |
+Similarly, for watching in ffplay: |
|
339 |
+ |
|
340 |
+@example |
|
341 |
+ffmpeg -re -i @var{input} -f sap sap://224.0.0.255 |
|
342 |
+@end example |
|
343 |
+ |
|
344 |
+And for watching in ffplay, over IPv6: |
|
345 |
+ |
|
346 |
+@example |
|
347 |
+ffmpeg -re -i @var{input} -f sap sap://[ff0e::1:2:3:4] |
|
348 |
+@end example |
|
349 |
+ |
|
350 |
+@subsection Demuxer |
|
351 |
+ |
|
352 |
+The syntax for a SAP url given to the demuxer is: |
|
353 |
+@example |
|
354 |
+sap://[@var{address}][:@var{port}] |
|
355 |
+@end example |
|
356 |
+ |
|
357 |
+@var{address} is the multicast address to listen for announcements on, |
|
358 |
+if omitted, the default 224.2.127.254 (sap.mcast.net) is used. @var{port} |
|
359 |
+is the port that is listened on, 9875 if omitted. |
|
360 |
+ |
|
361 |
+The demuxers listens for announcements on the given address and port. |
|
362 |
+Once an announcement is received, it tries to receive that particular stream. |
|
363 |
+ |
|
364 |
+Example command lines follow. |
|
365 |
+ |
|
366 |
+To play back the first stream announced on the normal SAP multicast address: |
|
367 |
+ |
|
368 |
+@example |
|
369 |
+ffplay sap:// |
|
370 |
+@end example |
|
371 |
+ |
|
372 |
+To play back the first stream announced on one the default IPv6 SAP multicast address: |
|
373 |
+ |
|
374 |
+@example |
|
375 |
+ffplay sap://[ff0e::2:7ffe] |
|
376 |
+@end example |
|
377 |
+ |
|
338 | 378 |
@section tcp |
339 | 379 |
|
340 | 380 |
Trasmission Control Protocol. |
... | ... |
@@ -231,6 +231,7 @@ OBJS-$(CONFIG_RTP_MUXER) += rtp.o \ |
231 | 231 |
avc.o |
232 | 232 |
OBJS-$(CONFIG_RTSP_DEMUXER) += rtsp.o httpauth.o |
233 | 233 |
OBJS-$(CONFIG_RTSP_MUXER) += rtsp.o rtspenc.o httpauth.o |
234 |
+OBJS-$(CONFIG_SAP_DEMUXER) += sapdec.o |
|
234 | 235 |
OBJS-$(CONFIG_SAP_MUXER) += sapenc.o rtpenc_chain.o |
235 | 236 |
OBJS-$(CONFIG_SDP_DEMUXER) += rtsp.o \ |
236 | 237 |
rdt.o \ |
... | ... |
@@ -181,7 +181,7 @@ void av_register_all(void) |
181 | 181 |
REGISTER_MUXDEMUX (RSO, rso); |
182 | 182 |
REGISTER_MUXER (RTP, rtp); |
183 | 183 |
REGISTER_MUXDEMUX (RTSP, rtsp); |
184 |
- REGISTER_MUXER (SAP, sap); |
|
184 |
+ REGISTER_MUXDEMUX (SAP, sap); |
|
185 | 185 |
REGISTER_DEMUXER (SDP, sdp); |
186 | 186 |
#if CONFIG_SDP_DEMUXER |
187 | 187 |
av_register_rtp_dynamic_payload_handlers(); |
... | ... |
@@ -22,7 +22,7 @@ |
22 | 22 |
#define AVFORMAT_AVFORMAT_H |
23 | 23 |
|
24 | 24 |
#define LIBAVFORMAT_VERSION_MAJOR 52 |
25 |
-#define LIBAVFORMAT_VERSION_MINOR 81 |
|
25 |
+#define LIBAVFORMAT_VERSION_MINOR 82 |
|
26 | 26 |
#define LIBAVFORMAT_VERSION_MICRO 0 |
27 | 27 |
|
28 | 28 |
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ |
29 | 29 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,238 @@ |
0 |
+/* |
|
1 |
+ * Session Announcement Protocol (RFC 2974) demuxer |
|
2 |
+ * Copyright (c) 2010 Martin Storsjo |
|
3 |
+ * |
|
4 |
+ * This file is part of FFmpeg. |
|
5 |
+ * |
|
6 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
7 |
+ * modify it under the terms of the GNU Lesser General Public |
|
8 |
+ * License as published by the Free Software Foundation; either |
|
9 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
10 |
+ * |
|
11 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
14 |
+ * Lesser General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU Lesser General Public |
|
17 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
19 |
+ */ |
|
20 |
+ |
|
21 |
+#include "avformat.h" |
|
22 |
+#include "libavutil/avstring.h" |
|
23 |
+#include "libavutil/intreadwrite.h" |
|
24 |
+#include "network.h" |
|
25 |
+#include "os_support.h" |
|
26 |
+#include "internal.h" |
|
27 |
+#if HAVE_SYS_SELECT_H |
|
28 |
+#include <sys/select.h> |
|
29 |
+#endif |
|
30 |
+ |
|
31 |
+struct SAPState { |
|
32 |
+ URLContext *ann_fd; |
|
33 |
+ AVFormatContext *sdp_ctx; |
|
34 |
+ ByteIOContext sdp_pb; |
|
35 |
+ uint16_t hash; |
|
36 |
+ char *sdp; |
|
37 |
+ int eof; |
|
38 |
+}; |
|
39 |
+ |
|
40 |
+static int sap_probe(AVProbeData *p) |
|
41 |
+{ |
|
42 |
+ if (av_strstart(p->filename, "sap:", NULL)) |
|
43 |
+ return AVPROBE_SCORE_MAX; |
|
44 |
+ return 0; |
|
45 |
+} |
|
46 |
+ |
|
47 |
+static int sap_read_close(AVFormatContext *s) |
|
48 |
+{ |
|
49 |
+ struct SAPState *sap = s->priv_data; |
|
50 |
+ if (sap->sdp_ctx) |
|
51 |
+ av_close_input_stream(sap->sdp_ctx); |
|
52 |
+ if (sap->ann_fd) |
|
53 |
+ url_close(sap->ann_fd); |
|
54 |
+ av_freep(&sap->sdp); |
|
55 |
+ ff_network_close(); |
|
56 |
+ return 0; |
|
57 |
+} |
|
58 |
+ |
|
59 |
+static int sap_read_header(AVFormatContext *s, |
|
60 |
+ AVFormatParameters *ap) |
|
61 |
+{ |
|
62 |
+ struct SAPState *sap = s->priv_data; |
|
63 |
+ char host[1024], path[1024], url[1024]; |
|
64 |
+ uint8_t recvbuf[1500]; |
|
65 |
+ int port; |
|
66 |
+ int ret, i; |
|
67 |
+ AVInputFormat* infmt; |
|
68 |
+ |
|
69 |
+ if (!ff_network_init()) |
|
70 |
+ return AVERROR(EIO); |
|
71 |
+ |
|
72 |
+ av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, |
|
73 |
+ path, sizeof(path), s->filename); |
|
74 |
+ if (port < 0) |
|
75 |
+ port = 9875; |
|
76 |
+ |
|
77 |
+ if (!host[0]) { |
|
78 |
+ /* Listen for announcements on sap.mcast.net if no host was specified */ |
|
79 |
+ av_strlcpy(host, "224.2.127.254", sizeof(host)); |
|
80 |
+ } |
|
81 |
+ |
|
82 |
+ ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d", |
|
83 |
+ port); |
|
84 |
+ ret = url_open(&sap->ann_fd, url, URL_RDONLY); |
|
85 |
+ if (ret) |
|
86 |
+ goto fail; |
|
87 |
+ |
|
88 |
+ while (1) { |
|
89 |
+ int addr_type, auth_len; |
|
90 |
+ int pos; |
|
91 |
+ |
|
92 |
+ ret = url_read(sap->ann_fd, recvbuf, sizeof(recvbuf) - 1); |
|
93 |
+ if (ret == AVERROR(EAGAIN)) |
|
94 |
+ continue; |
|
95 |
+ if (ret < 0) |
|
96 |
+ goto fail; |
|
97 |
+ recvbuf[ret] = '\0'; /* Null terminate for easier parsing */ |
|
98 |
+ if (ret < 8) { |
|
99 |
+ av_log(s, AV_LOG_WARNING, "Received too short packet\n"); |
|
100 |
+ continue; |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ if ((recvbuf[0] & 0xe0) != 0x20) { |
|
104 |
+ av_log(s, AV_LOG_WARNING, "Unsupported SAP version packet " |
|
105 |
+ "received\n"); |
|
106 |
+ continue; |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ if (recvbuf[0] & 0x04) { |
|
110 |
+ av_log(s, AV_LOG_WARNING, "Received stream deletion " |
|
111 |
+ "announcement\n"); |
|
112 |
+ continue; |
|
113 |
+ } |
|
114 |
+ addr_type = recvbuf[0] & 0x10; |
|
115 |
+ auth_len = recvbuf[1]; |
|
116 |
+ sap->hash = AV_RB16(&recvbuf[2]); |
|
117 |
+ pos = 4; |
|
118 |
+ if (addr_type) |
|
119 |
+ pos += 16; /* IPv6 */ |
|
120 |
+ else |
|
121 |
+ pos += 4; /* IPv4 */ |
|
122 |
+ pos += auth_len * 4; |
|
123 |
+ if (pos + 4 >= ret) { |
|
124 |
+ av_log(s, AV_LOG_WARNING, "Received too short packet\n"); |
|
125 |
+ continue; |
|
126 |
+ } |
|
127 |
+#define MIME "application/sdp" |
|
128 |
+ if (strcmp(&recvbuf[pos], MIME) == 0) { |
|
129 |
+ pos += strlen(MIME) + 1; |
|
130 |
+ } else if (strncmp(&recvbuf[pos], "v=0\r\n", 5) == 0) { |
|
131 |
+ // Direct SDP without a mime type |
|
132 |
+ } else { |
|
133 |
+ av_log(s, AV_LOG_WARNING, "Unsupported mime type %s\n", |
|
134 |
+ &recvbuf[pos]); |
|
135 |
+ continue; |
|
136 |
+ } |
|
137 |
+ |
|
138 |
+ sap->sdp = av_strdup(&recvbuf[pos]); |
|
139 |
+ break; |
|
140 |
+ } |
|
141 |
+ |
|
142 |
+ av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", sap->sdp); |
|
143 |
+ init_put_byte(&sap->sdp_pb, sap->sdp, strlen(sap->sdp), 0, NULL, NULL, |
|
144 |
+ NULL, NULL); |
|
145 |
+ |
|
146 |
+ infmt = av_find_input_format("sdp"); |
|
147 |
+ if (!infmt) |
|
148 |
+ goto fail; |
|
149 |
+ sap->sdp_ctx = avformat_alloc_context(); |
|
150 |
+ if (!sap->sdp_ctx) { |
|
151 |
+ ret = AVERROR(ENOMEM); |
|
152 |
+ goto fail; |
|
153 |
+ } |
|
154 |
+ sap->sdp_ctx->max_delay = s->max_delay; |
|
155 |
+ ap->prealloced_context = 1; |
|
156 |
+ ret = av_open_input_stream(&sap->sdp_ctx, &sap->sdp_pb, "temp.sdp", |
|
157 |
+ infmt, ap); |
|
158 |
+ if (ret < 0) |
|
159 |
+ goto fail; |
|
160 |
+ if (sap->sdp_ctx->ctx_flags & AVFMTCTX_NOHEADER) |
|
161 |
+ s->ctx_flags |= AVFMTCTX_NOHEADER; |
|
162 |
+ for (i = 0; i < sap->sdp_ctx->nb_streams; i++) { |
|
163 |
+ AVStream *st = av_new_stream(s, i); |
|
164 |
+ if (!st) { |
|
165 |
+ ret = AVERROR(ENOMEM); |
|
166 |
+ goto fail; |
|
167 |
+ } |
|
168 |
+ avcodec_copy_context(st->codec, sap->sdp_ctx->streams[i]->codec); |
|
169 |
+ st->time_base = sap->sdp_ctx->streams[i]->time_base; |
|
170 |
+ } |
|
171 |
+ |
|
172 |
+ return 0; |
|
173 |
+ |
|
174 |
+fail: |
|
175 |
+ sap_read_close(s); |
|
176 |
+ return ret; |
|
177 |
+} |
|
178 |
+ |
|
179 |
+static int sap_fetch_packet(AVFormatContext *s, AVPacket *pkt) |
|
180 |
+{ |
|
181 |
+ struct SAPState *sap = s->priv_data; |
|
182 |
+ int fd = url_get_file_handle(sap->ann_fd); |
|
183 |
+ int n, ret; |
|
184 |
+ fd_set rfds; |
|
185 |
+ struct timeval tv; |
|
186 |
+ uint8_t recvbuf[1500]; |
|
187 |
+ |
|
188 |
+ if (sap->eof) |
|
189 |
+ return AVERROR_EOF; |
|
190 |
+ |
|
191 |
+ while (1) { |
|
192 |
+ FD_ZERO(&rfds); |
|
193 |
+ FD_SET(fd, &rfds); |
|
194 |
+ tv.tv_sec = tv.tv_usec = 0; |
|
195 |
+ n = select(fd + 1, &rfds, NULL, NULL, &tv); |
|
196 |
+ if (n <= 0 || !FD_ISSET(fd, &rfds)) |
|
197 |
+ break; |
|
198 |
+ ret = url_read(sap->ann_fd, recvbuf, sizeof(recvbuf)); |
|
199 |
+ if (ret >= 8) { |
|
200 |
+ uint16_t hash = AV_RB16(&recvbuf[2]); |
|
201 |
+ /* Should ideally check the source IP address, too */ |
|
202 |
+ if (recvbuf[0] & 0x04 && hash == sap->hash) { |
|
203 |
+ /* Stream deletion */ |
|
204 |
+ sap->eof = 1; |
|
205 |
+ return AVERROR_EOF; |
|
206 |
+ } |
|
207 |
+ } |
|
208 |
+ } |
|
209 |
+ ret = av_read_frame(sap->sdp_ctx, pkt); |
|
210 |
+ if (ret < 0) |
|
211 |
+ return ret; |
|
212 |
+ if (s->ctx_flags & AVFMTCTX_NOHEADER) { |
|
213 |
+ while (sap->sdp_ctx->nb_streams > s->nb_streams) { |
|
214 |
+ int i = s->nb_streams; |
|
215 |
+ AVStream *st = av_new_stream(s, i); |
|
216 |
+ if (!st) { |
|
217 |
+ av_free_packet(pkt); |
|
218 |
+ return AVERROR(ENOMEM); |
|
219 |
+ } |
|
220 |
+ avcodec_copy_context(st->codec, sap->sdp_ctx->streams[i]->codec); |
|
221 |
+ st->time_base = sap->sdp_ctx->streams[i]->time_base; |
|
222 |
+ } |
|
223 |
+ } |
|
224 |
+ return ret; |
|
225 |
+} |
|
226 |
+ |
|
227 |
+AVInputFormat sap_demuxer = { |
|
228 |
+ "sap", |
|
229 |
+ NULL_IF_CONFIG_SMALL("SAP input format"), |
|
230 |
+ sizeof(struct SAPState), |
|
231 |
+ sap_probe, |
|
232 |
+ sap_read_header, |
|
233 |
+ sap_fetch_packet, |
|
234 |
+ sap_read_close, |
|
235 |
+ .flags = AVFMT_NOFILE, |
|
236 |
+}; |
|
237 |
+ |