看前端如何通过WebAssembly实现播放器预览能力
最
近
,
团
队
小
组
内
部
体
验
W
e
b
浏
览
器
上
课
的
音
视
频
播
放
功
能
,
除
了
对
比
同
行
产
品
,
也
对
比
了
主
流
视
频
内
容
的
网
站
平
台
。
计
划
补
齐
和
增
强
与
播
放
体
验
相
关
的
能
力
。
其
中
有
一
项
能
力
在
主
流
媒
体
视
频
网
站
都
支
持
的
,
那
就
是
进
度
条
帧
预
览
:
在
鼠
标
进
度
条
停
留
,
不
必
跳
转
进
度
,
即
可
展
示
所
指
画
面
。
在
简
单
分
析
了
B
站
、
腾
讯
视
频
后
,
发
现
都
是
采
取
在
上
架
视
频
时
,
由
后
台
生
成
专
门
用
来
帧
预
览
的
组
合
s
p
r
i
t
e
图
,
然
后
前
端
拉
取
后
再
计
算
进
度
进
行
展
示
。
由
于
目
前
的
我
们
后
台
云
点
播
录
制
没
有
生
成
帧
预
览
图
功
能
。
另
一
方
面
,
即
便
升
级
可
能
大
量
的
存
量
存
储
视
频
无
法
帧
预
览
。
于
是
我
们
决
定
尝
试
前
端
实
现
动
态
帧
预
览
的
方
案
。
浏
览
器
获
取
视
频
画
面
的
方
法
:
目
前
浏
览
器
视
频
帧
提
取
的
方
案
主
要
有
:
第
一
种
方
案
对
于
单
个
M
P
4
文
件
还
是
合
适
的
,
但
h
l
s
资
源
不
是
完
整
加
载
,
并
且
浏
览
器
不
能
直
接
复
用
t
s
格
式
,
所
以
行
不
通
。
H
L
S
动
态
解
密
t
s
分
片
w
a
s
m
f
f
m
p
e
g
获
取
帧
画
面
的
技
术
方
案
整
体
技
术
方
案
:
①
通
过
解
析
H
L
S
m
a
s
t
e
r
P
l
a
y
L
i
s
t
和
l
e
v
e
l
P
l
a
y
L
i
s
t
,
拿
到
低
清
晰
度
的
t
s
文
件
索
引
数
组
。
②
支
持
区
分
判
断
H
L
S
加
密
,
获
取
解
密
秘
钥
,
A
E
S
解
密
t
s
文
件
数
据
。
③
t
s
文
件
a
r
r
a
y
b
u
f
f
e
r
数
据
,
申
请
内
存
并
写
入
w
a
s
m
,
调
用
w
a
s
m
封
装
截
图
方
法
,
返
回
R
G
B
数
据
。
④
将
R
G
B
数
据
转
为
c
a
n
v
a
s
i
m
a
g
e
d
a
t
a
,
更
新
展
示
帧
画
面
,
并
缓
存
。
监
听
鼠
标
事
件
定
位
帧
缓
存
画
面
,
或
加
载
新
数
据
。
F
F
m
p
e
g
编
译
至
W
e
b
A
s
s
e
m
b
l
y
前
置
准
备
安
装
e
m
s
c
r
i
p
t
e
n
的
e
m
s
d
k
,
实
际
上
会
遇
到
不
少
困
难
。
按
照
e
m
s
c
r
i
p
t
e
n
官
网
的
指
示
一
步
一
步
,
遇
到
阻
碍
及
时
谷
歌
变
更
解
决
。
安
装
完
成
后
执
行
e
m
c
c
–
v
能
查
看
版
本
,
代
表
安
装
成
功
。
# Get the emsdk repo
git clone https://github.com/emscripten-core/emsdk.git
# Enter that directory
cd emsdk
# Download and install the latest SDK tools.
./emsdk install latest
# Make the "latest" SDK "active" for the current user. (writes .emscripten file)
./emsdk activate latest
# Activate PATH and other environment variables in the current terminal
source ./emsdk_env.sh
F
F
m
p
e
g
编
译
F
F
m
p
e
g
是
个
优
秀
的
音
视
频
处
理
库
,
包
含
了
采
集
、
格
式
转
化
、
编
解
码
、
截
图
、
滤
镜
等
能
力
。
我
们
需
要
禁
用
掉
大
部
分
能
力
,
只
编
译
我
们
需
要
的
部
分
,
最
后
编
译
产
物
是
c
依
赖
库
和
相
关
头
文
件
。
emconfigure ./configure \
--prefix=$WEB_CAPTURE_PATH/lib/ffmpeg-emcc \
--cc="emcc" \
--cxx="em++" \
--ar="emar" \
--cpu=generic \
--target-os=none \
--arch=x86_32 \
--enable-gpl \
--enable-version3 \
--enable-cross-compile \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-doc \
--disable-ffserver \
--disable-swresample \
--disable-postproc \
--disable-programs \
--disable-avfilter \
--disable-pthreads \
--disable-w32threads \
--disable-os2threads \
--disable-network \
--disable-logging \
--disable-everything \
--enable-protocol=file \
--enable-demuxer=mpegts \
--enable-decoder=h264 \
--disable-asm \
--disable-debug \
分
析
f
f
m
p
e
g
提
取
帧
流
程
视
频
文
件
数
据
到
帧
的
图
像
数
据
,
按
照
流
程
:
解
格
式
封
装
、
视
频
解
码
,
图
像
数
据
转
换
(
Y
U
V
=
>
R
G
B
)
。
则
按
照
H
L
S
分
片
提
取
图
像
数
据
流
程
,
需
要
涉
及
到
以
下
f
f
m
p
e
g
中
的
库
。
编
译
至
W
a
s
m
最
后
需
要
通
过
e
m
c
c
来
将
d
e
m
u
x
e
r
和
d
e
c
o
d
e
r
和
依
赖
的
相
关
库
编
译
为
w
a
s
m
然
后
提
供
浏
览
器
使
用
j
a
v
a
s
c
r
i
p
t
进
行
调
用
。
编
译
选
项
如
下
:
emcc ./getframe.c ./ffmpeg/lib/libavformat.a ./ffmpeg/lib/libavcodec.a ./ffmpeg/lib/libswscale.a ./ffmpeg/lib/libavutil.a \
-O3 \
-I "./ffmpeg/include" \
-s WASM=1 \
-s TOTAL_MEMORY=33554432 \
-s EXPORTED_FUNCTIONS='["_main", "_free", "_getFrame", "_setFile"]' \
-s ASSERTIONS=1 \
-s ALLOW_MEMORY_GROWTH=1 \
-s MAXIMUM_MEMORY=4GB \
-o getframe.js
e
m
c
c
编
译
选
项
,
请
参
考
:
h
t
t
p
s
:
/
/
e
m
s
c
r
i
p
t
e
n
.
o
r
g
/
d
o
c
s
/
t
o
o
l
s
_
r
e
f
e
r
e
n
c
e
/
e
m
c
c
.
h
t
m
l
最
后
编
译
w
a
s
m
成
功
是
一
个
w
a
s
m
的
二
进
制
文
件
,
和
一
个
胶
水
代
码
j
s
文
件
。
B
l
o
c
k
q
u
o
t
e
E
X
P
O
R
T
E
D
_
F
U
N
C
T
I
O
N
S
:
参
数
告
诉
编
译
器
,
代
码
里
面
需
要
输
出
的
函
数
名
。
函
数
名
前
面
要
加
下
划
线
.
A
S
S
E
R
T
I
O
N
S
:
A
S
S
E
R
T
I
O
N
S
=
1
用
于
为
内
存
分
配
错
误
启
用
运
行
时
检
查
(
例
如
,
写
入
比
分
配
更
多
的
内
存
)
。
它
还
定
义
了
E
m
s
c
r
i
p
t
e
n
如
何
处
理
程
序
流
中
的
错
误
。
可
以
将
值
设
置
为
A
S
S
E
R
T
I
O
N
S
=
2
,
以
便
运
行
额
外
的
测
试
。
A
L
L
O
W
_
M
E
M
O
R
Y
_
G
R
O
W
T
H
:
E
m
s
c
r
i
p
t
e
n
堆
一
经
初
始
化
,
容
量
就
固
定
了
,
无
法
再
扩
容
。
而
某
些
程
序
在
运
行
时
需
要
的
内
存
容
量
在
不
同
工
况
下
可
能
有
很
大
的
波
动
。
为
了
满
足
某
些
极
端
工
况
的
需
求
而
将
T
O
T
A
L
_
M
E
M
O
R
Y
设
置
得
非
常
高
无
疑
是
非
常
浪
费
的
,
为
此
,
E
m
s
c
r
i
p
t
e
n
提
供
了
可
在
运
行
时
扩
大
内
存
容
量
的
模
式
,
欲
开
启
该
模
式
,
需
要
在
编
译
时
增
加
–
s
A
L
L
O
W
_
M
E
M
O
R
Y
_
G
R
O
W
T
H
=
1
参
数
。
封
装
A
P
I
这
里
参
考
了
网
上
一
些
现
成
的
做
法
,
虽
然
可
以
生
成
f
f
m
p
e
g
.
j
s
和
f
f
p
m
e
g
.
w
a
s
m
,
并
提
供
M
o
d
u
l
e
对
象
来
操
控
,
但
是
这
样
J
S
的
数
据
类
型
和
C
的
数
据
类
型
差
异
比
较
多
,
频
繁
地
调
C
的
A
P
I
,
让
数
据
传
来
传
去
比
较
麻
烦
。
这
里
参
考
网
上
的
教
程
、
前
置
封
装
f
f
m
p
e
g
的
A
P
I
,
具
体
参
考
这
里
的
实
现
和
教
程
:
h
t
t
p
s
:
/
/
g
i
t
h
u
b
.
c
o
m
/
l
i
y
i
n
c
h
e
n
g
/
f
f
m
p
e
g
–
w
a
s
m
–
v
i
d
e
o
–
t
o
–
p
i
c
t
u
r
e
h
t
t
p
:
/
/
d
r
a
n
g
e
r
.
c
o
m
/
f
f
m
p
e
g
/
t
u
t
o
r
i
a
l
0
1
.
h
t
m
l
。
f
f
e
m
p
g
可
调
用
函
数
:
h
t
t
p
:
/
/
d
r
a
n
g
e
r
.
c
o
m
/
f
f
m
p
e
g
/
f
u
n
c
t
i
o
n
s
.
h
t
m
l
。
#include <span data-raw-text="<libavcodec/avcodec.h" data-textnode-index="163" data-index="3694">>
#include <span data-raw-text="<libavformat/avformat.h" data-textnode-index="167" data-index="3727">>
#include <span data-raw-text="<libavutil/imgutils.h" data-textnode-index="171" data-index="3758">>
#include <span data-raw-text="<libswscale/swscale.h" data-textnode-index="175" data-index="3789">>
int main(int argc, char const *argv[]) {
av_register_all();
return 0;
}
AVFormatContext *pFormatCtx = NULL;
// Open video file
if(avformat_open_input(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
return -1; // Couldn't open file
// Retrieve stream information
if(avformat_find_stream_info(pFormatCtx, NULL)<span data-raw-text="<0)
return -1; // Couldn't find stream information
AVCodec *pCodec = NULL;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx-" data-textnode-index="212" data-index="4277">>codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
// Copy context
pCodecCtx = avcodec_alloc_context3(pCodec);
if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
fprintf(stderr, "Couldn't copy codec context");
return -1; // Error copying codec context
}
// Open codec
if(avcodec_open2(pCodecCtx, pCodec)<span data-raw-text="<0)
return -1; // Could not open codec
其
中
主
要
步
骤
在
于
,
读
取
整
个
数
据
流
,
方
法
是
读
取
数
据
包
,
将
其
解
码
为
帧
,
一
旦
帧
完
成
,
我
们
将
对
其
进
行
转
换
R
G
B
(
P
I
X
_
F
M
T
_
R
G
B
2
4
)
并
保
存
。
struct SwsContext *sws_ctx = NULL;
int frameFinished;
AVPacket packet;
// initialize SWS context for software scaling
sws_ctx = sws_getContext(pCodecCtx-" data-textnode-index="252" data-index="4894">>width,
pCodecCtx-" data-textnode-index="253" data-index="4915">>height,
pCodecCtx-" data-textnode-index="254" data-index="4937">>pix_fmt,
pCodecCtx-" data-textnode-index="255" data-index="4960">>width,
pCodecCtx-" data-textnode-index="256" data-index="4981">>height,
PIX_FMT_RGB24,
SWS_BILINEAR,
NULL,
NULL,
NULL
);
i=0;
while(av_read_frame(pFormatCtx, &packet)" data-textnode-index="272" data-index="5100">>=0) {
// Is this a packet from the video stream?
if(packet.stream_index==videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);
// Did we get a video frame?
if(frameFinished) {
// Convert the image from its native format to RGB
sws_scale(sws_ctx, (uint8_t const * const *)pFrame-" data-textnode-index="297" data-index="5454">>data,
pFrame-" data-textnode-index="298" data-index="5471">>linesize, 0, pCodecCtx-" data-textnode-index="300" data-index="5495">>height,
pFrameRGB-" data-textnode-index="301" data-index="5517">>data, pFrameRGB-" data-textnode-index="301" data-index="5534">>linesize);
// Save the frame to disk
if(++i<span data-raw-text="<=5)
SaveFrame(pFrameRGB, pCodecCtx-" data-textnode-index="310" data-index="5638">>width,
pCodecCtx-" data-textnode-index="311" data-index="5676">>height, i);
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&packet);
}
j
a
v
a
s
c
r
i
p
t
调
用
w
a
s
m
h
t
t
p
s
:
/
/
w
w
w
.
c
n
t
o
f
u
.
c
o
m
/
b
o
o
k
/
1
5
0
/
z
h
/
c
h
2
–
c
–
j
s
/
c
h
2
–
0
1
–
j
s
–
c
a
l
l
–
c
.
m
d
j
a
v
a
s
c
r
i
p
t
调
用
w
a
s
m
,
简
单
概
括
是
就
是
内
存
的
写
入
与
读
取
的
过
程
。
理
论
上
H
L
S
文
件
拿
到
t
s
分
片
文
件
,
将
文
件
保
存
U
n
i
t
8
A
r
r
a
y
,
并
写
入
到
w
a
s
m
中
。
let tsBuffer = new Uint8Array(tsFileArrayBuffer);
let tsBufferPtr = Module._malloc(tsBuffer.length);
Module.HEAP8.set(tsBuffer, tsBufferPtr);
let imgData = Module._getFrame(tsBufferPtr, tsBuffer.length, time)
去
的
R
G
B
数
据
在
j
s
层
转
化
c
a
n
v
a
s
可
用
数
据
。
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
let imageData = ctx.createImageData(width, height);
let j = 0;
for (let i = 0; i <span data-raw-text="< imageBuffer.length; i++) {
if (i && i % 3 == 0) {
imageData.data[j] = 255;
j += 1;
}
imageData.data[j] = imageBuffer[i];
j += 1;
}
ctx.putImageData(imageData, 0, 0, 0, 0, width, height);
const finalData = canvas.toDataURL('image/jpeg');
H
L
S
动
态
加
载
t
s
分
片
及
解
密
H
L
S
m
a
s
t
e
r
P
l
a
y
L
i
s
t
/
l
e
v
e
l
P
a
l
y
L
i
s
t
解
析
H
L
S
点
播
资
源
并
非
单
文
件
,
而
是
一
个
m
3
u
8
协
议
的
索
引
。
要
取
拿
到
帧
数
据
,
必
须
要
加
载
t
s
分
片
文
件
数
据
。
必
须
先
H
L
S
解
析
m
3
u
8
文
件
。
由
于
我
们
取
帧
图
片
拿
来
做
预
览
,
并
不
需
要
很
大
的
尺
寸
和
清
晰
度
。
当
包
含
多
个
l
e
v
e
l
(
清
晰
度
)
的
情
况
下
,
优
先
选
取
最
低
清
晰
度
的
l
e
v
e
l
P
l
a
y
L
i
s
t
。
M
S
E
H
L
S
解
析
:
一
般
M
S
E
H
L
S
使
用
h
l
s
.
j
s
加
载
视
频
播
放
,
通
过
其
创
建
实
例
(
c
l
i
e
n
t
)
,
在
onManifestParsed
事
件
后
通
过
client.levels
可
以
读
取
到
到
不
同
l
e
v
e
l
的
参
数
。
N
a
t
i
v
e
H
L
S
解
析
:
对
于
移
动
端
浏
览
器
,
或
者
s
a
f
a
r
i
等
浏
览
器
,
使
用
n
a
t
i
v
e
播
放
m
3
u
8
的
模
式
。
我
们
可
以
自
己
解
析
m
3
u
8
的
m
a
s
t
e
r
P
l
a
y
L
i
s
t
,
然
后
自
行
解
析
。
比
如
通
过
B
A
N
D
W
I
D
T
H
和
R
E
S
O
L
U
T
I
O
N
,
取
出
最
低
清
晰
度
,
或
者
可
以
借
助
m3u8-parser
进
行
解
析
。
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=2099325,RESOLUTION=1920x1080
v.f124099.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=197642,RESOLUTION=1280x720
v.f22239.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=142162,RESOLUTION=960x540
v.f22240.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=0,BANDWIDTH=95767,RESOLUTION=480x270
v.f22241.m3u8
获
取
p
l
a
y
L
i
s
t
的
s
e
g
m
e
n
t
s
映
射
时
间
段
我
们
需
要
知
道
定
位
时
间
距
离
最
近
的
s
e
g
m
e
n
t
,
直
接
维
护
最
低
清
晰
度
p
l
a
y
l
i
s
t
数
组
,
根
据
#EXTINF
得
知
每
个
s
e
g
m
e
n
t
的
d
u
r
a
t
i
o
n
时
长
,
计
算
出
总
时
长
,
及
每
个
s
e
g
m
e
n
t
的
开
始
时
间
和
结
束
时
间
。
当
我
们
定
位
到
指
定
时
间
,
即
可
匹
配
到
最
近
的
t
s
文
件
作
为
被
解
析
的
数
据
。
......
#EXTINF:10.000000,
v.f22241.ts?start=260400&end=382047&type=mpegts
#EXT-X-KEY:METHOD=AES-128,URI="http://getkeyurl",IV=0x00000000000000000000000000000000
#EXTINF:10.000000,
v.f22241.ts?start=382048&end=502943&type=mpegts
#EXT-X-KEY:METHOD=AES-128,URI="http://getkeyurl",IV=0x00000000000000000000000000000000
#EXTINF:10.000000,
v.f22241.ts?start=502944&end=623455&type=mpegts
#EXT-X-KEY:METHOD=AES-128,URI="http://getkeyurl",IV=0x00000000000000000000000000000000
#EXTINF:10.000000,
v.f22241.ts?start=623456&end=748303&type=mpegts
.....
A
E
S
解
密
t
s
文
件
获
取
解
密
K
e
y
由
于
点
播
H
L
S
资
源
已
经
进
行
了
加
密
,
t
s
文
件
数
据
无
法
直
接
给
到
w
a
s
m
截
取
帧
画
面
。
所
以
要
对
t
s
进
行
解
密
。
当
解
析
p
l
a
y
l
i
s
t
时
候
匹
配
到
#EXT-X-KEY:METHOD=AES-128
则
需
要
解
密
。
K
E
Y
需
要
从
URI
属
性
的
地
址
请
求
获
取
,
一
般
具
备
登
录
态
的
请
求
正
确
返
回
。
I
V
数
据
直
接
取
p
l
a
y
l
i
s
t
上
的
IV
即
可
。
同
样
的
,
在
M
S
E
H
L
S
播
放
的
,
h
l
s
.
j
s
实
例
上
能
读
取
到
K
E
Y
和
I
V
;
对
于
n
a
t
i
v
e
h
l
s
播
放
的
,
需
要
自
己
二
次
请
求
获
取
。
W
e
b
C
r
y
p
t
o
A
S
E
解
密
参
考
h
l
s
.
j
s
源
码
,
将
请
求
到
的
t
s
分
片
进
行
解
密
。
h
t
t
p
s
:
/
/
g
i
t
h
u
b
.
c
o
m
/
v
i
d
e
o
j
s
/
a
e
s
–
d
e
c
r
y
p
t
e
r
let decrypter = new Decrypter();
const { key, iv } = levelKey;
decrypter.decrypt(data, key, iv, (data) =" data-textnode-index="460" data-index="8607">> {
const finalSegmentFile = new Blob([data], {
type: 'video/mp2t',
});
// 给到wasm写入finalSegmentFile
});
点
播
进
度
帧
预
览
逻
辑
及
缓
存
策
略
动
态
节
流
加
载
,
并
缓
存
至
对
应
时
间
区
间
:
由
于
用
户
的
鼠
标
在
进
度
条
可
能
频
繁
移
动
,
这
里
设
计
应
该
监
听
m
o
u
s
e
m
o
v
e
但
节
流
触
发
。
从
解
析
p
l
a
y
l
i
s
t
开
始
,
到
t
s
文
件
加
载
与
解
密
,
w
a
s
m
解
码
获
取
帧
数
据
拿
到
i
m
a
g
e
d
a
t
a
,
设
置
5
0
0
m
s
触
发
阈
值
,
获
取
帧
图
像
数
据
缓
存
到
对
应
时
间
区
间
。
就
近
读
取
缓
存
帧
画
面
:
一
般
来
说
,
相
邻
进
度
的
帧
画
面
往
往
是
相
似
,
但
加
载
到
解
帧
的
整
个
过
程
异
步
且
存
在
一
定
耗
时
,
优
先
展
示
相
邻
分
片
区
间
的
缓
存
帧
图
像
数
据
,
可
以
让
用
户
第
一
时
间
感
知
,
提
升
体
验
效
果
。
问
题
与
小
结
用
w
a
s
m
做
前
端
播
放
帧
预
览
的
能
力
,
已
经
在
业
务
侧
灰
度
上
线
。
由
于
我
们
只
需
要
解
复
用
m
p
e
g
t
s
和
h
6
2
4
d
e
c
o
d
e
r
,
编
译
w
a
s
m
大
小
2
.
6
M
B
左
右
。
主
要
受
限
于
加
载
分
片
的
网
络
耗
时
,
从
h
o
v
e
r
进
度
条
到
预
览
图
展
示
约
在
1
.
1
秒
左
右
,
w
a
s
m
解
帧
耗
时
6
0
m
s
以
内
。
在
支
持
w
a
s
m
的
P
C
浏
览
器
上
c
h
r
o
m
e
、
新
版
f
i
r
e
f
o
x
和
s
a
f
a
r
i
也
都
没
什
么
太
大
问
题
。
目
前
一
个
完
整
6
0
0
M
左
右
的
高
清
回
放
资
源
,
如
果
加
载
完
整
的
资
源
用
于
帧
预
览
的
消
耗
3
0
–
5
0
M
B
流
量
,
但
实
际
情
况
下
并
不
会
完
整
的
加
载
,
一
般
都
只
在
1
0
M
以
内
。
虽
然
这
部
分
资
源
被
h
t
t
p
缓
存
,
但
是
如
果
不
是
因
为
网
络
差
而
走
到
低
清
晰
度
的
情
况
下
,
这
部
分
资
源
的
流
量
多
少
还
是
有
点
被
浪
费
了
。
这
几
年
w
a
s
m
从
一
些
d
e
m
o
尝
试
,
到
业
务
真
正
落
地
,
越
来
越
多
场
景
中
被
得
到
应
用
。
以
前
一
些
客
户
端
a
p
p
才
有
的
功
能
,
现
在
浏
览
器
也
并
非
不
可
想
象
。
期
待
可
以
落
地
更
多
有
趣
和
实
用
的
功
能
。