Skip to content

Commit

Permalink
Merge branch 'feat/v1'
Browse files Browse the repository at this point in the history
  • Loading branch information
hughfenghen committed Oct 31, 2024
2 parents 8c04c08 + 5f2f595 commit 61f0ae2
Show file tree
Hide file tree
Showing 161 changed files with 3,266 additions and 4,353 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_1-decode-video.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ order: 1
The video resources for testing are hosted on github pages, and launching the DEMO may take some network load time.
:::

<code src="./decode-video.tsx"></code>
<code src="./1_1_1-decode-video.tsx"></code>
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_1-decode-video.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ order: 1
测试用的视频资源托管在 github pages,启动 DEMO 可能需要一些网络加载时间
:::

<code src="./decode-video.tsx"></code>
<code src="./1_1_1-decode-video.tsx"></code>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MP4Clip } from '@webav/av-cliper';
import { Button, Divider, Radio } from 'antd';
import React, { useState } from 'react';
import { useState } from 'react';
import { assetsPrefix } from './utils';

const videos = assetsPrefix({
Expand Down
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_2-decode-audio.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ order: 2

# Audio Decoder

<code src="./decode-audio.tsx"></code>
<code src="./1_2_1-decode-audio.tsx"></code>
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_2-decode-audio.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ order: 2

# 解码音频

<code src="./decode-audio.tsx"></code>
<code src="./1_2_1-decode-audio.tsx"></code>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AudioClip, DEFAULT_AUDIO_CONF } from '@webav/av-cliper';
import { AudioClip } from '@webav/av-cliper';
import { Button, Radio } from 'antd';
import React, { useState, useEffect } from 'react';
import { useState, useEffect } from 'react';
import { assetsPrefix } from './utils';

const audios = assetsPrefix({
Expand Down Expand Up @@ -30,7 +30,7 @@ async function start(audioType: keyof typeof audios) {
return;
}

const buf = ctx.createBuffer(2, len, DEFAULT_AUDIO_CONF.sampleRate);
const buf = ctx.createBuffer(2, len, 48000);
buf.copyToChannel(audio[0], 0);
buf.copyToChannel(audio[1], 1);
const source = ctx.createBufferSource();
Expand Down
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_3-decode-image.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ A GIF is composed of multiple still image frames, which can be decoded using the

Or use `ImgClip.tick(time)` to get an image frame at a specified time;

<code src="./decode-image.tsx"></code>
<code src="./1_3_1-decode-image.tsx"></code>

:::info
Note: `ImgClip.meta.duration = Infinity`, GIFs will loop over frames based on the time parameter by default.
Expand Down
2 changes: 1 addition & 1 deletion doc-site/docs/demo/1_3-decode-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ order: 3
动图是由多个静态图片帧组成的,可使用浏览器 API `ImageDecoder` 解码;
或使用 `ImgClip.tick(time)` 获取指定时间的图像帧;

<code src="./decode-image.tsx"></code>
<code src="./1_3_1-decode-image.tsx"></code>

:::info
注意:`ImgClip.meta.duration = Infinity`,动图默认会根据时间参数循环取帧。
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ImgClip } from '@webav/av-cliper';
import { Button, Radio } from 'antd';
import React, { useState } from 'react';
import { useState } from 'react';
import { assetsPrefix } from './utils';

const imgs = assetsPrefix({
Expand Down
148 changes: 1 addition & 147 deletions doc-site/docs/demo/1_4-play-video.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,150 +23,4 @@ The **cost** of this solution is complexity, and using `video` should be preferr

### Play MP4

<code src="./play-mp4.tsx"></code>

### Play HLS(fmp4)Stream

```tsx
import React, { useState, useEffect } from 'react';
import { Slider, Button } from 'antd';
import { MP4Clip, createHLSLoader } from '@webav/av-cliper';
import { assetsPrefix } from './utils';

const videoSrc = assetsPrefix(['video/m3u8/bunny.m3u8']);

let clip;
let mp4Dur;
async function start() {
const hlsLoader = await createHLSLoader(videoSrc[0]);

const [{ stream }] = hlsLoader.load();
clip = new MP4Clip(stream);
const { duration, width, height } = await clip.ready;
mp4Dur = Math.round(duration / 1e6);
}

let timer;
const audioCtx = new AudioContext();
let audioSource;
function play(ctx, initTime, updateTime) {
let curTime = initTime;
let startAt = 0;
let first = true;

stop();
timer = setInterval(async () => {
const { state, video, audio } = await clip.tick(Math.round(curTime));
curTime += (1000 / 30) * 1000;
updateTime(curTime);
if (state === 'done') {
clearInterval(timer);
return;
}
if (video != null && state === 'success') {
ctx.clearRect(0, 0, 900, 500);
ctx.drawImage(video, 0, 0, 900, 500);
video.close();
}

if (first) {
// 首次播放丢弃当前音频数据
// 比如 seek 到 10s,播放的音频数据应该是从第 10s 开始,需要丢弃前面音频数据
first = false;
return;
}

const len = audio[0]?.length ?? 0;
if (len === 0) return;
const buf = audioCtx.createBuffer(2, len, 48000);
buf.copyToChannel(audio[0], 0);
buf.copyToChannel(audio[1], 1);
audioSource = audioCtx.createBufferSource();
audioSource.buffer = buf;
audioSource.connect(audioCtx.destination);
startAt = Math.max(audioCtx.currentTime, startAt);
audioSource.start(startAt);

startAt += buf.duration;
}, 1000 / 30);
}

function stop() {
audioSource?.stop();
clearInterval(timer);
}

export default function UI() {
const [curTime, setCurTime] = useState(0);
const [duration, setDuration] = useState(0);
const [ctx, setCtx] = useState<null | undefined | CanvasRenderingContext2D>();
const [playing, setPlaying] = useState(false);

useEffect(() => {
(async () => {
if (ctx == null) return;
await start();
setDuration(mp4Dur);
preview(0.5);
})();
}, [ctx]);

async function preview(val) {
setPlaying(false);
stop();
setCurTime(val);
const time = val * 1e6;
console.log('preview time:', time);
const { video } = await clip.tick(time);
if (video != null) {
ctx.drawImage(video, 0, 0, 900, 500);
video.close();
}
}

return (
<div>
{duration === 0 ? (
'loading...'
) : (
<div className="flex items-center">
<span>预览</span>
<div className="flex-1 ml-4">
<Slider
min={0}
max={duration}
step={0.1}
value={curTime}
onChange={preview}
/>
</div>
<span className="mx-4">{duration}s</span>
<Button
onClick={() => {
if (playing) {
stop();
} else {
console.log(curTime);
play(ctx, curTime * 1e6, (playTime) => {
setCurTime(playTime / 1e6);
});
}
setPlaying(!playing);
}}
>
{playing ? '暂停' : '播放'}
</Button>
</div>
)}
<canvas
className="w-full"
width={900}
height={500}
ref={(c) => {
setCtx(c?.getContext('2d'));
}}
/>
</div>
);
}
```
<code src="./1_4_1-play-video.tsx"></code>
152 changes: 1 addition & 151 deletions doc-site/docs/demo/1_4-play-video.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,154 +24,4 @@ order: 4

### 播放 MP4

<code src="./play-mp4.tsx"></code>

### 播放 HLS(fmp4) 流

```tsx
import React, { useState, useEffect } from 'react';
import { Slider, Button } from 'antd';
import { MP4Clip, createHLSLoader } from '@webav/av-cliper';
import { assetsPrefix } from './utils';

const videoSrc = assetsPrefix(['video/m3u8/bunny.m3u8']);

let clip;
let mp4Dur;
async function start() {
const hlsLoader = await createHLSLoader(videoSrc[0]);

const [{ stream }] = hlsLoader.load();
clip = new MP4Clip(stream);
const { duration, width, height } = await clip.ready;
mp4Dur = Math.round(duration / 1e6);
}

let timer;
const audioCtx = new AudioContext();
let audioSource;
function play(ctx, initTime, updateTime) {
let curTime = initTime;
let startAt = 0;
let first = true;

stop();
timer = setInterval(async () => {
const { state, video, audio } = await clip.tick(Math.round(curTime));
curTime += (1000 / 30) * 1000;
updateTime(curTime);
if (state === 'done') {
clearInterval(timer);
return;
}
if (video != null && state === 'success') {
ctx.clearRect(0, 0, 900, 500);
ctx.drawImage(video, 0, 0, 900, 500);
video.close();
}

if (first) {
// 首次播放丢弃当前音频数据
// 比如 seek 到 10s,播放的音频数据应该是从第 10s 开始,需要丢弃前面音频数据
first = false;
return;
}

const len = audio[0]?.length ?? 0;
if (len === 0) return;
const buf = audioCtx.createBuffer(2, len, 48000);
buf.copyToChannel(audio[0], 0);
buf.copyToChannel(audio[1], 1);
audioSource = audioCtx.createBufferSource();
audioSource.buffer = buf;
audioSource.connect(audioCtx.destination);
startAt = Math.max(audioCtx.currentTime, startAt);
audioSource.start(startAt);

startAt += buf.duration;
}, 1000 / 30);
}

function stop() {
audioSource?.stop();
clearInterval(timer);
}

export default function UI() {
const [curTime, setCurTime] = useState(0);
const [duration, setDuration] = useState(0);
const [ctx, setCtx] = useState<null | undefined | CanvasRenderingContext2D>();
const [playing, setPlaying] = useState(false);

useEffect(() => {
return () => stop();
}, [stop]);

useEffect(() => {
(async () => {
if (ctx == null) return;
await start();
setDuration(mp4Dur);
preview(0.5);
})();
}, [ctx]);

async function preview(val) {
setPlaying(false);
stop();
setCurTime(val);
const time = val * 1e6;
console.log('preview time:', time);
const { video } = await clip.tick(time);
if (video != null) {
ctx.drawImage(video, 0, 0, 900, 500);
video.close();
}
}

return (
<div>
{duration === 0 ? (
'loading...'
) : (
<div className="flex items-center">
<span>预览</span>
<div className="flex-1 ml-4">
<Slider
min={0}
max={duration}
step={0.1}
value={curTime}
onChange={preview}
/>
</div>
<span className="mx-4">{duration}s</span>
<Button
onClick={() => {
if (playing) {
stop();
} else {
console.log(curTime);
play(ctx, curTime * 1e6, (playTime) => {
setCurTime(playTime / 1e6);
});
}
setPlaying(!playing);
}}
>
{playing ? '暂停' : '播放'}
</Button>
</div>
)}
<canvas
className="w-full"
width={900}
height={500}
ref={(c) => {
setCtx(c?.getContext('2d'));
}}
/>
</div>
);
}
```
<code src="./1_4_1-play-video.tsx"></code>
Loading

0 comments on commit 61f0ae2

Please sign in to comment.