2 * Session Announcement Protocol (RFC 2974) demuxer
3 * Copyright (c) 2010 Martin Storsjo
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
23 #include "libavutil/avassert.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/intreadwrite.h"
27 #include "os_support.h"
29 #include "avio_internal.h"
38 AVFormatContext
*sdp_ctx
;
45 static int sap_probe(AVProbeData
*p
)
47 if (av_strstart(p
->filename
, "sap:", NULL
))
48 return AVPROBE_SCORE_MAX
;
52 static int sap_read_close(AVFormatContext
*s
)
54 struct SAPState
*sap
= s
->priv_data
;
56 avformat_close_input(&sap
->sdp_ctx
);
58 ffurl_close(sap
->ann_fd
);
64 static int sap_read_header(AVFormatContext
*s
)
66 struct SAPState
*sap
= s
->priv_data
;
67 char host
[1024], path
[1024], url
[1024];
68 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
73 if (!ff_network_init())
76 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &port
,
77 path
, sizeof(path
), s
->filename
);
82 /* Listen for announcements on sap.mcast.net if no host was specified */
83 av_strlcpy(host
, "224.2.127.254", sizeof(host
));
86 ff_url_join(url
, sizeof(url
), "udp", NULL
, host
, port
, "?localport=%d",
88 ret
= ffurl_open_whitelist(&sap
->ann_fd
, url
, AVIO_FLAG_READ
,
89 &s
->interrupt_callback
, NULL
,
90 s
->protocol_whitelist
, s
->protocol_blacklist
, NULL
);
95 int addr_type
, auth_len
;
98 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
) - 1);
99 if (ret
== AVERROR(EAGAIN
))
103 recvbuf
[ret
] = '\0'; /* Null terminate for easier parsing */
105 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
109 if ((recvbuf
[0] & 0xe0) != 0x20) {
110 av_log(s
, AV_LOG_WARNING
, "Unsupported SAP version packet "
115 if (recvbuf
[0] & 0x04) {
116 av_log(s
, AV_LOG_WARNING
, "Received stream deletion "
120 addr_type
= recvbuf
[0] & 0x10;
121 auth_len
= recvbuf
[1];
122 sap
->hash
= AV_RB16(&recvbuf
[2]);
125 pos
+= 16; /* IPv6 */
129 if (pos
+ 4 >= ret
) {
130 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
133 #define MIME "application/sdp"
134 if (strcmp(&recvbuf
[pos
], MIME
) == 0) {
135 pos
+= strlen(MIME
) + 1;
136 } else if (strncmp(&recvbuf
[pos
], "v=0\r\n", 5) == 0) {
137 // Direct SDP without a mime type
139 av_log(s
, AV_LOG_WARNING
, "Unsupported mime type %s\n",
144 sap
->sdp
= av_strdup(&recvbuf
[pos
]);
148 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", sap
->sdp
);
149 ffio_init_context(&sap
->sdp_pb
, sap
->sdp
, strlen(sap
->sdp
), 0, NULL
, NULL
,
152 infmt
= av_find_input_format("sdp");
155 sap
->sdp_ctx
= avformat_alloc_context();
157 ret
= AVERROR(ENOMEM
);
160 sap
->sdp_ctx
->max_delay
= s
->max_delay
;
161 sap
->sdp_ctx
->pb
= &sap
->sdp_pb
;
162 sap
->sdp_ctx
->interrupt_callback
= s
->interrupt_callback
;
164 if ((ret
= ff_copy_whiteblacklists(sap
->sdp_ctx
, s
)) < 0)
167 ret
= avformat_open_input(&sap
->sdp_ctx
, "temp.sdp", infmt
, NULL
);
170 if (sap
->sdp_ctx
->ctx_flags
& AVFMTCTX_NOHEADER
)
171 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
172 for (i
= 0; i
< sap
->sdp_ctx
->nb_streams
; i
++) {
173 AVStream
*st
= avformat_new_stream(s
, NULL
);
175 ret
= AVERROR(ENOMEM
);
179 avcodec_parameters_copy(st
->codecpar
, sap
->sdp_ctx
->streams
[i
]->codecpar
);
180 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
190 static int sap_fetch_packet(AVFormatContext
*s
, AVPacket
*pkt
)
192 struct SAPState
*sap
= s
->priv_data
;
193 int fd
= ffurl_get_file_handle(sap
->ann_fd
);
195 struct pollfd p
= {fd
, POLLIN
, 0};
196 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
203 if (n
<= 0 || !(p
.revents
& POLLIN
))
205 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
));
207 uint16_t hash
= AV_RB16(&recvbuf
[2]);
208 /* Should ideally check the source IP address, too */
209 if (recvbuf
[0] & 0x04 && hash
== sap
->hash
) {
210 /* Stream deletion */
216 ret
= av_read_frame(sap
->sdp_ctx
, pkt
);
219 if (s
->ctx_flags
& AVFMTCTX_NOHEADER
) {
220 while (sap
->sdp_ctx
->nb_streams
> s
->nb_streams
) {
221 int i
= s
->nb_streams
;
222 AVStream
*st
= avformat_new_stream(s
, NULL
);
224 av_packet_unref(pkt
);
225 return AVERROR(ENOMEM
);
228 avcodec_parameters_copy(st
->codecpar
, sap
->sdp_ctx
->streams
[i
]->codecpar
);
229 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
235 AVInputFormat ff_sap_demuxer
= {
237 .long_name
= NULL_IF_CONFIG_SMALL("SAP input"),
238 .priv_data_size
= sizeof(struct SAPState
),
239 .read_probe
= sap_probe
,
240 .read_header
= sap_read_header
,
241 .read_packet
= sap_fetch_packet
,
242 .read_close
= sap_read_close
,
243 .flags
= AVFMT_NOFILE
,