From 251adf906b2fdc2f6bb5777365739c39f0905877 Mon Sep 17 00:00:00 2001 From: mid <> Date: Thu, 25 Jul 2024 10:52:24 +0300 Subject: [PATCH] README --- README.md | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba8fb99 --- /dev/null +++ b/README.md @@ -0,0 +1,173 @@ +# Eebie + +Eebie is a very small C99 library for reading and writing EBML files, namely Matroska & WebM. + +The library is meant to be embedded directly into another project. + +## Reading example + + #include"eebie/reader.h" + + #include + #include + #include + + static EBMLElementType on_enter_element(EBMLReader *reader, uint64_t id, uint64_t length) { + printf("Found element %lX of size %lu at depth %u\n", id, length, reader->currentDepth); + + if(id == 0x2468AC || id == 0x1A45DFA3) { + // Delve deeper. + return EBML_TREE; + } + + // Don't delve deeper, just get the data. + // Important: the only distinction here is between EBML_TREE and not EBML_TREE. + + return EBML_BINARY; + } + + static uint8_t *accumulationBuffer; + static size_t accumulationBufferLength = 0; + static void on_data_chunk(EBMLReader *reader, const uint8_t *data, size_t length) { + // Add new data into our accumulation buffer. + + accumulationBuffer = realloc(accumulationBuffer, accumulationBufferLength + length); + + memcpy(accumulationBuffer + accumulationBufferLength, data, length); + + accumulationBufferLength += length; + } + + static void on_exit_element(EBMLReader *reader) { + // If needed, the current element ID can be found at `reader->inside.id`. + + // Use accumulated buffer here. + + free(accumulationBuffer); + accumulationBuffer = NULL; + accumulationBufferLength = 0; + } + + int main() { + EBMLReader reader; + ebml_reader_init(&reader); + + // Set userdata here. + reader.ud = NULL; + + // eventEnterElement is the only mandatory event handler. + // It must specify whether an EBML element is a master or a primitive value. + reader.eventEnterElement = on_enter_element; + + // eventDataChunk is called for every *CHUNK* of data of the element that has been read. + // Once the element is exited, the accumulated buffer may be decoded fully. + reader.eventDataChunk = on_data_chunk; + + // eventExitElement is called for every element finished parsing, including masters or primitive values. + reader.eventExitElement = on_exit_element; + + FILE *f = fopen("test.mkv", "rb"); + + size_t i = 0; + uint8_t buffer[4096]; + + while(1) { + + // Fill up buffer + size_t readBytes = fread(buffer + i, 1, sizeof(buffer) - i, f); + + i += readBytes; + + // Use buffer + size_t fedBytes = ebml_reader_feed(&reader, buffer, i); + + // Shift buffer by used amount + memmove(buffer, buffer + fedBytes, sizeof(buffer) - fedBytes); + + i -= fedBytes; + + if(readBytes == 0 && fedBytes == 0) { + // Give up. + + break; + } + + } + } + +## Writing example + + TBA + +## `schemagen` + +Given an EBML schema, `schemagen` generates a specialized header-only parser that uses `EBMLReader` to call handlers for each defined element, decoding them all to their correct types. It does not yet perform validation such as enums or ranges. + +Example using the [Matroska schema](https://github.com/ietf-wg-cellar/matroska-specification/blob/master/ebml_matroska.xml): + + cd schemagen + make + ./schemagen /path/to/the/ebml_matroska.xml Mkv > mkv.h + +The first argument is the path to the schema file. The second argument is the prefix to use in CamelCase. After this, `mkv.h` may be used similarly to EBMLReader: + + #define MKV_PARSER_IMPLEMENTATION + #include"mkv.h" + + #include + + static void on_segment(MkvParser *parser) { + printf("Entered new segment.\n"); + } + + static void on_cluster(MkvParser *parser) { + printf("Entered new cluster.\n"); + } + + static void on_timestamp(MkvParser *parser, uint64_t timestamp) { + printf("Cluster timestamp: %lu\n", timestamp); + } + + int main() { + MkvParser parser; + mkv_parser_init(&parser); + + parser.ud = NULL; + + parser.OnSegment = on_segment; + parser.OnCluster = on_cluster; + parser.OnTimestamp = on_timestamp; + // Many more event handlers can be used. + + FILE *f = fopen("test.mkv", "rb"); + + size_t i = 0; + uint8_t buffer[4096]; + + while(1) { + + // Fill up buffer + size_t readBytes = fread(buffer + i, 1, sizeof(buffer) - i, f); + + i += readBytes; + + // Use buffer + size_t fedBytes = mkv_parser_feed(&parser, buffer, i); + + // Shift buffer by used amount + memmove(buffer, buffer + fedBytes, sizeof(buffer) - fedBytes); + + i -= fedBytes; + + if(readBytes == 0 && fedBytes == 0) { + // Give up. + + break; + } + + } + } + +## License + +Eebie is licensed under the BSD 3-clause license (SPDX identifier "BSD-3-Clause").