to implement plugins for other audio formats. Of course, the usual mix
of other improvements/changes/bugfixes also made it into the release.
- - simplified audio format handlers (some of the handling functions
+ - simplified audio format handlers (most of the handling functions
were moved one layer up to the virtual streaming system).
+ - para_server uses mmap to read audio files
- repositioning of mp3 streams is much faster, in particular for
jumping near the end of large mp3 files.
- permission flags DB_READ,DB_WRITE have been renamed to AFS_READ
/** \file aac_afh.c para_server's aac audio format handler */
-#include "server.cmdline.h"
#include "server.h"
-#include "vss.h"
#include "error.h"
#include "string.h"
#include "aac.h"
-#include "fd.h"
static int aac_find_stsz(unsigned char *buf, off_t buflen, off_t *skip)
{
PARA_INFO_LOG("%luHz, %fs (%lu x %lums)\n",
mp4ASC->samplingFrequency, ms / 1000,
afi->chunks_total, tv2ms(&afi->chunk_tv));
- return ms / 1000;
+ return ms < 1000? -E_MP4ASC : ms / 1000;
}
/*
ret = -E_AACDEC_INIT;
if (NeAACDecInit(handle, map + skip, decoder_len, &rate, &channels))
goto out;
+ if (!channels)
+ goto out;
PARA_INFO_LOG("rate: %lu, channels: %d\n", rate, channels);
ret = -E_MP4ASC;
if (NeAACDecAudioSpecificConfig(map + skip, numbytes - skip, &mp4ASC))
goto out;
+ if (!mp4ASC.samplingFrequency)
+ goto out;
ret = aac_compute_chunk_table(afi, map, numbytes);
if (ret < 0)
goto out;
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"
+ afi->channels = channels;
+ afi->frequency = rate;
+ ret = (afi->chunk_table[afi->chunks_total] - afi->chunk_table[0]) * 8; /* bits */
+ ret += (channels * afi->seconds_total * 500); /* avoid rounding error */
+ afi->bitrate = ret / (channels * afi->seconds_total * 1000);
+ sprintf(afi->info_string, "audio_file_info1:%lu x %lums, "
+ "%uHz, %d channel%s, %ukb/s\n"
"audio_file_info2:\n"
"audio_file_info3:\n",
- afi->chunks_total,
- tv2ms(&afi->chunk_tv));
+ afi->chunks_total, tv2ms(&afi->chunk_tv),
+ afi->frequency, channels, channels == 1? "" : "s", afi->bitrate
+ );
tv_scale(20, &afi->chunk_tv, &afi->eof_tv);
ret = 1;
out:
/*
- * Copyright (C) 2005-2006 Andre Noll <maan@systemlinux.org>
+ * Copyright (C) 2005-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
/** 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 */
+ * The header is needed by senders in case a new client connects in the
+ * middle of the stream. The length of the header defaults to zero
+ * which means that this audio format does not need any special header
+ * treatment. The audio format handler does not need to set this to
+ * zero in this case.
+ */
unsigned header_len;
+ /**
+ * The position of the header within the audio file. Ignored if \a
+ * header_len equals zero.
+ */
+ unsigned header_offset;
+ uint8_t channels;
+ uint16_t frequency;
+ uint16_t bitrate;
};
/**
void *ret = mmap(NULL, length, prot, flags, fd, offset);
if (ret != MAP_FAILED)
return ret;
- PARA_EMERG_LOG("mmap failed: %s", strerror(errno));
+ PARA_EMERG_LOG("mmap failed: %s\n", strerror(errno));
+ PARA_EMERG_LOG("length: %zu, flags: %d, fd: %d, offset: %zu\n",
+ length, flags, fd, offset);
exit(EXIT_FAILURE);
}
* Johannes Overmann <overmann@iname.com>
*/
-#include "server.cmdline.h"
#include "server.h"
-#include "vss.h"
#include "error.h"
-#include "fd.h"
#include "string.h"
/** \cond some defines and structs which are only used in this file */
int id3_isvalid;
struct id3tag id3;
int vbr;
- long unsigned br_average;
- int freq;
};
/** \endcond */
return mode_text[h->mode];
}
+static int header_channels(struct mp3header *h)
+{
+ if (h->mode > 3)
+ return 0;
+ if (h->mode < 3)
+ return 2;
+ return 1;
+}
+
static int header_bitrate(struct mp3header *h)
{
if (!h->layer || h->layer > 3 || h->bitrate > 14 || !h->bitrate)
int v = mp3.id3_isvalid;
snprintf(afi->info_string, MMD_INFO_SIZE,
- "audio_file_info1:%lu x %lums, %lu kbit/s (%cbr) %i KHz %s\n"
+ "audio_file_info1:%lu x %lums, %u kbit/s (%cbr) %i KHz %s\n"
"audio_file_info2:%s, by %s\n"
"audio_file_info3:A: %s, Y: %s, C: %s\n",
afi->chunks_total,
tv2ms(&afi->chunk_tv),
- mp3.br_average,
+ afi->bitrate,
mp3.vbr? 'v' : 'c',
- mp3.freq / 1000,
+ afi->frequency / 1000,
header_mode(&mp3.header),
v && *mp3.id3.title? mp3.id3.title : "(title tag not set)",
v && *mp3.id3.artist? mp3.id3.artist : "(artist tag not set)",
mp3_get_id3(map, numbytes, &fpos);
fpos = 0;
mp3.vbr = 0;
- mp3.freq = 0;
while (1) {
int freq, br, fl;
struct timeval tmp, cct; /* current chunk time */
if (!afi->chunks_total || !freq_avg || !br_avg)
goto err_out;
afi->chunk_table[afi->chunks_total] = numbytes - 1;
- mp3.br_average = br_avg;
- mp3.freq = freq_avg;
+ afi->bitrate = br_avg;
+ afi->frequency = freq_avg;
+ afi->channels = header_channels(&mp3.header);
afi->seconds_total = (tv2ms(&total_time) + 500) / 1000;
tv_divide(afi->chunks_total, &total_time, &afi->chunk_tv);
PARA_DEBUG_LOG("%lu chunks, each %lums\n", afi->chunks_total,
return info;
}
-
/* might return NULL */
static char *get_current_audio_file(void)
{
return name;
}
+/* If called as child, mmd_lock must be held */
+static void update_mmd(char *info)
+{
+ PARA_DEBUG_LOG("%s", "updating shared memory area\n");
+ strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1);
+ mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
+}
+
+static void refresh_selector_info(void)
+{
+ char *name = get_current_audio_file();
+ char *info;
+
+ if (!name)
+ return;
+ info = get_selector_info(name);
+ free(name);
+ mmd_lock();
+ update_mmd(info);
+ mmd_unlock();
+ free(info);
+}
+
/* list attributes / print database info */
static int com_la_info(int fd, int argc, char *argv[])
{
*/
int com_snp(int fd, int argc, char *argv[])
{
- return com_set(fd, argc, argv);
+ int ret = com_set(fd, argc, argv);
+ if (ret >= 0)
+ refresh_selector_info();
+ return ret;
}
/*
return ret;
}
-/* If called as child, mmd_lock must be held */
-static void update_mmd(char *info)
-{
- PARA_DEBUG_LOG("%s", "updating shared memory area\n");
- strncpy(mmd->selector_info, info, MMD_INFO_SIZE - 1);
- mmd->selector_info[MMD_INFO_SIZE - 1] = '\0';
-}
-
static void update_audio_file_server_handler(char *name)
{
char *info;
return -E_ESCAPE;
ret = update_audio_file(argv[1]);
free(tmp);
+ if (ret >= 0)
+ refresh_selector_info();
return ret;
}
-static void refresh_selector_info(void)
-{
- char *name = get_current_audio_file();
- char *info;
-
- if (!name)
- return;
- info = get_selector_info(name);
- free(name);
- mmd_lock();
- update_mmd(info);
- mmd_unlock();
- free(info);
-}
-
/* select previous / next stream */
static int com_ps_ns(__a_unused int fd, int argc, char *argv[])
{
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
-#include "server.cmdline.h"
#include "server.h"
-#include "vss.h"
#include "error.h"
#include "string.h"
}
-static void ogg_save_header(char *map, struct audio_format_info *afi)
-{
- afi->header = para_malloc(afi->header_len);
- memcpy(afi->header, map, afi->header_len);
-}
-
static int ogg_compute_header_len(char *map, off_t numbytes,
struct audio_format_info *afi)
{
while (ogg_stream_flush(stream_out, &page))
afi->header_len += page.body_len + page.header_len;
PARA_INFO_LOG("header_len = %d\n", afi->header_len);
- ogg_save_header(map, afi);
+ afi->header_offset = 0;
ret = 1;
err2:
ogg_stream_destroy(stream_in);
num = time_total / chunk_time + 3;
PARA_DEBUG_LOG("chunk time: %g allocating %d chunk pointers\n",
chunk_time, num);
- afi->chunk_table = para_malloc(num * sizeof(size_t));
+ afi->chunk_table = para_malloc((num + 1) * sizeof(size_t));
afi->chunk_table[0] = 0;
max_chunk_len = 0;
- for (i = 1; ret == 0; i++) {
+ for (i = 1; ret <= num; i++) {
ogg_int64_t diff;
ret = ov_time_seek(of, i * chunk_time);
if (ret)
old_pos = pos;
}
num_chunks = i - 1;
- afi->chunk_table[i] = pos;
+//fi->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;
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;
const ov_callbacks ovc = {
.read_func = cb_read,
vi = ov_info(&of, 0);
if (!vi)
goto err;
- 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(&of, 0);
- afi->chunks_total = ogg_compute_chunk_table(&of, afi, time_total);
- sprintf(afi->info_string, "audio_file_info1:%lu x %lu, %ldkHz, "
- "%d channels, %ldkbps\n"
+ afi->seconds_total = ov_time_total(&of, -1);
+ afi->frequency = vi->rate;
+ afi->bitrate = ov_bitrate(&of, 0);
+ afi->channels = vi->channels;
+ afi->chunks_total = ogg_compute_chunk_table(&of, afi, afi->seconds_total);
+ sprintf(afi->info_string, "audio_file_info1:%lu x %lu, %ukHz, "
+ "%d channels, %ukbps\n"
"audio_file_info2: \n"
"audio_file_info3: \n",
afi->chunks_total, (long unsigned) (chunk_time * 1000 * 1000),
- vi_sampling_rate / 1000, vi->channels, vi_bitrate / 1000
+ afi->frequency / 1000, vi->channels, afi->bitrate / 1000
);
afi->chunk_tv.tv_sec = 0;
afi->chunk_tv.tv_usec = 250 * 1000;
ret = 1;
err:
ov_clear(&of); /* keeps the file open */
- if (ret < 0)
- free(afi->header);
return ret;
}
map = para_mmap(file_status.st_size, PROT_READ, MAP_PRIVATE,
audio_file, 0);
strcpy(mmd->filename, sl[i]);
+ mmd->afi.header_len = 0; /* default: no header */
if (update_mmd() < 0) { /* invalid file */
close(audio_file);
munmap(map, mmd->size);
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->afi.info_string, tmp);
*/
char *vss_get_header(int *header_len)
{
- if (mmd->audio_format < 0)
+ if (mmd->audio_format < 0 || !map || !mmd->afi.header_len)
return NULL;
*header_len = mmd->afi.header_len;
- return mmd->afi.header;
+ return map + mmd->afi.header_offset;
}
/**