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:
|
dist/ffmpeg-webtools.js:
|
||||||
mkdir -p dist && \
|
mkdir -p dist && \
|
||||||
emcc \
|
emcc --bind \
|
||||||
--bind \
|
|
||||||
-L/opt/ffmpeg/lib \
|
-L/opt/ffmpeg/lib \
|
||||||
-I/opt/ffmpeg/include/ \
|
-I/opt/ffmpeg/include/ \
|
||||||
-s EXTRA_EXPORTED_RUNTIME_METHODS="[FS, cwrap, ccall, getValue, setValue, writeAsciiToMemory]" \
|
-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.h>
|
||||||
#include <emscripten/bind.h>
|
#include <emscripten/bind.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace emscripten;
|
using namespace emscripten;
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavutil/avutil.h>
|
#include <libavutil/avutil.h>
|
||||||
|
};
|
||||||
|
|
||||||
const std::string c_avformat_version() {
|
const std::string c_avformat_version() {
|
||||||
return AV_STRINGIFY(LIBAVFORMAT_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;
|
std::string name;
|
||||||
int bit_rate;
|
int bit_rate;
|
||||||
int duration;
|
int duration;
|
||||||
|
std::string url;
|
||||||
|
int nb_streams;
|
||||||
|
int flags;
|
||||||
|
std::vector<Stream> streams;
|
||||||
};
|
};
|
||||||
|
typedef struct FileInfoResponse FileInfoResponse;
|
||||||
|
|
||||||
Response c_openfile() {
|
FileInfoResponse get_file_info() {
|
||||||
FILE *file = fopen("testingfs", "rb");
|
FILE *file = fopen("file", "rb");
|
||||||
if (!file) {
|
if (!file) {
|
||||||
printf("cannot open file\n");
|
printf("cannot open file\n");
|
||||||
}
|
}
|
||||||
@@ -33,40 +44,65 @@ Response c_openfile() {
|
|||||||
if (!pFormatContext) {
|
if (!pFormatContext) {
|
||||||
printf("ERROR: could not allocate memory for Format Context\n");
|
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.
|
// Open the file and read header.
|
||||||
int ret;
|
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("ERROR: could not open the file. Error: %d\n", ret);
|
||||||
printf("%s", av_err2str(ret));
|
printf("%s", av_err2str(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("format: %s, duration: %lld us, bit_rate: %lld\n",
|
// Initialize response struct with format data.
|
||||||
pFormatContext->iformat->name,
|
FileInfoResponse r = {
|
||||||
pFormatContext->duration,
|
.name = pFormatContext->iformat->name,
|
||||||
pFormatContext->bit_rate);
|
.bit_rate = (int)pFormatContext->bit_rate,
|
||||||
|
.duration = (int)pFormatContext->duration,
|
||||||
|
.url = pFormatContext->url,
|
||||||
|
.nb_streams = (int)pFormatContext->nb_streams,
|
||||||
|
.flags = pFormatContext->flags
|
||||||
|
};
|
||||||
|
|
||||||
Response r;
|
// Get streams data.
|
||||||
r.name = pFormatContext->iformat->name;
|
AVCodec *pCodec = NULL;
|
||||||
r.duration = pFormatContext->duration;
|
AVCodecParameters *pCodecParameters = NULL;
|
||||||
r.bit_rate = pFormatContext->bit_rate;
|
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;
|
return r;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(my_constant_example) {
|
EMSCRIPTEN_BINDINGS(my_constant_example) {
|
||||||
function("c_avformat_version", &c_avformat_version);
|
function("c_avformat_version", &c_avformat_version);
|
||||||
}
|
}
|
||||||
|
|
||||||
EMSCRIPTEN_BINDINGS(a_struct) {
|
EMSCRIPTEN_BINDINGS(FileInfoResponse_struct) {
|
||||||
emscripten::value_object<Response>("Response")
|
emscripten::value_object<Stream>("Stream")
|
||||||
.field("name", &Response::name)
|
.field("id", &Stream::id)
|
||||||
.field("duration", &Response::duration)
|
.field("start_time", &Stream::start_time)
|
||||||
.field("bit_rate", &Response::bit_rate)
|
.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 v-if="data">
|
||||||
<div class="mt-3">Selected file: {{ file ? `${file.name}: ${file.size} bytes` : '' }}</div>
|
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Overview from './Overview.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'File',
|
name: 'File',
|
||||||
components: {
|
components: {
|
||||||
|
Overview,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -44,18 +50,10 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
avformat_version() {
|
avformat_version() {
|
||||||
// return window.Module.ccall('c_avformat_version', 'string');
|
|
||||||
return window.Module.c_avformat_version();
|
return window.Module.c_avformat_version();
|
||||||
},
|
},
|
||||||
info() {
|
info() {
|
||||||
const data = this.data;
|
return this.data && window.Module.get_file_info();
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -71,6 +69,7 @@ export default {
|
|||||||
reader.onload = (event) => {
|
reader.onload = (event) => {
|
||||||
this.progress = 100;
|
this.progress = 100;
|
||||||
this.data = new Uint8Array(event.target.result);
|
this.data = new Uint8Array(event.target.result);
|
||||||
|
window.Module.FS.writeFile('file', new Uint8Array(this.data));
|
||||||
setTimeout(() => { this.showProgress = false; }, 2000);
|
setTimeout(() => { this.showProgress = false; }, 2000);
|
||||||
}
|
}
|
||||||
reader.onprogress = (event) => {
|
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'
|
publicPath: process.env.NODE_ENV === 'production'
|
||||||
? '/ffmpeg-webtools/'
|
? '/ffmpeg-webtools/'
|
||||||
: '/',
|
: '/',
|
||||||
|
configureWebpack: {
|
||||||
|
devServer: {
|
||||||
|
headers: {
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user