forked from forks/ffprobe-wasm
Add more bindings for AVFormatContext data. Add Overview page in Vue. Add required CORS headers for supporting SharedArrayBuffer in Firefox.
This commit is contained in:
12
Makefile
12
Makefile
@@ -1,16 +1,6 @@
|
||||
# dist/ffmpeg-webtools.js:
|
||||
# mkdir -p dist && \
|
||||
# emcc -L/opt/ffmpeg/lib -I/opt/ffmpeg/include/ src/main.c \
|
||||
# -s EXPORTED_FUNCTIONS='["_c_avformat_version", "_openfile", "_addOne"]' \
|
||||
# -s EXTRA_EXPORTED_RUNTIME_METHODS="[FS, cwrap, ccall, getValue, setValue, writeAsciiToMemory]" \
|
||||
# -s INITIAL_MEMORY=268435456 \
|
||||
# -lavcodec -lavformat -lavfilter -lavdevice -lswresample -lswscale -lavutil -lm -lx264 -pthread \
|
||||
# -o www/public/ffmpeg-webtools.js
|
||||
|
||||
dist/ffmpeg-webtools.js:
|
||||
mkdir -p dist && \
|
||||
emcc \
|
||||
--bind \
|
||||
emcc --bind \
|
||||
-L/opt/ffmpeg/lib \
|
||||
-I/opt/ffmpeg/include/ \
|
||||
-s EXTRA_EXPORTED_RUNTIME_METHODS="[FS, cwrap, ccall, getValue, setValue, writeAsciiToMemory]" \
|
||||
|
||||
38
src/main.c
38
src/main.c
@@ -1,38 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avutil.h>
|
||||
|
||||
|
||||
void version() {
|
||||
printf("%s\n", AV_STRINGIFY(LIBAVFORMAT_VERSION));
|
||||
}
|
||||
|
||||
void openfile() {
|
||||
FILE *file = fopen("testingfs", "rb");
|
||||
if (!file) {
|
||||
printf("cannot open file\n");
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
AVFormatContext *pFormatContext = avformat_alloc_context();
|
||||
if (!pFormatContext) {
|
||||
printf("ERROR: could not allocate memory for Format Context\n");
|
||||
}
|
||||
|
||||
printf("opening the input file: %s and loading format (container) header\n", "testingfs");
|
||||
|
||||
// Open the file and read header.
|
||||
int ret;
|
||||
if ((ret = avformat_open_input(&pFormatContext, "testingfs", NULL, NULL)) < 0) {
|
||||
printf("ERROR: could not open the file. Error: %d\n", ret);
|
||||
printf("%s", av_err2str(ret));
|
||||
}
|
||||
|
||||
printf("format: %s, duration: %lld us, bit_rate: %lld\n",
|
||||
pFormatContext->iformat->name,
|
||||
pFormatContext->duration,
|
||||
pFormatContext->bit_rate);
|
||||
}
|
||||
88
src/main.cpp
88
src/main.cpp
@@ -1,29 +1,40 @@
|
||||
#include <vector>
|
||||
#include <emscripten.h>
|
||||
#include <emscripten/bind.h>
|
||||
|
||||
|
||||
using namespace emscripten;
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/avutil.h>
|
||||
|
||||
};
|
||||
|
||||
const std::string c_avformat_version() {
|
||||
return AV_STRINGIFY(LIBAVFORMAT_VERSION);
|
||||
}
|
||||
|
||||
struct Response {
|
||||
struct Stream {
|
||||
int id;
|
||||
int start_time;
|
||||
int duration;
|
||||
int codec_type;
|
||||
};
|
||||
typedef struct Stream Stream;
|
||||
|
||||
struct FileInfoResponse {
|
||||
std::string name;
|
||||
int bit_rate;
|
||||
int duration;
|
||||
std::string url;
|
||||
int nb_streams;
|
||||
int flags;
|
||||
std::vector<Stream> streams;
|
||||
};
|
||||
typedef struct FileInfoResponse FileInfoResponse;
|
||||
|
||||
Response c_openfile() {
|
||||
FILE *file = fopen("testingfs", "rb");
|
||||
FileInfoResponse get_file_info() {
|
||||
FILE *file = fopen("file", "rb");
|
||||
if (!file) {
|
||||
printf("cannot open file\n");
|
||||
}
|
||||
@@ -33,40 +44,65 @@ Response c_openfile() {
|
||||
if (!pFormatContext) {
|
||||
printf("ERROR: could not allocate memory for Format Context\n");
|
||||
}
|
||||
printf("%p\n", pFormatContext);
|
||||
|
||||
printf("opening the input file: %s and loading format (container) header\n", "testingfs");
|
||||
|
||||
// Open the file and read header.
|
||||
int ret;
|
||||
if ((ret = avformat_open_input(&pFormatContext, "testingfs", NULL, NULL)) < 0) {
|
||||
if ((ret = avformat_open_input(&pFormatContext, "file", NULL, NULL)) < 0) {
|
||||
printf("ERROR: could not open the file. Error: %d\n", ret);
|
||||
printf("%s", av_err2str(ret));
|
||||
}
|
||||
|
||||
printf("format: %s, duration: %lld us, bit_rate: %lld\n",
|
||||
pFormatContext->iformat->name,
|
||||
pFormatContext->duration,
|
||||
pFormatContext->bit_rate);
|
||||
// Initialize response struct with format data.
|
||||
FileInfoResponse r = {
|
||||
.name = pFormatContext->iformat->name,
|
||||
.bit_rate = (int)pFormatContext->bit_rate,
|
||||
.duration = (int)pFormatContext->duration,
|
||||
.url = pFormatContext->url,
|
||||
.nb_streams = (int)pFormatContext->nb_streams,
|
||||
.flags = pFormatContext->flags
|
||||
};
|
||||
|
||||
Response r;
|
||||
r.name = pFormatContext->iformat->name;
|
||||
r.duration = pFormatContext->duration;
|
||||
r.bit_rate = pFormatContext->bit_rate;
|
||||
// Get streams data.
|
||||
AVCodec *pCodec = NULL;
|
||||
AVCodecParameters *pCodecParameters = NULL;
|
||||
int video_stream_index = -1;
|
||||
|
||||
// Loop through the streams and print its information.
|
||||
for (int i = 0; i < pFormatContext->nb_streams; i++) {
|
||||
AVCodecParameters *pLocalCodecParameters = NULL;
|
||||
pLocalCodecParameters = pFormatContext->streams[i]->codecpar;
|
||||
Stream s = {
|
||||
.id = (int)pFormatContext->streams[i]->id,
|
||||
.start_time = (int)pFormatContext->streams[i]->start_time,
|
||||
.duration = (int)pFormatContext->streams[i]->duration,
|
||||
.codec_type = (int)pLocalCodecParameters->codec_type
|
||||
};
|
||||
r.streams.push_back(s);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
EMSCRIPTEN_BINDINGS(my_constant_example) {
|
||||
function("c_avformat_version", &c_avformat_version);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_BINDINGS(a_struct) {
|
||||
emscripten::value_object<Response>("Response")
|
||||
.field("name", &Response::name)
|
||||
.field("duration", &Response::duration)
|
||||
.field("bit_rate", &Response::bit_rate)
|
||||
EMSCRIPTEN_BINDINGS(FileInfoResponse_struct) {
|
||||
emscripten::value_object<Stream>("Stream")
|
||||
.field("id", &Stream::id)
|
||||
.field("start_time", &Stream::start_time)
|
||||
.field("duration", &Stream::duration)
|
||||
.field("codec_type", &Stream::codec_type)
|
||||
;
|
||||
function("c_openfile", &c_openfile);
|
||||
register_vector<Stream>("Stream");
|
||||
emscripten::value_object<FileInfoResponse>("FileInfoResponse")
|
||||
.field("name", &FileInfoResponse::name)
|
||||
.field("duration", &FileInfoResponse::duration)
|
||||
.field("bit_rate", &FileInfoResponse::bit_rate)
|
||||
.field("url", &FileInfoResponse::url)
|
||||
.field("nb_streams", &FileInfoResponse::nb_streams)
|
||||
.field("flags", &FileInfoResponse::flags)
|
||||
.field("streams", &FileInfoResponse::streams)
|
||||
;
|
||||
function("get_file_info", &get_file_info);
|
||||
}
|
||||
@@ -20,19 +20,25 @@
|
||||
|
||||
<div v-if="data">
|
||||
<div class="mt-3">Selected file: {{ file ? `${file.name}: ${file.size} bytes` : '' }}</div>
|
||||
<hr />
|
||||
{{ info }}
|
||||
|
||||
avformat_version: {{ avformat_version }}
|
||||
<b-tabs class="mt-4">
|
||||
<b-tab title="Overview" class="mt-2">
|
||||
<div v-if="data">
|
||||
<Overview :info="info" />
|
||||
</div>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Overview from './Overview.vue';
|
||||
|
||||
export default {
|
||||
name: 'File',
|
||||
components: {
|
||||
Overview,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -44,18 +50,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
avformat_version() {
|
||||
// return window.Module.ccall('c_avformat_version', 'string');
|
||||
return window.Module.c_avformat_version();
|
||||
},
|
||||
info() {
|
||||
const data = this.data;
|
||||
console.log('writing data to memfs');
|
||||
window.Module.FS.writeFile('testingfs', new Uint8Array(data));
|
||||
console.log('writing data to memfs done');
|
||||
|
||||
// window.Module.ccall('version');
|
||||
// Window.Module.ccall('openfile');
|
||||
return 'info';
|
||||
return this.data && window.Module.get_file_info();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -71,6 +69,7 @@ export default {
|
||||
reader.onload = (event) => {
|
||||
this.progress = 100;
|
||||
this.data = new Uint8Array(event.target.result);
|
||||
window.Module.FS.writeFile('file', new Uint8Array(this.data));
|
||||
setTimeout(() => { this.showProgress = false; }, 2000);
|
||||
}
|
||||
reader.onprogress = (event) => {
|
||||
|
||||
39
www/src/components/Overview.vue
Normal file
39
www/src/components/Overview.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<h4>AVContext Info</h4>
|
||||
<b-table stacked :items="items"></b-table>
|
||||
|
||||
<h4>AVContext Streams</h4>
|
||||
<b-table striped hover :items="streams"></b-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Overview',
|
||||
props: ['info'],
|
||||
computed: {
|
||||
streams() {
|
||||
const s = [];
|
||||
for (let i = 0; i < this.info.streams.size(); i++) {
|
||||
s.push(this.info.streams.get(i));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
items: [
|
||||
{
|
||||
name: this.info.name,
|
||||
duration: this.info.duration,
|
||||
bit_rate: this.info.bit_rate,
|
||||
url: this.info.url,
|
||||
nb_streams: this.info.nb_streams,
|
||||
flags: this.info.flags,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -8,4 +8,12 @@ module.exports = {
|
||||
publicPath: process.env.NODE_ENV === 'production'
|
||||
? '/ffmpeg-webtools/'
|
||||
: '/',
|
||||
configureWebpack: {
|
||||
devServer: {
|
||||
headers: {
|
||||
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user