forked from forks/ffprobe-wasm
Add stream metadata tags. (#17)
This commit is contained in:
@@ -27,6 +27,11 @@ const std::string c_avutil_version() {
|
|||||||
return AV_STRINGIFY(LIBAVUTIL_VERSION);
|
return AV_STRINGIFY(LIBAVUTIL_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct Tag {
|
||||||
|
std::string key;
|
||||||
|
std::string value;
|
||||||
|
} Tag;
|
||||||
|
|
||||||
typedef struct Stream {
|
typedef struct Stream {
|
||||||
int id;
|
int id;
|
||||||
int start_time;
|
int start_time;
|
||||||
@@ -42,13 +47,9 @@ typedef struct Stream {
|
|||||||
int channels;
|
int channels;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
int frame_size;
|
int frame_size;
|
||||||
|
std::vector<Tag> tags;
|
||||||
} Stream;
|
} Stream;
|
||||||
|
|
||||||
typedef struct Tag {
|
|
||||||
std::string key;
|
|
||||||
std::string value;
|
|
||||||
} Tag;
|
|
||||||
|
|
||||||
typedef struct Chapter {
|
typedef struct Chapter {
|
||||||
int id;
|
int id;
|
||||||
std::string time_base;
|
std::string time_base;
|
||||||
@@ -152,6 +153,17 @@ FileInfoResponse get_file_info(std::string filename) {
|
|||||||
.sample_rate = (int)pLocalCodecParameters->sample_rate,
|
.sample_rate = (int)pLocalCodecParameters->sample_rate,
|
||||||
.frame_size = (int)pLocalCodecParameters->frame_size,
|
.frame_size = (int)pLocalCodecParameters->frame_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add tags to stream.
|
||||||
|
const AVDictionaryEntry *tag = NULL;
|
||||||
|
while ((tag = av_dict_get(pFormatContext->streams[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||||
|
Tag t = {
|
||||||
|
.key = tag->key,
|
||||||
|
.value = tag->value,
|
||||||
|
};
|
||||||
|
stream.tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
r.streams.push_back(stream);
|
r.streams.push_back(stream);
|
||||||
free(fourcc);
|
free(fourcc);
|
||||||
}
|
}
|
||||||
@@ -322,6 +334,12 @@ EMSCRIPTEN_BINDINGS(constants) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(structs) {
|
EMSCRIPTEN_BINDINGS(structs) {
|
||||||
|
emscripten::value_object<Tag>("Tag")
|
||||||
|
.field("key", &Tag::key)
|
||||||
|
.field("value", &Tag::value)
|
||||||
|
;
|
||||||
|
register_vector<Tag>("Tag");
|
||||||
|
|
||||||
emscripten::value_object<Stream>("Stream")
|
emscripten::value_object<Stream>("Stream")
|
||||||
.field("id", &Stream::id)
|
.field("id", &Stream::id)
|
||||||
.field("start_time", &Stream::start_time)
|
.field("start_time", &Stream::start_time)
|
||||||
@@ -337,15 +355,10 @@ EMSCRIPTEN_BINDINGS(structs) {
|
|||||||
.field("channels", &Stream::channels)
|
.field("channels", &Stream::channels)
|
||||||
.field("sample_rate", &Stream::sample_rate)
|
.field("sample_rate", &Stream::sample_rate)
|
||||||
.field("frame_size", &Stream::frame_size)
|
.field("frame_size", &Stream::frame_size)
|
||||||
|
.field("tags", &Stream::tags)
|
||||||
;
|
;
|
||||||
register_vector<Stream>("Stream");
|
register_vector<Stream>("Stream");
|
||||||
|
|
||||||
emscripten::value_object<Tag>("Tag")
|
|
||||||
.field("key", &Tag::key)
|
|
||||||
.field("value", &Tag::value)
|
|
||||||
;
|
|
||||||
register_vector<Tag>("Tag");
|
|
||||||
|
|
||||||
emscripten::value_object<Chapter>("Chapter")
|
emscripten::value_object<Chapter>("Chapter")
|
||||||
.field("id", &Chapter::id)
|
.field("id", &Chapter::id)
|
||||||
.field("time_base", &Chapter::time_base)
|
.field("time_base", &Chapter::time_base)
|
||||||
|
|||||||
@@ -18,19 +18,22 @@ onmessage = (e) => {
|
|||||||
// Remap streams into collection.
|
// Remap streams into collection.
|
||||||
const s = [];
|
const s = [];
|
||||||
for (let i = 0; i < info.streams.size(); i++) {
|
for (let i = 0; i < info.streams.size(); i++) {
|
||||||
s.push(info.streams.get(i));
|
const tags = {};
|
||||||
|
for (let j = 0; j < info.streams.get(i).tags.size(); j++) {
|
||||||
|
const t = info.streams.get(i).tags.get(j);
|
||||||
|
tags[t.key] = t.value;
|
||||||
|
}
|
||||||
|
s.push({...info.streams.get(i), ...{ tags}});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remap chapters into collection.
|
// Remap chapters into collection.
|
||||||
const c = [];
|
const c = [];
|
||||||
for (let i = 0; i < info.chapters.size(); i++) {
|
for (let i = 0; i < info.chapters.size(); i++) {
|
||||||
const t = info.chapters.get(i).tags.get(0);
|
const tags = {};
|
||||||
|
for (let j = 0; j < info.chapters.get(i).tags.size(); j++) {
|
||||||
// Remap tags too.
|
const t = info.chapters.get(i).tags.get(j);
|
||||||
const tags = [];
|
tags[t.key] = t.value;
|
||||||
const obj = {};
|
}
|
||||||
obj[t.key] = t.value;
|
|
||||||
tags.push(obj);
|
|
||||||
c.push({...info.chapters.get(i), ...{tags}});
|
c.push({...info.chapters.get(i), ...{tags}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,41 @@
|
|||||||
<b-table stacked :items="items"></b-table>
|
<b-table stacked :items="items"></b-table>
|
||||||
|
|
||||||
<h4>Streams</h4>
|
<h4>Streams</h4>
|
||||||
<b-table striped hover :items="info.streams"></b-table>
|
<b-table striped hover :items="info.streams" :fields="stream_fields">
|
||||||
|
<template #cell(show_details)="row">
|
||||||
|
<b-button size="sm" @click="row.toggleDetails" class="mr-2">
|
||||||
|
{{ row.detailsShowing ? 'Hide' : 'Show'}}
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
<template #row-details="row">
|
||||||
|
<b-card>
|
||||||
|
<div v-for="(value, key) in row.item.tags" :key="key">
|
||||||
|
<b-row class="mb-2">
|
||||||
|
<b-col sm="3" class="text-sm-right"><b>{{ key }}:</b></b-col>
|
||||||
|
<b-col>{{ value }}</b-col>
|
||||||
|
</b-row>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
|
||||||
<div v-show="info.chapters.length > 0">
|
<div v-show="info.chapters.length > 0">
|
||||||
<h4>Chapters</h4>
|
<h4>Chapters</h4>
|
||||||
<b-table striped hover :items="info.chapters">
|
<b-table striped hover :items="info.chapters" :fields="chapter_fields">
|
||||||
<template #cell(tags)="data">
|
<template #cell(show_details)="row">
|
||||||
<b-table-lite small stacked outlined :items="data.item.tags"></b-table-lite>
|
<b-button size="sm" @click="row.toggleDetails" class="mr-2">
|
||||||
|
{{ row.detailsShowing ? 'Hide' : 'Show'}}
|
||||||
|
</b-button>
|
||||||
|
</template>
|
||||||
|
<template #row-details="row">
|
||||||
|
<b-card>
|
||||||
|
<div v-for="(value, key) in row.item.tags" :key="key">
|
||||||
|
<b-row class="mb-2">
|
||||||
|
<b-col sm="3" class="text-sm-right"><b>{{ key }}:</b></b-col>
|
||||||
|
<b-col>{{ value }}</b-col>
|
||||||
|
</b-row>
|
||||||
|
</div>
|
||||||
|
</b-card>
|
||||||
</template>
|
</template>
|
||||||
</b-table>
|
</b-table>
|
||||||
</div>
|
</div>
|
||||||
@@ -24,6 +52,28 @@
|
|||||||
export default {
|
export default {
|
||||||
name: 'Overview',
|
name: 'Overview',
|
||||||
props: ['info'],
|
props: ['info'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
stream_fields: [
|
||||||
|
'id',
|
||||||
|
'start_time',
|
||||||
|
'duration',
|
||||||
|
'codec_type',
|
||||||
|
'codec_name',
|
||||||
|
'format',
|
||||||
|
'bit_rate',
|
||||||
|
'profile',
|
||||||
|
'level',
|
||||||
|
'width',
|
||||||
|
'height',
|
||||||
|
'channels',
|
||||||
|
'sample_rate',
|
||||||
|
'frame_size',
|
||||||
|
{ key: 'show_details', label: 'Tags' },
|
||||||
|
],
|
||||||
|
chapter_fields: ['id', 'time_base', 'start', 'end', { key: 'show_details', label: 'Tags' }],
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
items() {
|
items() {
|
||||||
return [
|
return [
|
||||||
|
|||||||
Reference in New Issue
Block a user