log file and/or increase output verbosity by using the -l switch for
server and client.
-------------------------------------
+-------------------------------------
Specify mysql data (port, passwd,...)
-------------------------------------
+-------------------------------------
Type
to find out. If mysql is not mentioned as a supported selector,
you'll have to recompile. If configure does not detect your mysql
-installation, use the --enable-mysql-headers and --enable-mysql-libs
+installation, use the --with-mysql-headers and --with-mysql-libs
options to specify the mysql path explicitly . Example:
- ./configure --enable-mysql-headers=/Library/MySQL/include \
- --enable-mysql-libs=/Library/MySQL/lib/mysql
+ ./configure --with-mysql-headers=/Library/MySQL/include \
+ --with-mysql-libs=/Library/MySQL/lib/mysql
---------------------
#include "server.cmdline.h"
#include "server.h"
#include "vss.h"
-#include "afh.h"
#include "error.h"
#include "string.h"
#include "aac.h"
#include "fd.h"
/** size of the input buffer, must be big enough to hold header */
-#define DEFAULT_INBUF_SIZE 65536
-
-static struct audio_format_handler *af;
-static FILE *infile;
-static unsigned char *inbuf;
-static size_t inbuf_size, inbuf_len, *chunk_table, num_chunks;
-
-static void aac_close_audio_file(void)
-{
- if (!infile)
- return;
- fclose(infile);
- infile = NULL;
- free(inbuf);
- inbuf = NULL;
- free(chunk_table);
- chunk_table = NULL;
-}
+#define AAC_INBUF_SIZE 65536
static int aac_find_stsz(unsigned char *buf, unsigned buflen, size_t *skip)
{
return -E_STSZ;
}
-static int read_chunk_table(size_t skip)
+static int read_chunk_table(FILE *file, struct audio_format_info *afi,
+ unsigned char *inbuf, size_t inbuf_len, size_t skip)
{
int ret, i;
size_t sum = 0;
ret = aac_find_stsz(inbuf, inbuf_len, &skip);
if (ret >= 0)
break;
- ret = read(fileno(infile), inbuf, inbuf_size);
+ ret = read(fileno(file), inbuf, AAC_INBUF_SIZE);
if (ret <= 0)
return -E_AAC_READ;
+ inbuf_len = ret;
PARA_INFO_LOG("next buffer: %d bytes\n", ret);
}
- num_chunks = ret;
- PARA_INFO_LOG("sz table has %zu entries\n", num_chunks);
- chunk_table = para_malloc((num_chunks + 1) * sizeof(size_t));
- for (i = 1; i <= num_chunks; i++) {
+ afi->chunks_total = ret;
+ PARA_INFO_LOG("sz table has %lu entries\n", afi->chunks_total);
+ afi->chunk_table = para_malloc((afi->chunks_total + 1) * sizeof(size_t));
+ for (i = 1; i <= afi->chunks_total; i++) {
if (skip + 4 > inbuf_len) {
skip = inbuf_len - skip;
memmove(inbuf, inbuf + inbuf_len - skip, skip);
- ret = read(fileno(infile), inbuf + skip, inbuf_size - skip);
+ ret = read(fileno(file), inbuf + skip,
+ AAC_INBUF_SIZE - skip);
if (ret <= 0)
return -E_AAC_READ;
inbuf_len = ret + skip;
PARA_INFO_LOG("next buffer: %zu bytes\n", inbuf_len);
}
sum += aac_read_int32(inbuf + skip);
- chunk_table[i] = sum;
+ afi->chunk_table[i] = sum;
skip += 4;
- if (i < 10 || i + 10 > num_chunks)
- PARA_DEBUG_LOG("offset #%d: %zu\n", i, chunk_table[i]);
+// if (i < 10 || i + 10 > afi->chunks_total)
+// PARA_DEBUG_LOG("offset #%d: %zu\n", i, afi->chunk_table[i]);
}
return 1;
}
-static long unsigned aac_set_chunk_tv(mp4AudioSpecificConfig *mp4ASC)
+static long unsigned aac_set_chunk_tv(struct audio_format_info *afi,
+ mp4AudioSpecificConfig *mp4ASC)
{
float tmp = mp4ASC->sbr_present_flag == 1? 2047 : 1023,
- ms = 1000.0 * num_chunks * tmp / mp4ASC->samplingFrequency;
+ ms = 1000.0 * afi->chunks_total * tmp / mp4ASC->samplingFrequency;
struct timeval total;
ms2tv(ms, &total);
- tv_divide(num_chunks, &total, &af->chunk_tv);
- PARA_INFO_LOG("%luHz, %fs (%zd x %lums)\n",
+ tv_divide(afi->chunks_total, &total, &afi->chunk_tv);
+ PARA_INFO_LOG("%luHz, %fs (%lu x %lums)\n",
mp4ASC->samplingFrequency, ms / 1000,
- num_chunks, tv2ms(&af->chunk_tv));
+ afi->chunks_total, tv2ms(&afi->chunk_tv));
return ms / 1000;
}
/*
* Init m4a file and write some tech data to given pointers.
*/
-static int aac_get_file_info(FILE *file, char *info_str, long unsigned *frames,
- int *seconds, size_t **vss_chunk_table)
+static int aac_get_file_info(FILE *file, struct audio_format_info *afi)
{
int i, ret, decoder_len;
- size_t skip;
+ size_t inbuf_len, skip;
unsigned long rate = 0;
- unsigned char channels = 0;
+ unsigned char channels = 0, *inbuf = para_malloc(AAC_INBUF_SIZE);
mp4AudioSpecificConfig mp4ASC;
NeAACDecHandle handle;
- inbuf_size = DEFAULT_INBUF_SIZE;
- inbuf = para_malloc(inbuf_size);
- infile = file;
-
- ret = read(fileno(infile), inbuf, inbuf_size);
- if (ret <= 0)
- return -E_AAC_READ;
+ ret = read(fileno(file), inbuf, AAC_INBUF_SIZE);
+ if (ret <= 0) {
+ ret = -E_AAC_READ;
+ goto out;
+ }
inbuf_len = ret;
ret = aac_find_esds(inbuf, inbuf_len, &skip);
if (ret < 0)
- return ret;
+ goto out;
decoder_len = ret;
handle = aac_open();
- ret = NeAACDecInit(handle, inbuf + skip,
- decoder_len, &rate, &channels);
- if (ret < 0)
- return -E_AACDEC_INIT;
+ ret = NeAACDecInit(handle, inbuf + skip, decoder_len, &rate, &channels);
+ if (ret < 0) {
+ ret = -E_AACDEC_INIT;
+ goto out;
+ }
skip += ret;
PARA_INFO_LOG("rate: %lu, channels: %d\n", rate, channels);
- ret = NeAACDecAudioSpecificConfig(inbuf + skip, inbuf_len - skip,
- &mp4ASC);
+ ret = -E_MP4ASC;
+ if (NeAACDecAudioSpecificConfig(inbuf + skip, inbuf_len - skip,
+ &mp4ASC) < 0)
+ goto out;
+ ret = read_chunk_table(file, afi, inbuf, inbuf_len, skip);
if (ret < 0)
- return -E_MP4ASC;
- ret = read_chunk_table(skip);
- if (ret < 0)
- return ret;
- *frames = num_chunks;
- *seconds = aac_set_chunk_tv(&mp4ASC);
- *vss_chunk_table = chunk_table;
+ goto out;
+ afi->seconds_total = aac_set_chunk_tv(afi, &mp4ASC);
for (;;) {
ret = aac_find_entry_point(inbuf, inbuf_len, &skip);
if (ret >= 0)
break;
- ret = read(fileno(infile), inbuf, inbuf_size);
- if (ret <= 0)
- return -E_AAC_READ;
+ ret = read(fileno(file), inbuf, AAC_INBUF_SIZE);
+ if (ret <= 0) {
+ ret = -E_AAC_READ;
+ goto out;
+ }
+ inbuf_len = ret;
PARA_INFO_LOG("next buffer: %d bytes\n", ret);
}
- chunk_table[0] = ret;
- for (i = 1; i<= num_chunks; i++)
- chunk_table[i] += ret;
- sprintf(info_str, "audio_file_info1:%zu x %lums\n"
+ afi->chunk_table[0] = ret;
+ for (i = 1; i<= afi->chunks_total; i++)
+ afi->chunk_table[i] += ret;
+ sprintf(afi->info_string, "audio_file_info1:%lu x %lums\n"
"audio_file_info2:\n"
"audio_file_info3:\n",
- num_chunks,
- tv2ms(&af->chunk_tv));
- tv_scale(20, &af->chunk_tv, &af->eof_tv);
- return 1;
+ afi->chunks_total,
+ tv2ms(&afi->chunk_tv));
+ tv_scale(20, &afi->chunk_tv, &afi->eof_tv);
+ ret = 1;
+out:
+ free(inbuf);
+ return ret;
}
static const char* aac_suffixes[] = {"m4a", "mp4", NULL};
-/** the init function of the aac audio format handler */
-void aac_afh_init(struct audio_format_handler *p)
+/**
+ * the init function of the aac audio format handler
+ *
+ * \param afh pointer to the struct to initialize
+ */
+void aac_afh_init(struct audio_format_handler *afh)
{
- af = p;
- af->get_file_info = aac_get_file_info,
- af->close_audio_file = aac_close_audio_file;
- af->get_header_info = NULL;
- af->suffixes = aac_suffixes;
+ afh->get_file_info = aac_get_file_info,
+ afh->suffixes = aac_suffixes;
}
/** \endcond */
+/** size of the audio_file info string */
+#define AUDIO_FILE_INFO_SIZE 16384
+
+struct audio_format_info {
+ /** the number of chunks this audio file contains */
+ long unsigned chunks_total;
+ /** the length of the audio file in seconds */
+ int seconds_total;
+ /** a string that gets filled in by the audio format handler */
+ char info_string[AUDIO_FILE_INFO_SIZE];
+ /**
+ * the table that specifies the offset of the individual pieces in
+ * the current audio file.
+ */
+ size_t *chunk_table;
+ /** period of time between sending data chunks */
+ struct timeval chunk_tv;
+ /** end of file timeout - do not load new audio file until this time */
+ struct timeval eof_tv;
+ /**
+ * optional audio file header
+ *
+ * This is read from a sender in case a new client connects in the
+ * middle of the stream. The audio format handler does not need to set
+ * this if the audio format does not need any special header treatment.
+ * If non-NULL, it must point to a buffer holding the current audio
+ * file header.
+ */
+ char *header;
+ /** the length of the header, ignored if \a header is \p NULL */
+ unsigned header_len;
+};
+
/**
* structure for audio format handling
*
* Must initialize all function pointers and is assumed to succeed.
*/
void (*init)(struct audio_format_handler*);
- /**
- * period of time between sending data chunks
- */
- struct timeval chunk_tv; /* length of one chunk of data */
- /**
- * end of file timeout - do not load new audio file until this time
- *
- */
- struct timeval eof_tv; /* timeout on eof */
- /**
- * Pointer to the optional get-header function.
- *
- * This is called from a sender in case a new client connects in the middle of
- * the stream. The audio format handler may set this to NULL to indicate that
- * this audio format does not need any special header treatment. If non-NULL,
- * the function it points to must return a pointer to a buffer holding the
- * current audio file header, together with the header length.
- */
- char *(*get_header_info)(int *header_len);
/**
* check if this audio format handler can handle the file
*
- * This is a pointer to a function returning whether a given file is valid for
- * this audio format. A negative return value indicates that this audio format
- * handler did not recognize the given file. On success, the function is
- * expected to return a positive value and to fill in \arg info_str, \arg
- * chunks and \arg seconds appropriately and to return the chunk table
- * via \a vss_chunk_table.
- */
- int (*get_file_info)(FILE *audio_file, char *info_str,
- long unsigned *chunks, int *seconds, size_t **vss_chunk_table);
- /**
- * cleanup function of this audio format handler
+ * This is a pointer to a function returning whether a given file is
+ * valid for this audio format. A negative return value indicates that
+ * this audio format handler did not recognize the given file. On
+ * success, the function must return a positive value and fill in the
+ * given struct audio_format_info.
*
- * This close function should deallocate any resources
- * associated with the current audio file. In particular, it is responsible
- * for closing the file handle. It is assumed to succeed.
+ * \sa struct audio_format_info
*/
- void (*close_audio_file)(void);
+ int (*get_file_info)(FILE *audio_file, struct audio_format_info *afi);
};
+
extern struct misc_meta_data *mmd;
extern struct audio_file_selector selectors[];
extern struct sender senders[];
-extern char *user_list;
static void dummy(__a_unused int s)
{}
return para_strdup("");
if (!base[0])
return base;
- if (nmmd->chunks_total) {
- secs = (long long) nmmd->seconds_total * nmmd->chunks_sent
- / nmmd->chunks_total;
- rsecs = (long long) nmmd->seconds_total *
- (nmmd->chunks_total - nmmd->chunks_sent)
- / nmmd->chunks_total;
+ if (nmmd->afi.chunks_total) {
+ secs = (long long) nmmd->afi.seconds_total * nmmd->chunks_sent
+ / nmmd->afi.chunks_total;
+ rsecs = (long long) nmmd->afi.seconds_total *
+ (nmmd->afi.chunks_total - nmmd->chunks_sent)
+ / nmmd->afi.chunks_total;
percent = 100 * ((nmmd->chunks_sent + 5) / 10)
- / ((nmmd->chunks_total + 5) / 10);
+ / ((nmmd->afi.chunks_total + 5) / 10);
}
ret = make_message("%llu:%02llu [%llu:%02llu] (%llu%%) %s",
secs / 60, secs % 60,
"%s:%s\n" "%s:%lu.%lu\n" "%s:%lu.%lu\n",
status_item_list[SI_FILE_SIZE], nmmd->size / 1024,
status_item_list[SI_MTIME], mtime,
- status_item_list[SI_LENGTH], nmmd->seconds_total,
+ status_item_list[SI_LENGTH], nmmd->afi.seconds_total,
status_item_list[SI_NUM_PLAYED], nmmd->num_played,
status_item_list[SI_STATUS_BAR], bar ? bar : "(none)",
status_item_list[SI_OFFSET], offset,
status_item_list[SI_FORMAT], audio_format_name(nmmd->audio_format),
nmmd->selector_info,
- nmmd->audio_file_info,
+ nmmd->afi.info_string,
status_item_list[SI_UPTIME], ut,
status_item_list[SI_STREAM_START],
backwards = 1; /* jmp backwards */
mmd_lock();
ret = -E_NO_AUDIO_FILE;
- if (!mmd->chunks_total || !mmd->seconds_total)
+ if (!mmd->afi.chunks_total || !mmd->afi.seconds_total)
goto out;
- promille = (1000 * mmd->current_chunk) / mmd->chunks_total;
+ promille = (1000 * mmd->current_chunk) / mmd->afi.chunks_total;
if (backwards)
- promille -= 1000 * i / mmd->seconds_total;
+ promille -= 1000 * i / mmd->afi.seconds_total;
else
- promille += 1000 * i / mmd->seconds_total;
+ promille += 1000 * i / mmd->afi.seconds_total;
if (promille < 0)
promille = 0;
if (promille > 1000) {
mmd->new_vss_status_flags |= VSS_NEXT;
goto out;
}
- mmd->repos_request = (mmd->chunks_total * promille) / 1000;
+ mmd->repos_request = (mmd->afi.chunks_total * promille) / 1000;
mmd->new_vss_status_flags |= VSS_REPOS;
mmd->new_vss_status_flags &= ~VSS_NEXT;
mmd->events++;
return -E_COMMAND_SYNTAX;
mmd_lock();
ret = -E_NO_AUDIO_FILE;
- if (!mmd->chunks_total)
+ if (!mmd->afi.chunks_total)
goto out;
if (i > 100)
i = 100;
PARA_INFO_LOG("jumping to %lu%%\n", i);
- mmd->repos_request = (mmd->chunks_total * i + 50)/ 100;
+ mmd->repos_request = (mmd->afi.chunks_total * i + 50)/ 100;
PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
mmd->chunks_sent, mmd->offset);
mmd->new_vss_status_flags |= VSS_REPOS;
/*
- * Copyright (C) 2006 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2006-2007 Andre Noll <maan@systemlinux.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
goto again;
}
+/**
+ * paralash's wrapper for fseek(3)
+ *
+ * \param stream stream to seek
+ * \param offset added to the position specified by whence
+ * \param whence \p SEEK_SET, \p SEEK_CUR, or \p SEEK_END
+ *
+ * \return positive on success, -E_FSEEK on errors.
+ *
+ * \sa fseek(3)
+ */
int para_fseek(FILE *stream, long offset, int whence)
{
int ret = fseek(stream, offset, whence);
- return ret < 0? -E_FSEEK : ret;
+ return ret < 0? -E_FSEEK : 1;
}
#include "server.cmdline.h"
#include "server.h"
#include "vss.h"
-#include "afh.h"
#include "error.h"
#include "fd.h"
#include "string.h"
* to see before we decide we are looking at a real MP3 file
*/
#define MIN_CONSEC_GOOD_FRAMES 4
-
#define FRAME_HEADER_SIZE 4
#define MIN_FRAME_SIZE 21
struct id3tag id3;
int vbr;
long unsigned br_average;
- long unsigned seconds;
int freq;
};
static const int frame_size_index[] = {24000, 72000, 72000};
static const char *mode_text[] = {"stereo", "joint stereo", "dual channel", "mono", "invalid"};
-static FILE *infile;
static struct mp3info mp3;
-static struct audio_format_handler *af;
-static ssize_t *chunk_table, num_chunks;
static int header_frequency(struct mp3header *h)
{
+ header->padding;
}
-static void write_info_str(char *info_str)
+static void write_info_str(struct audio_format_info *afi)
{
int v = mp3.id3_isvalid;
- snprintf(info_str, MMD_INFO_SIZE,
- "audio_file_info1:%d x %lums, %lu kbit/s (%cbr) %i KHz %s\n"
+ snprintf(afi->info_string, MMD_INFO_SIZE,
+ "audio_file_info1:%lu x %lums, %lu kbit/s (%cbr) %i KHz %s\n"
"audio_file_info2:%s, by %s\n"
"audio_file_info3:A: %s, Y: %s, C: %s\n",
- num_chunks,
- tv2ms(&af->chunk_tv),
+ afi->chunks_total,
+ tv2ms(&afi->chunk_tv),
mp3.br_average,
mp3.vbr? 'v' : 'c',
mp3.freq / 1000,
* returned.
*
*/
-static int mp3_seek_next_header(void)
+static int mp3_seek_next_header(FILE *file)
{
int k, l = 0, c, first_len, ret;
struct mp3header h, h2;
long valid_start = 0;
while (1) {
- while ((c = fgetc(infile)) != 255 && (c != EOF))
+ while ((c = fgetc(file)) != 255 && (c != EOF))
; /* nothing */
if (c != 255)
return 0;
- ungetc(c, infile);
- valid_start = ftell(infile);
- first_len = get_header(infile, &h);
+ ungetc(c, file);
+ valid_start = ftell(file);
+ first_len = get_header(file, &h);
if (first_len <= 0)
continue;
- ret = para_fseek(infile, first_len - FRAME_HEADER_SIZE, SEEK_CUR);
+ ret = para_fseek(file, first_len - FRAME_HEADER_SIZE, SEEK_CUR);
if (ret < 0)
return ret;
for (k = 1; k < MIN_CONSEC_GOOD_FRAMES; k++) {
- if ((l = get_header(infile, &h2)) <= 0)
+ if ((l = get_header(file, &h2)) <= 0)
break;
if (!compare_headers(&h, &h2))
break;
- ret = para_fseek(infile, l - FRAME_HEADER_SIZE, SEEK_CUR);
+ ret = para_fseek(file, l - FRAME_HEADER_SIZE, SEEK_CUR);
if (ret < 0)
return ret;
}
if (k == MIN_CONSEC_GOOD_FRAMES) {
- ret = para_fseek(infile, valid_start, SEEK_SET);
+ ret = para_fseek(file, valid_start, SEEK_SET);
if (ret < 0)
return ret;
memcpy(&(mp3.header), &h2, sizeof(struct mp3header));
}
}
-static int mp3_get_id3(void)
+static int mp3_get_id3(FILE *file)
{
char fbuf[4];
int ret;
mp3.id3.album[0] = '\0';
mp3.id3.comment[0] = '\0';
mp3.id3.year[0] = '\0';
- ret = para_fseek(infile, -128, SEEK_END);
+ ret = para_fseek(file, -128, SEEK_END);
if (ret < 0 )
return ret;
- if (para_fread(fbuf, 1, 3, infile) < 0)
+ if (para_fread(fbuf, 1, 3, file) < 0)
return -E_FREAD;
fbuf[3] = '\0';
if (strcmp("TAG", fbuf)) {
PARA_INFO_LOG("%s", "no id3 tag\n");
return 0;
}
- ret = para_fseek(infile, -125, SEEK_END);
+ ret = para_fseek(file, -125, SEEK_END);
if (ret < 0)
return ret;
- if (para_fread(mp3.id3.title, 1, 30, infile) != 30)
+ if (para_fread(mp3.id3.title, 1, 30, file) != 30)
return -E_FREAD;
mp3.id3.title[30] = '\0';
- if (para_fread(mp3.id3.artist, 1, 30, infile) != 30)
+ if (para_fread(mp3.id3.artist, 1, 30, file) != 30)
return -E_FREAD;
mp3.id3.artist[30] = '\0';
- if (para_fread(mp3.id3.album, 1, 30, infile) != 30)
+ if (para_fread(mp3.id3.album, 1, 30, file) != 30)
return -E_FREAD;
mp3.id3.album[30] = '\0';
- if (para_fread(mp3.id3.year, 1, 4, infile) != 4)
+ if (para_fread(mp3.id3.year, 1, 4, file) != 4)
return -E_FREAD;
mp3.id3.year[4] = '\0';
- if (para_fread(mp3.id3.comment, 1, 30, infile) != 30)
+ if (para_fread(mp3.id3.comment, 1, 30, file) != 30)
return -E_FREAD;
mp3.id3.comment[30] = '\0';
mp3.id3_isvalid = 1;
return 1;
}
-static int find_valid_start(void)
+static int find_valid_start(FILE *file)
{
int ret, frame_len;
- if (!infile)
- return -E_MP3_NO_FILE;
- frame_len = get_header(infile, &mp3.header);
+ frame_len = get_header(file, &mp3.header);
if (frame_len < 0)
return frame_len;
if (!frame_len) {
- frame_len = mp3_seek_next_header();
+ frame_len = mp3_seek_next_header(file);
if (frame_len <= 0)
return frame_len;
} else {
- ret = para_fseek(infile, -FRAME_HEADER_SIZE, SEEK_CUR);
+ ret = para_fseek(file, -FRAME_HEADER_SIZE, SEEK_CUR);
if (ret < 0)
return ret;
}
return frame_len;
}
-static int mp3_read_info(void)
+static int mp3_read_info(FILE *file, struct audio_format_info *afi)
{
long fl_avg = 0, freq_avg = 0, br_avg = 0;
int ret, len = 0, old_br = -1;
struct timeval total_time = {0, 0};
unsigned chunk_table_size = 1000; /* gets increased on demand */
- num_chunks = 0;
- chunk_table = para_malloc(chunk_table_size * sizeof(size_t));
- ret = mp3_get_id3();
+ afi->chunks_total = 0;
+ afi->chunk_table = para_malloc(chunk_table_size * sizeof(size_t));
+ ret = mp3_get_id3(file);
if (ret < 0)
goto err_out;
- rewind(infile);
+ rewind(file);
mp3.vbr = 0;
mp3.freq = 0;
while (1) {
int freq, br, fl;
struct timeval tmp, cct; /* current chunk time */
if (len > 0) {
- ret = para_fseek(infile, len, SEEK_CUR);
+ ret = para_fseek(file, len, SEEK_CUR);
if (ret < 0)
goto err_out;
}
- len = find_valid_start();
+ len = find_valid_start(file);
if (len <= 0)
break;
freq = header_frequency(&mp3.header);
tv_add(&cct, &total_time, &tmp);
total_time = tmp;
//PARA_DEBUG_LOG("%s: br: %d, freq: %d, fl: %d, cct: %lu\n", __func__, br, freq, fl, cct.tv_usec);
- if (num_chunks >= chunk_table_size) {
+ if (afi->chunks_total >= chunk_table_size) {
chunk_table_size *= 2;
- chunk_table = para_realloc(chunk_table,
+ afi->chunk_table = para_realloc(afi->chunk_table,
chunk_table_size * sizeof(size_t));
}
- chunk_table[num_chunks] = ftell(infile);
- if (num_chunks < 10 || !(num_chunks % 1000))
- PARA_INFO_LOG("chunk #%d: %zd\n", num_chunks,
- chunk_table[num_chunks]);
- num_chunks++;
- if (num_chunks == 1) {
-// entry = ftell(infile);
-// PARA_INFO_LOG("entry: %zd\n", entry);
+ afi->chunk_table[afi->chunks_total] = ftell(file);
+// if (afi->chunks_total < 10 || !(afi->chunks_total % 1000))
+// PARA_INFO_LOG("chunk #%lu: %zd\n", afi->chunks_total,
+// afi->chunk_table[afi->chunks_total]);
+ afi->chunks_total++;
+ if (afi->chunks_total == 1) {
freq_avg = freq;
br_avg = br;
old_br = br;
fl_avg = fl;
continue;
}
- freq_avg += (freq - freq_avg) / (num_chunks + 1);
- fl_avg += (fl - fl_avg) / (num_chunks + 1);
- br_avg += (br - br_avg) / (num_chunks + 1);
+ freq_avg += (freq - freq_avg) / (afi->chunks_total + 1);
+ fl_avg += (fl - fl_avg) / (afi->chunks_total + 1);
+ br_avg += (br - br_avg) / (afi->chunks_total + 1);
if (old_br != br)
mp3.vbr = 1;
old_br = br;
}
ret = -E_MP3_INFO;
- if (!num_chunks || !freq_avg || !br_avg)
+ if (!afi->chunks_total || !freq_avg || !br_avg)
goto err_out;
- ret= para_fseek(infile, 0, SEEK_END);
+ ret= para_fseek(file, 0, SEEK_END);
if (ret < 0)
goto err_out;
- chunk_table[num_chunks] = ftell(infile);
+ afi->chunk_table[afi->chunks_total] = ftell(file);
mp3.br_average = br_avg;
mp3.freq = freq_avg;
- mp3.seconds = (tv2ms(&total_time) + 500) / 1000;
- tv_divide(num_chunks, &total_time, &af->chunk_tv);
- rewind(infile);
- PARA_DEBUG_LOG("%zu chunks, each %lums\n", num_chunks, tv2ms(&af->chunk_tv));
- tv_scale(3, &af->chunk_tv, &af->eof_tv);
- PARA_DEBUG_LOG("eof timeout: %lu\n", tv2ms(&af->eof_tv));
+ afi->seconds_total = (tv2ms(&total_time) + 500) / 1000;
+ tv_divide(afi->chunks_total, &total_time, &afi->chunk_tv);
+ rewind(file);
+ PARA_DEBUG_LOG("%lu chunks, each %lums\n", afi->chunks_total,
+ tv2ms(&afi->chunk_tv));
+ tv_scale(3, &afi->chunk_tv, &afi->eof_tv);
+ PARA_DEBUG_LOG("eof timeout: %lu\n", tv2ms(&afi->eof_tv));
return 1;
err_out:
PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret));
- free(chunk_table);
+ free(afi->chunk_table);
return ret;
}
/*
* Read mp3 information from audio file
*/
-static int mp3_get_file_info(FILE *audio_file, char *info_str,
- long unsigned *frames, int *seconds, size_t **vss_chunk_table)
+static int mp3_get_file_info(FILE *file, struct audio_format_info *afi)
{
int ret;
- if (!audio_file)
- return -E_MP3_NO_FILE;
- infile = audio_file;
- ret = mp3_read_info();
- if (ret < 0) {
- infile = NULL;
+ ret = mp3_read_info(file, afi);
+ if (ret < 0)
return ret;
- }
- write_info_str(info_str);
- *frames = num_chunks;
- *seconds = mp3.seconds;
- *vss_chunk_table = chunk_table;
- if (*seconds < 2 || !*frames)
+ write_info_str(afi);
+ if (afi->seconds_total < 2 || !afi->chunks_total)
return -E_MP3_INFO;
return 1;
}
-static void mp3_close_audio_file(void)
-{
- if (!infile)
- return;
- fclose(infile);
- infile = NULL;
- free(chunk_table);
-}
-
static const char* mp3_suffixes[] = {"mp3", NULL};
/**
* the init function of the mp3 audio format handler
*
- * \param p pointer to the struct to initialize
+ * \param afh pointer to the struct to initialize
*/
-void mp3_init(struct audio_format_handler *p)
+void mp3_init(struct audio_format_handler *afh)
{
- af = p;
- af->get_file_info = mp3_get_file_info;
- af->close_audio_file = mp3_close_audio_file;
- af->get_header_info = NULL;
- /* eof_tv gets overwritten in mp3_get_file_info() */
- af->eof_tv.tv_sec = 0;
- af->eof_tv.tv_usec = 100 * 1000;
- af->suffixes = mp3_suffixes;
+ afh->get_file_info = mp3_get_file_info;
+ afh->suffixes = mp3_suffixes;
}
#include "server.cmdline.h"
#include "server.h"
#include "vss.h"
-#include "afh.h"
#include "error.h"
#include "string.h"
#define CHUNK_SIZE 32768
static double chunk_time = 0.25;
-static OggVorbis_File *oggvorbis_file;
-static int header_len;
-static char *header;
-static ssize_t *chunk_table;
-static struct audio_format_handler *af;
+static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
+{
+ FILE *f = datasource;
+ return fread(buf, size, nmemb, f);
+}
+
+static int cb_seek(__a_unused void *datasource, ogg_int64_t offset,
+ int whence)
+{
+ FILE *f = datasource;
+ return fseek(f, offset, whence);
+}
+
+/* don't do anything as vss still needs the open filehandle */
+static int cb_close(__a_unused void *datasource)
+{
+ return 0;
+}
+
+static long cb_tell(void *datasource)
+{
+ FILE *f = datasource;
+ return ftell(f);
+}
+
+static int ogg_open_callbacks(void *datasource, OggVorbis_File *vf, ov_callbacks c)
+{
+ int ret = ov_open_callbacks(datasource, vf,
+ NULL, /* no initial buffer */
+ 0, /* no initial bytes */
+ c); /* the ov_open_callbacks */
+
+ /* FIXME: provide better error codes */
+ if (ret == OV_EREAD)
+ return -E_OGG_READ;
+ if (ret == OV_ENOTVORBIS)
+ return -E_OGG_READ;
+ if (ret == OV_EVERSION)
+ return -E_OGG_READ;
+ if (ret == OV_EBADHEADER)
+ return -E_OGG_READ;
+ if (ret < 0)
+ return -E_OGG_READ;
+ return 1;
+
+}
+
+static int ogg_save_header(FILE *file, struct audio_format_info *afi)
+{
+ int ret;
+
+ afi->header = para_malloc(afi->header_len);
+ rewind(file);
+ ret = read(fileno(file), afi->header, afi->header_len);
+ if (ret == afi->header_len)
+ return 1;
+ free(afi->header);
+ return -E_OGG_READ;
+}
-static int ogg_compute_header_len(FILE *file)
+static int ogg_compute_header_len(FILE *file, struct audio_format_info *afi)
{
int ret, len, in = fileno(file);
unsigned int serial;
ogg_stream_packetout(stream_in, &packet);
ogg_stream_packetin(stream_out, &packet);
- header_len = 0;
+ afi->header_len = 0;
while (ogg_stream_flush(stream_out, &page))
- header_len += page.body_len + page.header_len;
- ret = len;
- PARA_INFO_LOG("header_len = %d\n", header_len);
+ afi->header_len += page.body_len + page.header_len;
+ PARA_INFO_LOG("header_len = %d\n", afi->header_len);
+ ret = ogg_save_header(file, afi);
err2:
ogg_stream_destroy(stream_in);
ogg_stream_destroy(stream_out);
return ret;
}
-static void tunetable(long unsigned num_chunks)
-{
- int i = 1, j = -1, lp = 1;
- while (i < num_chunks) {
- if (chunk_table[i] == chunk_table[lp]) {
- i++;
- continue;
- }
- if (j < 0)
- tv_scale(i, &af->chunk_tv, &af->eof_tv);
- for (j = lp; j < i; j++)
- chunk_table[j] = chunk_table[i];
- lp = i;
- }
-#if 1
- for (i = 2; i < num_chunks; i++)
- if (chunk_table[i] != chunk_table[1])
- break;
- lp = i;
- for (i = 2; i < num_chunks - lp; i++)
- chunk_table[i] = chunk_table[i + lp];
-#endif
-}
-
-
/*
* Alloc and fill array table of byte offsets. chunk_table[i] is the
* offset in the current input file at which the sample containing time i *
- * CHUNK_TIME begins.
+ * CHUNK_TIME begins. Always successful.
*/
-static long unsigned ogg_compute_chunk_table(double time_total)
+static long unsigned ogg_compute_chunk_table(OggVorbis_File *of,
+ struct audio_format_info *afi, double time_total)
{
int i, ret, num;
ssize_t max_chunk_len, pos = 0, min = 0, old_pos;
num = time_total / chunk_time + 3;
PARA_DEBUG_LOG("chunk time: %g allocating %d chunk pointers\n",
chunk_time, num);
- chunk_table = para_malloc(num * sizeof(size_t));
- chunk_table[0] = 0;
+ afi->chunk_table = para_malloc(num * sizeof(size_t));
+ afi->chunk_table[0] = 0;
max_chunk_len = 0;
for (i = 1; ret == 0; i++) {
ogg_int64_t diff;
- ret = ov_time_seek(oggvorbis_file, i * chunk_time);
+ ret = ov_time_seek(of, i * chunk_time);
if (ret)
break;
- pos = ov_raw_tell(oggvorbis_file);
+ pos = ov_raw_tell(of);
diff = pos - old_pos;
max_chunk_len = PARA_MAX(max_chunk_len, diff);
min = (i == 1)? diff : PARA_MIN(min, diff);
- chunk_table[i] = pos;
- if (i < 11 || !((i - 1) % 1000)|| i > num - 11)
- PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %zd, "
- "size: %zd\n", i - 1,
- i * chunk_time, pos, pos - old_pos);
+ afi->chunk_table[i] = pos;
+// if (i < 11 || !((i - 1) % 1000)|| i > num - 11)
+// PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %zd, "
+// "size: %zd\n", i - 1,
+// i * chunk_time, pos, pos - old_pos);
old_pos = pos;
}
num_chunks = i - 1;
- chunk_table[i] = pos;
- tunetable(num_chunks);
+ afi->chunk_table[i] = pos;
PARA_INFO_LOG("%lu chunks (%fs), max chunk: %zd, min chunk: %zd\n",
num_chunks, chunk_time, max_chunk_len, min);
return num_chunks;
}
-static void ogg_close_audio_file(void)
-{
- if (oggvorbis_file) {
- PARA_DEBUG_LOG("%s", "ov_clear\n");
- ov_clear(oggvorbis_file);
- free(oggvorbis_file);
- oggvorbis_file = NULL;
- }
- free(header);
- header = NULL;
- header_len = 0;
- free(chunk_table);
- chunk_table = NULL;
-}
-
-static int ogg_save_header(FILE *file, int len)
-{
- int ret;
-
- header = para_malloc(len);
- rewind(file);
- ret = read(fileno(file), header, len);
- if (ret != len)
- return -E_OGG_READ;
- return 1;
-}
-
/*
* Init oggvorbis file and write some tech data to given pointers.
*/
-static int ogg_get_file_info(FILE *file, char *info_str, long unsigned *frames,
- int *seconds, size_t **vss_chunk_table)
+static int ogg_get_file_info(FILE *file, struct audio_format_info *afi)
{
int ret;
double time_total;
vorbis_info *vi;
ogg_int64_t raw_total;
long vi_sampling_rate, vi_bitrate;
+ OggVorbis_File of;
+ static const ov_callbacks ovc = {
+ .read_func = cb_read,
+ .seek_func = cb_seek,
+ .close_func = cb_close,
+ .tell_func = cb_tell
+ };
if (!file)
return -E_OGG_NO_FILE;
- ret = ogg_compute_header_len(file);
- if (ret < 0)
- return ret;
- ret = ogg_save_header(file, header_len);
+ ret = ogg_compute_header_len(file, afi);
if (ret < 0)
return ret;
rewind(file);
- oggvorbis_file = para_malloc(sizeof(OggVorbis_File));
- ret = ov_open(file, oggvorbis_file, NULL, 0);
- if (ret < 0) {
- free(oggvorbis_file);
- free(header);
- return -E_OGG_OPEN;
- }
+ ret = ogg_open_callbacks(file, &of, ovc);
+ if (ret < 0)
+ goto err;
ret = -E_OGG_INFO;
- vi = ov_info(oggvorbis_file, 0);
+ vi = ov_info(&of, 0);
if (!vi)
goto err;
- time_total = ov_time_total(oggvorbis_file, -1);
- raw_total = ov_raw_total(oggvorbis_file, -1);
- *seconds = time_total;
+ time_total = ov_time_total(&of, -1);
+ raw_total = ov_raw_total(&of, -1);
+ afi->seconds_total = time_total;
vi_sampling_rate = vi->rate;
- vi_bitrate = ov_bitrate(oggvorbis_file, 0);
+ vi_bitrate = ov_bitrate(&of, 0);
rewind(file);
- *frames = ogg_compute_chunk_table(time_total);
+ afi->chunks_total = ogg_compute_chunk_table(&of, afi, time_total);
rewind(file);
- *vss_chunk_table = chunk_table;
- sprintf(info_str, "audio_file_info1:%lu x %lu, %ldkHz, %d channels, %ldkbps\n"
+ sprintf(afi->info_string, "audio_file_info1:%lu x %lu, %ldkHz, "
+ "%d channels, %ldkbps\n"
"audio_file_info2: \n"
"audio_file_info3: \n",
- *frames, (long unsigned) (chunk_time * 1000 * 1000),
+ afi->chunks_total, (long unsigned) (chunk_time * 1000 * 1000),
vi_sampling_rate / 1000, vi->channels, vi_bitrate / 1000
);
rewind(file);
- return 1;
+ afi->chunk_tv.tv_sec = 0;
+ afi->chunk_tv.tv_usec = 250 * 1000;
+ tv_scale(3, &afi->chunk_tv, &afi->eof_tv);
+ ret = 1;
err:
- ogg_close_audio_file();
+ ov_clear(&of); /* keeps the file open */
+ if (ret < 0)
+ free(afi->header);
return ret;
}
-static char *ogg_get_header_info(int *len)
-{
- *len = header_len;
- return header;
-}
-
static const char* ogg_suffixes[] = {"ogg", NULL};
/**
* the init function of the ogg vorbis audio format handler
*
- * \param p pointer to the struct to initialize
+ * \param afh pointer to the struct to initialize
*/
-void ogg_init(struct audio_format_handler *p)
+void ogg_init(struct audio_format_handler *afh)
{
- af = p;
- af->get_file_info = ogg_get_file_info,
- af->close_audio_file = ogg_close_audio_file;
- af->get_header_info = ogg_get_header_info;
- af->chunk_tv.tv_sec = 0;
- af->chunk_tv.tv_usec = 250 * 1000;
- tv_scale(3, &af->chunk_tv, &af->eof_tv);
- af->suffixes = ogg_suffixes;
+ afh->get_file_info = ogg_get_file_info,
+ afh->suffixes = ogg_suffixes;
}
if (conf.daemon_given)
daemon_init();
init_selector();
+// PARA_ERROR_LOG("num: %d\n", mmd->selector_num);
PARA_NOTICE_LOG("%s", "initializing virtual streaming system\n");
vss_init();
mmd->server_pid = getpid();
/** \file server.h common server data structures */
#include "para.h"
+#include "afh.h"
#include <openssl/pem.h>
/** size of the selector_info and audio_file info strings of struct misc_meta_data */
* - The contents are listed in the stat command and have to be up to
* date.
*/
-struct misc_meta_data{
+struct misc_meta_data {
+/** information on the current audio file */
+ struct audio_format_info afi;
/** the size of the current audio file in bytes */
long unsigned int size;
/** the full path of the current audio file */
unsigned int new_vss_status_flags;
/** the number of data chunks sent for the current audio file */
long unsigned chunks_sent;
-/** the number of chunks this audio file contains */
- long unsigned chunks_total;
/** set by the jmp/ff commands to the new position in chunks */
long unsigned repos_request;
/** the number of the chunk currently sent out*/
long unsigned current_chunk;
/** the milliseconds that have been skipped of the current audio file */
long offset;
-/** the length of the audio file in seconds */
- int seconds_total;
/** the time para_server started to stream */
struct timeval stream_start;
-/** a string that gets filled in by the audio format handler */
- char audio_file_info[MMD_INFO_SIZE];
/** the event counter
*
* commands may increase this to force a status update to be sent to all
#include <sys/time.h> /* gettimeofday */
#include "server.cmdline.h"
#include "afs.h"
-#include "afh.h"
#include "vss.h"
#include "send.h"
#include "error.h"
extern struct audio_file_selector selectors[];
extern struct sender senders[];
static char *inbuf;
-static size_t *chunk_table, inbuf_size;
+static size_t inbuf_size;
static FILE *audio_file = NULL;
static int get_file_info(int i)
{
- return afl[i].get_file_info(audio_file, mmd->audio_file_info,
- &mmd->chunks_total, &mmd->seconds_total, &chunk_table);
+ return afl[i].get_file_info(audio_file, &mmd->afi);
}
/**
return 1;
}
-static void get_song(void)
+static void vss_get_audio_file(void)
{
char **sl = selectors[mmd->selector_num].get_audio_file_list(10);
int i;
{
struct timeval tmp;
- tv_scale(mmd->chunks_sent, &afl[mmd->audio_format].chunk_tv, &tmp);
+ tv_scale(mmd->chunks_sent, &mmd->afi.chunk_tv, &tmp);
tv_add(&tmp, &mmd->stream_start, due);
}
return;
}
gettimeofday(&now, NULL);
- tv_add(&af->eof_tv, &now, &eof_barrier);
- af->close_audio_file();
+ tv_add(&mmd->afi.eof_tv, &now, &eof_barrier);
+ fclose(audio_file);
audio_file = NULL;
mmd->audio_format = -1;
af = NULL;
mmd->chunks_sent = 0;
mmd->offset = 0;
- mmd->seconds_total = 0;
+ mmd->afi.seconds_total = 0;
+ free(mmd->afi.chunk_table);
+ mmd->afi.chunk_table = NULL;
+ free(mmd->afi.header);
+ mmd->afi.header = NULL;
tmp = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_AUDIO_INFO1],
status_item_list[SI_AUDIO_INFO2], status_item_list[SI_AUDIO_INFO3]);
- strcpy(mmd->audio_file_info, tmp);
+ strcpy(mmd->afi.info_string, tmp);
free(tmp);
tmp = make_message("%s:\n%s:\n%s:\n", status_item_list[SI_DBINFO1],
status_item_list[SI_DBINFO2], status_item_list[SI_DBINFO3]);
*/
char *vss_get_header(int *header_len)
{
- *header_len = 0;
if (mmd->audio_format < 0)
return NULL;
- if (!afl[mmd->audio_format].get_header_info)
- return NULL;
- return afl[mmd->audio_format].get_header_info(header_len);
+ *header_len = mmd->afi.header_len;
+ return mmd->afi.header;
}
/**
{
if (mmd->audio_format < 0)
return NULL;
- return &afl[mmd->audio_format].chunk_tv;
+ return &mmd->afi.chunk_tv;
}
/**
struct timeval now;
gettimeofday(&now, NULL);
if (!vss_paused() || mmd->chunks_sent)
- tv_add(&af->eof_tv, &now, &eof_barrier);
+ tv_add(&mmd->afi.eof_tv, &now, &eof_barrier);
if (vss_repos())
tv_add(&now, &announce_tv, &data_send_barrier);
if (mmd->new_vss_status_flags & VSS_NOMORE)
if (!ret && !audio_file && vss_playing() &&
!(mmd->new_vss_status_flags & VSS_NOMORE)) {
PARA_DEBUG_LOG("%s", "ready and playing, but no audio file\n");
- get_song();
+ vss_get_audio_file();
goto again;
}
return ret;
/**
* read a chunk of data from the current audio file
*
- * \param current_chunk the chunk number to read
- *
* \return The length of the chunk on success, zero on end of file, negative on
* errors. Note: If the current chunk is of length zero, but the end of the
* file is not yet reached, this function returns -E_EMPTY_CHUNK.
- * */
+ */
ssize_t vss_read_chunk(void)
{
ssize_t len;
int ret;
long unsigned cc = mmd->current_chunk;
- if (cc >= mmd->chunks_total) /* eof */
+ if (cc >= mmd->afi.chunks_total) /* eof */
return 0;
- len = chunk_table[cc + 1] - chunk_table[cc];
+ len = mmd->afi.chunk_table[cc + 1] - mmd->afi.chunk_table[cc];
if (!len) /* nothing to send for this run */
return -E_EMPTY_CHUNK;
- pos = chunk_table[cc];
+ pos = mmd->afi.chunk_table[cc];
if (inbuf_size < len) {
PARA_INFO_LOG("increasing inbuf for chunk #%lu/%lu to %zu bytes\n",
- cc, mmd->chunks_total, len);
+ cc, mmd->afi.chunks_total, len);
inbuf = para_realloc(inbuf, len);
inbuf_size = len;
}
* the current audio format handler to obtain a pointer to the data to be
* sent out as well as its length. This information is then passed to each
* supported sender's send() function which does the actual sending.
- *
- * Return value: Positive return value on success, zero on eof and negative
- * on errors.
*/
void vss_send_chunk(void)
{
if (!mmd->chunks_sent) {
struct timeval tmp;
gettimeofday(&mmd->stream_start, NULL);
- tv_scale(mmd->current_chunk, &af->chunk_tv, &tmp);
+ tv_scale(mmd->current_chunk, &mmd->afi.chunk_tv, &tmp);
mmd->offset = tv2ms(&tmp);
mmd->events++;
}