Compare commits

...

28 Commits

Author SHA1 Message Date
Max Kellermann
ddb5390d88 release v0.18.8 2014-02-07 00:06:31 +01:00
Max Kellermann
fce20e514e NEWS: fix 0.18.7 release year 2014-02-07 00:06:31 +01:00
Max Kellermann
af66ed2505 doc/user: document the RoarAudio output plugin 2014-02-06 21:46:29 +01:00
Max Kellermann
ea4068757d configure.ac: link the Vorbis encoder with libvorbis
Since the encoder plugin uses a libvorbis function (and not only
libvorbisenc functions), we need to link with libvorbis explicitly.
2014-02-06 21:32:50 +01:00
Max Kellermann
2b10ecfa37 IcyMetadataParser: more robust tag parser
Allow semicolons and single quotes in the stream title.  This is not
part of any specification, but found in real life.
2014-01-27 10:08:21 +01:00
Max Kellermann
f7eb2b697e test/test_icy_parser: unit test for IcyMetaDataParser.cxx 2014-01-27 09:51:31 +01:00
Max Kellermann
da67260c95 new developer mailing list 2014-01-20 17:20:57 +01:00
Max Kellermann
ab9c9068d4 Queue: rename struct queue to Queue
Works around a build failure on Solaris because annoyingly, Solaris
reserves the name "queue".  This rename was pending anyway.
2014-01-20 08:57:46 +01:00
Max Kellermann
6b4d7d7315 Queue: make the constructor "explicit" 2014-01-20 08:57:41 +01:00
Max Kellermann
313d1d5d83 decoder/ffmpeg: support libav v10_alpha1 2014-01-15 11:33:18 +01:00
Max Kellermann
b7d6133593 decoder/ffmpeg: include cleanup 2014-01-15 11:31:51 +01:00
Max Kellermann
5b6bb114ad decoder/ffmpeg: check for av_samples_get_buffer_size() errors
Fixes potential nullptr dereference.
2014-01-15 11:25:58 +01:00
Max Kellermann
56f082c9d4 util/PeakBuffer: fix nullptr dereference when peak_size==0 2014-01-15 11:24:29 +01:00
Max Kellermann
a1b798e555 SongFilter, TagConfig: cast TAG_NUM_OF_ITEM_TYPES to integer
Fixes clang warning.
2014-01-15 11:23:41 +01:00
Max Kellermann
c91e08fbfd OutputAPI: fix typo in include guard 2014-01-15 11:22:59 +01:00
Max Kellermann
f882434547 configure.ac: prepare for 0.18.8 2014-01-15 11:22:06 +01:00
Max Kellermann
05ad335ae9 release v0.18.7 2014-01-13 11:39:27 +01:00
Max Kellermann
7faeb2ff2b configure.ac: reject libmpcdec SV7 in configure script
Look for symbol "mpc_demux_init" which does not exist in SV7.  This
avoids build failures when SV7 was found by configure.ac.
2014-01-11 21:02:12 +01:00
Max Kellermann
fdd76b3461 decoder/faad: fix memory leak 2014-01-08 22:11:00 +01:00
Max Kellermann
e490e5d0ab playlist/pls: don't free stack buffer 2014-01-08 19:50:44 +01:00
Max Kellermann
3a05c421e0 doc/user: fix typo 2014-01-07 18:06:58 +01:00
Max Kellermann
afc70c120e util/UriUtil: uri_get_suffix() fails if name begins with dot
A file called ".jpg" is not a JPEG file with an empty name; it is
merely a hidden file.
2013-12-29 17:40:51 +01:00
Max Kellermann
d7f80eab68 configure.ac: improved check for libyajl 1.0
If we have libyajl 2.0.1 (without a pkg-config file), our configure.ac
would assume this is the libyajl 1.0 API, because the function
yajl_alloc() exists in both.  This commit changes the library check to
the function yajl_parse_complete() which was removed in the 2.0 API.
This fixes build failure with libyajl 2.0.1.
2013-12-29 14:12:33 +01:00
Max Kellermann
e30b356eb0 daemon: no initgroups() when already running as the configured user
We can assume that initgroups() would be a no-op in that case, however
initgroups() is not allowed for unprivileged users anyway.
2013-12-29 13:59:05 +01:00
Max Kellermann
09a0803116 Daemon: fix typo in comment 2013-12-29 13:59:05 +01:00
Max Kellermann
20ffedc745 Daemon: simplify nested "if" 2013-12-29 13:57:12 +01:00
Max Kellermann
0b1ad27ba8 Daemon: fix typo in cast 2013-12-29 13:47:29 +01:00
Max Kellermann
6a1b2f0387 configure.ac: prepare for 0.18.7 2013-12-29 10:40:59 +01:00
29 changed files with 332 additions and 101 deletions

@@ -1061,6 +1061,7 @@ C_TESTS = \
test/test_util \
test/test_byte_reverse \
test/test_mixramp \
test/test_icy_parser \
test/test_pcm \
test/test_queue_priority
@@ -1496,6 +1497,16 @@ test_test_mixramp_LDADD = \
$(GLIB_LIBS) \
$(CPPUNIT_LIBS)
test_test_icy_parser_SOURCES = \
src/Log.cxx \
test/test_icy_parser.cxx
test_test_icy_parser_CPPFLAGS = $(AM_CPPFLAGS) $(CPPUNIT_CFLAGS) -DCPPUNIT_HAVE_RTTI=0
test_test_icy_parser_CXXFLAGS = $(AM_CXXFLAGS) -Wno-error=deprecated-declarations
test_test_icy_parser_LDADD = \
libtag.a \
$(GLIB_LIBS) \
$(CPPUNIT_LIBS)
test_test_pcm_SOURCES = \
test/test_pcm_util.hxx \
test/test_pcm_dither.cxx \

20
NEWS

@@ -1,3 +1,23 @@
ver 0.18.8 (2014/02/07)
* decoder
- ffmpeg: support libav v10_alpha1
* encoder
- vorbis: fix linker failure
* output
- roar: documentation
* more robust Icy-Metadata parser
* fix Solaris build failure
ver 0.18.7 (2014/01/13)
* playlist
- pls: fix crash after parser error
- soundcloud: fix build failure with libyajl 2.0.1
* decoder
- faad: fix memory leak
- mpcdec: reject libmpcdec SV7 in configure script
* daemon: don't initialize supplementary groups when already running
as the configured user
ver 0.18.6 (2013/12/24)
* input
- cdio_paranoia: support libcdio-paranoia 0.90

@@ -1,6 +1,6 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.18.6, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.18.8, mpd-devel@musicpd.org)
VERSION_MAJOR=0
VERSION_MINOR=18
@@ -732,7 +732,7 @@ dnl --------------------------------- Soundcloud ------------------------------
if test x$enable_soundcloud != xno; then
PKG_CHECK_MODULES([YAJL], [yajl >= 2.0],
[found_soundcloud=yes],
AC_CHECK_LIB([yajl], [yajl_alloc],
AC_CHECK_LIB([yajl], [yajl_parse_complete],
[found_soundcloud=yes YAJL_CFLAGS=-DHAVE_YAJL1 YAJL_LIBS=-lyajl],
[found_soundcloud=no]))
fi
@@ -960,7 +960,7 @@ AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes)
dnl --------------------------------- musepack --------------------------------
MPD_AUTO_LIB(mpc, MPCDEC, mpcdec, main, [-lmpcdec], [],
MPD_AUTO_LIB(mpc, MPCDEC, mpcdec, mpc_demux_init, [-lmpcdec], [],
[mpcdec], [libmpcdec not found])
if test x$enable_mpc = xyes; then
AC_DEFINE(HAVE_MPCDEC, 1, [Define to use libmpcdec for MPC decoding])
@@ -1139,7 +1139,7 @@ fi
AM_CONDITIONAL(ENABLE_FLAC_ENCODER, test x$enable_flac_encoder = xyes)
dnl ---------------------------- Ogg Vorbis Encoder ---------------------------
MPD_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc],
MPD_AUTO_PKG(vorbis_encoder, VORBISENC, [vorbisenc vorbis],
[Ogg Vorbis encoder], [libvorbisenc not found])
if test x$enable_vorbis_encoder = xyes; then

@@ -155,7 +155,7 @@ foo(const char *abc, int xyz)
<para>
Send your patches to the mailing list:
musicpd-dev-team@lists.sourceforge.net
mpd-devel@musicpd.org
</para>
</chapter>
</book>

@@ -829,7 +829,7 @@ systemctl start mpd.socket</programlisting>
<tbody>
<row>
<entry>
<varname>default_bute_order</varname>
<varname>default_byte_order</varname>
<parameter>little_endian|big_endian</parameter>
</entry>
<entry>
@@ -1852,6 +1852,51 @@ systemctl start mpd.socket</programlisting>
</informaltable>
</section>
<section>
<title><varname>roar</varname></title>
<para>
The <varname>roar</varname> plugin connects to a <ulink
url="http://roaraudio.keep-cool.org/">RoarAudio</ulink>
server.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>server</varname>
<parameter>HOSTNAME</parameter>
</entry>
<entry>
The host name of the RoarAudio server. If not
specified, then MPD will connect to the default
locations.
</entry>
</row>
<row>
<entry>
<varname>role</varname>
<parameter>ROLE</parameter>
</entry>
<entry>
The "role" that MPD registers itself as in the
RoarAudio server. The default is "music".
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>recorder</varname></title>

@@ -52,7 +52,7 @@ static char *user_name;
static uid_t user_uid = (uid_t)-1;
/** the Unix group id which MPD runs as */
static gid_t user_gid = (pid_t)-1;
static gid_t user_gid = (gid_t)-1;
/** the absolute path of the pidfile */
static AllocatedPath pidfile = AllocatedPath::Null();
@@ -106,18 +106,21 @@ daemonize_set_user(void)
return;
/* set gid */
if (user_gid != (gid_t)-1 && user_gid != getgid()) {
if (setgid(user_gid) == -1) {
FormatFatalSystemError("Failed to set group %d",
(int)user_gid);
}
if (user_gid != (gid_t)-1 && user_gid != getgid() &&
setgid(user_gid) == -1) {
FormatFatalSystemError("Failed to set group %d",
(int)user_gid);
}
#ifdef _BSD_SOURCE
/* init suplementary groups
/* init supplementary groups
* (must be done before we change our uid)
*/
if (!had_group && initgroups(user_name, user_gid) == -1) {
if (!had_group &&
/* no need to set the new user's supplementary groups if
we are already this user */
user_uid != getuid() &&
initgroups(user_name, user_gid) == -1) {
FormatFatalSystemError("Failed to set supplementary groups "
"of user \"%s\"",
user_name);

@@ -81,31 +81,85 @@ icy_add_item(Tag &tag, TagType type, const char *value)
}
static void
icy_parse_tag_item(Tag &tag, const char *item)
icy_parse_tag_item(Tag &tag, const char *name, const char *value)
{
gchar **p = g_strsplit(item, "=", 0);
if (strcmp(name, "StreamTitle") == 0)
icy_add_item(tag, TAG_TITLE, value);
else
FormatDebug(icy_metadata_domain,
"unknown icy-tag: '%s'", name);
}
if (p[0] != nullptr && p[1] != nullptr) {
if (strcmp(p[0], "StreamTitle") == 0)
icy_add_item(tag, TAG_TITLE, p[1]);
else
FormatDebug(icy_metadata_domain,
"unknown icy-tag: '%s'", p[0]);
/**
* Find a single quote that is followed by a semicolon (or by the end
* of the string). If that fails, return the first single quote. If
* that also fails, return #end.
*/
static char *
find_end_quote(char *p, char *const end)
{
char *fallback = std::find(p, end, '\'');
if (fallback >= end - 1 || fallback[1] == ';')
return fallback;
p = fallback + 1;
while (true) {
p = std::find(p, end, '\'');
if (p == end)
return fallback;
if (p == end - 1 || p[1] == ';')
return p;
++p;
}
g_strfreev(p);
}
static Tag *
icy_parse_tag(const char *p)
icy_parse_tag(char *p, char *const end)
{
assert(p != nullptr);
assert(end != nullptr);
assert(p <= end);
Tag *tag = new Tag();
gchar **items = g_strsplit(p, ";", 0);
for (unsigned i = 0; items[i] != nullptr; ++i)
icy_parse_tag_item(*tag, items[i]);
while (p != end) {
const char *const name = p;
char *eq = std::find(p, end, '=');
if (eq == end)
break;
g_strfreev(items);
*eq = 0;
p = eq + 1;
if (*p != '\'') {
/* syntax error; skip to the next semicolon,
try to recover */
char *semicolon = std::find(p, end, ';');
if (semicolon == end)
break;
p = semicolon + 1;
continue;
}
++p;
const char *const value = p;
char *quote = find_end_quote(p, end);
if (quote == end)
break;
*quote = 0;
p = quote + 1;
icy_parse_tag_item(*tag, name, value);
char *semicolon = std::find(p, end, ';');
if (semicolon == end)
break;
p = semicolon + 1;
}
return tag;
}
@@ -152,15 +206,11 @@ IcyMetaDataParser::Meta(const void *data, size_t length)
++length;
if (meta_position == meta_size) {
/* null-terminate the string */
meta_data[meta_size] = 0;
/* parse */
delete tag;
tag = icy_parse_tag(meta_data);
tag = icy_parse_tag(meta_data, meta_data + meta_size);
g_free(meta_data);
/* change back to normal data mode */

@@ -18,7 +18,7 @@
*/
#ifndef MPD_OUTPUT_API_HXX
#define MPD_OUTPUT_API_HxX
#define MPD_OUTPUT_API_HXX
#include "OutputPlugin.hxx"
#include "OutputInternal.hxx"

@@ -30,7 +30,7 @@ struct playlist {
/**
* The song queue - it contains the "real" playlist.
*/
struct queue queue;
struct Queue queue;
/**
* This value is true if the player is currently playing (or

@@ -40,7 +40,7 @@
void
playlist_print_uris(Client &client, const playlist &playlist)
{
const queue &queue = playlist.queue;
const Queue &queue = playlist.queue;
queue_print_uris(client, queue, 0, queue.GetLength());
}
@@ -49,7 +49,7 @@ bool
playlist_print_info(Client &client, const playlist &playlist,
unsigned start, unsigned end)
{
const queue &queue = playlist.queue;
const Queue &queue = playlist.queue;
if (end > queue.GetLength())
/* correct the "end" offset */

@@ -65,7 +65,7 @@ playlist_print_uri(FILE *file, const char *uri)
}
PlaylistResult
spl_save_queue(const char *name_utf8, const queue &queue)
spl_save_queue(const char *name_utf8, const Queue &queue)
{
if (map_spl_path().IsNull())
return PlaylistResult::DISABLED;

@@ -25,7 +25,7 @@
#include <stdio.h>
struct Song;
struct queue;
struct Queue;
struct playlist;
struct PlayerControl;
class Error;
@@ -40,7 +40,7 @@ playlist_print_uri(FILE *fp, const char *uri);
* Saves a queue object into a stored playlist file.
*/
PlaylistResult
spl_save_queue(const char *name_utf8, const queue &queue);
spl_save_queue(const char *name_utf8, const Queue &queue);
/**
* Saves a playlist object into a stored playlist file.

@@ -23,7 +23,7 @@
#include <stdlib.h>
queue::queue(unsigned _max_length)
Queue::Queue(unsigned _max_length)
:max_length(_max_length), length(0),
version(1),
items(new Item[max_length]),
@@ -36,7 +36,7 @@ queue::queue(unsigned _max_length)
{
}
queue::~queue()
Queue::~Queue()
{
Clear();
@@ -45,7 +45,7 @@ queue::~queue()
}
int
queue::GetNextOrder(unsigned _order) const
Queue::GetNextOrder(unsigned _order) const
{
assert(_order < length);
@@ -62,7 +62,7 @@ queue::GetNextOrder(unsigned _order) const
}
void
queue::IncrementVersion()
Queue::IncrementVersion()
{
static unsigned long max = ((uint32_t) 1 << 31) - 1;
@@ -77,7 +77,7 @@ queue::IncrementVersion()
}
void
queue::ModifyAtOrder(unsigned _order)
Queue::ModifyAtOrder(unsigned _order)
{
assert(_order < length);
@@ -86,7 +86,7 @@ queue::ModifyAtOrder(unsigned _order)
}
unsigned
queue::Append(Song *song, uint8_t priority)
Queue::Append(Song *song, uint8_t priority)
{
assert(!IsFull());
@@ -105,7 +105,7 @@ queue::Append(Song *song, uint8_t priority)
}
void
queue::SwapPositions(unsigned position1, unsigned position2)
Queue::SwapPositions(unsigned position1, unsigned position2)
{
unsigned id1 = items[position1].id;
unsigned id2 = items[position2].id;
@@ -120,7 +120,7 @@ queue::SwapPositions(unsigned position1, unsigned position2)
}
void
queue::MovePostion(unsigned from, unsigned to)
Queue::MovePostion(unsigned from, unsigned to)
{
const Item tmp = items[from];
@@ -156,7 +156,7 @@ queue::MovePostion(unsigned from, unsigned to)
}
void
queue::MoveRange(unsigned start, unsigned end, unsigned to)
Queue::MoveRange(unsigned start, unsigned end, unsigned to)
{
Item tmp[end - start];
// Copy the original block [start,end-1]
@@ -198,7 +198,7 @@ queue::MoveRange(unsigned start, unsigned end, unsigned to)
}
void
queue::MoveOrder(unsigned from_order, unsigned to_order)
Queue::MoveOrder(unsigned from_order, unsigned to_order)
{
assert(from_order < length);
assert(to_order <= length);
@@ -217,7 +217,7 @@ queue::MoveOrder(unsigned from_order, unsigned to_order)
}
void
queue::DeletePosition(unsigned position)
Queue::DeletePosition(unsigned position)
{
assert(position < length);
@@ -254,7 +254,7 @@ queue::DeletePosition(unsigned position)
}
void
queue::Clear()
Queue::Clear()
{
for (unsigned i = 0; i < length; i++) {
Item *item = &items[i];
@@ -270,7 +270,7 @@ queue::Clear()
}
static void
queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
queue_sort_order_by_priority(Queue *queue, unsigned start, unsigned end)
{
assert(queue != nullptr);
assert(queue->random);
@@ -278,8 +278,8 @@ queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
assert(end <= queue->length);
auto cmp = [queue](unsigned a_pos, unsigned b_pos){
const queue::Item &a = queue->items[a_pos];
const queue::Item &b = queue->items[b_pos];
const Queue::Item &a = queue->items[a_pos];
const Queue::Item &b = queue->items[b_pos];
return a.priority > b.priority;
};
@@ -288,7 +288,7 @@ queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
}
void
queue::ShuffleOrderRange(unsigned start, unsigned end)
Queue::ShuffleOrderRange(unsigned start, unsigned end)
{
assert(random);
assert(start <= end);
@@ -303,7 +303,7 @@ queue::ShuffleOrderRange(unsigned start, unsigned end)
* priority group.
*/
void
queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
Queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
{
assert(random);
assert(start <= end);
@@ -337,13 +337,13 @@ queue::ShuffleOrderRangeWithPriority(unsigned start, unsigned end)
}
void
queue::ShuffleOrder()
Queue::ShuffleOrder()
{
ShuffleOrderRangeWithPriority(0, length);
}
void
queue::ShuffleOrderFirst(unsigned start, unsigned end)
Queue::ShuffleOrderFirst(unsigned start, unsigned end)
{
rand.AutoCreate();
@@ -352,7 +352,7 @@ queue::ShuffleOrderFirst(unsigned start, unsigned end)
}
void
queue::ShuffleOrderLast(unsigned start, unsigned end)
Queue::ShuffleOrderLast(unsigned start, unsigned end)
{
rand.AutoCreate();
@@ -361,7 +361,7 @@ queue::ShuffleOrderLast(unsigned start, unsigned end)
}
void
queue::ShuffleRange(unsigned start, unsigned end)
Queue::ShuffleRange(unsigned start, unsigned end)
{
assert(start <= end);
assert(end <= length);
@@ -377,7 +377,7 @@ queue::ShuffleRange(unsigned start, unsigned end)
}
unsigned
queue::FindPriorityOrder(unsigned start_order, uint8_t priority,
Queue::FindPriorityOrder(unsigned start_order, uint8_t priority,
unsigned exclude_order) const
{
assert(random);
@@ -394,7 +394,7 @@ queue::FindPriorityOrder(unsigned start_order, uint8_t priority,
}
unsigned
queue::CountSamePriority(unsigned start_order, uint8_t priority) const
Queue::CountSamePriority(unsigned start_order, uint8_t priority) const
{
assert(random);
assert(start_order <= length);
@@ -410,7 +410,7 @@ queue::CountSamePriority(unsigned start_order, uint8_t priority) const
}
bool
queue::SetPriority(unsigned position, uint8_t priority, int after_order)
Queue::SetPriority(unsigned position, uint8_t priority, int after_order)
{
assert(position < length);
@@ -468,7 +468,7 @@ queue::SetPriority(unsigned position, uint8_t priority, int after_order)
}
bool
queue::SetPriorityRange(unsigned start_position, unsigned end_position,
Queue::SetPriorityRange(unsigned start_position, unsigned end_position,
uint8_t priority, int after_order)
{
assert(start_position <= end_position);

@@ -41,7 +41,7 @@ struct Song;
* - the unique id (which stays the same, regardless of moves)
* - the order number (which only differs from "position" in random mode)
*/
struct queue {
struct Queue {
/**
* reserve max_length * HASH_MULT elements in the id
* number space
@@ -103,16 +103,16 @@ struct queue {
/** random number generator for shuffle and random mode */
LazyRandomEngine rand;
queue(unsigned max_length);
explicit Queue(unsigned max_length);
/**
* Deinitializes a queue object. It does not free the queue
* pointer itself.
*/
~queue();
~Queue();
queue(const queue &other) = delete;
queue &operator=(const queue &other) = delete;
Queue(const Queue &) = delete;
Queue &operator=(const Queue &) = delete;
unsigned GetLength() const {
assert(length <= max_length);

@@ -38,7 +38,7 @@ extern "C" {
* @param end the index of the last song (excluding)
*/
static void
queue_print_song_info(Client &client, const queue &queue,
queue_print_song_info(Client &client, const Queue &queue,
unsigned position)
{
song_print_info(client, queue.Get(position));
@@ -51,7 +51,7 @@ queue_print_song_info(Client &client, const queue &queue,
}
void
queue_print_info(Client &client, const queue &queue,
queue_print_info(Client &client, const Queue &queue,
unsigned start, unsigned end)
{
assert(start <= end);
@@ -62,7 +62,7 @@ queue_print_info(Client &client, const queue &queue,
}
void
queue_print_uris(Client &client, const queue &queue,
queue_print_uris(Client &client, const Queue &queue,
unsigned start, unsigned end)
{
assert(start <= end);
@@ -75,7 +75,7 @@ queue_print_uris(Client &client, const queue &queue,
}
void
queue_print_changes_info(Client &client, const queue &queue,
queue_print_changes_info(Client &client, const Queue &queue,
uint32_t version)
{
for (unsigned i = 0; i < queue.GetLength(); i++) {
@@ -85,7 +85,7 @@ queue_print_changes_info(Client &client, const queue &queue,
}
void
queue_print_changes_position(Client &client, const queue &queue,
queue_print_changes_position(Client &client, const Queue &queue,
uint32_t version)
{
for (unsigned i = 0; i < queue.GetLength(); i++)
@@ -95,7 +95,7 @@ queue_print_changes_position(Client &client, const queue &queue,
}
void
queue_find(Client &client, const queue &queue,
queue_find(Client &client, const Queue &queue,
const SongFilter &filter)
{
for (unsigned i = 0; i < queue.GetLength(); i++) {

@@ -27,28 +27,28 @@
#include <stdint.h>
struct queue;
struct Queue;
class SongFilter;
class Client;
void
queue_print_info(Client &client, const queue &queue,
queue_print_info(Client &client, const Queue &queue,
unsigned start, unsigned end);
void
queue_print_uris(Client &client, const queue &queue,
queue_print_uris(Client &client, const Queue &queue,
unsigned start, unsigned end);
void
queue_print_changes_info(Client &client, const queue &queue,
queue_print_changes_info(Client &client, const Queue &queue,
uint32_t version);
void
queue_print_changes_position(Client &client, const queue &queue,
queue_print_changes_position(Client &client, const Queue &queue,
uint32_t version);
void
queue_find(Client &client, const queue &queue,
queue_find(Client &client, const Queue &queue,
const SongFilter &filter);
#endif

@@ -60,7 +60,7 @@ queue_save_song(FILE *fp, int idx, const Song &song)
}
void
queue_save(FILE *fp, const queue &queue)
queue_save(FILE *fp, const Queue &queue)
{
for (unsigned i = 0; i < queue.GetLength(); i++) {
uint8_t prio = queue.GetPriorityAtPosition(i);
@@ -72,7 +72,7 @@ queue_save(FILE *fp, const queue &queue)
}
void
queue_load_song(TextFile &file, const char *line, queue &queue)
queue_load_song(TextFile &file, const char *line, Queue &queue)
{
if (queue.IsFull())
return;

@@ -27,16 +27,16 @@
#include <stdio.h>
struct queue;
struct Queue;
class TextFile;
void
queue_save(FILE *fp, const queue &queue);
queue_save(FILE *fp, const Queue &queue);
/**
* Loads one song from the state file and appends it to the queue.
*/
void
queue_load_song(TextFile &file, const char *line, queue &queue);
queue_load_song(TextFile &file, const char *line, Queue &queue);
#endif

@@ -101,7 +101,7 @@ bool
SongFilter::Item::Match(const Tag &_tag) const
{
bool visited_types[TAG_NUM_OF_ITEM_TYPES];
std::fill_n(visited_types, TAG_NUM_OF_ITEM_TYPES, false);
std::fill_n(visited_types, size_t(TAG_NUM_OF_ITEM_TYPES), false);
for (unsigned i = 0; i < _tag.num_items; i++) {
visited_types[_tag.items[i]->type] = true;

@@ -393,6 +393,7 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is)
if (!ret) {
LogError(error);
NeAACDecClose(decoder);
decoder_buffer_free(buffer);
return;
}
@@ -461,6 +462,7 @@ faad_stream_decode(Decoder &mpd_decoder, InputStream &is)
/* cleanup */
NeAACDecClose(decoder);
decoder_buffer_free(buffer);
}
static bool

@@ -38,7 +38,10 @@ extern "C" {
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h>
#include <libavutil/dict.h>
#if LIBAVUTIL_VERSION_MAJOR >= 53
#include <libavutil/frame.h>
#endif
}
#include <assert.h>
@@ -223,6 +226,9 @@ copy_interleave_frame(const AVCodecContext *codec_context,
codec_context->channels,
frame->nb_samples,
codec_context->sample_fmt, 1);
if (data_size <= 0)
return data_size;
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
codec_context->channels > 1) {
if(*global_buffer_size < data_size) {
@@ -451,7 +457,11 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
decoder_initialized(decoder, audio_format,
input.seekable, total_time);
#if LIBAVUTIL_VERSION_MAJOR >= 53
AVFrame *frame = av_frame_alloc();
#else
AVFrame *frame = avcodec_alloc_frame();
#endif
if (!frame) {
LogError(ffmpeg_domain, "Could not allocate frame");
avformat_close_input(&format_context);
@@ -495,7 +505,9 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
}
} while (cmd != DecoderCommand::STOP);
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
#if LIBAVUTIL_VERSION_MAJOR >= 53
av_frame_free(&frame);
#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 28, 0)
avcodec_free_frame(&frame);
#else
av_freep(&frame);

@@ -21,8 +21,6 @@
#define MPD_FFMPEG_METADATA_HXX
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include <libavutil/dict.h>
}
@@ -35,6 +33,6 @@ struct tag_handler;
void
ffmpeg_scan_dictionary(AVDictionary *dict,
const struct tag_handler *handler, void *handler_ctx);
const tag_handler *handler, void *handler_ctx);
#endif

@@ -68,7 +68,6 @@ pls_parser(GKeyFile *keyfile, std::forward_list<SongPointer> &songs)
FormatError(pls_domain, "Invalid PLS entry %s: '%s'",
key, error->message);
g_error_free(error);
g_free(key);
return;
}

@@ -39,7 +39,7 @@ TagLoadConfig()
if (value == nullptr)
return;
std::fill_n(ignore_tag_items, TAG_NUM_OF_ITEM_TYPES, true);
std::fill_n(ignore_tag_items, size_t(TAG_NUM_OF_ITEM_TYPES), true);
if (StringEqualsCaseASCII(value, "none"))
return;

@@ -130,8 +130,9 @@ PeakBuffer::Append(const void *data, size_t length)
return true;
}
if (peak_buffer == nullptr && peak_size > 0) {
peak_buffer = (fifo_buffer *)HugeAllocate(peak_size);
if (peak_buffer == nullptr) {
if (peak_size > 0)
peak_buffer = (fifo_buffer *)HugeAllocate(peak_size);
if (peak_buffer == nullptr)
return false;

@@ -32,7 +32,8 @@ const char *
uri_get_suffix(const char *uri)
{
const char *suffix = strrchr(uri, '.');
if (suffix == nullptr)
if (suffix == nullptr || suffix == uri ||
suffix[-1] == '/' || suffix[-1] == '\\')
return nullptr;
++suffix;

85
test/test_icy_parser.cxx Normal file

@@ -0,0 +1,85 @@
/*
* Unit tests for class IcyMetaDataParser.
*/
#include "config.h"
/* include the .cxx file to get access to internal functions */
#include "IcyMetaDataParser.cxx"
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>
#include <string>
#include <string.h>
static Tag *
icy_parse_tag(const char *p)
{
char *q = strdup(p);
Tag *tag = icy_parse_tag(q, q + strlen(q));
free(q);
return tag;
}
static void
CompareTagTitle(const Tag &tag, const std::string &title)
{
CPPUNIT_ASSERT_EQUAL(1u, tag.num_items);
const TagItem &item = *tag.items[0];
CPPUNIT_ASSERT_EQUAL(TAG_TITLE, item.type);
CPPUNIT_ASSERT_EQUAL(title, std::string(item.value));
}
static void
TestIcyParserTitle(const char *input, const char *title)
{
Tag *tag = icy_parse_tag(input);
CompareTagTitle(*tag, title);
delete tag;
}
static void
TestIcyParserEmpty(const char *input)
{
Tag *tag = icy_parse_tag(input);
CPPUNIT_ASSERT_EQUAL(0u, tag->num_items);
delete tag;
}
class IcyTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(IcyTest);
CPPUNIT_TEST(TestIcyMetadataParser);
CPPUNIT_TEST_SUITE_END();
public:
void TestIcyMetadataParser() {
TestIcyParserEmpty("foo=bar;");
TestIcyParserTitle("StreamTitle='foo bar'", "foo bar");
TestIcyParserTitle("StreamTitle='foo bar';", "foo bar");
TestIcyParserTitle("StreamTitle='foo\"bar';", "foo\"bar");
TestIcyParserTitle("StreamTitle='foo=bar';", "foo=bar");
TestIcyParserTitle("a=b;StreamTitle='foo';", "foo");
TestIcyParserTitle("a=;StreamTitle='foo';", "foo");
TestIcyParserTitle("a=b;StreamTitle='foo';c=d", "foo");
TestIcyParserTitle("a=b;StreamTitle='foo'", "foo");
TestIcyParserTitle("a='b;c';StreamTitle='foo;bar'", "foo;bar");
TestIcyParserTitle("a='b'c';StreamTitle='foo'bar'", "foo'bar");
TestIcyParserTitle("StreamTitle='fo'o'b'ar';a='b'c'd'", "fo'o'b'ar");
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(IcyTest);
int
main(gcc_unused int argc, gcc_unused char **argv)
{
CppUnit::TextUi::TestRunner runner;
auto &registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest(registry.makeTest());
return runner.run() ? EXIT_SUCCESS : EXIT_FAILURE;
}

@@ -26,7 +26,7 @@ Song::Free()
}
static void
check_descending_priority(const struct queue *queue,
check_descending_priority(const Queue *queue,
unsigned start_order)
{
assert(start_order < queue->GetLength());
@@ -55,7 +55,7 @@ QueuePriorityTest::TestPriority()
{
static Song songs[16];
struct queue queue(32);
Queue queue(32);
for (unsigned i = 0; i < ARRAY_SIZE(songs); ++i)
queue.Append(&songs[i], 0);

@@ -29,6 +29,10 @@ public:
"jpg"));
CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo.png/bar.jpg"),
"jpg"));
CPPUNIT_ASSERT_EQUAL((const char *)nullptr,
uri_get_suffix(".jpg"));
CPPUNIT_ASSERT_EQUAL((const char *)nullptr,
uri_get_suffix("/foo/.jpg"));
}
void TestRemoveAuth() {