From 23049a7ff69b2f62d1696c63729dc1dc2f646961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Sat, 8 Dec 2018 11:24:17 -0500 Subject: [PATCH] introduce ot::is_opus_stream --- src/ogg.cc | 9 +++++++++ src/opustags.h | 7 +++++++ t/ogg.cc | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/ogg.cc b/src/ogg.cc index a8fd51f..456da4a 100644 --- a/src/ogg.cc +++ b/src/ogg.cc @@ -14,6 +14,15 @@ using namespace std::literals::string_literals; +bool ot::is_opus_stream(const ogg_page& identification_header) +{ + if (ogg_page_bos(&identification_header) == 0) + return false; + if (identification_header.body_len < 8) + return false; + return (memcmp(identification_header.body, "OpusHead", 8) == 0); +} + ot::status ot::ogg_reader::read_page() { while (ogg_sync_pageout(&sync, &page) != 1) { diff --git a/src/opustags.h b/src/opustags.h index 26c0b96..e5455bd 100644 --- a/src/opustags.h +++ b/src/opustags.h @@ -146,6 +146,13 @@ struct ogg_logical_stream : ogg_stream_state { } }; +/** + * Identify the codec of a logical stream based on the first bytes of the first packet of the first + * page. For Opus, the first 8 bytes must be OpusHead. Any other signature is assumed to be another + * codec. + */ +bool is_opus_stream(const ogg_page& identification_header); + /** * Ogg reader, combining a FILE input, an ogg_sync_state reading the pages. * diff --git a/t/ogg.cc b/t/ogg.cc index f1c4d4c..d1ac483 100644 --- a/t/ogg.cc +++ b/t/ogg.cc @@ -14,6 +14,8 @@ static void check_ref_ogg() ot::status rc = reader.read_page(); if (rc != ot::st::ok) throw failure("could not read the first page"); + if (!ot::is_opus_stream(reader.page)) + throw failure("failed to identify the stream as opus"); rc = reader.read_header_packet([](ogg_packet& p) { if (p.bytes != 19) throw failure("unexpected length for the first packet"); @@ -114,10 +116,44 @@ static void check_memory_ogg() } } +void check_identification() +{ + auto good_header = (unsigned char*) + "\x4f\x67\x67\x53\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x42\xf2" + "\xe6\xc7\x00\x00\x00\x00\x7e\xc3\x57\x2b\x01\x13"; + auto good_body = (unsigned char*) "OpusHeadABCD"; + + ogg_page id; + id.header = good_header; + id.header_len = 28; + id.body = good_body; + id.body_len = 12; + if (!ot::is_opus_stream(id)) + throw failure("could not identify opus header"); + + // Bad body + id.body_len = 7; + if (ot::is_opus_stream(id)) + throw failure("opus header was too short to be valid"); + id.body_len = 12; + id.body = (unsigned char*) "Not_OpusHead"; + if (ot::is_opus_stream(id)) + throw failure("was not an opus header"); + id.body = good_body; + + // Remove the BoS bit from the header. + id.header = (unsigned char*) + "\x4f\x67\x67\x53\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\xf2" + "\xe6\xc7\x00\x00\x00\x00\x7e\xc3\x57\x2b\x01\x13"; + if (ot::is_opus_stream(id)) + throw failure("was not the beginning of a stream"); +} + int main(int argc, char **argv) { - std::cout << "1..2\n"; + std::cout << "1..3\n"; run(check_ref_ogg, "check a reference ogg stream"); run(check_memory_ogg, "build and check a fresh stream"); + run(check_identification, "stream identification"); return 0; }