mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-11-03 04:17:18 -05:00
Add new playlist handling functions.
git-svn-id: https://svn.xiph.org/trunk/ezstream@12525 0101bb08-14d6-0310-b084-bc0e0c8e3800
This commit is contained in:
parent
5d932e4191
commit
2b0c3e6149
422
src/playlist.c
Normal file
422
src/playlist.c
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2007 Moritz Grimm <gtgbr@gmx.net>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_SYS_TYPES_H
|
||||||
|
# include <sys/types.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "playlist.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
# define snprintf _snprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SIZE_T_MAX
|
||||||
|
# define SIZE_T_MAX UINT_MAX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
# define PATH_MAX 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int errno;
|
||||||
|
extern char *__progname;
|
||||||
|
|
||||||
|
struct playlist {
|
||||||
|
char *filename;
|
||||||
|
char **list;
|
||||||
|
size_t size;
|
||||||
|
size_t num;
|
||||||
|
size_t index;
|
||||||
|
};
|
||||||
|
|
||||||
|
playlist_t * playlist_create(const char *);
|
||||||
|
int playlist_add(playlist_t *, const char *);
|
||||||
|
unsigned int playlist_random(void);
|
||||||
|
|
||||||
|
playlist_t *
|
||||||
|
playlist_create(const char *filename)
|
||||||
|
{
|
||||||
|
playlist_t *pl;
|
||||||
|
|
||||||
|
pl = xcalloc(1, sizeof(playlist_t));
|
||||||
|
pl->filename = xstrdup(filename);
|
||||||
|
|
||||||
|
return (pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_add(playlist_t *pl, const char *entry)
|
||||||
|
{
|
||||||
|
size_t num;
|
||||||
|
|
||||||
|
if (pl == NULL || entry == NULL) {
|
||||||
|
printf("%s: playlist_add(): Internal error: Bad arguments\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
num = pl->num + 1;
|
||||||
|
|
||||||
|
if (pl->size == 0) {
|
||||||
|
pl->list = xcalloc(2, sizeof(char *));
|
||||||
|
pl->size = 2 * sizeof(char *);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->size / sizeof(char *) <= num) {
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
pl->list = xrealloc(pl->list, 2, pl->size);
|
||||||
|
pl->size = 2 * pl->size;
|
||||||
|
|
||||||
|
for (i = num; i < pl->size / sizeof(char *); i++)
|
||||||
|
pl->list[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pl->list[num - 1] = xstrdup(entry);
|
||||||
|
pl->num = num;
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int
|
||||||
|
playlist_random(void)
|
||||||
|
{
|
||||||
|
unsigned int ret = 0;
|
||||||
|
|
||||||
|
#ifdef HAVE_ARC4RANDOM
|
||||||
|
ret = arc4random();
|
||||||
|
#elif HAVE_RANDOM
|
||||||
|
ret = (unsigned int)random();
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* Throw away the lower 12 bits; they go through a cyclic pattern.
|
||||||
|
* While this means that items in gigantic playlists won't be put
|
||||||
|
* beyond a certain distance from their original place in the list,
|
||||||
|
* it still improves the quality of the shuffler if this PRNG has
|
||||||
|
* to be used.
|
||||||
|
*/
|
||||||
|
ret = (unsigned int)rand() >> 12;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
playlist_init(void)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_RANDOM
|
||||||
|
# ifdef HAVE_SRANDOMDEV
|
||||||
|
srandomdev();
|
||||||
|
# else
|
||||||
|
srandom((unsigned int)time(NULL));
|
||||||
|
# endif /* HAVE_SRANDOMDEV */
|
||||||
|
#else
|
||||||
|
srand((unsigned int)time(NULL));
|
||||||
|
#endif /* HAVE_RANDOM */
|
||||||
|
}
|
||||||
|
|
||||||
|
void playlist_shutdown(void) {}
|
||||||
|
|
||||||
|
playlist_t *
|
||||||
|
playlist_read(const char *filename)
|
||||||
|
{
|
||||||
|
playlist_t *pl;
|
||||||
|
unsigned long line;
|
||||||
|
FILE *filep;
|
||||||
|
char buf[PATH_MAX + 1];
|
||||||
|
|
||||||
|
pl = playlist_create(filename);
|
||||||
|
|
||||||
|
if ((filep = fopen(filename, "r")) == NULL) {
|
||||||
|
printf("%s: playlist_read(): %s: %s\n", __progname, filename,
|
||||||
|
strerror(errno));
|
||||||
|
playlist_free(pl);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = 0;
|
||||||
|
while (fgets(buf, sizeof(buf), filep) != NULL) {
|
||||||
|
line++;
|
||||||
|
|
||||||
|
if (strlen(buf) == sizeof(buf) - 1) {
|
||||||
|
char skip_buf[2];
|
||||||
|
|
||||||
|
printf("%s[%lu]: File or path name too long\n",
|
||||||
|
filename, line);
|
||||||
|
/* Discard any excess chars in that line. */
|
||||||
|
while (fgets(skip_buf, sizeof(skip_buf), filep) != NULL) {
|
||||||
|
if (skip_buf[0] == '\n')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fgets() would happily fill a buffer with
|
||||||
|
* { '\0', ..., '\n', '\0' }. Also skip lines that begin with
|
||||||
|
* a '#', which is considered a comment.
|
||||||
|
*/
|
||||||
|
if (buf[0] == '\0' || buf[0] == '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Trim any trailing newlines and carriage returns. */
|
||||||
|
if (buf[strlen(buf) - 1] == '\n')
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
if (buf[0] != '\0' && buf[strlen(buf) - 1] == '\r')
|
||||||
|
buf[strlen(buf) - 1] = '\0';
|
||||||
|
|
||||||
|
/* Skip lines that are empty after the trimming. */
|
||||||
|
if (buf[0] == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* We got one. */
|
||||||
|
if (!playlist_add(pl, buf)) {
|
||||||
|
fclose(filep);
|
||||||
|
playlist_free(pl);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ferror(filep)) {
|
||||||
|
printf("%s: playlist_read(): Error while reading %s: %s\n",
|
||||||
|
__progname, filename, strerror(errno));
|
||||||
|
fclose(filep);
|
||||||
|
playlist_free(pl);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(filep);
|
||||||
|
return (pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
playlist_free(playlist_t *pl)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (pl != NULL) {
|
||||||
|
if (pl->filename != NULL) {
|
||||||
|
xfree(pl->filename);
|
||||||
|
pl->filename = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->list != NULL) {
|
||||||
|
if (pl->size > 0) {
|
||||||
|
for (i = 0; i < pl->size / sizeof(char *); i++) {
|
||||||
|
if (pl->list[i] != NULL) {
|
||||||
|
xfree(pl->list[i]);
|
||||||
|
pl->list[i] = NULL;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(pl->list);
|
||||||
|
pl->list = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
xfree(pl);
|
||||||
|
pl = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
playlist_get_next(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_get_next(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((const char *)pl->list[pl->index++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
playlist_peek_next(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_peek_next(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((const char *)pl->list[pl->index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
playlist_skip_next(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_skip_next(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->list[pl->index] != NULL)
|
||||||
|
pl->index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
playlist_get_num_items(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_get_position(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((unsigned long)pl->num);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long
|
||||||
|
playlist_get_position(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_get_position(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((unsigned long)pl->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_set_position(playlist_t *pl, unsigned long index)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_set_position(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > pl->num - 1)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
pl->index = (size_t)index;
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_goto_entry(playlist_t *pl, const char *entry)
|
||||||
|
{
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
if (pl == NULL || entry == NULL) {
|
||||||
|
printf("%s: playlist_goto_entry(): Internal error: Bad arguments\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->num == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
for (i = 0; i < pl->num; i++) {
|
||||||
|
if (strcmp(pl->list[i], entry) == 0) {
|
||||||
|
pl->index = (size_t)i;
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
playlist_rewind(playlist_t *pl)
|
||||||
|
{
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_rewind(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pl->index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
playlist_reread(playlist_t **plist)
|
||||||
|
{
|
||||||
|
playlist_t *new_pl, *pl;
|
||||||
|
|
||||||
|
if (plist == NULL || *plist == NULL) {
|
||||||
|
printf("%s: playlist_reread(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pl = *plist;
|
||||||
|
|
||||||
|
if ((new_pl = playlist_read(pl->filename)) == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
playlist_free(pl);
|
||||||
|
*plist = new_pl;
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Yet another implementation of the "Knuth Shuffle":
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
playlist_shuffle(playlist_t *pl)
|
||||||
|
{
|
||||||
|
unsigned long d, i, range;
|
||||||
|
char *temp;
|
||||||
|
|
||||||
|
if (pl == NULL) {
|
||||||
|
printf("%s: playlist_shuffle(): Internal error: NULL argument\n",
|
||||||
|
__progname);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pl->num < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < pl->num; i++) {
|
||||||
|
range = pl->num - i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only accept a random number if it is smaller than the
|
||||||
|
* largest multiple of our range. This reduces PRNG bias.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
d = (unsigned long)playlist_random();
|
||||||
|
} while (d > RAND_MAX - (RAND_MAX % range));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The range starts at the item we want to shuffle, excluding
|
||||||
|
* already shuffled items.
|
||||||
|
*/
|
||||||
|
d = i + (d % range);
|
||||||
|
|
||||||
|
temp = pl->list[d];
|
||||||
|
pl->list[d] = pl->list[i];
|
||||||
|
pl->list[i] = temp;
|
||||||
|
}
|
||||||
|
}
|
104
src/playlist.h
Normal file
104
src/playlist.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2007 Moritz Grimm <gtgbr@gmx.net>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __PLAYLIST_H__
|
||||||
|
#define __PLAYLIST_H__
|
||||||
|
|
||||||
|
typedef struct playlist playlist_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the playlist routines. Should be called before any of the other
|
||||||
|
* playlist functions.
|
||||||
|
*/
|
||||||
|
void playlist_init(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clean up for clean shutdowns. No-op at the moment.
|
||||||
|
*/
|
||||||
|
void playlist_shutdown(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read a playlist file (in .M3U format), and return a new playlist handler
|
||||||
|
* on success, or NULL on failure.
|
||||||
|
*/
|
||||||
|
playlist_t * playlist_read(const char * /* filename */);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free all memory used by a playlist handler that was created with
|
||||||
|
* playlist_read().
|
||||||
|
*/
|
||||||
|
void playlist_free(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the next item in the playlist. Returns a NUL-terminated string of a
|
||||||
|
* playlist entry, or NULL if the end of the list has been reached.
|
||||||
|
*/
|
||||||
|
const char * playlist_get_next(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the next item in the playlist without moving on to the following entry.
|
||||||
|
* Returns a NUL-terminated string of the next playlist entry, or NULL if the
|
||||||
|
* currently playing song is the last one in the list.
|
||||||
|
*/
|
||||||
|
const char * playlist_peek_next(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip the playlist item that would be played next.
|
||||||
|
*/
|
||||||
|
void playlist_skip_next(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the number of items in the playlist.
|
||||||
|
*/
|
||||||
|
unsigned long playlist_get_num_items(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the current position in the playlist.
|
||||||
|
*/
|
||||||
|
unsigned long playlist_get_position(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set a position in the playlist. Returns 1 on success, and 0 on failure.
|
||||||
|
*/
|
||||||
|
int playlist_set_position(playlist_t *, unsigned long /* index */);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Search for a given entry in the playlist and reposition to it. Returns 1 on
|
||||||
|
* success and 0 on failure. A subsequent call to playlist_get_next() will
|
||||||
|
* return this list item again.
|
||||||
|
*/
|
||||||
|
int playlist_goto_entry(playlist_t *, const char * /* name */);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rewind the playlist to the beginning, so that it can be replayed. Does
|
||||||
|
* not reread the playlist file.
|
||||||
|
*/
|
||||||
|
void playlist_rewind(playlist_t *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reread the playlist file and rewind to the beginning. Equivalent to calling
|
||||||
|
* playlist_free() and playlist_read(). Entries will no longer be shuffled
|
||||||
|
* after calling this function.
|
||||||
|
* Returns 1 on success, and 0 on error.
|
||||||
|
*/
|
||||||
|
int playlist_reread(playlist_t **);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shuffle the entries of the playlist randomly.
|
||||||
|
*/
|
||||||
|
void playlist_shuffle(playlist_t *);
|
||||||
|
|
||||||
|
#endif /* __PLAYLIST_H__ */
|
Loading…
Reference in New Issue
Block a user