Renumber the pages past the OpusTags packet

We will soon be able to process OpusTags packets spanning multiple pages, which would offset the
page number of all the succeeding pages. This change prepares the process loop for that feature.
This commit is contained in:
Frédéric Mangano 2023-01-27 15:17:00 +09:00
parent bbe03f8030
commit d88498e4fd
5 changed files with 68 additions and 15 deletions

View File

@ -358,6 +358,13 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
{
bool focused = false; /*< the stream on which we operate is defined */
int focused_serialno; /*< when focused, the serialno of the focused stream */
/** When the number of pages the OpusTags packet takes differs from the input stream to the
* output stream, we need to renumber all the succeeding pages. If the input stream
* contains gaps, the offset will naively reproduce the gaps: page numbers 0 (1) 2 4 will
* become 0 (1 2) 3 5, where () is the OpusTags packet, and not 0 (1 2) 3 4. */
int pageno_offset = 0;
while (reader.next_page()) {
auto serialno = ogg_page_serialno(&reader.page);
auto pageno = ogg_page_pageno(&reader.page);
@ -384,11 +391,13 @@ static void process(ot::ogg_reader& reader, ot::ogg_writer* writer, const ot::op
}
auto packet = ot::render_tags(tags);
writer->write_header_packet(serialno, pageno, packet);
pageno_offset = writer->next_page_no - 1 - reader.absolute_page_no;
} else {
ot::print_comments(tags.comments, stdout, opt.raw);
break;
}
} else if (writer) {
ot::renumber_page(reader.page, pageno + pageno_offset);
writer->write_page(reader.page);
}
}

View File

@ -86,6 +86,12 @@ void ot::ogg_writer::write_page(const ogg_page& page)
{
if (page.header_len < 0 || page.body_len < 0)
throw status {st::int_overflow, "Overflowing page length"};
long pageno = ogg_page_pageno(&page);
if (pageno != next_page_no)
fprintf(stderr, "Output page number mismatch: expected %ld, got %ld.\n", next_page_no, pageno);
next_page_no = pageno + 1;
auto header_len = static_cast<size_t>(page.header_len);
auto body_len = static_cast<size_t>(page.body_len);
if (fwrite(page.header, 1, header_len, file) < header_len)
@ -116,3 +122,16 @@ void ot::ogg_writer::write_header_packet(int serialno, int pageno, ogg_packet& p
if (ogg_stream_check(&stream) != 0)
throw status {st::libogg_error, "ogg_stream_check failed"};
}
void ot::renumber_page(ogg_page& page, long new_pageno)
{
// Quick optimization: dont bother recomputing the CRC if the pageno did not change.
long old_pageno = ogg_page_pageno(&page);
if (old_pageno == new_pageno)
return;
/** The pageno field is located at bytes 18 to 21 (0-indexed, little-endian). */
uint32_t le_pageno = htole32(new_pageno);
memcpy(&page.header[18], &le_pageno, 4);
ogg_page_checksum_set(&page);
}

View File

@ -25,20 +25,6 @@
#include <string.h>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
#define htole32(x) OSSwapHostToLittleInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#endif
ot::opus_tags ot::parse_tags(const ogg_packet& packet)
{
if (packet.bytes < 0)

View File

@ -39,6 +39,20 @@
#include <string_view>
#include <vector>
#ifdef HAVE_ENDIAN_H
# include <endian.h>
#endif
#ifdef HAVE_SYS_ENDIAN_H
# include <sys/endian.h>
#endif
#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
#define htole32(x) OSSwapHostToLittleInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
#endif
namespace ot {
/**
@ -299,6 +313,11 @@ struct ogg_writer {
* Path to the output file.
*/
std::optional<std::string> path;
/**
* Custom counter for the sequential page number to be written. It allows us to detect
* ogg_page_pageno mismatches and renumber the pages if needed.
*/
long next_page_no = 0;
};
/**
@ -318,6 +337,9 @@ private:
std::unique_ptr<unsigned char[]> data;
};
/** Update the Ogg pageno field in the given page. The CRC is recomputed if needed. */
void renumber_page(ogg_page& page, long new_pageno);
/** \} */
/***********************************************************************************************//**

View File

@ -139,12 +139,29 @@ void check_identification()
throw failure("was not the beginning of a stream");
}
void check_renumber_page()
{
ot::file input = fopen("gobble.opus", "r");
if (input == nullptr)
throw failure("could not open gobble.opus");
ot::ogg_reader reader(input.get());
if (reader.next_page() != true)
throw failure("could not read the first page");
long new_pageno = 1234;
ot::renumber_page(reader.page, new_pageno);
if (ogg_page_pageno(&reader.page) != new_pageno)
throw failure("renumbering failed");
}
int main(int argc, char **argv)
{
std::cout << "1..4\n";
std::cout << "1..5\n";
run(check_ref_ogg, "check a reference ogg stream");
run(check_memory_ogg, "build and check a fresh stream");
run(check_bad_stream, "read a non-ogg stream");
run(check_identification, "stream identification");
run(check_renumber_page, "page renumbering");
return 0;
}