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 {
77  IcecastContext *s = h->priv_data;
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 {
85  IcecastContext *s = h->priv_data;
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  }
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 };
IcecastContext::description
char * description
Definition: icecast.c:38
options
static const AVOption options[]
Definition: icecast.c:55
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
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:235
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:69
AVIO_FLAG_READ_WRITE
#define AVIO_FLAG_READ_WRITE
read-write pseudo flag
Definition: avio.h:656
IcecastContext::send_started
int send_started
Definition: icecast.c:34
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:127
icecast_write
static int icecast_write(URLContext *h, const uint8_t *buf, int size)
Definition: icecast.c:178
name
const char * name
Definition: avisynth_c.h:867
AVOption
AVOption.
Definition: opt.h:246
ffurl_close
int ffurl_close(URLContext *h)
Definition: avio.c:470
icecast_close
static int icecast_close(URLContext *h)
Definition: icecast.c:75
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:50
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:176
buf
void * buf
Definition: avisynth_c.h:766
ff_icecast_protocol
const URLProtocol ff_icecast_protocol
Definition: icecast.c:212
DEFAULT_ICE_USER
#define DEFAULT_ICE_USER
Definition: icecast.c:48
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:307
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:36
OFFSET
#define OFFSET(x)
Definition: icecast.c:52
key
const char * key
Definition: hwcontext_opencl.c:168
pass
#define pass
Definition: fft_template.c:619
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:67
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:191
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:11134
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:4756
uint8_t
uint8_t
Definition: audio_convert.c:194
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
icecast_context_class
static const AVClass icecast_context_class
Definition: icecast.c:205
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:72
avformat.h
av_bprintf
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition: bprint.c:94
network.h
IcecastContext::pass
char * pass
Definition: icecast.c:42
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:424
IcecastContext::content_type
char * content_type
Definition: icecast.c:37
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:654
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:251
icecast_open
static int icecast_open(URLContext *h, const char *uri, int flags)
Definition: icecast.c:83
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:69
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
AV_OPT_TYPE_BOOL
@ AV_OPT_TYPE_BOOL
Definition: opt.h:240
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:53
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:565
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:227
snprintf
#define snprintf
Definition: snprintf.h:34