* apparent at packetout.
*/
ogg_stream_pagein(stream, &page);
+ PARA_INFO_LOG("ogg page serial: %d\n",
+ ogg_page_serialno(&page));
while (i < 2) {
ret = ogg_stream_packetout(stream, &packet);
if (ret == 0)
break;
if (ret < 0)
return -E_STREAM_PACKETOUT;
- ret = ci->packet_callback(&packet, i + 1, afhi,
+ ret = ci->packet_callback(&packet, i + 1,
+ ogg_page_serialno(&page), afhi,
ci->private_data);
if (ret < 0)
return ret;
ret = -E_STREAM_PACKETOUT;
if (ogg_stream_packetout(&stream, &packet) != 1)
goto out;
- ret = ci->packet_callback(&packet, 0, afhi, ci->private_data);
+ ret = ci->packet_callback(&packet, 0, ogg_page_serialno(&page),
+ afhi, ci->private_data);
if (ret < 0)
goto out;
ret = process_packets_2_and_3(oss, &stream, afhi, ci);
if (ret < 0)
goto out;
- afhi->header_offset = 0;
- afhi->header_len = oss->returned;
ret = 1;
out:
ogg_stream_clear(&stream);
return ret;
}
-static void set_chunk_tv(int num_frames, int num_chunks, int frequency,
+static void set_chunk_tv(int frames_per_chunk, int frequency,
struct timeval *result)
{
- uint64_t x = (uint64_t)num_frames * 1000 * 1000
- / frequency / num_chunks;
+ uint64_t x = (uint64_t)frames_per_chunk * 1000 * 1000 / frequency;
result->tv_sec = x / 1000 / 1000;
result->tv_usec = x % (1000 * 1000);
- PARA_INFO_LOG("%d chunks, chunk time: %lums\n", num_chunks,
- tv2ms(result));
+ PARA_INFO_LOG("%d frames per chunk, chunk time: %lums\n",
+ frames_per_chunk, tv2ms(result));
}
/**
* given by \a map and \a numbytes and passes each packet to the callback
* defined by \a ci.
*
- * If the packet callback indicates success, the chunk table is built. Chunk
- * zero contains the first three ogg packets while all other chunks consist of
- * exactly one ogg page.
+ * If the packet callback indicates success and \a afhi is not \p NULL, the
+ * chunk table is built. Chunk zero contains the first three ogg packets while
+ * all other chunks consist of exactly one ogg page.
*
* \param map Audio file data.
* \param numbytes The length of \a map.
ret = process_ogg_packets(&oss, afhi, ci);
if (ret < 0)
goto out;
+ if (!afhi)
+ goto out;
+ afhi->header_len = oss.returned;
oss.returned = 0;
oss.fill = numbytes;
- /* count ogg packages and get duration of the file */
+ /* count ogg pages and get duration of the file */
for (i = 0; ogg_sync_pageseek(&oss, &op) > 0; i++)
num_frames = ogg_page_granulepos(&op);
PARA_INFO_LOG("%d pages, %llu frames\n", i, num_frames);
}
}
afhi->chunks_total = j;
- set_chunk_tv(num_frames, j, afhi->frequency, &afhi->chunk_tv);
+ set_chunk_tv(frames_per_chunk, afhi->frequency, &afhi->chunk_tv);
ret = 0;
out:
ogg_sync_clear(&oss);
/** Used by the scheduler. */
struct task task;
/** Pointer to the header of the mapped audio file. */
- const char *header_buf;
+ char *header_buf;
/** Length of the audio file header. */
size_t header_len;
/** Time between audio file headers are sent. */
struct timeval next_header_time;
/** Used for the last source pointer of an audio file. */
unsigned char *extra_src_buf;
+ /** Needed for the last slice of the audio file header. */
+ unsigned char *extra_header_buf;
/** Extra slices needed to store largest chunk + header. */
int num_extra_slices;
/** Contains the FEC-encoded data. */
write_u32(buf + 14, g->bytes);
write_u8(buf + 18, fc->current_slice_num);
+ write_u8(buf + 19, 0); /* unused */
write_u16(buf + 20, g->slice_bytes);
write_u8(buf + 22, g->first_chunk? 0 : 1);
write_u8(buf + 23, vsst->header_len? 1 : 0);
- memset(buf + 24, 0, 7);
+ memset(buf + 24, 0, 8);
}
static bool need_audio_header(struct fec_client *fc, struct vss_task *vsst)
fc->src_data = para_realloc(fc->src_data, k * sizeof(char *));
fc->enc_buf = para_realloc(fc->enc_buf, fc->mps);
fc->extra_src_buf = para_realloc(fc->extra_src_buf, fc->mps);
+ fc->extra_header_buf = para_realloc(fc->extra_header_buf, fc->mps);
fc->state = FEC_STATE_READY_TO_RUN;
fc->next_header_time.tv_sec = 0;
return 1;
}
+ static void vss_get_chunk(int chunk_num, struct vss_task *vsst,
+ char **buf, size_t *sz)
+ {
+ /*
+ * Chunk zero is special for header streams: It is the first portion of
+ * the audio file which consists of the audio file header. It may be
+ * arbitrary large due to embedded meta data. Audio format handlers may
+ * replace the header by a stripped one with meta data omitted which is
+ * of bounded size. We always use the stripped header for streaming
+ * rather than the unmodified header (chunk zero).
+ */
+ if (chunk_num == 0 && vsst->header_len > 0) {
+ *buf = vsst->header_buf; /* stripped header */
+ *sz = vsst->header_len;
+ return;
+ }
+ afh_get_chunk(chunk_num, &mmd->afd.afhi, vsst->map, (const char **)buf,
+ sz);
+ }
+
static void compute_group_size(struct vss_task *vsst, struct fec_group *g,
int max_bytes)
{
* of exactly one chunk for these audio formats.
*/
for (i = 0;; i++) {
- const char *buf;
+ char *buf;
size_t len;
int chunk_num = g->first_chunk + i;
break;
if (chunk_num >= mmd->afd.afhi.chunks_total) /* eof */
break;
- afh_get_chunk(chunk_num, &mmd->afd.afhi, vsst->map, &buf, &len);
+ vss_get_chunk(chunk_num, vsst, &buf, &len);
if (g->bytes + len > max_bytes)
break;
/* Include this chunk */
{
int ret, i, k, n, data_slices;
size_t len;
- const char *buf;
+ char *buf;
struct fec_group *g = &fc->group;
if (fc->state == FEC_STATE_NONE) {
fc->current_slice_num = 0;
if (g->num == 0)
set_group_timing(fc, vsst);
-
/* setup header slices */
buf = vsst->header_buf;
for (i = 0; i < g->num_header_slices; i++) {
- fc->src_data[i] = (const unsigned char *)buf;
- buf += g->slice_bytes;
+ if (buf + g->slice_bytes <= vsst->header_buf + vsst->header_len) {
+ fc->src_data[i] = (const unsigned char *)buf;
+ buf += g->slice_bytes;
+ continue;
+ }
+ /*
+ * Can not use vss->header_buf for this slice as it
+ * goes beyond the buffer. This slice will not be fully
+ * used.
+ */
+ uint32_t payload_size = vsst->header_buf
+ + vsst->header_len - buf;
+ memcpy(fc->extra_header_buf, buf, payload_size);
+ if (payload_size < g->slice_bytes)
+ memset(fc->extra_header_buf + payload_size, 0,
+ g->slice_bytes - payload_size);
+ fc->src_data[i] = fc->extra_header_buf;
+ assert(i == g->num_header_slices - 1);
}
/* setup data slices */
- afh_get_chunk(g->first_chunk, &mmd->afd.afhi, vsst->map, &buf, &len);
+ vss_get_chunk(g->first_chunk, vsst, &buf, &len);
for (; i < g->num_header_slices + data_slices; i++) {
if (buf + g->slice_bytes > vsst->map + mmd->size) {
/*
* Can not use the memory mapped audio file for this
- * slice as it goes beyond the map. This slice will not
- * be fully used.
+ * slice as it goes beyond the map.
*/
uint32_t payload_size = vsst->map + mmd->size - buf;
memcpy(fc->extra_src_buf, buf, payload_size);
free(fc->src_data);
free(fc->enc_buf);
free(fc->extra_src_buf);
+ free(fc->extra_header_buf);
fec_free(fc->parms);
free(fc);
}
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;
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);
compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
&mmd->stream_start, &due);
if (tv_diff(&due, now, NULL) <= 0) {
- const char *buf;
+ char *buf;
size_t len;
if (!mmd->chunks_sent) {
* they might have still some data queued which can be sent in
* this case.
*/
- afh_get_chunk(mmd->current_chunk, &mmd->afd.afhi, vsst->map,
- &buf, &len);
+ vss_get_chunk(mmd->current_chunk, vsst, &buf, &len);
for (i = 0; senders[i].name; i++) {
if (!senders[i].send)
continue;
tv_add(&vsst->autoplay_barrier, &vsst->announce_tv,
&vsst->data_send_barrier);
}
+ sprintf(vsst->task.status, "vss task");
register_task(&vsst->task);
}