FFmpeg
icecast.c
Go to the documentation of this file.
1 /*
2  * Icecast protocol for FFmpeg
3  * Copyright (c) 2014 Marvin Scholz
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 
23 #include "libavutil/avstring.h"
24 #include "libavutil/bprint.h"
25 #include "libavutil/opt.h"
26 
27 #include "avformat.h"
28 #include "network.h"
29 
30 
31 typedef struct IcecastContext {
32  const AVClass *class;
35  char *user;
36  // Options
37  char *content_type;
38  char *description;
39  char *genre;
41  char *name;
42  char *pass;
43  int public;
44  char *url;
45  char *user_agent;
46  int tls;
48 
49 #define DEFAULT_ICE_USER "source"
50 
51 #define NOT_EMPTY(s) (s && s[0])
52 
53 #define OFFSET(x) offsetof(IcecastContext, x)
54 #define E AV_OPT_FLAG_ENCODING_PARAM
55 
56 static const AVOption options[] = {
57  { "ice_genre", "set stream genre", OFFSET(genre), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
58  { "ice_name", "set stream description", OFFSET(name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
59  { "ice_description", "set stream description", OFFSET(description), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
60  { "ice_url", "set stream website", OFFSET(url), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
61  { "ice_public", "set if stream is public", OFFSET(public), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
62  { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
63  { "password", "set password", OFFSET(pass), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
64  { "content_type", "set content-type, MUST be set if not audio/mpeg", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
65  { "legacy_icecast", "use legacy SOURCE method, for Icecast < v2.4", OFFSET(legacy_icecast), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
66  { "tls", "use a TLS connection", OFFSET(tls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
67  { NULL }
68 };
69 
70 
71 static void cat_header(AVBPrint *bp, const char key[], const char value[])
72 {
73  if (NOT_EMPTY(value))
74  av_bprintf(bp, "%s: %s\r\n", key, value);
75 }
76 
78 {
80  ffurl_closep(&s->hd);
81  return 0;
82 }
83 
84 static int icecast_open(URLContext *h, const char *uri, int flags)
85 {
87 
88  // Dict to set options that we pass to the HTTP protocol
89  AVDictionary *opt_dict = NULL;
90 
91  // URI part variables
92  char h_url[1024], host[1024], auth[1024], path[1024];
93  char *headers, *user = NULL;
94  int port, ret;
95  AVBPrint bp;
96 
97  if (flags & AVIO_FLAG_READ)
98  return AVERROR(ENOSYS);
99 
101 
102  // Build header strings
103  cat_header(&bp, "Ice-Name", s->name);
104  cat_header(&bp, "Ice-Description", s->description);
105  cat_header(&bp, "Ice-URL", s->url);
106  cat_header(&bp, "Ice-Genre", s->genre);
107  cat_header(&bp, "Ice-Public", s->public ? "1" : "0");
108  if (!av_bprint_is_complete(&bp)) {
109  av_bprint_finalize(&bp, NULL);
110  return AVERROR(ENOMEM);
111  }
112  if ((ret = av_bprint_finalize(&bp, &headers)) < 0)
113  return ret;
114 
115  // Set options
116  av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0);
117  av_dict_set(&opt_dict, "auth_type", "basic", 0);
118  av_dict_set(&opt_dict, "headers", headers, AV_DICT_DONT_STRDUP_VAL);
119  av_dict_set(&opt_dict, "chunked_post", "0", 0);
120  av_dict_set(&opt_dict, "send_expect_100", s->legacy_icecast ? "-1" : "1", 0);
121  if (NOT_EMPTY(s->content_type))
122  av_dict_set(&opt_dict, "content_type", s->content_type, 0);
123  else
124  av_dict_set(&opt_dict, "content_type", "audio/mpeg", 0);
125  if (NOT_EMPTY(s->user_agent))
126  av_dict_set(&opt_dict, "user_agent", s->user_agent, 0);
127 
128  // Parse URI
129  av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host),
130  &port, path, sizeof(path), uri);
131 
132  // Check for auth data in URI
133  if (auth[0]) {
134  char *sep = strchr(auth, ':');
135  if (sep) {
136  *sep = 0;
137  sep++;
138  if (s->pass) {
139  av_free(s->pass);
140  av_log(h, AV_LOG_WARNING, "Overwriting -password <pass> with URI password!\n");
141  }
142  if (!(s->pass = av_strdup(sep))) {
143  ret = AVERROR(ENOMEM);
144  goto cleanup;
145  }
146  }
147  if (!(user = av_strdup(auth))) {
148  ret = AVERROR(ENOMEM);
149  goto cleanup;
150  }
151  }
152 
153  // Build new authstring
154  snprintf(auth, sizeof(auth),
155  "%s:%s",
156  user ? user : DEFAULT_ICE_USER,
157  s->pass ? s->pass : "");
158 
159  // Check for mountpoint (path)
160  if (!path[0] || strcmp(path, "/") == 0) {
161  av_log(h, AV_LOG_ERROR, "No mountpoint (path) specified!\n");
162  ret = AVERROR(EIO);
163  goto cleanup;
164  }
165 
166  // Build new URI for passing to http protocol
167  ff_url_join(h_url, sizeof(h_url),
168  s->tls ? "https" : "http",
169  auth, host, port, "%s", path);
170  // Finally open http proto handler
172  &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
173 
174 cleanup:
175  av_freep(&user);
176  av_dict_free(&opt_dict);
177 
178  return ret;
179 }
180 
181 static int icecast_write(URLContext *h, const uint8_t *buf, int size)
182 {
183  IcecastContext *s = h->priv_data;
184  if (!s->send_started) {
185  s->send_started = 1;
186  if (!s->content_type && size >= 8) {
187  static const uint8_t oggs[4] = { 0x4F, 0x67, 0x67, 0x53 };
188  static const uint8_t webm[4] = { 0x1A, 0x45, 0xDF, 0xA3 };
189  static const uint8_t opus[8] = { 0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
190  if (memcmp(buf, oggs, sizeof(oggs)) == 0) {
191  av_log(h, AV_LOG_WARNING, "Streaming Ogg but appropriate content type NOT set!\n");
192  av_log(h, AV_LOG_WARNING, "Set it with -content_type application/ogg\n");
193  } else if (memcmp(buf, opus, sizeof(opus)) == 0) {
194  av_log(h, AV_LOG_WARNING, "Streaming Opus but appropriate content type NOT set!\n");
195  av_log(h, AV_LOG_WARNING, "Set it with -content_type audio/ogg\n");
196  } else if (memcmp(buf, webm, sizeof(webm)) == 0) {
197  av_log(h, AV_LOG_WARNING, "Streaming WebM but appropriate content type NOT set!\n");
198  av_log(h, AV_LOG_WARNING, "Set it with -content_type video/webm\n");
199  } else {
200  av_log(h, AV_LOG_WARNING, "It seems you are streaming an unsupported format.\n");
201  av_log(h, AV_LOG_WARNING, "It might work, but is not officially supported in Icecast!\n");
202  }
203  }
204  }
205  return ffurl_write(s->hd, buf, size);
206 }
207 
209  .class_name = "icecast",
210  .item_name = av_default_item_name,
211  .option = options,
212  .version = LIBAVUTIL_VERSION_INT,
213 };
214 
216  .name = "icecast",
217  .url_open = icecast_open,
218  .url_write = icecast_write,
219  .url_close = icecast_close,
220  .priv_data_size = sizeof(IcecastContext),
221  .priv_data_class = &icecast_context_class,
223 };
void av_url_split(char *proto, int proto_size, char *authorization, int authorization_size, char *hostname, int hostname_size, int *port_ptr, char *path, int path_size, const char *url)
Split a URL string into components.
Definition: utils.c:4723
#define NULL
Definition: coverity.c:32
static int icecast_open(URLContext *h, const char *uri, int flags)
Definition: icecast.c:84
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags, const AVIOInterruptCB *int_cb, AVDictionary **options, const char *whitelist, const char *blacklist, URLContext *parent)
Create an URLContext for accessing to the resource indicated by url, and open it. ...
Definition: avio.c:310
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
char * pass
Definition: icecast.c:42
AVOption.
Definition: opt.h:248
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:200
char * user_agent
Definition: icecast.c:45
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
int ffurl_write(URLContext *h, const unsigned char *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: avio.c:423
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:235
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
const char * key
char * user
Definition: icecast.c:35
static const AVOption options[]
Definition: icecast.c:56
static int icecast_write(URLContext *h, const uint8_t *buf, int size)
Definition: icecast.c:181
#define DEFAULT_ICE_USER
Definition: icecast.c:49
int send_started
Definition: icecast.c:34
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
#define NOT_EMPTY(s)
Definition: icecast.c:51
#define OFFSET(x)
Definition: icecast.c:53
uint8_t
AVOptions.
int legacy_icecast
Definition: icecast.c:40
GLsizei GLboolean const GLfloat * value
Definition: opengl_enc.c:108
const URLProtocol ff_icecast_protocol
Definition: icecast.c:215
static int icecast_close(URLContext *h)
Definition: icecast.c:77
ptrdiff_t size
Definition: opengl_enc.c:100
#define av_log(a,...)
URLContext * hd
Definition: icecast.c:33
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:194
const char * protocol_whitelist
Definition: url.h:49
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values. ...
Definition: dict.c:203
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that&#39;s been allocated with av_malloc() or another memory allocation functio...
Definition: dict.h:76
#define s(width, name)
Definition: cbs_vp9.c:257
char * genre
Definition: icecast.c:39
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:185
char * content_type
Definition: icecast.c:37
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:446
int ff_url_join(char *str, int size, const char *proto, const char *authorization, const char *hostname, int port, const char *fmt,...)
Definition: url.c:38
char * name
Definition: icecast.c:41
#define AV_BPRINT_SIZE_AUTOMATIC
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:253
const char * protocol_blacklist
Definition: url.h:50
Definition: url.h:38
#define AVIO_FLAG_READ_WRITE
read-write pseudo flag
Definition: avio.h:676
int av_dict_set(AVDictionary **pm, const char *key, const char *value, int flags)
Set the given entry in *pm, overwriting an existing entry.
Definition: dict.c:70
static const AVClass icecast_context_class
Definition: icecast.c:208
Describe the class of an AVClass context structure.
Definition: log.h:67
void * priv_data
Definition: url.h:41
char * url
Definition: icecast.c:44
#define snprintf
Definition: snprintf.h:34
static void cat_header(AVBPrint *bp, const char key[], const char value[])
Definition: icecast.c:71
const char * name
Definition: url.h:55
#define flags(name, subs,...)
Definition: cbs_av1.c:560
Main libavformat public API header.
#define E
Definition: icecast.c:54
#define av_free(p)
#define av_freep(p)
char * description
Definition: icecast.c:38
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later.That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another.Frame references ownership and permissions
FFmpeg currently uses a custom build this text attempts to document some of its obscure features and options Makefile the full command issued by make and its output will be shown on the screen DBG Preprocess x86 external assembler files to a dbg asm file in the object which then gets compiled Helps in developing those assembler files DESTDIR Destination directory for the install useful to prepare packages or install FFmpeg in cross environments GEN Set to ‘1’ to generate the missing or mismatched references Makefile builds all the libraries and the executables fate Run the fate test note that you must have installed it fate list List all fate regression test targets install Install headers
Definition: build_system.txt:1
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:127
const char * name
Definition: opengl_enc.c:102