Skip to content

Commit

Permalink
feat: support get video meta data by key
Browse files Browse the repository at this point in the history
  • Loading branch information
mengxiaoxuan committed May 26, 2024
1 parent d22dd21 commit 4f515aa
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 73 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/release-beta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: publish package

on:
workflow_dispatch:

jobs:
publish:
runs-on: macos-12
permissions:
contents: read
packages: write
steps:
- name: Download Artifact
id: download-artifact
uses: dawidd6/action-download-artifact@v2
with:
name: output
search_artifacts: true
workflow: build.yml

- name: npm release
# # Setup .npmrc file to publish to GitHub Packages
uses: actions/setup-node@v3
with:
node-version: '16.x'
registry-url: 'https://registry.npmjs.org'
# # - run: npm install
- run: ls && tar -xvf output.tar.gz && cd output && npm publish --access public --tag beta
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ node_modules
script/ffmpeg-3.4.8
emsdk
dist
.DS_Store
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
基于自定义编译ffmpeg的截帧工具,支持Mp4、Mov、Avi、Webm、Mkv等主流格式。

## API
> 整体调用流程,调用`initCapture`方法,传入worker和wasm路径,返回cheetahCapture对象,调用cheetahCapture上的方法进行mount文件、抽帧、获取元数据等操作,结束后调用free进行释放。
`initCapture``({workerPath, wasmPath}) => Promise<capture>` 初始化worker环境,拉取wasm,返回capture方法
接受参数如下
Expand All @@ -14,18 +15,32 @@
| workerPath | URL / string | woker路径,eg:node_modules/dist/capture.worker.js,因为有woker有同源限制,你可以传递BlobUrl来解决 | y |
| wasmPath | URL / string | wasm路径,eg:node_modules/dist/capture.worker.wasm | y |

`mountFile`: `(file: File) => string` 挂载文件,返回mountFile的`fileName`,接受参数如下
接受参数如下
| 参数 | 类型 |含义 | 是否必须 |
| ---- | ---- |---- |---- |
| path | string | workerfs建立文件目录 | n,默认`/working` |
| file | File / blob | 文件 | y |

为了兼容V0.1.x版本,`capture`方法兼容不调用mountFile可以直接使用,传入mountFile需要的参数,会在capture内部进行mountFile,如果您选择这种方式我们将会主动接管你的生命周期,为你进行内存释放操作。
`capture`: `(args) => void` 在worker里执行截帧方法
接受参数如下
| 参数 | 类型 |含义 | 是否必须 |
| ---- | ---- |---- |---- |
| info | number[] / number | 传递number是按照数目抽帧,传递数组是指定抽帧的时间 | y |
| path | string | workerfs建立文件目录 | n |
| file | File / blob | 文件 | y |
| file | File / blob | 文件 | n,v0.1必须 |
| onChange | (prev: PrevType, now: nowType, info: {width: number, height: number, duration: number}) => void | 当抽帧结果变化的回调 | n |
| onSuccess | (prev: PrevType) => void | 当抽帧结束并成功的回调 | n |
| onError | (errmeg: string) => void | 当抽帧过程出现错误的回调 | n |

例子可以参考 `demo/index.html`
`getMetadata: (args: {info: string; })=> void` 获取视频元数据,具体args参数如下
| 参数 | 类型 |含义 | 是否必须 |
| ---- | ---- |---- |---- |
| info | string | 要获取的元数据的key | y |
| onSuccess | (args: {meta: string}) => void | 读取成功的回调,无论是否有该key都会执行,没有返回的空字符串 | n |

* 使用例子可以参考 `demo/index.html`

## 依赖库&编译工具

Expand Down
37 changes: 25 additions & 12 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,33 @@
console.log('===>file', file);
let startTime = Date.now();
capturePro.then((res) => {
res.capture({
res.mountFile({
file,
info: 11,
onChange: (list={}, now={}, info={}) => {
console.log('==>onchange', list, now, info);
const { width, height, duration } = info;
const img = document.createElement('img');
img.src = now.url;
resultContainer.append(img);
onSuccess: () => {
console.log('===>mountFile success');
res.getMetadata({
info: 'aigc',
onSuccess: (data) => {
console.log('===>getMetadata', data);
}
});
res.capture({
file,
info: 11,
onChange: (list = {}, now = {}, info = {}) => {
console.log('==>onchange', list, now, info);
const { width, height, duration } = info;
const img = document.createElement('img');
img.src = now.url;
resultContainer.append(img);

infoContainer.innerHTML = `耗时:${Date.now() - startTime}ms<br>宽度:${width}<br>高度:${height}<br>时长:${duration}s`;
},
onSuccess: (list) => {
console.log('==>onSuccess', list);
infoContainer.innerHTML = `耗时:${Date.now() - startTime}ms<br>宽度:${width}<br>高度:${height}<br>时长:${duration}s`;
},
onSuccess: (list) => {
console.log('==>onSuccess', list);
res.free({onSuccess: () => {console.log('===free success')}});
}
})
}
})
})
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "cheetah-capture",
"version": "0.1.6",
"version": "0.2.0-beta.1",
"description": "cheetah-capture是基于ffmpeg的wasm截取视频帧工具",
"keywords": [
"ffmpeg",
Expand Down
2 changes: 1 addition & 1 deletion script/build_wasm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ emcc $WEB_CAPTURE_PATH/src/capture.c $FFMPEG_PATH/lib/libavformat.a $FFMPEG_PATH
-s WASM=1 \
-s TOTAL_MEMORY=$TOTAL_MEMORY \
-s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' \
-s EXPORTED_FUNCTIONS='["_main", "_free", "_captureByMs", "_captureByCount"]' \
-s EXPORTED_FUNCTIONS='["_main", "_free", "_captureByMs", "_captureByCount", "_getMetaDataByKey"]' \
-s ASSERTIONS=0 \
-s ALLOW_MEMORY_GROWTH=1 \
-o $WEB_CAPTURE_PATH/dist/capture.worker.js
Expand Down
30 changes: 30 additions & 0 deletions src/capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,36 @@ ImageData **captureByMs(char *ms, char *path, int id)
avformat_close_input(&pFormatCtx);
return dataList;
}
// 获取视频的元数据
char *getMetaDataByKey(const char *key, const char *path) {
AVFormatContext *pFormatCtx = avformat_alloc_context();
if (!pFormatCtx) {
fprintf(stderr, "Could not allocate AVFormatContext.\n");
return NULL;
}

AVCodec *pCodec = NULL;
int videoStream = -1;
AVCodecContext *pCodecCtx = initFileAndGetInfo(pFormatCtx, path, pCodec, &videoStream);

if (!pCodecCtx) {
fprintf(stderr, "Failed to initialize file and get codec context.\n");
avformat_free_context(pFormatCtx);
return NULL;
}

const char *value = dump_metadata(NULL, pFormatCtx->metadata, key, "");
avformat_free_context(pFormatCtx);

if (!value) {
fprintf(stderr, "Failed to get metadata value.\n");
return NULL;
}

// 创建一个字符串副本,因为原始值可能随 pFormatCtx 释放而失效
char *result = strdup(value);
return result;
}
int main(int argc, char const *argv[])
{
av_register_all();
Expand Down
Loading

0 comments on commit 4f515aa

Please sign in to comment.