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 {
79  IcecastContext *s = h->priv_data;
80  ffurl_closep(&s->hd);
81  return 0;
82 }
83 
84 static int icecast_open(URLContext *h, const char *uri, int flags)
85 {
86  IcecastContext *s = h->priv_data;
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 };
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:186
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
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
av_bprint_finalize
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition: bprint.c:234
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
av_bprint_init
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition: bprint.c:68
AVIO_FLAG_READ_WRITE
#define AVIO_FLAG_READ_WRITE
read-write pseudo flag
Definition: avio.h:657
IcecastContext::send_started
int send_started
Definition: icecast.c:34
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:128
icecast_write
static int icecast_write(URLContext *h, const uint8_t *buf, int size)
Definition: icecast.c:181
AVOption
AVOption.
Definition: opt.h:247
icecast_close
static int icecast_close(URLContext *h)
Definition: icecast.c:77
AVDictionary
Definition: dict.c:30
URLProtocol
Definition: url.h:54
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:72
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:180
ff_icecast_protocol
const URLProtocol ff_icecast_protocol
Definition: icecast.c:215
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:306
s
#define s(width, name)
Definition: cbs_vp9.c:257
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:38
OFFSET
#define OFFSET(x)
Definition: icecast.c:53
key
const char * key
Definition: hwcontext_opencl.c:168
pass
#define pass
Definition: fft_template.c:603
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:66
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:235
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:185
size
int size
Definition: twinvq_data.h:10344
URLProtocol::name
const char * name
Definition: url.h:55
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:203
bprint.h
URLContext
Definition: url.h:38
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:4653
icecast_context_class
static const AVClass icecast_context_class
Definition: icecast.c:208
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:438
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:71
avformat.h
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:93
network.h
IcecastContext::pass
char * pass
Definition: icecast.c:42
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 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:34
ffurl_write
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:415
IcecastContext::content_type
char * content_type
Definition: icecast.c:37
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:655
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:259
icecast_open
static int icecast_open(URLContext *h, const char *uri, int flags)
Definition: icecast.c:84
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
Definition: opt.h:241
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:70
E
#define E
Definition: icecast.c:54
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:561
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
h
h
Definition: vp9dsp_template.c:2038
avstring.h
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Definition: opt.h:228
snprintf
#define snprintf
Definition: snprintf.h:34