Jessibuca是一款开源的纯H5直播流播放器,通过Emscripten将音视频解码库编译成Js(ams.js/wasm)运行于浏览器之中。兼容几乎所有浏览器,可以运行在PC、手机、微信中,无需额外安装插件。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

167 lines
3.3 KiB

#pragma once
struct VideoPacket
{
clock_t timestamp;
IOBuffer data;
VideoPacket(clock_t t, IOBuffer data) : timestamp(t), data(data)
{
}
VideoPacket() : timestamp(0), data()
{
}
};
class VideoDecoder
{
public:
val *jsObject;
u8 *heap;
u32 videoWidth;
u32 videoHeight;
u32 p_yuv[3];
int NAL_unit_length;
bool webgl;
u32 compositionTime;
VideoDecoder() : heap(nullptr), webgl(false), NAL_unit_length(0), videoWidth(0), videoHeight(0)
{
}
virtual ~VideoDecoder()
{
if (!webgl && heap)
free(heap);
emscripten_log(0, "video decoder release!\n");
}
virtual void clear()
{
if (!webgl && heap)
free(heap);
}
void decodeVideoSize(u32 width, u32 height)
{
videoWidth = width;
videoHeight = height;
//emscripten_log(0, "canvas:%d,%d", videoWidth, videoHeight);
if (webgl)
{
heap = (u8 *)p_yuv;
jsObject->call<void>("setVideoSize", videoWidth, videoHeight, (int)heap >> 2);
}
else
{
auto outSize = videoHeight * videoHeight << 2;
auto cacheSize = 0x2000000;
auto size = outSize + cacheSize;
auto chunkSize = 0x1000000;
auto heapSize = chunkSize;
while (heapSize < size)
{
heapSize += chunkSize;
}
heap = (u8 *)malloc(heapSize);
jsObject->call<void>("setVideoSize", videoWidth, videoHeight, (int)heap);
}
}
void decodeYUV420()
{
if (!webgl)
{
yuv420toRGB((u8 *)p_yuv[0], (u8 *)p_yuv[1], (u8 *)p_yuv[2], heap, videoWidth, videoHeight);
}
jsObject->call<void>("draw", compositionTime);
}
virtual void decodeH264Header(IOBuffer &data)
{
u8 lengthSizeMinusOne = data[4];
lengthSizeMinusOne &= 0x03;
NAL_unit_length = lengthSizeMinusOne;
data >>= 6;
//data.consoleHex();
int spsLen = 0;
int ppsLen = 0;
data.read2B(spsLen);
if (spsLen > 0)
{
_decode(data(0, spsLen));
data >>= spsLen;
}
data >>= 1;
data.read2B(ppsLen);
if (ppsLen > 0)
{
_decode(data(0, ppsLen));
}
}
virtual void decodeH265Header(IOBuffer &data)
{
u8 lengthSizeMinusOne = data[22];
lengthSizeMinusOne &= 0x03;
NAL_unit_length = lengthSizeMinusOne;
data >>= 26;
//data.consoleHex();
int vps = 0, sps = 0, pps = 0;
data.read2B(vps);
_decode(data(0, vps));
data >>= vps + 3;
data.read2B(sps);
_decode(data(0, sps));
data >>= sps + 3;
data.read2B(pps);
_decode(data(0, pps));
}
bool isAVCSequence(IOBuffer &data)
{
return data[0] >> 4 == 1 && data[1] == 0; //0为AVCSequence Header,1为AVC NALU,2为AVC end ofsequence
}
virtual void decode(IOBuffer &data)
{
if (isAVCSequence(data))
{
int codec_id = data[0] & 0x0F;
data >>= 5;
//emscripten_log(0, "codec = %d", codec_id);
switch (codec_id)
{
case 7:
decodeH264Header(data);
break;
case 12:
decodeH265Header(data);
break;
default:
emscripten_log(0, "codec not support: %d", codec_id);
break;
}
}
else
{
data >>= 2;
compositionTime = data.readUInt24B();
decodeBody(data);
}
}
virtual void decodeBody(IOBuffer &data)
{
int NALUnitLength = 0;
while (data.length > 4)
{
switch (NAL_unit_length)
{
case 3:
data.read4B(NALUnitLength);
break;
case 2:
data.read3B(NALUnitLength);
break;
case 1:
data.read2B(NALUnitLength);
break;
default:
data.read1(NALUnitLength);
}
_decode(data(0, NALUnitLength));
data >>= NALUnitLength;
}
}
virtual void _decode(IOBuffer data) = 0;
};