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(&sap
->ann_fd
, url
, AVIO_FLAG_READ
,
89 &s
->interrupt_callback
, 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
]);
147 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", sap
->sdp
);
148 ffio_init_context(&sap
->sdp_pb
, sap
->sdp
, strlen(sap
->sdp
), 0, NULL
, NULL
,
151 infmt
= av_find_input_format("sdp");
154 sap
->sdp_ctx
= avformat_alloc_context();
156 ret
= AVERROR(ENOMEM
);
159 sap
->sdp_ctx
->max_delay
= s
->max_delay
;
160 sap
->sdp_ctx
->pb
= &sap
->sdp_pb
;
161 sap
->sdp_ctx
->interrupt_callback
= s
->interrupt_callback
;
163 if ((ret
= ff_copy_whitelists(sap
->sdp_ctx
, s
)) < 0)
166 ret
= avformat_open_input(&sap
->sdp_ctx
, "temp.sdp", infmt
, NULL
);
169 if (sap
->sdp_ctx
->ctx_flags
& AVFMTCTX_NOHEADER
)
170 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
171 for (i
= 0; i
< sap
->sdp_ctx
->nb_streams
; i
++) {
172 AVStream
*st
= avformat_new_stream(s
, NULL
);
174 ret
= AVERROR(ENOMEM
);
178 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
179 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
189 static int sap_fetch_packet(AVFormatContext
*s
, AVPacket
*pkt
)
191 struct SAPState
*sap
= s
->priv_data
;
192 int fd
= ffurl_get_file_handle(sap
->ann_fd
);
194 struct pollfd p
= {fd
, POLLIN
, 0};
195 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
202 if (n
<= 0 || !(p
.revents
& POLLIN
))
204 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
));
206 uint16_t hash
= AV_RB16(&recvbuf
[2]);
207 /* Should ideally check the source IP address, too */
208 if (recvbuf
[0] & 0x04 && hash
== sap
->hash
) {
209 /* Stream deletion */
215 ret
= av_read_frame(sap
->sdp_ctx
, pkt
);
218 if (s
->ctx_flags
& AVFMTCTX_NOHEADER
) {
219 while (sap
->sdp_ctx
->nb_streams
> s
->nb_streams
) {
220 int i
= s
->nb_streams
;
221 AVStream
*st
= avformat_new_stream(s
, NULL
);
224 return AVERROR(ENOMEM
);
227 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
228 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
234 AVInputFormat ff_sap_demuxer
= {
236 .long_name
= NULL_IF_CONFIG_SMALL("SAP input"),
237 .priv_data_size
= sizeof(struct SAPState
),
238 .read_probe
= sap_probe
,
239 .read_header
= sap_read_header
,
240 .read_packet
= sap_fetch_packet
,
241 .read_close
= sap_read_close
,
242 .flags
= AVFMT_NOFILE
,