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/bprint.h"
24 #include "libavutil/mem.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  if (strpbrk(value, "\r\n")) {
76  "Refusing to send '%s' header: value contains CR/LF\n", key);
77  return;
78  }
79  av_bprintf(bp, "%s: %s\r\n", key, value);
80  }
81 }
82 
84 {
85  IcecastContext *s = h->priv_data;
86  ffurl_closep(&s->hd);
87  return 0;
88 }
89 
90 static int icecast_open(URLContext *h, const char *uri, int flags)
91 {
92  IcecastContext *s = h->priv_data;
93 
94  // Dict to set options that we pass to the HTTP protocol
95  AVDictionary *opt_dict = NULL;
96 
97  // URI part variables
98  char h_url[1024], host[1024], auth[1024], path[1024];
99  char *headers, *user = NULL;
100  int port, ret;
101  AVBPrint bp;
102 
103  if (flags & AVIO_FLAG_READ)
104  return AVERROR(ENOSYS);
105 
107 
108  // Build header strings
109  cat_header(&bp, "Ice-Name", s->name);
110  cat_header(&bp, "Ice-Description", s->description);
111  cat_header(&bp, "Ice-URL", s->url);
112  cat_header(&bp, "Ice-Genre", s->genre);
113  cat_header(&bp, "Ice-Public", s->public ? "1" : "0");
114  if (!av_bprint_is_complete(&bp)) {
115  av_bprint_finalize(&bp, NULL);
116  return AVERROR(ENOMEM);
117  }
118  if ((ret = av_bprint_finalize(&bp, &headers)) < 0)
119  return ret;
120 
121  // Set options
122  av_dict_set(&opt_dict, "method", s->legacy_icecast ? "SOURCE" : "PUT", 0);
123  av_dict_set(&opt_dict, "auth_type", "basic", 0);
124  av_dict_set(&opt_dict, "headers", headers, AV_DICT_DONT_STRDUP_VAL);
125  av_dict_set(&opt_dict, "chunked_post", "0", 0);
126  av_dict_set(&opt_dict, "send_expect_100", s->legacy_icecast ? "-1" : "1", 0);
127  if (NOT_EMPTY(s->content_type))
128  av_dict_set(&opt_dict, "content_type", s->content_type, 0);
129  else
130  av_dict_set(&opt_dict, "content_type", "audio/mpeg", 0);
131  if (NOT_EMPTY(s->user_agent))
132  av_dict_set(&opt_dict, "user_agent", s->user_agent, 0);
133 
134  // Parse URI
135  av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host),
136  &port, path, sizeof(path), uri);
137 
138  // Check for auth data in URI
139  if (auth[0]) {
140  char *sep = strchr(auth, ':');
141  if (sep) {
142  *sep = 0;
143  sep++;
144  if (s->pass) {
145  av_free(s->pass);
146  av_log(h, AV_LOG_WARNING, "Overwriting -password <pass> with URI password!\n");
147  }
148  if (!(s->pass = av_strdup(sep))) {
149  ret = AVERROR(ENOMEM);
150  goto cleanup;
151  }
152  }
153  if (!(user = av_strdup(auth))) {
154  ret = AVERROR(ENOMEM);
155  goto cleanup;
156  }
157  }
158 
159  // Build new authstring
160  snprintf(auth, sizeof(auth),
161  "%s:%s",
162  user ? user : DEFAULT_ICE_USER,
163  s->pass ? s->pass : "");
164 
165  // Check for mountpoint (path)
166  if (!path[0] || strcmp(path, "/") == 0) {
167  av_log(h, AV_LOG_ERROR, "No mountpoint (path) specified!\n");
168  ret = AVERROR(EIO);
169  goto cleanup;
170  }
171 
172  // Build new URI for passing to http protocol
173  ff_url_join(h_url, sizeof(h_url),
174  s->tls ? "https" : "http",
175  auth, host, port, "%s", path);
176  // Finally open http proto handler
178  &opt_dict, h->protocol_whitelist, h->protocol_blacklist, h);
179 
180 cleanup:
181  av_freep(&user);
182  av_dict_free(&opt_dict);
183 
184  return ret;
185 }
186 
187 static int icecast_write(URLContext *h, const uint8_t *buf, int size)
188 {
189  IcecastContext *s = h->priv_data;
190  if (!s->send_started) {
191  s->send_started = 1;
192  if (!s->content_type && size >= 8) {
193  static const uint8_t oggs[4] = { 0x4F, 0x67, 0x67, 0x53 };
194  static const uint8_t webm[4] = { 0x1A, 0x45, 0xDF, 0xA3 };
195  static const uint8_t opus[8] = { 0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64 };
196  if (memcmp(buf, oggs, sizeof(oggs)) == 0) {
197  av_log(h, AV_LOG_WARNING, "Streaming Ogg but appropriate content type NOT set!\n");
198  av_log(h, AV_LOG_WARNING, "Set it with -content_type application/ogg\n");
199  } else if (memcmp(buf, opus, sizeof(opus)) == 0) {
200  av_log(h, AV_LOG_WARNING, "Streaming Opus but appropriate content type NOT set!\n");
201  av_log(h, AV_LOG_WARNING, "Set it with -content_type audio/ogg\n");
202  } else if (memcmp(buf, webm, sizeof(webm)) == 0) {
203  av_log(h, AV_LOG_WARNING, "Streaming WebM but appropriate content type NOT set!\n");
204  av_log(h, AV_LOG_WARNING, "Set it with -content_type video/webm\n");
205  } else {
206  av_log(h, AV_LOG_WARNING, "It seems you are streaming an unsupported format.\n");
207  av_log(h, AV_LOG_WARNING, "It might work, but is not officially supported in Icecast!\n");
208  }
209  }
210  }
211  return ffurl_write(s->hd, buf, size);
212 }
213 
215  .class_name = "icecast",
216  .item_name = av_default_item_name,
217  .option = options,
218  .version = LIBAVUTIL_VERSION_INT,
219 };
220 
222  .name = "icecast",
223  .url_open = icecast_open,
224  .url_write = icecast_write,
225  .url_close = icecast_close,
226  .priv_data_size = sizeof(IcecastContext),
227  .priv_data_class = &icecast_context_class,
229 };
flags
const SwsFlags flags[]
Definition: swscale.c:72
IcecastContext::description
char * description
Definition: icecast.c:38
options
static const AVOption options[]
Definition: icecast.c:56
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:216
name
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
av_bprint_is_complete
static int av_bprint_is_complete(const AVBPrint *buf)
Test if the print buffer is complete (not truncated).
Definition: bprint.h:218
AVERROR
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
opt.h
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:33
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:69
AVIO_FLAG_READ_WRITE
#define AVIO_FLAG_READ_WRITE
read-write pseudo flag
Definition: avio.h:619
IcecastContext::send_started
int send_started
Definition: icecast.c:34
ffurl_write
static int ffurl_write(URLContext *h, const uint8_t *buf, int size)
Write size bytes from buf to the resource accessed by h.
Definition: url.h:202
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:130
icecast_write
static int icecast_write(URLContext *h, const uint8_t *buf, int size)
Definition: icecast.c:187
AVOption
AVOption.
Definition: opt.h:429
icecast_close
static int icecast_close(URLContext *h)
Definition: icecast.c:83
AVDictionary
Definition: dict.c:32
URLProtocol
Definition: url.h:51
IcecastContext::genre
char * genre
Definition: icecast.c:39
IcecastContext::legacy_icecast
int legacy_icecast
Definition: icecast.c:40
IcecastContext::url
char * url
Definition: icecast.c:44
AV_BPRINT_SIZE_AUTOMATIC
#define AV_BPRINT_SIZE_AUTOMATIC
NOT_EMPTY
#define NOT_EMPTY(s)
Definition: icecast.c:51
AV_DICT_DONT_STRDUP_VAL
#define AV_DICT_DONT_STRDUP_VAL
Take ownership of a value that's been allocated with av_malloc() or another memory allocation functio...
Definition: dict.h:79
description
Tag description
Definition: snow.txt:206
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:210
ff_icecast_protocol
const URLProtocol ff_icecast_protocol
Definition: icecast.c:221
DEFAULT_ICE_USER
#define DEFAULT_ICE_USER
Definition: icecast.c:49
ffurl_open_whitelist
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:368
s
#define s(width, name)
Definition: cbs_vp9.c:198
IcecastContext
Definition: icecast.c:31
ff_url_join
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:40
headers
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 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 fate list failing List the fate tests that failed the last time they were executed fate clear reports Remove the test reports from previous test libraries and programs examples Build all examples located in doc examples checkheaders Check headers dependencies alltools Build all tools in tools directory config Reconfigure the project with the current configuration tools target_dec_< decoder > _fuzzer Build fuzzer to fuzz the specified decoder tools target_bsf_< filter > _fuzzer Build fuzzer to fuzz the specified bitstream filter Useful standard make this is useful to reduce unneeded rebuilding when changing headers
Definition: build_system.txt:59
OFFSET
#define OFFSET(x)
Definition: icecast.c:53
key
const char * key
Definition: hwcontext_opencl.c:189
IcecastContext::hd
URLContext * hd
Definition: icecast.c:33
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:76
NULL
#define NULL
Definition: coverity.c:32
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:242
options
Definition: swscale.c:45
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:235
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:52
IcecastContext::user_agent
char * user_agent
Definition: icecast.c:45
av_dict_free
void av_dict_free(AVDictionary **pm)
Free all the memory allocated for an AVDictionary struct and all keys and values.
Definition: dict.c:233
bprint.h
URLContext
Definition: url.h:35
value
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf default value
Definition: writing_filters.txt:86
av_url_split
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:361
icecast_context_class
static const AVClass icecast_context_class
Definition: icecast.c:214
IcecastContext::tls
int tls
Definition: icecast.c:46
ffurl_closep
int ffurl_closep(URLContext **hh)
Close the resource accessed by the URLContext h, and free the memory used by it.
Definition: avio.c:594
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
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:81
avformat.h
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:122
network.h
IcecastContext::pass
char * pass
Definition: icecast.c:42
IcecastContext::content_type
char * content_type
Definition: icecast.c:37
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:617
icecast_open
static int icecast_open(URLContext *h, const char *uri, int flags)
Definition: icecast.c:90
mem.h
av_strdup
#define av_strdup(s)
Definition: ops_asmgen.c:47
IcecastContext::user
char * user
Definition: icecast.c:35
IcecastContext::name
char * name
Definition: icecast.c:41
cat_header
static void cat_header(AVBPrint *bp, const char key[], const char value[])
Definition: icecast.c:71
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AV_OPT_TYPE_BOOL
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition: opt.h:327
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
av_dict_set
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:86
E
#define E
Definition: icecast.c:54
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:27
h
h
Definition: vp9dsp_template.c:2070
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition: opt.h:276
snprintf
#define snprintf
Definition: snprintf.h:34