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;
47 
48 #define DEFAULT_ICE_USER "source"
49 
50 #define NOT_EMPTY(s) (s && s[0])
51 
52 #define OFFSET(x) offsetof(IcecastContext, x)
53 #define E AV_OPT_FLAG_ENCODING_PARAM
54 
55 static const AVOption options[] = {
56  { "ice_genre", "set stream genre", OFFSET(genre), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
57  { "ice_name", "set stream description", OFFSET(name), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
58  { "ice_description", "set stream description", OFFSET(description), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
59  { "ice_url", "set stream website", OFFSET(url), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
60  { "ice_public", "set if stream is public", OFFSET(public), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
61  { "user_agent", "override User-Agent header", OFFSET(user_agent), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
62  { "password", "set password", OFFSET(pass), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
63  { "content_type", "set content-type, MUST be set if not audio/mpeg", OFFSET(content_type), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
64  { "legacy_icecast", "use legacy SOURCE method, for Icecast < v2.4", OFFSET(legacy_icecast), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
65  { NULL }
66 };
67 
68 
69 static void cat_header(AVBPrint *bp, const char key[], const char value[])
70 {
71  if (NOT_EMPTY(value))
72  av_bprintf(bp, "%s: %s\r\n", key, value);
73 }
74 
76 {
78  if (s->hd)
79  ffurl_close(s->hd);
80  return 0;
81 }
82 
83 static int icecast_open(URLContext *h, const char *uri, int flags)
84 {
86 
87  // Dict to set options that we pass to the HTTP protocol
88  AVDictionary *opt_dict = NULL;
89 
90  // URI part variables
91  char h_url[1024], host[1024], auth[1024], path[1024];
92  char *headers = NULL, *user = NULL;
93  int port, ret;
94  AVBPrint bp;
95 
96  if (flags & AVIO_FLAG_READ)
97  return AVERROR(ENOSYS);
98 
100 
101  // Build header strings
102  cat_header(&bp, "Ice-Name", s->name);
103  cat_header(&bp, "Ice-Description", s->description);
104  cat_header(&bp, "Ice-URL", s->url);
105  cat_header(&bp, "Ice-Genre", s->genre);
106  cat_header(&bp, "Ice-Public", s->public ? "1" : "0");
107  if (!av_bprint_is_complete(&bp)) {
108  ret = AVERROR(ENOMEM);
109  goto cleanup;
110  }
111  av_bprint_finalize(&bp, &headers);
112 
113  // Set options
114  av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0);
115  av_dict_set(&opt_dict, "auth_type", "basic", 0);
116  av_dict_set(&opt_dict, "headers", headers, 0);
117  av_dict_set(&opt_dict, "chunked_post", "0", 0);
118  av_dict_set(&opt_dict, "send_expect_100", s->legacy_icecast ? "-1" : "1", 0);
119  if (NOT_EMPTY(s->content_type))
120  av_dict_set(&opt_dict, "content_type", s->content_type, 0);
121  else
122  av_dict_set(&opt_dict, "content_type", "audio/mpeg", 0);
123  if (NOT_EMPTY(s->user_agent))
124  av_dict_set(&opt_dict, "user_agent", s->user_agent, 0);
125 
126  // Parse URI
127  av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host),
128  &port, path, sizeof(path), uri);
129 
130  // Check for auth data in URI
131  if (auth[0]) {
132  char *sep = strchr(auth, ':');
133  if (sep) {
134  *sep = 0;
135  sep++;
136  if (s->pass) {
137  av_free(s->pass);
138  av_log(h, AV_LOG_WARNING, "Overwriting -password <pass> with URI password!\n");
139  }
140  if (!(s->pass = av_strdup(sep))) {
141  ret = AVERROR(ENOMEM);
142  goto cleanup;
143  }
144  }
145  if (!(user = av_strdup(auth))) {
146  ret = AVERROR(ENOMEM);
147  goto cleanup;
148  }
149  }
150 
151  // Build new authstring
152  snprintf(auth, sizeof(auth),
153  "%s:%s",
154  user ? user : DEFAULT_ICE_USER,
155  s->pass ? s->pass : "");
156 
157  // Check for mountpoint (path)
158  if (!path[0] || strcmp(path, "/") == 0) {
159  av_log(h, AV_LOG_ERROR, "No mountpoint (path) specified!\n");
160  ret = AVERROR(EIO);
161  goto cleanup;
162  }
163 
164  // Build new URI for passing to http protocol
165  ff_url_join(h_url, sizeof(h_url), "http", auth, host, port, "%s", path);
166  // Finally open http proto handler
168  &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
169 
170 cleanup:
171  av_freep(&user);
172  av_freep(&headers);
173  av_dict_free(&opt_dict);
174 
175  return ret;
176 }
177 
178 static int icecast_write(URLContext *h, const uint8_t *buf, int size)
179 {
180  IcecastContext *s = h->priv_data;
181  if (!s->send_started) {
182  s->send_started = 1;
183  if (!s->content_type && size >= 8) {
184  static const uint8_t oggs[4] = { 0x4F, 0x67, 0x67, 0x53 };
185  static const uint8_t webm[4] = { 0x1A, 0x45, 0xDF, 0xA3 };
186  static const uint8_t opus[8] = { 0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
187  if (memcmp(buf, oggs, sizeof(oggs)) == 0) {
188  av_log(h, AV_LOG_WARNING, "Streaming Ogg but appropriate content type NOT set!\n");
189  av_log(h, AV_LOG_WARNING, "Set it with -content_type application/ogg\n");
190  } else if (memcmp(buf, opus, sizeof(opus)) == 0) {
191  av_log(h, AV_LOG_WARNING, "Streaming Opus but appropriate content type NOT set!\n");
192  av_log(h, AV_LOG_WARNING, "Set it with -content_type audio/ogg\n");
193  } else if (memcmp(buf, webm, sizeof(webm)) == 0) {
194  av_log(h, AV_LOG_WARNING, "Streaming WebM but appropriate content type NOT set!\n");
195  av_log(h, AV_LOG_WARNING, "Set it with -content_type video/webm\n");
196  } else {
197  av_log(h, AV_LOG_WARNING, "It seems you are streaming an unsupported format.\n");
198  av_log(h, AV_LOG_WARNING, "It might work, but is not officially supported in Icecast!\n");
199  }
200  }
201  }
202  return ffurl_write(s->hd, buf, size);
203 }
204 
206  .class_name = "icecast",
207  .item_name = av_default_item_name,
208  .option = options,
209  .version = LIBAVUTIL_VERSION_INT,
210 };
211 
213  .name = "icecast",
214  .url_open = icecast_open,
215  .url_write = icecast_write,
216  .url_close = icecast_close,
217  .priv_data_size = sizeof(IcecastContext),
218  .priv_data_class = &icecast_context_class,
220 };
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:4742
#define NULL
Definition: coverity.c:32
static int icecast_open(URLContext *h, const char *uri, int flags)
Definition: icecast.c:83
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:307
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
char * pass
Definition: icecast.c:42
AVOption.
Definition: opt.h:246
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
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:421
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
#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:55
static int icecast_write(URLContext *h, const uint8_t *buf, int size)
Definition: icecast.c:178
#define DEFAULT_ICE_USER
Definition: icecast.c:48
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:50
#define OFFSET(x)
Definition: icecast.c:52
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:212
static int icecast_close(URLContext *h)
Definition: icecast.c:75
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:176
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 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 ff_url_join(char *str, int size, const char *proto, const char *authorization, const char *hostname, int port, const char *fmt,...)
Definition: url.c:36
char * name
Definition: icecast.c:41
#define AV_BPRINT_SIZE_AUTOMATIC
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:251
const char * protocol_blacklist
Definition: url.h:50
void * buf
Definition: avisynth_c.h:766
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:205
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:69
const char * name
Definition: url.h:55
#define flags(name, subs,...)
Definition: cbs_av1.c:561
int ffurl_close(URLContext *h)
Definition: avio.c:467
Main libavformat public API header.
#define E
Definition: icecast.c:53
#define av_free(p)
#define av_freep(p)
char * description
Definition: icecast.c:38
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
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
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:127
const char * name
Definition: opengl_enc.c:102