vorbis allows to add arbitrary many comments to an audio file. These
comments are stored in ogg packet #2, which is part of the audio file
header of ogg vorbis files. Hence ogg/vorbis headers may be arbitrary
large. Since the header is sent periodically, this wastes bandwidth
and may cause FEC parameters to become unnecessary large.
This introductory patch paves the way for replacing ogg packet #2 at
stream time by a dummy packet with does not contain any comments and
is therefore of bounded size.
In order to do so, we need audio file headers which are not necessarily
an unmodified portion of the audio file. Therefore we call back into
the audio format handler code at stream time to compute an appropriate
audio file header. Hence an optional new method ->get_header() is
added to struct audio_format_hander. If it is non-NULL, is is called
instead of using the first header_len bytes of the file.
Unfortunately, this implies that the virtual streaming system must
be aware of the audio format id in order to know which get_header()
method should be called. Thus we store the audio format id in struct
audio_file_data which is passed from the afs process to the server
process. Vss then calls afh_get_header() with the audio format id as
an additional argument.
ATM, the new functionality is not yet used as as no audio format
handler defines ->get_header() yet.
-static int cat_file(void *audio_file_data, struct afh_info *afhi)
+static int cat_file(struct afh_info *afhi, int audio_format_id,
+ void *audio_file_data, size_t audio_file_size)
{
int ret;
struct timeval stream_start;
long unsigned i, first_chunk, last_chunk;
const char *buf;
{
int ret;
struct timeval stream_start;
long unsigned i, first_chunk, last_chunk;
const char *buf;
if (conf.begin_chunk_arg < 0) {
if (-conf.begin_chunk_arg > afhi->chunks_total)
return -ERRNO_TO_PARA_ERROR(EINVAL);
if (conf.begin_chunk_arg < 0) {
if (-conf.begin_chunk_arg > afhi->chunks_total)
return -ERRNO_TO_PARA_ERROR(EINVAL);
return -ERRNO_TO_PARA_ERROR(EINVAL);
if (!afhi->chunks_total)
return 1;
return -ERRNO_TO_PARA_ERROR(EINVAL);
if (!afhi->chunks_total)
return 1;
- afh_get_header(afhi, audio_file_data, &buf, &size);
- if (size && first_chunk && !conf.no_header_given) {
- PARA_INFO_LOG("writing audio file header (%zu bytes)\n", size);
- ret = write(STDOUT_FILENO, buf, size);
- if (ret < 0)
- return ret;
- if (ret != size)
- return -E_AFH_SHORT_WRITE;
+ if (first_chunk > 0 && !conf.no_header_given) {
+ afh_get_header(afhi, audio_format_id, audio_file_data, audio_file_size,
+ &header, &size);
+ if (size > 0) {
+ PARA_INFO_LOG("writing header (%zu bytes)\n", size);
+ ret = write(STDOUT_FILENO, header, size); /* FIXME */
+ afh_free_header(header, audio_format_id);
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -E_AFH_SHORT_WRITE;
+ }
}
PARA_NOTICE_LOG("writing chunks %lu - %lu\n", first_chunk, last_chunk);
gettimeofday(&stream_start, NULL);
}
PARA_NOTICE_LOG("writing chunks %lu - %lu\n", first_chunk, last_chunk);
gettimeofday(&stream_start, NULL);
fd, &afhi);
if (ret < 0)
goto out;
fd, &afhi);
if (ret < 0)
goto out;
audio_format_num = ret;
if (conf.stream_given)
audio_format_num = ret;
if (conf.stream_given)
- ret = cat_file(audio_file_data, &afhi);
+ ret = cat_file(&afhi, audio_format_num,
+ audio_file_data, audio_file_size);
else {
printf("File %d: %s\n", i + 1, conf.inputs[i]);
print_info(audio_format_num, &afhi);
else {
printf("File %d: %s\n", i + 1, conf.inputs[i]);
print_info(audio_format_num, &afhi);
*/
int (*get_file_info)(char *map, size_t numbytes, int fd,
struct afh_info *afi);
*/
int (*get_file_info)(char *map, size_t numbytes, int fd,
struct afh_info *afi);
+
+ void (*get_header)(void *map, size_t mapsize, char **buf, size_t *len);
const char *audio_format_name(int);
void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
void *map, const char **buf, size_t *len);
const char *audio_format_name(int);
void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
void *map, const char **buf, size_t *len);
-void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len);
+uint32_t afh_get_largest_chunk_size(struct afh_info *afhi);
+void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
+ void *map, size_t mapsize, char **buf, size_t *len);
+void afh_free_header(char *header_buf, uint8_t audio_format_id);
* Get the header of an audio file.
*
* \param afhi The audio file handler data describing the file.
* Get the header of an audio file.
*
* \param afhi The audio file handler data describing the file.
+ * \param audio_format_id Determines the audio format handler.
* \param map The data of the audio file.
* \param map The data of the audio file.
+ * \param mapsize The amount of bytes of the mmapped audio file.
* \param buf The length of the header is stored here.
* \param len Points to a buffer containing the header on return.
*
* This function sets \a buf to \p NULL and \a len to zero if \a map or \a
* afhi is \p NULL, or if the current audio format does not need special
* header treatment.
* \param buf The length of the header is stored here.
* \param len Points to a buffer containing the header on return.
*
* This function sets \a buf to \p NULL and \a len to zero if \a map or \a
* afhi is \p NULL, or if the current audio format does not need special
* header treatment.
+ *
+ * Otherwise, it is checked whether the audio format handler given by
+ * \a audio_format_id defines a ->get_header() method. If it does, this
+ * method is called to obtain the header. If ->get_header() is \p NULL,
+ * a reference to the first chunk of the audio file is returned.
+ *
+ * Once the header is no longer needed, the caller must call \ref
+ * afh_free_header() to free the resources allocated by this function.
-void afh_get_header(struct afh_info *afhi, void *map, const char **buf, size_t *len)
+void afh_get_header(struct afh_info *afhi, uint8_t audio_format_id,
+ void *map, size_t mapsize, char **buf, size_t *len)
+ struct audio_format_handler *afh = afl + audio_format_id;
+
if (!map || !afhi || !afhi->header_len) {
*buf = NULL;
*len = 0;
return;
}
if (!map || !afhi || !afhi->header_len) {
*buf = NULL;
*len = 0;
return;
}
- *len = afhi->header_len;
- *buf = map;
+ if (!afh->get_header) {
+ *len = afhi->header_len;
+ *buf = map;
+ return;
+ }
+ afh->get_header(map, mapsize, buf, len);
+}
+
+/**
+ * Deallocate any resources obtained from afh_get_header().
+ *
+ * \param header_buf Pointer obtained via afh_get_header().
+ * \param audio_format_id Determines the audio format handler.
+ */
+void afh_free_header(char *header_buf, uint8_t audio_format_id)
+{
+ struct audio_format_handler *afh = afl + audio_format_id;
+
+ if (afh->get_header)
+ free(header_buf);
struct afh_info afhi;
/** Size of the largest chunk. */
uint32_t max_chunk_size;
struct afh_info afhi;
/** Size of the largest chunk. */
uint32_t max_chunk_size;
+ /** Needed to get the audio file header. */
+ uint8_t audio_format_id;
new_afsi.last_played = time(NULL);
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
new_afsi.last_played = time(NULL);
save_afsi(&new_afsi, &afsi_obj); /* in-place update */
+ afd->audio_format_id = old_afsi.audio_format_id;
load_chunk_table(&afd->afhi, chunk_table_obj.data);
ret = make_status_items(afd, &old_afsi, path, score, file_hash);
if (ret < 0)
load_chunk_table(&afd->afhi, chunk_table_obj.data);
ret = make_status_items(afd, &old_afsi, path, score, file_hash);
if (ret < 0)
/** Used by the scheduler. */
struct task task;
/** Pointer to the header of the mapped audio file. */
/** Used by the scheduler. */
struct task task;
/** Pointer to the header of the mapped audio file. */
- const char *header_buf;
/** Length of the audio file header. */
size_t header_len;
/** Time between audio file headers are sent. */
/** Length of the audio file header. */
size_t header_len;
/** Time between audio file headers are sent. */
if (mmd->new_vss_status_flags & VSS_NOMORE)
mmd->new_vss_status_flags = VSS_NEXT;
set_eof_barrier(vsst);
if (mmd->new_vss_status_flags & VSS_NOMORE)
mmd->new_vss_status_flags = VSS_NEXT;
set_eof_barrier(vsst);
+ afh_free_header(vsst->header_buf, mmd->afd.audio_format_id);
+ vsst->header_buf = NULL;
para_munmap(vsst->map, mmd->size);
vsst->map = NULL;
mmd->chunks_sent = 0;
para_munmap(vsst->map, mmd->size);
vsst->map = NULL;
mmd->chunks_sent = 0;
mmd->events++;
mmd->num_played++;
mmd->new_vss_status_flags &= (~VSS_NEXT);
mmd->events++;
mmd->num_played++;
mmd->new_vss_status_flags &= (~VSS_NEXT);
- afh_get_header(&mmd->afd.afhi, vsst->map, &vsst->header_buf,
- &vsst->header_len);
+ afh_get_header(&mmd->afd.afhi, mmd->afd.audio_format_id,
+ vsst->map, mmd->size, &vsst->header_buf, &vsst->header_len);
return;
err:
free(mmd->afd.afhi.chunk_table);
return;
err:
free(mmd->afd.afhi.chunk_table);