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/avstring.h"
24 #include "libavutil/intreadwrite.h"
26 #include "os_support.h"
28 #include "avio_internal.h"
37 AVFormatContext
*sdp_ctx
;
44 static int sap_probe(AVProbeData
*p
)
46 if (av_strstart(p
->filename
, "sap:", NULL
))
47 return AVPROBE_SCORE_MAX
;
51 static int sap_read_close(AVFormatContext
*s
)
53 struct SAPState
*sap
= s
->priv_data
;
55 avformat_close_input(&sap
->sdp_ctx
);
57 ffurl_close(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
];
72 if (!ff_network_init())
75 av_url_split(NULL
, 0, NULL
, 0, host
, sizeof(host
), &port
,
76 path
, sizeof(path
), s
->filename
);
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(&sap
->ann_fd
, url
, AVIO_FLAG_READ
,
88 &s
->interrupt_callback
, NULL
);
93 int addr_type
, auth_len
;
96 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
) - 1);
97 if (ret
== AVERROR(EAGAIN
))
101 recvbuf
[ret
] = '\0'; /* Null terminate for easier parsing */
103 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
107 if ((recvbuf
[0] & 0xe0) != 0x20) {
108 av_log(s
, AV_LOG_WARNING
, "Unsupported SAP version packet "
113 if (recvbuf
[0] & 0x04) {
114 av_log(s
, AV_LOG_WARNING
, "Received stream deletion "
118 addr_type
= recvbuf
[0] & 0x10;
119 auth_len
= recvbuf
[1];
120 sap
->hash
= AV_RB16(&recvbuf
[2]);
123 pos
+= 16; /* IPv6 */
127 if (pos
+ 4 >= ret
) {
128 av_log(s
, AV_LOG_WARNING
, "Received too short packet\n");
131 #define MIME "application/sdp"
132 if (strcmp(&recvbuf
[pos
], MIME
) == 0) {
133 pos
+= strlen(MIME
) + 1;
134 } else if (strncmp(&recvbuf
[pos
], "v=0\r\n", 5) == 0) {
135 // Direct SDP without a mime type
137 av_log(s
, AV_LOG_WARNING
, "Unsupported mime type %s\n",
142 sap
->sdp
= av_strdup(&recvbuf
[pos
]);
146 av_log(s
, AV_LOG_VERBOSE
, "SDP:\n%s\n", sap
->sdp
);
147 ffio_init_context(&sap
->sdp_pb
, sap
->sdp
, strlen(sap
->sdp
), 0, NULL
, NULL
,
150 infmt
= av_find_input_format("sdp");
153 sap
->sdp_ctx
= avformat_alloc_context();
155 ret
= AVERROR(ENOMEM
);
158 sap
->sdp_ctx
->max_delay
= s
->max_delay
;
159 sap
->sdp_ctx
->pb
= &sap
->sdp_pb
;
160 sap
->sdp_ctx
->interrupt_callback
= s
->interrupt_callback
;
161 ret
= avformat_open_input(&sap
->sdp_ctx
, "temp.sdp", infmt
, NULL
);
164 if (sap
->sdp_ctx
->ctx_flags
& AVFMTCTX_NOHEADER
)
165 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
166 for (i
= 0; i
< sap
->sdp_ctx
->nb_streams
; i
++) {
167 AVStream
*st
= avformat_new_stream(s
, NULL
);
169 ret
= AVERROR(ENOMEM
);
173 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
174 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
184 static int sap_fetch_packet(AVFormatContext
*s
, AVPacket
*pkt
)
186 struct SAPState
*sap
= s
->priv_data
;
187 int fd
= ffurl_get_file_handle(sap
->ann_fd
);
189 struct pollfd p
= {fd
, POLLIN
, 0};
190 uint8_t recvbuf
[RTP_MAX_PACKET_LENGTH
];
197 if (n
<= 0 || !(p
.revents
& POLLIN
))
199 ret
= ffurl_read(sap
->ann_fd
, recvbuf
, sizeof(recvbuf
));
201 uint16_t hash
= AV_RB16(&recvbuf
[2]);
202 /* Should ideally check the source IP address, too */
203 if (recvbuf
[0] & 0x04 && hash
== sap
->hash
) {
204 /* Stream deletion */
210 ret
= av_read_frame(sap
->sdp_ctx
, pkt
);
213 if (s
->ctx_flags
& AVFMTCTX_NOHEADER
) {
214 while (sap
->sdp_ctx
->nb_streams
> s
->nb_streams
) {
215 int i
= s
->nb_streams
;
216 AVStream
*st
= avformat_new_stream(s
, NULL
);
219 return AVERROR(ENOMEM
);
222 avcodec_copy_context(st
->codec
, sap
->sdp_ctx
->streams
[i
]->codec
);
223 st
->time_base
= sap
->sdp_ctx
->streams
[i
]->time_base
;
229 AVInputFormat ff_sap_demuxer
= {
231 .long_name
= NULL_IF_CONFIG_SMALL("SAP input"),
232 .priv_data_size
= sizeof(struct SAPState
),
233 .read_probe
= sap_probe
,
234 .read_header
= sap_read_header
,
235 .read_packet
= sap_fetch_packet
,
236 .read_close
= sap_read_close
,
237 .flags
= AVFMT_NOFILE
,