Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ffb3a9f526 | ||
![]() |
9761abf3b5 | ||
![]() |
599a562170 | ||
![]() |
d29a251547 | ||
![]() |
31da4bc566 | ||
![]() |
0f1a180e15 | ||
![]() |
01a45a53aa | ||
![]() |
a9a5907a0f | ||
![]() |
8fb20fcdf8 | ||
![]() |
72bf226608 | ||
![]() |
d4b5699403 | ||
![]() |
1dc27be015 | ||
![]() |
230a3eb400 | ||
![]() |
e39382dedd | ||
![]() |
fd016f4507 | ||
![]() |
9d728b365d | ||
![]() |
ddc0283339 | ||
![]() |
03a401e477 | ||
![]() |
9994521b8c | ||
![]() |
adbe8c409a | ||
![]() |
58e600f408 | ||
![]() |
d34e55c370 | ||
![]() |
fbcbcdc001 | ||
![]() |
4227a325a5 | ||
![]() |
d115507502 | ||
![]() |
43d8252050 | ||
![]() |
674b4ab647 | ||
![]() |
fe8fc1081a | ||
![]() |
c7748fedab | ||
![]() |
c392efb481 | ||
![]() |
1ddd9dd52a | ||
![]() |
f672e4016f |
.gitignoreNEWSconfigure.ac
m4
mpd.service.insrc
test
4
.gitignore
vendored
4
.gitignore
vendored
@@ -67,3 +67,7 @@ test/run_ntp_server
|
||||
test/run_resolver
|
||||
test/run_tcp_connect
|
||||
test/test_pcm
|
||||
test/dump_rva2
|
||||
test/dump_text_file
|
||||
test/test_byte_reverse
|
||||
test/test_vorbis_encoder
|
||||
|
13
NEWS
13
NEWS
@@ -1,3 +1,16 @@
|
||||
ver 0.17.3 (2013/01/06)
|
||||
* output:
|
||||
- osx: fix pops during playback
|
||||
- recorder: fix I/O error check
|
||||
- shout: fix memory leak in error handler
|
||||
- recorder, shout: support Ogg packets that span more than one page
|
||||
* decoder:
|
||||
- ffmpeg: ignore negative time stamps
|
||||
- ffmpeg: support planar audio
|
||||
* playlist:
|
||||
- cue: fix memory leak
|
||||
- cue: fix CUE files with only one track
|
||||
|
||||
ver 0.17.2 (2012/09/30)
|
||||
* protocol:
|
||||
- fix crash in local file check
|
||||
|
@@ -1,6 +1,6 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.17.2, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.17.3, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=17
|
||||
|
@@ -73,7 +73,8 @@ AC_DEFUN([MPD_AUTO_PKG_LIB], [
|
||||
[eval "found_$1=yes"],
|
||||
AC_CHECK_LIB($4, $5,
|
||||
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
|
||||
[eval "found_$1=no"]))
|
||||
[eval "found_$1=no"],
|
||||
[$6]))
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$8], [$9])
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Music Player Daemon
|
||||
After=sound.target
|
||||
After=network.target sound.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@prefix@/bin/mpd --no-daemon
|
||||
|
@@ -213,12 +213,12 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
||||
if(g_file_test(system_path,
|
||||
G_FILE_TEST_IS_REGULAR)) {
|
||||
ret = config_read_file(system_path,error_r);
|
||||
g_free(system_path);
|
||||
break;
|
||||
}
|
||||
++i;;
|
||||
} else
|
||||
g_free(system_path);
|
||||
++i;
|
||||
}
|
||||
g_free(system_path);
|
||||
g_free(&system_config_dirs);
|
||||
}
|
||||
#else /* G_OS_WIN32 */
|
||||
char *path2;
|
||||
|
@@ -58,9 +58,35 @@ struct cue_parser {
|
||||
|
||||
char *filename;
|
||||
|
||||
struct song *current, *previous, *finished;
|
||||
/**
|
||||
* The song currently being edited.
|
||||
*/
|
||||
struct song *current;
|
||||
|
||||
/**
|
||||
* The previous song. It is remembered because its end_time
|
||||
* will be set to the current song's start time.
|
||||
*/
|
||||
struct song *previous;
|
||||
|
||||
/**
|
||||
* A song that is completely finished and can be returned to
|
||||
* the caller via cue_parser_get().
|
||||
*/
|
||||
struct song *finished;
|
||||
|
||||
/**
|
||||
* Set to true after previous.end_time has been updated to the
|
||||
* start time of the current song.
|
||||
*/
|
||||
bool last_updated;
|
||||
|
||||
/**
|
||||
* Tracks whether cue_parser_finish() has been called. If
|
||||
* true, then all remaining (partial) results will be
|
||||
* delivered by cue_parser_get().
|
||||
*/
|
||||
bool end;
|
||||
};
|
||||
|
||||
struct cue_parser *
|
||||
@@ -73,6 +99,7 @@ cue_parser_new(void)
|
||||
parser->current = NULL;
|
||||
parser->previous = NULL;
|
||||
parser->finished = NULL;
|
||||
parser->end = false;
|
||||
return parser;
|
||||
}
|
||||
|
||||
@@ -85,6 +112,9 @@ cue_parser_free(struct cue_parser *parser)
|
||||
if (parser->current != NULL)
|
||||
song_free(parser->current);
|
||||
|
||||
if (parser->previous != NULL)
|
||||
song_free(parser->previous);
|
||||
|
||||
if (parser->finished != NULL)
|
||||
song_free(parser->finished);
|
||||
|
||||
@@ -201,10 +231,32 @@ cue_parse_position(const char *p)
|
||||
return minutes * 60000 + seconds * 1000 + frames * 1000 / 75;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the current song. It will be moved to "previous", so the
|
||||
* next song may soon edit its end time (using the next song's start
|
||||
* time).
|
||||
*/
|
||||
static void
|
||||
cue_parser_commit(struct cue_parser *parser)
|
||||
{
|
||||
/* the caller of this library must call cue_parser_get() often
|
||||
enough */
|
||||
assert(parser->finished == NULL);
|
||||
assert(!parser->end);
|
||||
|
||||
if (parser->current == NULL)
|
||||
return;
|
||||
|
||||
parser->finished = parser->previous;
|
||||
parser->previous = parser->current;
|
||||
parser->current = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
cue_parser_feed2(struct cue_parser *parser, char *p)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
assert(!parser->end);
|
||||
assert(p != NULL);
|
||||
|
||||
const char *command = cue_next_token(&p);
|
||||
@@ -235,7 +287,7 @@ cue_parser_feed2(struct cue_parser *parser, char *p)
|
||||
else if (parser->state == TRACK)
|
||||
cue_add_tag(parser->current->tag, TAG_TITLE, p);
|
||||
} else if (strcmp(command, "FILE") == 0) {
|
||||
cue_parser_finish(parser);
|
||||
cue_parser_commit(parser);
|
||||
|
||||
const char *filename = cue_next_value(&p);
|
||||
if (filename == NULL)
|
||||
@@ -258,7 +310,7 @@ cue_parser_feed2(struct cue_parser *parser, char *p)
|
||||
} else if (parser->state == IGNORE_FILE) {
|
||||
return;
|
||||
} else if (strcmp(command, "TRACK") == 0) {
|
||||
cue_parser_finish(parser);
|
||||
cue_parser_commit(parser);
|
||||
|
||||
const char *nr = cue_next_token(&p);
|
||||
if (nr == NULL)
|
||||
@@ -310,6 +362,7 @@ void
|
||||
cue_parser_feed(struct cue_parser *parser, const char *line)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
assert(!parser->end);
|
||||
assert(line != NULL);
|
||||
|
||||
char *allocated = g_strdup(line);
|
||||
@@ -320,12 +373,12 @@ cue_parser_feed(struct cue_parser *parser, const char *line)
|
||||
void
|
||||
cue_parser_finish(struct cue_parser *parser)
|
||||
{
|
||||
if (parser->finished != NULL)
|
||||
song_free(parser->finished);
|
||||
if (parser->end)
|
||||
/* has already been called, ignore */
|
||||
return;
|
||||
|
||||
parser->finished = parser->previous;
|
||||
parser->previous = parser->current;
|
||||
parser->current = NULL;
|
||||
cue_parser_commit(parser);
|
||||
parser->end = true;
|
||||
}
|
||||
|
||||
struct song *
|
||||
@@ -333,6 +386,15 @@ cue_parser_get(struct cue_parser *parser)
|
||||
{
|
||||
assert(parser != NULL);
|
||||
|
||||
if (parser->finished == NULL && parser->end) {
|
||||
/* cue_parser_finish() has been called already:
|
||||
deliver all remaining (partial) results */
|
||||
assert(parser->current == NULL);
|
||||
|
||||
parser->finished = parser->previous;
|
||||
parser->previous = NULL;
|
||||
}
|
||||
|
||||
struct song *song = parser->finished;
|
||||
parser->finished = NULL;
|
||||
return song;
|
||||
|
@@ -234,6 +234,21 @@ time_to_ffmpeg(double t, const AVRational time_base)
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
|
||||
|
||||
static void
|
||||
copy_interleave_frame2(uint8_t *dest, uint8_t **src,
|
||||
unsigned nframes, unsigned nchannels,
|
||||
unsigned sample_size)
|
||||
{
|
||||
for (unsigned frame = 0; frame < nframes; ++frame) {
|
||||
for (unsigned channel = 0; channel < nchannels; ++channel) {
|
||||
memcpy(dest, src[channel] + frame * sample_size,
|
||||
sample_size);
|
||||
dest += sample_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy PCM data from a AVFrame to an interleaved buffer.
|
||||
*/
|
||||
@@ -254,11 +269,10 @@ copy_interleave_frame(const AVCodecContext *codec_context,
|
||||
|
||||
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
|
||||
codec_context->channels > 1) {
|
||||
for (int i = 0, channels = codec_context->channels;
|
||||
i < channels; i++) {
|
||||
memcpy(buffer, frame->extended_data[i], plane_size);
|
||||
buffer += plane_size;
|
||||
}
|
||||
copy_interleave_frame2(buffer, frame->extended_data,
|
||||
frame->nb_samples,
|
||||
codec_context->channels,
|
||||
av_get_bytes_per_sample(codec_context->sample_fmt));
|
||||
} else {
|
||||
memcpy(buffer, frame->extended_data[0], data_size);
|
||||
}
|
||||
@@ -273,7 +287,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
||||
AVCodecContext *codec_context,
|
||||
const AVRational *time_base)
|
||||
{
|
||||
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
|
||||
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE)
|
||||
decoder_timestamp(decoder,
|
||||
time_from_ffmpeg(packet->pts, *time_base));
|
||||
|
||||
@@ -352,12 +366,20 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
||||
return cmd;
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 94, 1)
|
||||
#define AVSampleFormat SampleFormat
|
||||
#endif
|
||||
|
||||
G_GNUC_CONST
|
||||
static enum sample_format
|
||||
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
||||
ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
|
||||
{
|
||||
switch (codec_context->sample_fmt) {
|
||||
switch (sample_fmt) {
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
#endif
|
||||
#else
|
||||
case SAMPLE_FMT_S16:
|
||||
#endif
|
||||
@@ -365,16 +387,30 @@ ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,17,0)
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
#endif
|
||||
#else
|
||||
case SAMPLE_FMT_S32:
|
||||
#endif
|
||||
return SAMPLE_FORMAT_S32;
|
||||
|
||||
default:
|
||||
g_warning("Unsupported libavcodec SampleFormat value: %d",
|
||||
codec_context->sample_fmt);
|
||||
return SAMPLE_FORMAT_UNDEFINED;
|
||||
break;
|
||||
}
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
|
||||
char buffer[64];
|
||||
const char *name = av_get_sample_fmt_string(buffer, sizeof(buffer),
|
||||
sample_fmt);
|
||||
if (name != NULL)
|
||||
g_warning("Unsupported libavcodec SampleFormat value: %s (%d)",
|
||||
name, sample_fmt);
|
||||
else
|
||||
#endif
|
||||
g_warning("Unsupported libavcodec SampleFormat value: %d",
|
||||
sample_fmt);
|
||||
return SAMPLE_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
static AVInputFormat *
|
||||
@@ -485,11 +521,16 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
||||
return;
|
||||
}
|
||||
|
||||
const enum sample_format sample_format =
|
||||
ffmpeg_sample_format(codec_context->sample_fmt);
|
||||
if (sample_format == SAMPLE_FORMAT_UNDEFINED)
|
||||
return;
|
||||
|
||||
GError *error = NULL;
|
||||
struct audio_format audio_format;
|
||||
if (!audio_format_init_checked(&audio_format,
|
||||
codec_context->sample_rate,
|
||||
ffmpeg_sample_format(codec_context),
|
||||
sample_format,
|
||||
codec_context->channels, &error)) {
|
||||
g_warning("%s", error->message);
|
||||
g_error_free(error);
|
||||
|
@@ -65,13 +65,11 @@ static bool
|
||||
vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
||||
const struct config_param *param, GError **error)
|
||||
{
|
||||
const char *value;
|
||||
char *endptr;
|
||||
|
||||
value = config_get_block_string(param, "quality", NULL);
|
||||
const char *value = config_get_block_string(param, "quality", NULL);
|
||||
if (value != NULL) {
|
||||
/* a quality was configured (VBR) */
|
||||
|
||||
char *endptr;
|
||||
encoder->quality = g_ascii_strtod(value, &endptr);
|
||||
|
||||
if (*endptr != '\0' || encoder->quality < -1.0 ||
|
||||
@@ -103,8 +101,9 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
||||
}
|
||||
|
||||
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) {
|
||||
g_set_error(error, vorbis_encoder_quark(), 0,
|
||||
"bitrate at line %i should be a positive integer",
|
||||
@@ -119,9 +118,7 @@ vorbis_encoder_configure(struct vorbis_encoder *encoder,
|
||||
static struct encoder *
|
||||
vorbis_encoder_init(const struct config_param *param, GError **error)
|
||||
{
|
||||
struct vorbis_encoder *encoder;
|
||||
|
||||
encoder = g_new(struct vorbis_encoder, 1);
|
||||
struct vorbis_encoder *encoder = g_new(struct vorbis_encoder, 1);
|
||||
encoder_struct_init(&encoder->encoder, &vorbis_encoder_plugin);
|
||||
|
||||
/* load configuration from "param" */
|
||||
@@ -211,14 +208,12 @@ vorbis_encoder_open(struct encoder *_encoder,
|
||||
GError **error)
|
||||
{
|
||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||
bool ret;
|
||||
|
||||
audio_format->format = SAMPLE_FORMAT_S16;
|
||||
|
||||
encoder->audio_format = *audio_format;
|
||||
|
||||
ret = vorbis_encoder_reinit(encoder, error);
|
||||
if (!ret)
|
||||
if (!vorbis_encoder_reinit(encoder, error))
|
||||
return false;
|
||||
|
||||
vorbis_encoder_send_header(encoder);
|
||||
@@ -251,11 +246,10 @@ static void
|
||||
vorbis_encoder_blockout(struct vorbis_encoder *encoder)
|
||||
{
|
||||
while (vorbis_analysis_blockout(&encoder->vd, &encoder->vb) == 1) {
|
||||
ogg_packet packet;
|
||||
|
||||
vorbis_analysis(&encoder->vb, NULL);
|
||||
vorbis_bitrate_addblock(&encoder->vb);
|
||||
|
||||
ogg_packet packet;
|
||||
while (vorbis_bitrate_flushpacket(&encoder->vd, &packet))
|
||||
ogg_stream_packetin(&encoder->os, &packet);
|
||||
}
|
||||
@@ -344,9 +338,9 @@ vorbis_encoder_write(struct encoder *_encoder,
|
||||
G_GNUC_UNUSED GError **error)
|
||||
{
|
||||
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 */
|
||||
|
||||
@@ -364,12 +358,10 @@ static size_t
|
||||
vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
||||
{
|
||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||
ogg_page page;
|
||||
int ret;
|
||||
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) {
|
||||
encoder->flush = false;
|
||||
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);
|
||||
|
||||
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)
|
||||
/* 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
|
||||
* 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 audio_format the encoder's input audio format; the plugin
|
||||
* 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.
|
||||
*
|
||||
* Call this repeatedly until no more data is returned.
|
||||
*
|
||||
* @param encoder the encoder
|
||||
* @param dest the destination buffer to copy to
|
||||
* @param length the maximum length of the destination buffer
|
||||
|
@@ -114,10 +114,6 @@ httpd_output_init(const struct config_param *param,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *encoder_name, *bind_to_address;
|
||||
const struct encoder_plugin *encoder_plugin;
|
||||
guint port;
|
||||
|
||||
/* read configuration */
|
||||
httpd->name =
|
||||
config_get_block_string(param, "name", "Set name in config");
|
||||
@@ -126,10 +122,12 @@ httpd_output_init(const struct config_param *param,
|
||||
httpd->website =
|
||||
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");
|
||||
encoder_plugin = encoder_plugin_get(encoder_name);
|
||||
const char *encoder_name =
|
||||
config_get_block_string(param, "encoder", "vorbis");
|
||||
const struct encoder_plugin *encoder_plugin =
|
||||
encoder_plugin_get(encoder_name);
|
||||
if (encoder_plugin == NULL) {
|
||||
g_set_error(error, httpd_output_quark(), 0,
|
||||
"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);
|
||||
|
||||
bind_to_address =
|
||||
const char *bind_to_address =
|
||||
config_get_block_string(param, "bind_to_address", NULL);
|
||||
bool success = bind_to_address != NULL &&
|
||||
strcmp(bind_to_address, "any") != 0
|
||||
@@ -275,8 +273,6 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
|
||||
static struct page *
|
||||
httpd_output_read_page(struct httpd_output *httpd)
|
||||
{
|
||||
size_t size = 0, nbytes;
|
||||
|
||||
if (httpd->unflushed_input >= 65536) {
|
||||
/* we have fed a lot of input into the encoder, but it
|
||||
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;
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
do {
|
||||
nbytes = encoder_read(httpd->encoder, httpd->buffer + size,
|
||||
sizeof(httpd->buffer) - size);
|
||||
size_t nbytes = encoder_read(httpd->encoder,
|
||||
httpd->buffer + size,
|
||||
sizeof(httpd->buffer) - size);
|
||||
if (nbytes == 0)
|
||||
break;
|
||||
|
||||
@@ -307,10 +305,7 @@ httpd_output_encoder_open(struct httpd_output *httpd,
|
||||
struct audio_format *audio_format,
|
||||
GError **error)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = encoder_open(httpd->encoder, audio_format, error);
|
||||
if (!success)
|
||||
if (!encoder_open(httpd->encoder, audio_format, error))
|
||||
return false;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct httpd_output *httpd = (struct httpd_output *)ao;
|
||||
bool success;
|
||||
|
||||
g_mutex_lock(httpd->mutex);
|
||||
|
||||
/* open the encoder */
|
||||
|
||||
success = httpd_output_encoder_open(httpd, audio_format, error);
|
||||
if (!success) {
|
||||
if (!httpd_output_encoder_open(httpd, audio_format, error)) {
|
||||
g_mutex_unlock(httpd->mutex);
|
||||
return false;
|
||||
}
|
||||
@@ -495,10 +488,7 @@ static bool
|
||||
httpd_output_encode_and_play(struct httpd_output *httpd,
|
||||
const void *chunk, size_t size, GError **error)
|
||||
{
|
||||
bool success;
|
||||
|
||||
success = encoder_write(httpd->encoder, chunk, size, error);
|
||||
if (!success)
|
||||
if (!encoder_write(httpd->encoder, chunk, size, error))
|
||||
return false;
|
||||
|
||||
httpd->unflushed_input += size;
|
||||
@@ -510,16 +500,12 @@ httpd_output_encode_and_play(struct httpd_output *httpd,
|
||||
|
||||
static size_t
|
||||
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;
|
||||
|
||||
if (httpd_output_lock_has_clients(httpd)) {
|
||||
bool success;
|
||||
|
||||
success = httpd_output_encode_and_play(httpd, chunk, size,
|
||||
error);
|
||||
if (!success)
|
||||
if (!httpd_output_encode_and_play(httpd, chunk, size, error_r))
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -562,7 +548,6 @@ httpd_output_tag(struct audio_output *ao, const struct tag *tag)
|
||||
|
||||
if (httpd->encoder->plugin->tag != NULL) {
|
||||
/* embed encoder tags */
|
||||
struct page *page;
|
||||
|
||||
/* 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
|
||||
new clients */
|
||||
|
||||
page = httpd_output_read_page(httpd);
|
||||
struct page *page = httpd_output_read_page(httpd);
|
||||
if (page != NULL) {
|
||||
if (httpd->header != NULL)
|
||||
page_unref(httpd->header);
|
||||
|
@@ -228,9 +228,13 @@ osx_render(void *vdata,
|
||||
g_cond_signal(od->condition);
|
||||
g_mutex_unlock(od->mutex);
|
||||
|
||||
if (nbytes < buffer_size)
|
||||
memset((unsigned char*)buffer->mData + nbytes, 0,
|
||||
buffer_size - nbytes);
|
||||
buffer->mDataByteSize = nbytes;
|
||||
|
||||
unsigned i;
|
||||
for (i = 1; i < buffer_list->mNumberBuffers; ++i) {
|
||||
buffer = &buffer_list->mBuffers[i];
|
||||
buffer->mDataByteSize = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -77,13 +77,12 @@ recorder_output_init(const struct config_param *param, GError **error_r)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *encoder_name;
|
||||
const struct encoder_plugin *encoder_plugin;
|
||||
|
||||
/* read configuration */
|
||||
|
||||
encoder_name = config_get_block_string(param, "encoder", "vorbis");
|
||||
encoder_plugin = encoder_plugin_get(encoder_name);
|
||||
const char *encoder_name =
|
||||
config_get_block_string(param, "encoder", "vorbis");
|
||||
const struct encoder_plugin *encoder_plugin =
|
||||
encoder_plugin_get(encoder_name);
|
||||
if (encoder_plugin == NULL) {
|
||||
g_set_error(error_r, recorder_output_quark(), 0,
|
||||
"No such encoder: %s", encoder_name);
|
||||
@@ -121,33 +120,22 @@ recorder_output_finish(struct audio_output *ao)
|
||||
g_free(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)
|
||||
recorder_write_to_file(struct recorder_output *recorder,
|
||||
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) {
|
||||
nbytes = write(recorder->fd, recorder->buffer + position,
|
||||
size - position);
|
||||
ssize_t nbytes = write(fd, data, end - data);
|
||||
if (nbytes > 0) {
|
||||
position += (size_t)nbytes;
|
||||
if (position >= size)
|
||||
data += nbytes;
|
||||
if (data == end)
|
||||
return true;
|
||||
} else if (nbytes == 0) {
|
||||
/* 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
|
||||
recorder_output_open(struct audio_output *ao,
|
||||
struct audio_format *audio_format,
|
||||
GError **error_r)
|
||||
{
|
||||
struct recorder_output *recorder = (struct recorder_output *)ao;
|
||||
bool success;
|
||||
|
||||
/* create the output file */
|
||||
|
||||
@@ -185,8 +197,14 @@ recorder_output_open(struct audio_output *ao,
|
||||
|
||||
/* open the encoder */
|
||||
|
||||
success = encoder_open(recorder->encoder, audio_format, error_r);
|
||||
if (!success) {
|
||||
if (!encoder_open(recorder->encoder, audio_format, error_r)) {
|
||||
close(recorder->fd);
|
||||
unlink(recorder->path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recorder_output_encoder_to_file(recorder, error_r)) {
|
||||
encoder_close(recorder->encoder);
|
||||
close(recorder->fd);
|
||||
unlink(recorder->path);
|
||||
return false;
|
||||
|
@@ -36,11 +36,6 @@
|
||||
|
||||
#define DEFAULT_CONN_TIMEOUT 2
|
||||
|
||||
struct shout_buffer {
|
||||
unsigned char data[32768];
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct shout_data {
|
||||
struct audio_output base;
|
||||
|
||||
@@ -54,7 +49,7 @@ struct shout_data {
|
||||
|
||||
int timeout;
|
||||
|
||||
struct shout_buffer buf;
|
||||
uint8_t buffer[32768];
|
||||
};
|
||||
|
||||
static int shout_init_count;
|
||||
@@ -114,24 +109,7 @@ static struct audio_output *
|
||||
my_shout_init_driver(const struct config_param *param,
|
||||
GError **error)
|
||||
{
|
||||
struct shout_data *sd;
|
||||
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();
|
||||
|
||||
struct shout_data *sd = new_shout_data();
|
||||
if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) {
|
||||
free_shout_data(sd);
|
||||
return NULL;
|
||||
@@ -152,13 +130,14 @@ my_shout_init_driver(const struct config_param *param,
|
||||
|
||||
shout_init_count++;
|
||||
|
||||
const struct block_param *block_param;
|
||||
check_block_param("host");
|
||||
host = block_param->value;
|
||||
char *host = block_param->value;
|
||||
|
||||
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) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"shout port must be configured");
|
||||
@@ -166,17 +145,18 @@ my_shout_init_driver(const struct config_param *param,
|
||||
}
|
||||
|
||||
check_block_param("password");
|
||||
passwd = block_param->value;
|
||||
const char *passwd = block_param->value;
|
||||
|
||||
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) {
|
||||
char *test;
|
||||
sd->quality = strtod(value, &test);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char *test;
|
||||
sd->bitrate = strtol(value, &test, 10);
|
||||
|
||||
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");
|
||||
encoder_plugin = shout_encoder_plugin_get(encoding);
|
||||
const char *encoding = config_get_block_string(param, "encoding",
|
||||
"ogg");
|
||||
const struct encoder_plugin *encoder_plugin =
|
||||
shout_encoder_plugin_get(encoding);
|
||||
if (encoder_plugin == NULL) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"couldn't find shout encoder plugin \"%s\"",
|
||||
@@ -223,11 +206,13 @@ my_shout_init_driver(const struct config_param *param,
|
||||
if (sd->encoder == NULL)
|
||||
goto failure;
|
||||
|
||||
unsigned shout_format;
|
||||
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
||||
shout_format = SHOUT_FORMAT_MP3;
|
||||
else
|
||||
shout_format = SHOUT_FORMAT_OGG;
|
||||
|
||||
unsigned protocol;
|
||||
value = config_get_block_string(param, "protocol", NULL);
|
||||
if (value != NULL) {
|
||||
if (0 == strcmp(value, "shoutcast") &&
|
||||
@@ -355,26 +340,24 @@ handle_shout_error(struct shout_data *sd, int err, GError **error)
|
||||
static bool
|
||||
write_page(struct shout_data *sd, GError **error)
|
||||
{
|
||||
int err;
|
||||
|
||||
assert(sd->encoder != NULL);
|
||||
|
||||
sd->buf.len = encoder_read(sd->encoder,
|
||||
sd->buf.data, sizeof(sd->buf.data));
|
||||
if (sd->buf.len == 0)
|
||||
return true;
|
||||
while (true) {
|
||||
size_t nbytes = encoder_read(sd->encoder,
|
||||
sd->buffer, sizeof(sd->buffer));
|
||||
if (nbytes == 0)
|
||||
return true;
|
||||
|
||||
err = shout_send(sd->shout_conn, sd->buf.data, sd->buf.len);
|
||||
if (!handle_shout_error(sd, err, error))
|
||||
return false;
|
||||
int err = shout_send(sd->shout_conn, sd->buffer, nbytes);
|
||||
if (!handle_shout_error(sd, err, error))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void close_shout_conn(struct shout_data * sd)
|
||||
{
|
||||
sd->buf.len = 0;
|
||||
|
||||
if (sd->encoder != NULL) {
|
||||
if (encoder_end(sd->encoder, NULL))
|
||||
write_page(sd, NULL);
|
||||
@@ -425,10 +408,7 @@ my_shout_close_device(struct audio_output *ao)
|
||||
static bool
|
||||
shout_connect(struct shout_data *sd, GError **error)
|
||||
{
|
||||
int state;
|
||||
|
||||
state = shout_open(sd->shout_conn);
|
||||
switch (state) {
|
||||
switch (shout_open(sd->shout_conn)) {
|
||||
case SHOUTERR_SUCCESS:
|
||||
case SHOUTERR_CONNECTED:
|
||||
return true;
|
||||
@@ -448,17 +428,17 @@ my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format,
|
||||
GError **error)
|
||||
{
|
||||
struct shout_data *sd = (struct shout_data *)ao;
|
||||
bool ret;
|
||||
|
||||
ret = shout_connect(sd, error);
|
||||
if (!ret)
|
||||
if (!shout_connect(sd, error))
|
||||
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) &&
|
||||
write_page(sd, error);
|
||||
if (!ret) {
|
||||
if (!write_page(sd, error)) {
|
||||
encoder_close(sd->encoder);
|
||||
shout_close(sd->shout_conn);
|
||||
return false;
|
||||
}
|
||||
@@ -528,32 +508,27 @@ static void my_shout_set_tag(struct audio_output *ao,
|
||||
const struct tag *tag)
|
||||
{
|
||||
struct shout_data *sd = (struct shout_data *)ao;
|
||||
bool ret;
|
||||
GError *error = NULL;
|
||||
|
||||
if (sd->encoder->plugin->tag != NULL) {
|
||||
/* encoder plugin supports stream tags */
|
||||
|
||||
ret = encoder_pre_tag(sd->encoder, &error);
|
||||
if (!ret) {
|
||||
if (!encoder_pre_tag(sd->encoder, &error)) {
|
||||
g_warning("%s", error->message);
|
||||
g_error_free(error);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = write_page(sd, NULL);
|
||||
if (!ret)
|
||||
if (!write_page(sd, NULL))
|
||||
return;
|
||||
|
||||
ret = encoder_tag(sd->encoder, tag, &error);
|
||||
if (!ret) {
|
||||
if (!encoder_tag(sd->encoder, tag, &error)) {
|
||||
g_warning("%s", error->message);
|
||||
g_error_free(error);
|
||||
}
|
||||
} else {
|
||||
/* no stream tag support: fall back to icy-metadata */
|
||||
char song[1024];
|
||||
|
||||
shout_tag_to_metadata(tag, song, sizeof(song));
|
||||
|
||||
shout_metadata_add(sd->shout_meta, "song", song);
|
||||
|
@@ -137,7 +137,8 @@ playlist_load_spl(struct playlist *playlist, struct player_control *pc,
|
||||
*p = '/';
|
||||
p++;
|
||||
}
|
||||
if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
|
||||
if ((playlist_append_uri(playlist, pc, temp2,
|
||||
NULL)) != PLAYLIST_RESULT_SUCCESS) {
|
||||
g_warning("can't add file \"%s\"", temp2);
|
||||
}
|
||||
g_free(temp2);
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "mapper.h"
|
||||
#include "song.h"
|
||||
#include "uri.h"
|
||||
#include "path.h"
|
||||
#include "ls.h"
|
||||
#include "tag.h"
|
||||
|
||||
@@ -62,8 +63,14 @@ apply_song_metadata(struct song *dest, const struct song *src)
|
||||
if (path_fs == NULL)
|
||||
return dest;
|
||||
|
||||
tmp = song_file_new(path_fs, NULL);
|
||||
g_free(path_fs);
|
||||
char *path_utf8 = fs_charset_to_utf8(path_fs);
|
||||
if (path_utf8 != NULL)
|
||||
g_free(path_fs);
|
||||
else
|
||||
path_utf8 = path_fs;
|
||||
|
||||
tmp = song_file_new(path_utf8, NULL);
|
||||
g_free(path_utf8);
|
||||
|
||||
merge_song_metadata(tmp, dest, src);
|
||||
} else {
|
||||
|
@@ -161,7 +161,7 @@ int main(int argc, char **argv)
|
||||
config_global_init();
|
||||
success = config_read_file(argv[1], &error);
|
||||
if (!success) {
|
||||
g_printerr("%s:", error->message);
|
||||
g_printerr("%s\n", error->message);
|
||||
g_error_free(error);
|
||||
return 1;
|
||||
}
|
||||
|
@@ -99,14 +99,15 @@ int main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
ret = encoder_open(encoder, &audio_format, &error);
|
||||
if (encoder == NULL) {
|
||||
if (!encoder_open(encoder, &audio_format, &error)) {
|
||||
g_printerr("Failed to open encoder: %s\n",
|
||||
error->message);
|
||||
g_error_free(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
encoder_to_stdout(encoder);
|
||||
|
||||
/* do it */
|
||||
|
||||
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);
|
||||
assert(success);
|
||||
|
||||
encoder_to_stdout(encoder);
|
||||
|
||||
/* write a block of data */
|
||||
|
||||
success = encoder_write(encoder, zero, sizeof(zero), NULL);
|
||||
|
Reference in New Issue
Block a user