From ff36b01373c09002a996d456993e826ecdcfdaf1 Mon Sep 17 00:00:00 2001 From: Alfred Gutierrez Date: Mon, 17 Jan 2022 20:02:28 -0800 Subject: [PATCH] feature: add support for displaying chapters (#15) * feat: add initial code for reading chapters * feat: add more fields for supporting chapters and chapter tags. --- src/ffprobe-wasm-wrapper.cpp | 66 ++++++++++++++++++++++++++++++++- www/package-lock.json | 2 +- www/package.json | 2 +- www/public/ffprobe-worker.js | 14 +++++++ www/src/components/Overview.vue | 10 +++++ 5 files changed, 91 insertions(+), 3 deletions(-) diff --git a/src/ffprobe-wasm-wrapper.cpp b/src/ffprobe-wasm-wrapper.cpp index d8fc1a2..950f062 100644 --- a/src/ffprobe-wasm-wrapper.cpp +++ b/src/ffprobe-wasm-wrapper.cpp @@ -11,6 +11,7 @@ extern "C" { #include #include #include +#include #include }; @@ -43,6 +44,19 @@ typedef struct Stream { int frame_size; } Stream; +typedef struct Tag { + std::string key; + std::string value; +} Tag; + +typedef struct Chapter { + int id; + std::string time_base; + int start; + int end; + std::vector tags; +} Chapter; + typedef struct Frame { int frame_number; char pict_type; @@ -60,6 +74,8 @@ typedef struct FileInfoResponse { int nb_streams; int flags; std::vector streams; + int nb_chapters; + std::vector chapters; } FileInfoResponse; typedef struct FramesResponse { @@ -103,7 +119,8 @@ FileInfoResponse get_file_info(std::string filename) { .duration = (int)pFormatContext->duration, .url = pFormatContext->url, .nb_streams = (int)pFormatContext->nb_streams, - .flags = pFormatContext->flags + .flags = pFormatContext->flags, + .nb_chapters = (int)pFormatContext->nb_chapters }; // Loop through the streams. @@ -138,6 +155,36 @@ FileInfoResponse get_file_info(std::string filename) { r.streams.push_back(stream); free(fourcc); } + + // Loop through the chapters (if any). + for (int i = 0; i < pFormatContext->nb_chapters; i++) { + AVChapter *chapter = pFormatContext->chapters[i]; + + // Format timebase string to buf. + AVBPrint buf; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&buf, "%d%s%d", chapter->time_base.num, (char *)"/", chapter->time_base.den); + + Chapter c = { + .id = (int)chapter->id, + .time_base = buf.str, + .start = (int)chapter->start, + .end = (int)chapter->end, + }; + + // Add tags to chapter. + const AVDictionaryEntry *tag = NULL; + while ((tag = av_dict_get(chapter->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { + Tag t = { + .key = tag->key, + .value = tag->value, + }; + c.tags.push_back(t); + } + + r.chapters.push_back(c); + } + avformat_close_input(&pFormatContext); return r; } @@ -293,6 +340,21 @@ EMSCRIPTEN_BINDINGS(structs) { ; register_vector("Stream"); + emscripten::value_object("Tag") + .field("key", &Tag::key) + .field("value", &Tag::value) + ; + register_vector("Tag"); + + emscripten::value_object("Chapter") + .field("id", &Chapter::id) + .field("time_base", &Chapter::time_base) + .field("start", &Chapter::start) + .field("end", &Chapter::end) + .field("tags", &Chapter::tags) + ; + register_vector("Chapter"); + emscripten::value_object("Frame") .field("frame_number", &Frame::frame_number) .field("pict_type", &Frame::pict_type) @@ -310,6 +372,8 @@ EMSCRIPTEN_BINDINGS(structs) { .field("nb_streams", &FileInfoResponse::nb_streams) .field("flags", &FileInfoResponse::flags) .field("streams", &FileInfoResponse::streams) + .field("nb_chapters", &FileInfoResponse::nb_chapters) + .field("chapters", &FileInfoResponse::chapters) ; function("get_file_info", &get_file_info); diff --git a/www/package-lock.json b/www/package-lock.json index 2d4d8c1..c647218 100644 --- a/www/package-lock.json +++ b/www/package-lock.json @@ -1,6 +1,6 @@ { "name": "ffprobe-wasm", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/www/package.json b/www/package.json index 77cec72..0fb35a8 100644 --- a/www/package.json +++ b/www/package.json @@ -1,6 +1,6 @@ { "name": "ffprobe-wasm", - "version": "0.6.0", + "version": "0.7.0", "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", diff --git a/www/public/ffprobe-worker.js b/www/public/ffprobe-worker.js index 33c32e4..4b33fc2 100644 --- a/www/public/ffprobe-worker.js +++ b/www/public/ffprobe-worker.js @@ -21,6 +21,19 @@ onmessage = (e) => { s.push(info.streams.get(i)); } + // Remap chapters into collection. + const c = []; + for (let i = 0; i < info.chapters.size(); i++) { + const t = info.chapters.get(i).tags.get(0); + + // Remap tags too. + const tags = []; + const obj = {}; + obj[t.key] = t.value; + tags.push(obj); + c.push({...info.chapters.get(i), ...{tags}}); + } + const versions = { libavutil: Module.avutil_version(), libavcodec: Module.avcodec_version(), @@ -31,6 +44,7 @@ onmessage = (e) => { data = { ...info, streams: s, + chapters: c, versions, } postMessage(data); diff --git a/www/src/components/Overview.vue b/www/src/components/Overview.vue index 2f47d35..518d2f4 100644 --- a/www/src/components/Overview.vue +++ b/www/src/components/Overview.vue @@ -8,6 +8,15 @@

Streams

+ +
+

Chapters

+ + + +
@@ -25,6 +34,7 @@ export default { url: this.info.url, nb_streams: this.info.nb_streams, flags: this.info.flags, + nb_chapters: this.info.nb_chapters, }, ] },