Merge branch 'v0.17.x'
This commit is contained in:
commit
d95e538020
6
NEWS
6
NEWS
@ -7,6 +7,12 @@ ver 0.18 (2012/??/??)
|
|||||||
- new option "tags" may be used to disable sending tags to output
|
- new option "tags" may be used to disable sending tags to output
|
||||||
* improved decoder/output error reporting
|
* improved decoder/output error reporting
|
||||||
|
|
||||||
|
ver 0.17.3 (2012/??/??)
|
||||||
|
* output:
|
||||||
|
- recorder: fix I/O error check
|
||||||
|
- shout: fix memory leak in error handler
|
||||||
|
- recorder, shout: support Ogg packets that span more than one page
|
||||||
|
|
||||||
ver 0.17.2 (2012/09/30)
|
ver 0.17.2 (2012/09/30)
|
||||||
* protocol:
|
* protocol:
|
||||||
- fix crash in local file check
|
- fix crash in local file check
|
||||||
|
@ -65,13 +65,11 @@ static bool
|
|||||||
vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
||||||
const struct config_param *param, GError **error)
|
const struct config_param *param, GError **error)
|
||||||
{
|
{
|
||||||
const char *value;
|
const char *value = config_get_block_string(param, "quality", NULL);
|
||||||
char *endptr;
|
|
||||||
|
|
||||||
value = config_get_block_string(param, "quality", NULL);
|
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
/* a quality was configured (VBR) */
|
/* a quality was configured (VBR) */
|
||||||
|
|
||||||
|
char *endptr;
|
||||||
encoder->quality = g_ascii_strtod(value, &endptr);
|
encoder->quality = g_ascii_strtod(value, &endptr);
|
||||||
|
|
||||||
if (*endptr != '\0' || encoder->quality < -1.0 ||
|
if (*endptr != '\0' || encoder->quality < -1.0 ||
|
||||||
@ -103,8 +101,9 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
|||||||
}
|
}
|
||||||
|
|
||||||
encoder->quality = -2.0;
|
encoder->quality = -2.0;
|
||||||
encoder->bitrate = g_ascii_strtoll(value, &endptr, 10);
|
|
||||||
|
|
||||||
|
char *endptr;
|
||||||
|
encoder->bitrate = g_ascii_strtoll(value, &endptr, 10);
|
||||||
if (*endptr != '\0' || encoder->bitrate <= 0) {
|
if (*endptr != '\0' || encoder->bitrate <= 0) {
|
||||||
g_set_error(error, vorbis_encoder_quark(), 0,
|
g_set_error(error, vorbis_encoder_quark(), 0,
|
||||||
"bitrate at line %i should be a positive integer",
|
"bitrate at line %i should be a positive integer",
|
||||||
@ -119,9 +118,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
|||||||
static struct encoder *
|
static struct encoder *
|
||||||
vorbis_encoder_init(const struct config_param *param, GError **error)
|
vorbis_encoder_init(const struct config_param *param, GError **error)
|
||||||
{
|
{
|
||||||
struct vorbis_encoder *encoder;
|
struct vorbis_encoder *encoder = g_new(struct vorbis_encoder, 1);
|
||||||
|
|
||||||
encoder = g_new(struct vorbis_encoder, 1);
|
|
||||||
encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
|
encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
|
||||||
|
|
||||||
/* load configuration from "param" */
|
/* load configuration from "param" */
|
||||||
@ -211,14 +208,12 @@ vorbis_encoder_open(struct encoder *_encoder,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||||
bool ret;
|
|
||||||
|
|
||||||
audio_format->format = SAMPLE_FORMAT_S16;
|
audio_format->format = SAMPLE_FORMAT_S16;
|
||||||
|
|
||||||
encoder->audio_format = *audio_format;
|
encoder->audio_format = *audio_format;
|
||||||
|
|
||||||
ret = vorbis_encoder_reinit(encoder, error);
|
if (!vorbis_encoder_reinit(encoder, error))
|
||||||
if (!ret)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
vorbis_encoder_send_header(encoder);
|
vorbis_encoder_send_header(encoder);
|
||||||
@ -251,11 +246,10 @@ static void
|
|||||||
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
|
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
|
||||||
{
|
{
|
||||||
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
|
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
|
||||||
ogg_packet packet;
|
|
||||||
|
|
||||||
vorbis_analysis(&encoder->vb, NULL);
|
vorbis_analysis(&encoder->vb, NULL);
|
||||||
vorbis_bitrate_addblock(&encoder->vb);
|
vorbis_bitrate_addblock(&encoder->vb);
|
||||||
|
|
||||||
|
ogg_packet packet;
|
||||||
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
|
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
|
||||||
ogg_stream_packetin(&encoder->os, &packet);
|
ogg_stream_packetin(&encoder->os, &packet);
|
||||||
}
|
}
|
||||||
@ -344,9 +338,9 @@ vorbis_encoder_write(struct encoder *_encoder,
|
|||||||
G_GNUC_UNUSED GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||||
unsigned num_frames;
|
|
||||||
|
|
||||||
num_frames = length / audio_format_frame_size(&encoder->audio_format);
|
unsigned num_frames = length
|
||||||
|
/ audio_format_frame_size(&encoder->audio_format);
|
||||||
|
|
||||||
/* this is for only 16-bit audio */
|
/* this is for only 16-bit audio */
|
||||||
|
|
||||||
@ -364,12 +358,10 @@ static size_t
|
|||||||
vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
||||||
{
|
{
|
||||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||||
ogg_page page;
|
|
||||||
int ret;
|
|
||||||
unsigned char *dest = _dest;
|
unsigned char *dest = _dest;
|
||||||
size_t nbytes;
|
|
||||||
|
|
||||||
ret = ogg_stream_pageout(&encoder->os, &page);
|
ogg_page page;
|
||||||
|
int ret = ogg_stream_pageout(&encoder->os, &page);
|
||||||
if (ret == 0 && encoder->flush) {
|
if (ret == 0 && encoder->flush) {
|
||||||
encoder->flush = false;
|
encoder->flush = false;
|
||||||
ret = ogg_stream_flush(&encoder->os, &page);
|
ret = ogg_stream_flush(&encoder->os, &page);
|
||||||
@ -381,7 +373,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
|||||||
|
|
||||||
assert(page.header_len > 0 || page.body_len > 0);
|
assert(page.header_len > 0 || page.body_len > 0);
|
||||||
|
|
||||||
nbytes = (size_t)page.header_len + (size_t)page.body_len;
|
size_t nbytes = (size_t)page.header_len + (size_t)page.body_len;
|
||||||
|
|
||||||
if (nbytes > length)
|
if (nbytes > length)
|
||||||
/* XXX better error handling */
|
/* XXX better error handling */
|
||||||
|
@ -119,6 +119,10 @@ encoder_finish(struct encoder *encoder)
|
|||||||
* Before you free it, you must call encoder_close(). You may open
|
* Before you free it, you must call encoder_close(). You may open
|
||||||
* and close (reuse) one encoder any number of times.
|
* and close (reuse) one encoder any number of times.
|
||||||
*
|
*
|
||||||
|
* After this function returns successfully and before the first
|
||||||
|
* encoder_write() call, you should invoke encoder_read() to obtain
|
||||||
|
* the file header.
|
||||||
|
*
|
||||||
* @param encoder the encoder
|
* @param encoder the encoder
|
||||||
* @param audio_format the encoder's input audio format; the plugin
|
* @param audio_format the encoder's input audio format; the plugin
|
||||||
* may modify the struct to adapt it to its abilities
|
* may modify the struct to adapt it to its abilities
|
||||||
@ -291,6 +295,8 @@ encoder_write(struct encoder *encoder, const void *data, size_t length,
|
|||||||
/**
|
/**
|
||||||
* Reads encoded data from the encoder.
|
* Reads encoded data from the encoder.
|
||||||
*
|
*
|
||||||
|
* Call this repeatedly until no more data is returned.
|
||||||
|
*
|
||||||
* @param encoder the encoder
|
* @param encoder the encoder
|
||||||
* @param dest the destination buffer to copy to
|
* @param dest the destination buffer to copy to
|
||||||
* @param length the maximum length of the destination buffer
|
* @param length the maximum length of the destination buffer
|
||||||
|
@ -114,10 +114,6 @@ httpd_output_init(const struct config_param *param,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *encoder_name, *bind_to_address;
|
|
||||||
const struct encoder_plugin *encoder_plugin;
|
|
||||||
guint port;
|
|
||||||
|
|
||||||
/* read configuration */
|
/* read configuration */
|
||||||
httpd->name =
|
httpd->name =
|
||||||
config_get_block_string(param, "name", "Set name in config");
|
config_get_block_string(param, "name", "Set name in config");
|
||||||
@ -126,10 +122,12 @@ httpd_output_init(const struct config_param *param,
|
|||||||
httpd->website =
|
httpd->website =
|
||||||
config_get_block_string(param, "website", "Set website in config");
|
config_get_block_string(param, "website", "Set website in config");
|
||||||
|
|
||||||
port = config_get_block_unsigned(param, "port", 8000);
|
guint port = config_get_block_unsigned(param, "port", 8000);
|
||||||
|
|
||||||
encoder_name = config_get_block_string(param, "encoder", "vorbis");
|
const char *encoder_name =
|
||||||
encoder_plugin = encoder_plugin_get(encoder_name);
|
config_get_block_string(param, "encoder", "vorbis");
|
||||||
|
const struct encoder_plugin *encoder_plugin =
|
||||||
|
encoder_plugin_get(encoder_name);
|
||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
g_set_error(error, httpd_output_quark(), 0,
|
g_set_error(error, httpd_output_quark(), 0,
|
||||||
"No such encoder: %s", encoder_name);
|
"No such encoder: %s", encoder_name);
|
||||||
@ -144,7 +142,7 @@ httpd_output_init(const struct config_param *param,
|
|||||||
|
|
||||||
httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
|
httpd->server_socket = server_socket_new(httpd_listen_in_event, httpd);
|
||||||
|
|
||||||
bind_to_address =
|
const char *bind_to_address =
|
||||||
config_get_block_string(param, "bind_to_address", NULL);
|
config_get_block_string(param, "bind_to_address", NULL);
|
||||||
bool success = bind_to_address != NULL &&
|
bool success = bind_to_address != NULL &&
|
||||||
strcmp(bind_to_address, "any") != 0
|
strcmp(bind_to_address, "any") != 0
|
||||||
@ -275,8 +273,6 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
|
|||||||
static struct page *
|
static struct page *
|
||||||
httpd_output_read_page(struct httpd_output *httpd)
|
httpd_output_read_page(struct httpd_output *httpd)
|
||||||
{
|
{
|
||||||
size_t size = 0, nbytes;
|
|
||||||
|
|
||||||
if (httpd->unflushed_input >= 65536) {
|
if (httpd->unflushed_input >= 65536) {
|
||||||
/* we have fed a lot of input into the encoder, but it
|
/* we have fed a lot of input into the encoder, but it
|
||||||
didn't give anything back yet - flush now to avoid
|
didn't give anything back yet - flush now to avoid
|
||||||
@ -285,9 +281,11 @@ httpd_output_read_page(struct httpd_output *httpd)
|
|||||||
httpd->unflushed_input = 0;
|
httpd->unflushed_input = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t size = 0;
|
||||||
do {
|
do {
|
||||||
nbytes = encoder_read(httpd->encoder, httpd->buffer + size,
|
size_t nbytes = encoder_read(httpd->encoder,
|
||||||
sizeof(httpd->buffer) - size);
|
httpd->buffer + size,
|
||||||
|
sizeof(httpd->buffer) - size);
|
||||||
if (nbytes == 0)
|
if (nbytes == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -307,10 +305,7 @@ httpd_output_encoder_open(struct httpd_output *httpd,
|
|||||||
struct audio_format *audio_format,
|
struct audio_format *audio_format,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
bool success;
|
if (!encoder_open(httpd->encoder, audio_format, error))
|
||||||
|
|
||||||
success = encoder_open(httpd->encoder, audio_format, error);
|
|
||||||
if (!success)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* we have to remember the encoder header, i.e. the first
|
/* we have to remember the encoder header, i.e. the first
|
||||||
@ -344,14 +339,12 @@ httpd_output_open(struct audio_output *ao, struct audio_format *audio_format,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
struct httpd_output *httpd = (struct httpd_output *)ao;
|
struct httpd_output *httpd = (struct httpd_output *)ao;
|
||||||
bool success;
|
|
||||||
|
|
||||||
g_mutex_lock(httpd->mutex);
|
g_mutex_lock(httpd->mutex);
|
||||||
|
|
||||||
/* open the encoder */
|
/* open the encoder */
|
||||||
|
|
||||||
success = httpd_output_encoder_open(httpd, audio_format, error);
|
if (!httpd_output_encoder_open(httpd, audio_format, error)) {
|
||||||
if (!success) {
|
|
||||||
g_mutex_unlock(httpd->mutex);
|
g_mutex_unlock(httpd->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -495,10 +488,7 @@ static bool
|
|||||||
httpd_output_encode_and_play(struct httpd_output *httpd,
|
httpd_output_encode_and_play(struct httpd_output *httpd,
|
||||||
const void *chunk, size_t size, GError **error)
|
const void *chunk, size_t size, GError **error)
|
||||||
{
|
{
|
||||||
bool success;
|
if (!encoder_write(httpd->encoder, chunk, size, error))
|
||||||
|
|
||||||
success = encoder_write(httpd->encoder, chunk, size, error);
|
|
||||||
if (!success)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
httpd->unflushed_input += size;
|
httpd->unflushed_input += size;
|
||||||
@ -510,16 +500,12 @@ httpd_output_encode_and_play(struct httpd_output *httpd,
|
|||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
|
httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
|
||||||
GError **error)
|
GError **error_r)
|
||||||
{
|
{
|
||||||
struct httpd_output *httpd = (struct httpd_output *)ao;
|
struct httpd_output *httpd = (struct httpd_output *)ao;
|
||||||
|
|
||||||
if (httpd_output_lock_has_clients(httpd)) {
|
if (httpd_output_lock_has_clients(httpd)) {
|
||||||
bool success;
|
if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
|
||||||
|
|
||||||
success = httpd_output_encode_and_play(httpd, chunk, size,
|
|
||||||
error);
|
|
||||||
if (!success)
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,7 +548,6 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
|
|||||||
|
|
||||||
if (httpd->encoder->plugin->tag != NULL) {
|
if (httpd->encoder->plugin->tag != NULL) {
|
||||||
/* embed encoder tags */
|
/* embed encoder tags */
|
||||||
struct page *page;
|
|
||||||
|
|
||||||
/* flush the current stream, and end it */
|
/* flush the current stream, and end it */
|
||||||
|
|
||||||
@ -578,7 +563,7 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
|
|||||||
used as the new "header" page, which is sent to all
|
used as the new "header" page, which is sent to all
|
||||||
new clients */
|
new clients */
|
||||||
|
|
||||||
page = httpd_output_read_page(httpd);
|
struct page *page = httpd_output_read_page(httpd);
|
||||||
if (page != NULL) {
|
if (page != NULL) {
|
||||||
if (httpd->header != NULL)
|
if (httpd->header != NULL)
|
||||||
page_unref(httpd->header);
|
page_unref(httpd->header);
|
||||||
|
@ -77,13 +77,12 @@ recorder_output_init(const struct config_param *param, GError **error_r)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *encoder_name;
|
|
||||||
const struct encoder_plugin *encoder_plugin;
|
|
||||||
|
|
||||||
/* read configuration */
|
/* read configuration */
|
||||||
|
|
||||||
encoder_name = config_get_block_string(param, "encoder", "vorbis");
|
const char *encoder_name =
|
||||||
encoder_plugin = encoder_plugin_get(encoder_name);
|
config_get_block_string(param, "encoder", "vorbis");
|
||||||
|
const struct encoder_plugin *encoder_plugin =
|
||||||
|
encoder_plugin_get(encoder_name);
|
||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
g_set_error(error_r, recorder_output_quark(), 0,
|
g_set_error(error_r, recorder_output_quark(), 0,
|
||||||
"No such encoder: %s", encoder_name);
|
"No such encoder: %s", encoder_name);
|
||||||
@ -121,33 +120,22 @@ recorder_output_finish(struct audio_output *ao)
|
|||||||
g_free(recorder);
|
g_free(recorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes pending data from the encoder to the output file.
|
|
||||||
*/
|
|
||||||
static bool
|
static bool
|
||||||
recorder_output_encoder_to_file(struct recorder_output *recorder,
|
recorder_write_to_file(struct recorder_output *recorder,
|
||||||
GError **error_r)
|
const void *_data, size_t length,
|
||||||
|
GError **error_r)
|
||||||
{
|
{
|
||||||
size_t size = 0, position, nbytes;
|
assert(length > 0);
|
||||||
|
|
||||||
assert(recorder->fd >= 0);
|
const int fd = recorder->fd;
|
||||||
|
|
||||||
/* read from the encoder */
|
const uint8_t *data = (const uint8_t *)_data, *end = data + length;
|
||||||
|
|
||||||
size = encoder_read(recorder->encoder, recorder->buffer,
|
|
||||||
sizeof(recorder->buffer));
|
|
||||||
if (size == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
/* write everything into the file */
|
|
||||||
|
|
||||||
position = 0;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
nbytes = write(recorder->fd, recorder->buffer + position,
|
ssize_t nbytes = write(fd, data, end - data);
|
||||||
size - position);
|
|
||||||
if (nbytes > 0) {
|
if (nbytes > 0) {
|
||||||
position += (size_t)nbytes;
|
data += nbytes;
|
||||||
if (position >= size)
|
if (data == end)
|
||||||
return true;
|
return true;
|
||||||
} else if (nbytes == 0) {
|
} else if (nbytes == 0) {
|
||||||
/* shouldn't happen for files */
|
/* shouldn't happen for files */
|
||||||
@ -163,13 +151,37 @@ recorder_output_encoder_to_file(struct recorder_output *recorder,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes pending data from the encoder to the output file.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
recorder_output_encoder_to_file(struct recorder_output *recorder,
|
||||||
|
GError **error_r)
|
||||||
|
{
|
||||||
|
assert(recorder->fd >= 0);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
/* read from the encoder */
|
||||||
|
|
||||||
|
size_t size = encoder_read(recorder->encoder, recorder->buffer,
|
||||||
|
sizeof(recorder->buffer));
|
||||||
|
if (size == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* write everything into the file */
|
||||||
|
|
||||||
|
if (!recorder_write_to_file(recorder, recorder->buffer, size,
|
||||||
|
error_r))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
recorder_output_open(struct audio_output *ao,
|
recorder_output_open(struct audio_output *ao,
|
||||||
struct audio_format *audio_format,
|
struct audio_format *audio_format,
|
||||||
GError **error_r)
|
GError **error_r)
|
||||||
{
|
{
|
||||||
struct recorder_output *recorder = (struct recorder_output *)ao;
|
struct recorder_output *recorder = (struct recorder_output *)ao;
|
||||||
bool success;
|
|
||||||
|
|
||||||
/* create the output file */
|
/* create the output file */
|
||||||
|
|
||||||
@ -185,8 +197,14 @@ recorder_output_open(struct audio_output *ao,
|
|||||||
|
|
||||||
/* open the encoder */
|
/* open the encoder */
|
||||||
|
|
||||||
success = encoder_open(recorder->encoder, audio_format, error_r);
|
if (!encoder_open(recorder->encoder, audio_format, error_r)) {
|
||||||
if (!success) {
|
close(recorder->fd);
|
||||||
|
unlink(recorder->path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recorder_output_encoder_to_file(recorder, error_r)) {
|
||||||
|
encoder_close(recorder->encoder);
|
||||||
close(recorder->fd);
|
close(recorder->fd);
|
||||||
unlink(recorder->path);
|
unlink(recorder->path);
|
||||||
return false;
|
return false;
|
||||||
|
@ -36,11 +36,6 @@
|
|||||||
|
|
||||||
#define DEFAULT_CONN_TIMEOUT 2
|
#define DEFAULT_CONN_TIMEOUT 2
|
||||||
|
|
||||||
struct shout_buffer {
|
|
||||||
unsigned char data[32768];
|
|
||||||
size_t len;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct shout_data {
|
struct shout_data {
|
||||||
struct audio_output base;
|
struct audio_output base;
|
||||||
|
|
||||||
@ -54,7 +49,7 @@ struct shout_data {
|
|||||||
|
|
||||||
int timeout;
|
int timeout;
|
||||||
|
|
||||||
struct shout_buffer buf;
|
uint8_t buffer[32768];
|
||||||
};
|
};
|
||||||
|
|
||||||
static int shout_init_count;
|
static int shout_init_count;
|
||||||
@ -114,24 +109,7 @@ static struct audio_output *
|
|||||||
my_shout_init_driver(const struct config_param *param,
|
my_shout_init_driver(const struct config_param *param,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
struct shout_data *sd;
|
struct shout_data *sd = new_shout_data();
|
||||||
char *test;
|
|
||||||
unsigned port;
|
|
||||||
char *host;
|
|
||||||
char *mount;
|
|
||||||
char *passwd;
|
|
||||||
const char *encoding;
|
|
||||||
const struct encoder_plugin *encoder_plugin;
|
|
||||||
unsigned shout_format;
|
|
||||||
unsigned protocol;
|
|
||||||
const char *user;
|
|
||||||
char *name;
|
|
||||||
const char *value;
|
|
||||||
const struct block_param *block_param;
|
|
||||||
int public;
|
|
||||||
|
|
||||||
sd = new_shout_data();
|
|
||||||
|
|
||||||
if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
|
if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
|
||||||
free_shout_data(sd);
|
free_shout_data(sd);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -152,13 +130,14 @@ my_shout_init_driver(const struct config_param *param,
|
|||||||
|
|
||||||
shout_init_count++;
|
shout_init_count++;
|
||||||
|
|
||||||
|
const struct block_param *block_param;
|
||||||
check_block_param("host");
|
check_block_param("host");
|
||||||
host = block_param->value;
|
char *host = block_param->value;
|
||||||
|
|
||||||
check_block_param("mount");
|
check_block_param("mount");
|
||||||
mount = block_param->value;
|
char *mount = block_param->value;
|
||||||
|
|
||||||
port = config_get_block_unsigned(param, "port", 0);
|
unsigned port = config_get_block_unsigned(param, "port", 0);
|
||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"shout port must be configured");
|
"shout port must be configured");
|
||||||
@ -166,17 +145,18 @@ my_shout_init_driver(const struct config_param *param,
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_block_param("password");
|
check_block_param("password");
|
||||||
passwd = block_param->value;
|
const char *passwd = block_param->value;
|
||||||
|
|
||||||
check_block_param("name");
|
check_block_param("name");
|
||||||
name = block_param->value;
|
const char *name = block_param->value;
|
||||||
|
|
||||||
public = config_get_block_bool(param, "public", false);
|
bool public = config_get_block_bool(param, "public", false);
|
||||||
|
|
||||||
user = config_get_block_string(param, "user", "source");
|
const char *user = config_get_block_string(param, "user", "source");
|
||||||
|
|
||||||
value = config_get_block_string(param, "quality", NULL);
|
const char *value = config_get_block_string(param, "quality", NULL);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
|
char *test;
|
||||||
sd->quality = strtod(value, &test);
|
sd->quality = strtod(value, &test);
|
||||||
|
|
||||||
if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
|
if (*test != '\0' || sd->quality < -1.0 || sd->quality > 10.0) {
|
||||||
@ -201,6 +181,7 @@ my_shout_init_driver(const struct config_param *param,
|
|||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *test;
|
||||||
sd->bitrate = strtol(value, &test, 10);
|
sd->bitrate = strtol(value, &test, 10);
|
||||||
|
|
||||||
if (*test != '\0' || sd->bitrate <= 0) {
|
if (*test != '\0' || sd->bitrate <= 0) {
|
||||||
@ -210,8 +191,10 @@ my_shout_init_driver(const struct config_param *param,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
encoding = config_get_block_string(param, "encoding", "ogg");
|
const char *encoding = config_get_block_string(param, "encoding",
|
||||||
encoder_plugin = shout_encoder_plugin_get(encoding);
|
"ogg");
|
||||||
|
const struct encoder_plugin *encoder_plugin =
|
||||||
|
shout_encoder_plugin_get(encoding);
|
||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"couldn't find shout encoder plugin \"%s\"",
|
"couldn't find shout encoder plugin \"%s\"",
|
||||||
@ -223,11 +206,13 @@ my_shout_init_driver(const struct config_param *param,
|
|||||||
if (sd->encoder == NULL)
|
if (sd->encoder == NULL)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
|
||||||
|
unsigned shout_format;
|
||||||
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
||||||
shout_format = SHOUT_FORMAT_MP3;
|
shout_format = SHOUT_FORMAT_MP3;
|
||||||
else
|
else
|
||||||
shout_format = SHOUT_FORMAT_OGG;
|
shout_format = SHOUT_FORMAT_OGG;
|
||||||
|
|
||||||
|
unsigned protocol;
|
||||||
value = config_get_block_string(param, "protocol", NULL);
|
value = config_get_block_string(param, "protocol", NULL);
|
||||||
if (value != NULL) {
|
if (value != NULL) {
|
||||||
if (0 == strcmp(value, "shoutcast") &&
|
if (0 == strcmp(value, "shoutcast") &&
|
||||||
@ -355,26 +340,24 @@ handle_shout_error(struct shout_data *sd, int err, GError **error)
|
|||||||
static bool
|
static bool
|
||||||
write_page(struct shout_data *sd, GError **error)
|
write_page(struct shout_data *sd, GError **error)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
|
|
||||||
assert(sd->encoder != NULL);
|
assert(sd->encoder != NULL);
|
||||||
|
|
||||||
sd->buf.len = encoder_read(sd->encoder,
|
while (true) {
|
||||||
sd->buf.data, sizeof(sd->buf.data));
|
size_t nbytes = encoder_read(sd->encoder,
|
||||||
if (sd->buf.len == 0)
|
sd->buffer, sizeof(sd->buffer));
|
||||||
return true;
|
if (nbytes == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
|
int err = shout_send(sd->shout_conn, sd->buffer, nbytes);
|
||||||
if (!handle_shout_error(sd, err, error))
|
if (!handle_shout_error(sd, err, error))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_shout_conn(struct shout_data * sd)
|
static void close_shout_conn(struct shout_data * sd)
|
||||||
{
|
{
|
||||||
sd->buf.len = 0;
|
|
||||||
|
|
||||||
if (sd->encoder != NULL) {
|
if (sd->encoder != NULL) {
|
||||||
if (encoder_end(sd->encoder, NULL))
|
if (encoder_end(sd->encoder, NULL))
|
||||||
write_page(sd, NULL);
|
write_page(sd, NULL);
|
||||||
@ -425,10 +408,7 @@ my_shout_close_device(struct audio_output *ao)
|
|||||||
static bool
|
static bool
|
||||||
shout_connect(struct shout_data *sd, GError **error)
|
shout_connect(struct shout_data *sd, GError **error)
|
||||||
{
|
{
|
||||||
int state;
|
switch (shout_open(sd->shout_conn)) {
|
||||||
|
|
||||||
state = shout_open(sd->shout_conn);
|
|
||||||
switch (state) {
|
|
||||||
case SHOUTERR_SUCCESS:
|
case SHOUTERR_SUCCESS:
|
||||||
case SHOUTERR_CONNECTED:
|
case SHOUTERR_CONNECTED:
|
||||||
return true;
|
return true;
|
||||||
@ -448,17 +428,17 @@ my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
struct shout_data *sd = (struct shout_data *)ao;
|
struct shout_data *sd = (struct shout_data *)ao;
|
||||||
bool ret;
|
|
||||||
|
|
||||||
ret = shout_connect(sd, error);
|
if (!shout_connect(sd, error))
|
||||||
if (!ret)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
sd->buf.len = 0;
|
if (!encoder_open(sd->encoder, audio_format, error)) {
|
||||||
|
shout_close(sd->shout_conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ret = encoder_open(sd->encoder, audio_format, error) &&
|
if (!write_page(sd, error)) {
|
||||||
write_page(sd, error);
|
encoder_close(sd->encoder);
|
||||||
if (!ret) {
|
|
||||||
shout_close(sd->shout_conn);
|
shout_close(sd->shout_conn);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -528,32 +508,27 @@ static void my_shout_set_tag(struct audio_output *ao,
|
|||||||
const struct tag *tag)
|
const struct tag *tag)
|
||||||
{
|
{
|
||||||
struct shout_data *sd = (struct shout_data *)ao;
|
struct shout_data *sd = (struct shout_data *)ao;
|
||||||
bool ret;
|
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
if (sd->encoder->plugin->tag != NULL) {
|
if (sd->encoder->plugin->tag != NULL) {
|
||||||
/* encoder plugin supports stream tags */
|
/* encoder plugin supports stream tags */
|
||||||
|
|
||||||
ret = encoder_pre_tag(sd->encoder, &error);
|
if (!encoder_pre_tag(sd->encoder, &error)) {
|
||||||
if (!ret) {
|
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = write_page(sd, NULL);
|
if (!write_page(sd, NULL))
|
||||||
if (!ret)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = encoder_tag(sd->encoder, tag, &error);
|
if (!encoder_tag(sd->encoder, tag, &error)) {
|
||||||
if (!ret) {
|
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* no stream tag support: fall back to icy-metadata */
|
/* no stream tag support: fall back to icy-metadata */
|
||||||
char song[1024];
|
char song[1024];
|
||||||
|
|
||||||
shout_tag_to_metadata(tag, song, sizeof(song));
|
shout_tag_to_metadata(tag, song, sizeof(song));
|
||||||
|
|
||||||
shout_metadata_add(sd->shout_meta, "song", song);
|
shout_metadata_add(sd->shout_meta, "song", song);
|
||||||
|
@ -99,14 +99,15 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = encoder_open(encoder, &audio_format, &error);
|
if (!encoder_open(encoder, &audio_format, &error)) {
|
||||||
if (encoder == NULL) {
|
|
||||||
g_printerr("Failed to open encoder: %s\n",
|
g_printerr("Failed to open encoder: %s\n",
|
||||||
error->message);
|
error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encoder_to_stdout(encoder);
|
||||||
|
|
||||||
/* do it */
|
/* do it */
|
||||||
|
|
||||||
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
|
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
|
||||||
|
@ -67,6 +67,8 @@ main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
|
|||||||
success = encoder_open(encoder, &audio_format, NULL);
|
success = encoder_open(encoder, &audio_format, NULL);
|
||||||
assert(success);
|
assert(success);
|
||||||
|
|
||||||
|
encoder_to_stdout(encoder);
|
||||||
|
|
||||||
/* write a block of data */
|
/* write a block of data */
|
||||||
|
|
||||||
success = encoder_write(encoder, zero, sizeof(zero), NULL);
|
success = encoder_write(encoder, zero, sizeof(zero), NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user