FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sapdec.c
Go to the documentation of this file.
1 /*
2  * Session Announcement Protocol (RFC 2974) demuxer
3  * Copyright (c) 2010 Martin Storsjo
4  *
5  * This file is part of FFmpeg.
6  *
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.
11  *
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.
16  *
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
20  */
21 
22 #include "avformat.h"
23 #include "libavutil/avstring.h"
24 #include "libavutil/intreadwrite.h"
25 #include "network.h"
26 #include "os_support.h"
27 #include "internal.h"
28 #include "avio_internal.h"
29 #include "url.h"
30 #if HAVE_POLL_H
31 #include <poll.h>
32 #endif
33 
34 struct SAPState {
38  uint16_t hash;
39  char *sdp;
40  int eof;
41 };
42 
43 static int sap_probe(AVProbeData *p)
44 {
45  if (av_strstart(p->filename, "sap:", NULL))
46  return AVPROBE_SCORE_MAX;
47  return 0;
48 }
49 
51 {
52  struct SAPState *sap = s->priv_data;
53  if (sap->sdp_ctx)
55  if (sap->ann_fd)
56  ffurl_close(sap->ann_fd);
57  av_freep(&sap->sdp);
59  return 0;
60 }
61 
63 {
64  struct SAPState *sap = s->priv_data;
65  char host[1024], path[1024], url[1024];
66  uint8_t recvbuf[1500];
67  int port;
68  int ret, i;
69  AVInputFormat* infmt;
70 
71  if (!ff_network_init())
72  return AVERROR(EIO);
73 
74  av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port,
75  path, sizeof(path), s->filename);
76  if (port < 0)
77  port = 9875;
78 
79  if (!host[0]) {
80  /* Listen for announcements on sap.mcast.net if no host was specified */
81  av_strlcpy(host, "224.2.127.254", sizeof(host));
82  }
83 
84  ff_url_join(url, sizeof(url), "udp", NULL, host, port, "?localport=%d",
85  port);
86  ret = ffurl_open(&sap->ann_fd, url, AVIO_FLAG_READ,
88  if (ret)
89  goto fail;
90 
91  while (1) {
92  int addr_type, auth_len;
93  int pos;
94 
95  ret = ffurl_read(sap->ann_fd, recvbuf, sizeof(recvbuf) - 1);
96  if (ret == AVERROR(EAGAIN))
97  continue;
98  if (ret < 0)
99  goto fail;
100  recvbuf[ret] = '\0'; /* Null terminate for easier parsing */
101  if (ret < 8) {
102  av_log(s, AV_LOG_WARNING, "Received too short packet\n");
103  continue;
104  }
105 
106  if ((recvbuf[0] & 0xe0) != 0x20) {
107  av_log(s, AV_LOG_WARNING, "Unsupported SAP version packet "
108  "received\n");
109  continue;
110  }
111 
112  if (recvbuf[0] & 0x04) {
113  av_log(s, AV_LOG_WARNING, "Received stream deletion "
114  "announcement\n");
115  continue;
116  }
117  addr_type = recvbuf[0] & 0x10;
118  auth_len = recvbuf[1];
119  sap->hash = AV_RB16(&recvbuf[2]);
120  pos = 4;
121  if (addr_type)
122  pos += 16; /* IPv6 */
123  else
124  pos += 4; /* IPv4 */
125  pos += auth_len * 4;
126  if (pos + 4 >= ret) {
127  av_log(s, AV_LOG_WARNING, "Received too short packet\n");
128  continue;
129  }
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
135  } else {
136  av_log(s, AV_LOG_WARNING, "Unsupported mime type %s\n",
137  &recvbuf[pos]);
138  continue;
139  }
140 
141  sap->sdp = av_strdup(&recvbuf[pos]);
142  break;
143  }
144 
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,
147  NULL, NULL);
148 
149  infmt = av_find_input_format("sdp");
150  if (!infmt)
151  goto fail;
153  if (!sap->sdp_ctx) {
154  ret = AVERROR(ENOMEM);
155  goto fail;
156  }
157  sap->sdp_ctx->max_delay = s->max_delay;
158  sap->sdp_ctx->pb = &sap->sdp_pb;
160  ret = avformat_open_input(&sap->sdp_ctx, "temp.sdp", infmt, NULL);
161  if (ret < 0)
162  goto fail;
163  if (sap->sdp_ctx->ctx_flags & AVFMTCTX_NOHEADER)
165  for (i = 0; i < sap->sdp_ctx->nb_streams; i++) {
167  if (!st) {
168  ret = AVERROR(ENOMEM);
169  goto fail;
170  }
171  st->id = i;
173  st->time_base = sap->sdp_ctx->streams[i]->time_base;
174  }
175 
176  return 0;
177 
178 fail:
179  sap_read_close(s);
180  return ret;
181 }
182 
184 {
185  struct SAPState *sap = s->priv_data;
186  int fd = ffurl_get_file_handle(sap->ann_fd);
187  int n, ret;
188  struct pollfd p = {fd, POLLIN, 0};
189  uint8_t recvbuf[1500];
190 
191  if (sap->eof)
192  return AVERROR_EOF;
193 
194  while (1) {
195  n = poll(&p, 1, 0);
196  if (n <= 0 || !(p.revents & POLLIN))
197  break;
198  ret = ffurl_read(sap->ann_fd, recvbuf, sizeof(recvbuf));
199  if (ret >= 8) {
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 */
204  sap->eof = 1;
205  return AVERROR_EOF;
206  }
207  }
208  }
209  ret = av_read_frame(sap->sdp_ctx, pkt);
210  if (ret < 0)
211  return ret;
212  if (s->ctx_flags & AVFMTCTX_NOHEADER) {
213  while (sap->sdp_ctx->nb_streams > s->nb_streams) {
214  int i = s->nb_streams;
216  if (!st) {
217  av_free_packet(pkt);
218  return AVERROR(ENOMEM);
219  }
220  st->id = i;
222  st->time_base = sap->sdp_ctx->streams[i]->time_base;
223  }
224  }
225  return ret;
226 }
227 
229  .name = "sap",
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,
237 };