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(const 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
);
57 ffurl_closep(&sap
->ann_fd
);
63 static int sap_read_header(AVFormatContext
*s
)
65 struct SAPState
*sap
= s
->priv_data
;
66 char host
[1024], path
[1024], url
[1024];
67 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
70 ff_const59 AVInputFormat
* infmt
;
72 if (!ff_network_init())
75 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &port
,
76 path
, sizeof(path
), s
->url
);
81 /* Listen for announcements on sap.mcast.net if no host was specified */
82 av_strlcpy(host
, "224.2.127.254", sizeof(host
));
85 ff_url_join(url
, sizeof(url
), "udp", NULL
, host
, port
, "?localport=%d",
87 ret
= ffurl_open_whitelist(&sap
->ann_fd
, url
, AVIO_FLAG_READ
,
88 &s
->interrupt_callback
, NULL
,
89 s
->protocol_whitelist
, s
->protocol_blacklist
, NULL
);
94 int addr_type
, auth_len
;
97 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
) - 1);
98 if (ret
== AVERROR(EAGAIN
))
102 recvbuf
[ret
] = '\0'; /* Null terminate for easier parsing */
104 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
108 if ((recvbuf
[0] & 0xe0) != 0x20) {
109 av_log(s
, AV_LOG_WARNING
, "Unsupported SAP version packet "
114 if (recvbuf
[0] & 0x04) {
115 av_log(s
, AV_LOG_WARNING
, "Received stream deletion "
119 addr_type
= recvbuf
[0] & 0x10;
120 auth_len
= recvbuf
[1];
121 sap
->hash
= AV_RB16(&recvbuf
[2]);
124 pos
+= 16; /* IPv6 */
128 if (pos
+ 4 >= ret
) {
129 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
132 #define MIME "application/sdp"
133 if (strcmp(&recvbuf
[pos
], MIME
) == 0) {
134 pos
+= strlen(MIME
) + 1;
135 } else if (strncmp(&recvbuf
[pos
], "v=0\r\n", 5) == 0) {
136 // Direct SDP without a mime type
138 av_log(s
, AV_LOG_WARNING
, "Unsupported mime type %s\n",
143 sap
->sdp
= av_strdup(&recvbuf
[pos
]);
145 ret
= AVERROR(ENOMEM
);
151 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", sap
->sdp
);
152 ffio_init_context(&sap
->sdp_pb
, sap
->sdp
, strlen(sap
->sdp
), 0, NULL
, NULL
,
155 infmt
= av_find_input_format("sdp");
158 sap
->sdp_ctx
= avformat_alloc_context();
160 ret
= AVERROR(ENOMEM
);
163 sap
->sdp_ctx
->max_delay
= s
->max_delay
;
164 sap
->sdp_ctx
->pb
= &sap
->sdp_pb
;
165 sap
->sdp_ctx
->interrupt_callback
= s
->interrupt_callback
;
167 if ((ret
= ff_copy_whiteblacklists(sap
->sdp_ctx
, s
)) < 0)
170 ret
= avformat_open_input(&sap
->sdp_ctx
, "temp.sdp", infmt
, NULL
);
173 if (sap
->sdp_ctx
->ctx_flags
& AVFMTCTX_NOHEADER
)
174 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
175 for (i
= 0; i
< sap
->sdp_ctx
->nb_streams
; i
++) {
176 AVStream
*st
= avformat_new_stream(s
, NULL
);
178 ret
= AVERROR(ENOMEM
);
182 avcodec_parameters_copy(st
->codecpar
, sap
->sdp_ctx
->streams
[i
]->codecpar
);
183 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
193 static int sap_fetch_packet(AVFormatContext
*s
, AVPacket
*pkt
)
195 struct SAPState
*sap
= s
->priv_data
;
196 int fd
= ffurl_get_file_handle(sap
->ann_fd
);
198 struct pollfd p
= {fd
, POLLIN
, 0};
199 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
209 if (n
<= 0 || !(p
.revents
& POLLIN
))
211 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
));
213 uint16_t hash
= AV_RB16(&recvbuf
[2]);
214 /* Should ideally check the source IP address, too */
215 if (recvbuf
[0] & 0x04 && hash
== sap
->hash
) {
216 /* Stream deletion */
222 ret
= av_read_frame(sap
->sdp_ctx
, pkt
);
225 if (s
->ctx_flags
& AVFMTCTX_NOHEADER
) {
226 while (sap
->sdp_ctx
->nb_streams
> s
->nb_streams
) {
227 int i
= s
->nb_streams
;
228 AVStream
*st
= avformat_new_stream(s
, NULL
);
230 return AVERROR(ENOMEM
);
233 avcodec_parameters_copy(st
->codecpar
, sap
->sdp_ctx
->streams
[i
]->codecpar
);
234 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
240 AVInputFormat ff_sap_demuxer
= {
242 .long_name
= NULL_IF_CONFIG_SMALL("SAP input"),
243 .priv_data_size
= sizeof(struct SAPState
),
244 .read_probe
= sap_probe
,
245 .read_header
= sap_read_header
,
246 .read_packet
= sap_fetch_packet
,
247 .read_close
= sap_read_close
,
248 .flags
= AVFMT_NOFILE
,