From 7b59c6078d59073e50be72f6eaee2100a26a2337 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Mon, 17 Jul 2017 23:07:06 +0200 Subject: [PATCH] opus_afh: Use custom header API to strip comment packet. Currently we define the header of an ogg/opus file as the first two ogg pages. This is problematic for two reasons: (a) the metadata packet (ogg packet #2) may be arbitrary large and should not be sent over the network as it is not needed for decoding the audio stream, and (b) the second ogg page may contain, in addition to the metadata packet, opus encoded audio packets which should not be part of the header. This patch employs the recently added custom header API to avoid both problems in the same way the ogg/vorbis audio format handler does: we create a custom header which contains the unmodified ID header packet and a dummy metadata packet. --- opus_afh.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/opus_afh.c b/opus_afh.c index bdc4a96d..53419243 100644 --- a/opus_afh.c +++ b/opus_afh.c @@ -84,6 +84,14 @@ static int opus_get_comments(char *comments, int length, return 1; } +/* + * Ogg/Opus has two mandatory header packets: + * + * 1. ID header (identifies the stream as Opus). Dedicated "BOS" ogg page. + * 2. Comment header (metadata). May span multiple pages. + * + * See doc/draft-ietf-codec-oggopus.xml in the opus source tree for details. + */ static int opus_packet_callback(ogg_packet *packet, int packet_num, __a_unused int serial, struct afh_info *afhi, void *private_data) @@ -227,6 +235,65 @@ static int opus_rewrite_tags(const char *map, size_t mapsize, return ret; } +/* + * See doc/draft-ietf-codec-oggopus.xml in the opus source tree for details + * about the format of the comment header. + */ +static int opus_get_header_callback(ogg_packet *packet, int packet_num, + int serial, __a_unused struct afh_info *afhi, void *private_data) +{ + struct oac_custom_header *h = private_data; + int ret; + static unsigned char dummy_tags[] = { /* a minimal comment header */ + 'O', 'p', 'u', 's', 'T', 'a', 'g', 's', + 0x06, 0x00, 0x00, 0x00, /* vendor string length */ + 'd', 'u', 'm', 'm', 'y', '\0', /* vendor string */ + 0x00, 0x00, 0x00, 0x00, /* user comment list length */ + }; + ogg_packet replacement; + + if (packet_num == 0) { + oac_custom_header_init(serial, h); + ret = oac_custom_header_append(packet, h); + if (ret < 0) + return ret; + oac_custom_header_flush(h); + return 1; + } + assert(packet_num == 1); + PARA_INFO_LOG("replacing metadata packet\n"); + replacement = *packet; + replacement.packet = dummy_tags; + replacement.bytes = sizeof(dummy_tags); + ret = oac_custom_header_append(&replacement, h); + if (ret < 0) + return ret; + oac_custom_header_flush(h); + return 0; +} + +static void opus_get_header(void *map, size_t mapsize, char **buf, + size_t *len) +{ + int ret; + struct oac_custom_header *h = oac_custom_header_new(); + struct oac_callback_info cb = { + .packet_callback = opus_get_header_callback, + .private_data = h, + }; + + ret = oac_get_file_info(map, mapsize, NULL, &cb); + *len = oac_custom_header_get(buf, h); + if (ret < 0) { + PARA_ERROR_LOG("could not create custom header: %s\n", + para_strerror(-ret)); + free(*buf); + *buf = NULL; + *len = 0; + } else + PARA_INFO_LOG("created %zu byte ogg/opus header\n", *len); +} + /** * The init function of the ogg/opus audio format handler. * @@ -235,6 +302,7 @@ static int opus_rewrite_tags(const char *map, size_t mapsize, void opus_afh_init(struct audio_format_handler *afh) { afh->get_file_info = opus_get_file_info, + afh->get_header = opus_get_header; afh->suffixes = opus_suffixes; afh->rewrite_tags = opus_rewrite_tags; } -- 2.39.2