看前端如何通过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',
});
// wasmfinalSegmentFile
});



























































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