2 * Session Announcement Protocol (RFC 2974) demuxer
3 * Copyright (c) 2010 Martin Storsjo
5 * This file is part of Libav.
7 * Libav 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 * Libav 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 Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "libavutil/avstring.h"
24 #include "libavutil/intreadwrite.h"
26 #include "os_support.h"
28 #include "avio_internal.h"
36 AVFormatContext
*sdp_ctx
;
43 static int sap_probe(AVProbeData
*p
)
45 if (av_strstart(p
->filename
, "sap:", NULL
))
46 return AVPROBE_SCORE_MAX
;
50 static int sap_read_close(AVFormatContext
*s
)
52 struct SAPState
*sap
= s
->priv_data
;
54 avformat_close_input(&sap
->sdp_ctx
);
56 ffurl_close(sap
->ann_fd
);
62 static int sap_read_header(AVFormatContext
*s
)
64 struct SAPState
*sap
= s
->priv_data
;
65 char host
[1024], path
[1024], url
[1024];
66 uint8_t recvbuf
[1500];
71 if (!ff_network_init())
74 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &port
,
75 path
, sizeof(path
), s
->filename
);
80 /* Listen for announcements on sap.mcast.net if no host was specified */
81 av_strlcpy(host
, "224.2.127.254", sizeof(host
));
84 ff_url_join(url
, sizeof(url
), "udp", NULL
, host
, port
, "?localport=%d",
86 ret
= ffurl_open(&sap
->ann_fd
, url
, AVIO_FLAG_READ
,
87 &s
->interrupt_callback
, NULL
);
92 int addr_type
, auth_len
;
95 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
) - 1);
96 if (ret
== AVERROR(EAGAIN
))
100 recvbuf
[ret
] = '\0'; /* Null terminate for easier parsing */
102 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
106 if ((recvbuf
[0] & 0xe0) != 0x20) {
107 av_log(s
, AV_LOG_WARNING
, "Unsupported SAP version packet "
112 if (recvbuf
[0] & 0x04) {
113 av_log(s
, AV_LOG_WARNING
, "Received stream deletion "
117 addr_type
= recvbuf
[0] & 0x10;
118 auth_len
= recvbuf
[1];
119 sap
->hash
= AV_RB16(&recvbuf
[2]);
122 pos
+= 16; /* IPv6 */
126 if (pos
+ 4 >= ret
) {
127 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
130 #define MIME "application/sdp"
131 if (strcmp(&recvbuf
[pos
], MIME
) == 0) {
132 pos
+= strlen(MIME
) + 1;
133 } else if (strncmp(&recvbuf
[pos
], "v=0\r\n", 5) == 0) {
134 // Direct SDP without a mime type
136 av_log(s
, AV_LOG_WARNING
, "Unsupported mime type %s\n",
141 sap
->sdp
= av_strdup(&recvbuf
[pos
]);
145 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", sap
->sdp
);
146 ffio_init_context(&sap
->sdp_pb
, sap
->sdp
, strlen(sap
->sdp
), 0, NULL
, NULL
,
149 infmt
= av_find_input_format("sdp");
152 sap
->sdp_ctx
= avformat_alloc_context();
154 ret
= AVERROR(ENOMEM
);
157 sap
->sdp_ctx
->max_delay
= s
->max_delay
;
158 sap
->sdp_ctx
->pb
= &sap
->sdp_pb
;
159 sap
->sdp_ctx
->interrupt_callback
= s
->interrupt_callback
;
160 ret
= avformat_open_input(&sap
->sdp_ctx
, "temp.sdp", infmt
, NULL
);
163 if (sap
->sdp_ctx
->ctx_flags
& AVFMTCTX_NOHEADER
)
164 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
165 for (i
= 0; i
< sap
->sdp_ctx
->nb_streams
; i
++) {
166 AVStream
*st
= avformat_new_stream(s
, NULL
);
168 ret
= AVERROR(ENOMEM
);
172 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
173 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
183 static int sap_fetch_packet(AVFormatContext
*s
, AVPacket
*pkt
)
185 struct SAPState
*sap
= s
->priv_data
;
186 int fd
= ffurl_get_file_handle(sap
->ann_fd
);
188 struct pollfd p
= {fd
, POLLIN
, 0};
189 uint8_t recvbuf
[1500];
196 if (n
<= 0 || !(p
.revents
& POLLIN
))
198 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
));
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 */
209 ret
= av_read_frame(sap
->sdp_ctx
, pkt
);
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
= avformat_new_stream(s
, NULL
);
218 return AVERROR(ENOMEM
);
221 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
222 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
228 AVInputFormat ff_sap_demuxer
= {
230 .long_name
= NULL_IF_CONFIG_SMALL("SAP input"),
231 .priv_data_size
= sizeof(struct SAPState
),
232 .read_probe
= sap_probe
,
233 .read_header
= sap_read_header
,
234 .read_packet
= sap_fetch_packet
,
235 .read_close
= sap_read_close
,
236 .flags
= AVFMT_NOFILE
,