From 85d1f19df312c38936cf7721d67dcfb7ca034cfc Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 3 Oct 2010 15:37:02 +0200 Subject: [PATCH] Introduce get_header() for ogg/vorbis. Define the get_header() method for the ogg vorbis audio format handler which replaces ogg packet #2 by a dummy packet which contains no meta data. We call ogg_get_file_info() with afhi == NULL which only passes the first three ogg packets to the given callback but does not compute the chunk table. Ogg packet #1 and #3 are copied unmodified into the header. --- error.h | 2 + ogg_afh.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++- ogg_afh_common.c | 19 +++++---- ogg_afh_common.h | 2 +- spx_afh.c | 3 +- 5 files changed, 115 insertions(+), 11 deletions(-) diff --git a/error.h b/error.h index 3dfee0dd..5a2e39b4 100644 --- a/error.h +++ b/error.h @@ -351,6 +351,8 @@ extern const char **para_errlist[]; #define OGG_AFH_ERRORS \ PARA_ERROR(VORBIS, "vorbis synthesis header-in error (not vorbis?)"), \ + PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \ + PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \ #define VSS_ERRORS \ diff --git a/ogg_afh.c b/ogg_afh.c index ef42c497..2837db61 100644 --- a/ogg_afh.c +++ b/ogg_afh.c @@ -21,7 +21,7 @@ struct private_vorbis_data { }; static int vorbis_packet_callback(ogg_packet *packet, int packet_num, - struct afh_info *afhi, void *private_data) + __a_unused int serial, struct afh_info *afhi, void *private_data) { struct private_vorbis_data *pvd = private_data; @@ -65,6 +65,101 @@ static int ogg_vorbis_get_file_info(char *map, size_t numbytes, __a_unused int f return ret; } +struct vorbis_get_header_data { + ogg_stream_state os; + char *buf; + size_t len; +}; + +static void add_ogg_page(ogg_page *og, struct vorbis_get_header_data *vghd) +{ + size_t old_len = vghd->len; + size_t new_len = vghd->len + og->header_len + og->body_len; + char *buf = para_realloc(vghd->buf, new_len), *p = buf + old_len; + + memcpy(p, og->header, og->header_len); + memcpy(p + og->header_len, og->body, og->body_len); + vghd->buf = buf; + vghd->len = new_len; + PARA_DEBUG_LOG("header/body/old/new: %lu/%lu/%zu/%zu\n", + og->header_len, og->body_len, old_len, new_len); +} + +static int vorbis_get_header_callback(ogg_packet *packet, int packet_num, + int serial, __a_unused struct afh_info *afhi, void *private_data) +{ + int ret; + struct vorbis_get_header_data *vghd = private_data; + ogg_page og; + static unsigned char dummy_packet[] = { + 0x03, + 'v', 'o', 'r', 'b', 'i', 's', + 0x06, 0x00, 0x00, 0x00, + 'd', 'u', 'm', 'm', 'y', '\0', + 0x00, 0x00, 0x00, 0x00, /* no comment :) */ + 0xff /* framing bit */ + }; + + PARA_DEBUG_LOG("processing ogg packet #%d\n", packet_num); + if (packet_num > 2) + return 0; + if (packet_num == 0) { + ogg_stream_init(&vghd->os, serial); + ret = -E_OGG_PACKET_IN; + ret = ogg_stream_packetin(&vghd->os, packet); + if (ret < 0) + goto out; + ret = -E_OGG_STREAM_FLUSH; + if (ogg_stream_flush(&vghd->os, &og) == 0) + goto out; + add_ogg_page(&og, vghd); + return 1; + } + if (packet_num == 1) { + PARA_INFO_LOG("replacing metadata packet\n"); + ogg_packet replacement = *packet; + replacement.packet = dummy_packet; + replacement.bytes = sizeof(dummy_packet); + ret = ogg_stream_packetin(&vghd->os, &replacement); + if (ret >= 0) + return 1; + ret = -E_OGG_PACKET_IN; + goto out; + } + ret = -E_OGG_PACKET_IN; + if (ogg_stream_packetin(&vghd->os, packet) < 0) + goto out; + ret = -E_OGG_STREAM_FLUSH; + if (ogg_stream_flush(&vghd->os, &og) == 0) + goto out; + add_ogg_page(&og, vghd); + ret = 0; +out: + ogg_stream_clear(&vghd->os); + return ret; +} + +static void vorbis_get_header(void *map, size_t mapsize, char **buf, + size_t *len) +{ + int ret; + struct vorbis_get_header_data vghd = {.len = 0}; + struct ogg_afh_callback_info cb = { + .packet_callback = vorbis_get_header_callback, + .private_data = &vghd, + }; + + ret = ogg_get_file_info(map, mapsize, NULL, &cb); + if (ret < 0) + goto fail; + *buf = vghd.buf; + *len = vghd.len; + PARA_INFO_LOG("created %zu byte ogg vorbis header\n", *len); + return; +fail: + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); +} + static const char* ogg_suffixes[] = {"ogg", NULL}; /** @@ -74,6 +169,7 @@ static const char* ogg_suffixes[] = {"ogg", NULL}; */ void ogg_init(struct audio_format_handler *afh) { - afh->get_file_info = ogg_vorbis_get_file_info, + afh->get_file_info = ogg_vorbis_get_file_info; + afh->get_header = vorbis_get_header; afh->suffixes = ogg_suffixes; } diff --git a/ogg_afh_common.c b/ogg_afh_common.c index 1deb5cda..ad5963eb 100644 --- a/ogg_afh_common.c +++ b/ogg_afh_common.c @@ -37,13 +37,16 @@ static int process_packets_2_and_3(ogg_sync_state *oss, * 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; @@ -77,14 +80,13 @@ static int process_ogg_packets(ogg_sync_state *oss, struct afh_info *afhi, 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); @@ -110,9 +112,9 @@ static void set_chunk_tv(int num_frames, int num_chunks, int frequency, * 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. @@ -143,6 +145,9 @@ int ogg_get_file_info(char *map, size_t numbytes, struct afh_info *afhi, 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 */ diff --git a/ogg_afh_common.h b/ogg_afh_common.h index 01f8d6c5..89c8268b 100644 --- a/ogg_afh_common.h +++ b/ogg_afh_common.h @@ -24,7 +24,7 @@ struct ogg_afh_callback_info { * reached and no further ogg packets should be processed. */ int (*packet_callback)(ogg_packet *packet, int packet_num, - struct afh_info *afhi, void *private_data); + int serial, struct afh_info *afhi, void *private_data); /** Vorbis/speex specific data. */ void *private_data; }; diff --git a/spx_afh.c b/spx_afh.c index 6414f4c4..e693c415 100644 --- a/spx_afh.c +++ b/spx_afh.c @@ -139,7 +139,8 @@ static int spx_get_comments(unsigned char *comments, int length, static const char* speex_suffixes[] = {"spx", "speex", NULL}; static int spx_packet_callback(ogg_packet *packet, int packet_num, - struct afh_info *afhi, void *private_data) + __a_unused int serial, struct afh_info *afhi, + void *private_data) { struct private_spx_data *psd = private_data; int ret; -- 2.39.2