diff --git a/src/cli.cc b/src/cli.cc index 97ed8b6..5fb6900 100644 --- a/src/cli.cc +++ b/src/cli.cc @@ -267,10 +267,6 @@ ot::status ot::process(ogg_reader& reader, ogg_writer& writer, const ot::options } // Initialize the streams from the first page. if (packet_count == -1) { - if (ogg_stream_init(&reader.stream, ogg_page_serialno(&reader.page)) == -1) { - error = "ogg_stream_init: couldn't create a decoder"; - break; - } if (writer.file) { if (ogg_stream_init(&writer.stream, ogg_page_serialno(&reader.page)) == -1) { error = "ogg_stream_init: couldn't create an encoder"; @@ -279,12 +275,8 @@ ot::status ot::process(ogg_reader& reader, ogg_writer& writer, const ot::options } packet_count = 0; } - if (ogg_stream_pagein(&reader.stream, &reader.page) == -1) { - error = "ogg_stream_pagein: invalid page"; - break; - } // Read all the packets. - while (ogg_stream_packetout(&reader.stream, &reader.packet) == 1) { + while ((rc = reader.read_packet()) == ot::status::ok) { packet_count++; if (packet_count == 1) { // Identification header rc = ot::validate_identification_header(reader.packet); @@ -310,8 +302,8 @@ ot::status ot::process(ogg_reader& reader, ogg_writer& writer, const ot::options } } } - if (ogg_stream_check(&reader.stream) != 0) { - error = "ogg_stream_check: internal error (decoder)"; + if (rc != ot::status::ok && rc != ot::status::end_of_page) { + error = "error reading the ogg packets"; break; } // Write the page. diff --git a/src/error.cc b/src/error.cc index f5ed14e..65c4b21 100644 --- a/src/error.cc +++ b/src/error.cc @@ -8,6 +8,8 @@ static const char* messages[] = { "Integer overflow", nullptr, /* standard error: call stderror */ "End of file", + "End of page", + "Stream is not ready", "libogg error", "Invalid identification header, not an Opus stream", "Invalid comment header", diff --git a/src/ogg.cc b/src/ogg.cc index 8d5a5a3..03fa170 100644 --- a/src/ogg.cc +++ b/src/ogg.cc @@ -6,13 +6,13 @@ ot::ogg_reader::ogg_reader(FILE* input) : file(input) { ogg_sync_init(&sync); - memset(&stream, 0, sizeof(stream)); } ot::ogg_reader::~ogg_reader() { + if (stream_ready) + ogg_stream_clear(&stream); ogg_sync_clear(&sync); - ogg_stream_clear(&stream); } ot::status ot::ogg_reader::read_page() @@ -31,9 +31,31 @@ ot::status ot::ogg_reader::read_page() if (ogg_sync_check(&sync) != 0) return status::libogg_error; } + if (!stream_ready) { + if (ogg_stream_init(&stream, ogg_page_serialno(&page)) == -1) + return status::libogg_error; + stream_ready = true; + } + stream_in_sync = false; return status::ok; } +ot::status ot::ogg_reader::read_packet() +{ + if (!stream_ready) + return status::stream_not_ready; + /* If the stream is on a different page, feed the current page. */ + if (!stream_in_sync) { + if (ogg_stream_pagein(&stream, &page) == -1) + return status::libogg_error; + stream_in_sync = true; + } + int rc = ogg_stream_packetout(&stream, &packet); + return rc == 0 ? status::end_of_page : + rc == 1 ? status::ok : + status::libogg_error; +} + ot::ogg_writer::ogg_writer(FILE* output) : file(output) { diff --git a/src/opustags.h b/src/opustags.h index 02919f0..19002d7 100644 --- a/src/opustags.h +++ b/src/opustags.h @@ -35,6 +35,8 @@ enum class status { /** On standard error, errno will give more details. */ standard_error, end_of_file, + end_of_page, + stream_not_ready, libogg_error, bad_identification_header, bad_comment_header, @@ -92,6 +94,13 @@ struct ogg_reader { * call to #read_page. Make sure you also check #status::end_of_file. */ status read_page(); + /** + * Read the next available packet from the current #page. The packet is available in the + * #packet field. + * + * If the end of page was reached, return #status::end_of_page. + */ + status read_packet(); /** * The file is our source of binary data. It is not integrated to libogg, so we need to * handle it ourselves. @@ -114,6 +123,22 @@ struct ogg_reader { * to ogg_sync_pageout. */ ogg_page page; + /** + * Current packet from the stream state. + * + * Its memory is managed by libogg, inside the stream state, and is valid until the next + * call to ogg_stream_packetout. + */ + ogg_packet packet; +private: + /** + * Indicates whether the stream has been initialized or not. + */ + bool stream_ready = false; + /** + * Indicates if the stream's last fed page is the current one. + */ + bool stream_in_sync; /** * The stream layer receives pages and yields a sequence of packets. * @@ -124,13 +149,6 @@ struct ogg_reader { * After we've read OpusHead and OpusTags, we don't need the stream layer anymore. */ ogg_stream_state stream; - /** - * Current packet from the stream state. - * - * Its memory is managed by libogg, inside the stream state, and is valid until the next - * call to ogg_stream_packetout. - */ - ogg_packet packet; }; struct ogg_writer {