@give-tech/ec-player 0.0.1-beta.4 → 0.0.1-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/demuxer/fMP4Demuxer.d.ts +17 -1
- package/dist/demuxer/fMP4Demuxer.d.ts.map +1 -1
- package/dist/index.js +202 -97
- package/dist/index.js.map +1 -1
- package/dist/player/FLVPlayer.d.ts.map +1 -1
- package/dist/player/HLSPlayer.d.ts +12 -0
- package/dist/player/HLSPlayer.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -50,11 +50,15 @@ export interface InitSegment {
|
|
|
50
50
|
trackId: number;
|
|
51
51
|
timescale: number;
|
|
52
52
|
avcC?: Uint8Array;
|
|
53
|
+
hvcC?: Uint8Array;
|
|
54
|
+
isHevc?: boolean;
|
|
53
55
|
}
|
|
54
56
|
export declare class fMP4Demuxer {
|
|
55
57
|
private timescale;
|
|
56
58
|
private trackId;
|
|
57
59
|
private avcC;
|
|
60
|
+
private hvcC;
|
|
61
|
+
private isHevc;
|
|
58
62
|
/**
|
|
59
63
|
* 解析初始化段 (ftyp + moov)
|
|
60
64
|
*/
|
|
@@ -84,13 +88,17 @@ export declare class fMP4Demuxer {
|
|
|
84
88
|
*/
|
|
85
89
|
private parseStbl;
|
|
86
90
|
/**
|
|
87
|
-
* 解析 stsd box 获取 avcC
|
|
91
|
+
* 解析 stsd box 获取 avcC 或 hvcC
|
|
88
92
|
*/
|
|
89
93
|
private parseStsd;
|
|
90
94
|
/**
|
|
91
95
|
* 解析 AVC Sample Entry
|
|
92
96
|
*/
|
|
93
97
|
private parseAvcSampleEntry;
|
|
98
|
+
/**
|
|
99
|
+
* 解析 HEVC Sample Entry
|
|
100
|
+
*/
|
|
101
|
+
private parseHevcSampleEntry;
|
|
94
102
|
/**
|
|
95
103
|
* 解析 moof box
|
|
96
104
|
*/
|
|
@@ -123,6 +131,14 @@ export declare class fMP4Demuxer {
|
|
|
123
131
|
* 获取 avcC 配置
|
|
124
132
|
*/
|
|
125
133
|
getAvcC(): Uint8Array | null;
|
|
134
|
+
/**
|
|
135
|
+
* 获取 hvcC 配置
|
|
136
|
+
*/
|
|
137
|
+
getHvcC(): Uint8Array | null;
|
|
138
|
+
/**
|
|
139
|
+
* 是否为 HEVC 编码
|
|
140
|
+
*/
|
|
141
|
+
isHevcStream(): boolean;
|
|
126
142
|
}
|
|
127
143
|
/**
|
|
128
144
|
* 检测是否为 fMP4 格式
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fMP4Demuxer.d.ts","sourceRoot":"","sources":["../../src/demuxer/fMP4Demuxer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA8CH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qBAAqB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,EAAE,UAAU,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,EAAE,OAAO,EAAE,CAAA;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,CAAA;IACjC,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"fMP4Demuxer.d.ts","sourceRoot":"","sources":["../../src/demuxer/fMP4Demuxer.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -415,6 +415,8 @@ class fMP4Demuxer {
|
|
|
415
415
|
this.timescale = 1e3;
|
|
416
416
|
this.trackId = 1;
|
|
417
417
|
this.avcC = null;
|
|
418
|
+
this.hvcC = null;
|
|
419
|
+
this.isHevc = false;
|
|
418
420
|
}
|
|
419
421
|
/**
|
|
420
422
|
* 解析初始化段 (ftyp + moov)
|
|
@@ -433,7 +435,9 @@ class fMP4Demuxer {
|
|
|
433
435
|
return {
|
|
434
436
|
trackId: this.trackId,
|
|
435
437
|
timescale: this.timescale,
|
|
436
|
-
avcC: this.avcC || void 0
|
|
438
|
+
avcC: this.avcC || void 0,
|
|
439
|
+
hvcC: this.hvcC || void 0,
|
|
440
|
+
isHevc: this.isHevc
|
|
437
441
|
};
|
|
438
442
|
}
|
|
439
443
|
/**
|
|
@@ -530,7 +534,7 @@ class fMP4Demuxer {
|
|
|
530
534
|
}
|
|
531
535
|
}
|
|
532
536
|
/**
|
|
533
|
-
* 解析 stsd box 获取 avcC
|
|
537
|
+
* 解析 stsd box 获取 avcC 或 hvcC
|
|
534
538
|
*/
|
|
535
539
|
parseStsd(data, stsdOffset, stsdSize) {
|
|
536
540
|
const boxDataOffset = stsdOffset + 8;
|
|
@@ -544,6 +548,10 @@ class fMP4Demuxer {
|
|
|
544
548
|
this.parseAvcSampleEntry(data, offset, entrySize);
|
|
545
549
|
return;
|
|
546
550
|
}
|
|
551
|
+
if (entryType === "hvc1" || entryType === "hev1") {
|
|
552
|
+
this.parseHevcSampleEntry(data, offset, entrySize);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
547
555
|
offset += entrySize;
|
|
548
556
|
}
|
|
549
557
|
}
|
|
@@ -564,6 +572,25 @@ class fMP4Demuxer {
|
|
|
564
572
|
offset += boxSize;
|
|
565
573
|
}
|
|
566
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* 解析 HEVC Sample Entry
|
|
577
|
+
*/
|
|
578
|
+
parseHevcSampleEntry(data, entryOffset, entrySize) {
|
|
579
|
+
this.isHevc = true;
|
|
580
|
+
let offset = entryOffset + 8 + 78;
|
|
581
|
+
const endOffset = entryOffset + entrySize;
|
|
582
|
+
while (offset < endOffset - 8) {
|
|
583
|
+
const boxSize = readUint32$1(data, offset);
|
|
584
|
+
const boxType = readFourCC(data, offset + 4);
|
|
585
|
+
if (boxSize < 8) break;
|
|
586
|
+
if (boxType === "hvcC") {
|
|
587
|
+
this.hvcC = data.slice(offset, offset + boxSize);
|
|
588
|
+
console.log("[fMP4Demuxer] Found hvcC, size:", boxSize);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
offset += boxSize;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
567
594
|
/**
|
|
568
595
|
* 解析 moof box
|
|
569
596
|
*/
|
|
@@ -757,6 +784,18 @@ class fMP4Demuxer {
|
|
|
757
784
|
getAvcC() {
|
|
758
785
|
return this.avcC;
|
|
759
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* 获取 hvcC 配置
|
|
789
|
+
*/
|
|
790
|
+
getHvcC() {
|
|
791
|
+
return this.hvcC;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* 是否为 HEVC 编码
|
|
795
|
+
*/
|
|
796
|
+
isHevcStream() {
|
|
797
|
+
return this.isHevc;
|
|
798
|
+
}
|
|
760
799
|
}
|
|
761
800
|
class NALParser {
|
|
762
801
|
/**
|
|
@@ -804,6 +843,93 @@ class NALParser {
|
|
|
804
843
|
return units;
|
|
805
844
|
}
|
|
806
845
|
}
|
|
846
|
+
const HEVC_NAL_TYPE = {
|
|
847
|
+
// Picture Parameter Set
|
|
848
|
+
IDR_W_RADL: 19
|
|
849
|
+
};
|
|
850
|
+
class HEVCDecoder {
|
|
851
|
+
constructor(wasmLoader) {
|
|
852
|
+
this.wasmModule = null;
|
|
853
|
+
this.decoderContext = null;
|
|
854
|
+
this.wasmModule = wasmLoader.getModule();
|
|
855
|
+
}
|
|
856
|
+
async init() {
|
|
857
|
+
if (!this.wasmModule) {
|
|
858
|
+
throw new Error("WASM module not loaded");
|
|
859
|
+
}
|
|
860
|
+
this.decoderContext = this.wasmModule._create_decoder(173);
|
|
861
|
+
if (this.decoderContext === 0 || this.decoderContext === null) {
|
|
862
|
+
throw new Error("Failed to create HEVC decoder context");
|
|
863
|
+
}
|
|
864
|
+
console.log("[HEVCDecoder] Initialized with codec_id=173");
|
|
865
|
+
}
|
|
866
|
+
decode(nalUnit) {
|
|
867
|
+
if (this.decoderContext === null || !this.wasmModule) {
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
const dataPtr = this.wasmModule._malloc(nalUnit.size);
|
|
871
|
+
this.wasmModule.HEAPU8.set(nalUnit.data, dataPtr);
|
|
872
|
+
const result = this.wasmModule._decode_video(
|
|
873
|
+
this.decoderContext,
|
|
874
|
+
dataPtr,
|
|
875
|
+
nalUnit.size
|
|
876
|
+
);
|
|
877
|
+
this.wasmModule._free(dataPtr);
|
|
878
|
+
if (result === 1) {
|
|
879
|
+
const width = this.wasmModule._get_frame_width(this.decoderContext);
|
|
880
|
+
const height = this.wasmModule._get_frame_height(this.decoderContext);
|
|
881
|
+
if (width <= 0 || height <= 0) {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
const yPtr = this.wasmModule._get_frame_data(this.decoderContext, 0);
|
|
885
|
+
const uPtr = this.wasmModule._get_frame_data(this.decoderContext, 1);
|
|
886
|
+
const vPtr = this.wasmModule._get_frame_data(this.decoderContext, 2);
|
|
887
|
+
const yLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 0);
|
|
888
|
+
const uLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 1);
|
|
889
|
+
const vLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 2);
|
|
890
|
+
const frameData = this.yuv420pToRgba(
|
|
891
|
+
yPtr,
|
|
892
|
+
uPtr,
|
|
893
|
+
vPtr,
|
|
894
|
+
width,
|
|
895
|
+
height,
|
|
896
|
+
yLineSize,
|
|
897
|
+
uLineSize,
|
|
898
|
+
vLineSize
|
|
899
|
+
);
|
|
900
|
+
return { width, height, data: frameData };
|
|
901
|
+
}
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
yuv420pToRgba(yPtr, uPtr, vPtr, width, height, yLineSize, uLineSize, vLineSize) {
|
|
905
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
906
|
+
for (let y = 0; y < height; y++) {
|
|
907
|
+
for (let x = 0; x < width; x++) {
|
|
908
|
+
const yIndex = y * yLineSize + x;
|
|
909
|
+
const uIndex = (y >> 1) * uLineSize + (x >> 1);
|
|
910
|
+
const vIndex = (y >> 1) * vLineSize + (x >> 1);
|
|
911
|
+
const yValue = this.wasmModule.HEAPU8[yPtr + yIndex];
|
|
912
|
+
const uValue = this.wasmModule.HEAPU8[uPtr + uIndex];
|
|
913
|
+
const vValue = this.wasmModule.HEAPU8[vPtr + vIndex];
|
|
914
|
+
const c = yValue - 16;
|
|
915
|
+
const d = uValue - 128;
|
|
916
|
+
const e = vValue - 128;
|
|
917
|
+
let r = 298 * c + 409 * e + 128 >> 8;
|
|
918
|
+
let g = 298 * c - 100 * d - 208 * e + 128 >> 8;
|
|
919
|
+
let b = 298 * c + 516 * d + 128 >> 8;
|
|
920
|
+
r = r < 0 ? 0 : r > 255 ? 255 : r;
|
|
921
|
+
g = g < 0 ? 0 : g > 255 ? 255 : g;
|
|
922
|
+
b = b < 0 ? 0 : b > 255 ? 255 : b;
|
|
923
|
+
const rgbaIndex = (y * width + x) * 4;
|
|
924
|
+
rgba[rgbaIndex] = r;
|
|
925
|
+
rgba[rgbaIndex + 1] = g;
|
|
926
|
+
rgba[rgbaIndex + 2] = b;
|
|
927
|
+
rgba[rgbaIndex + 3] = 255;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
return rgba;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
807
933
|
class BasePrefetcher {
|
|
808
934
|
constructor(config, callbacks = {}) {
|
|
809
935
|
this.isRunning = false;
|
|
@@ -1378,12 +1504,14 @@ class HLSPlayer extends BasePlayer {
|
|
|
1378
1504
|
this.pesExtractor = new PESExtractor();
|
|
1379
1505
|
this.nalParser = new NALParser();
|
|
1380
1506
|
this.fmp4Demuxer = new fMP4Demuxer();
|
|
1507
|
+
this.hevcDecoder = null;
|
|
1381
1508
|
this.isPrefetching = false;
|
|
1382
1509
|
this.currentSegmentIndex = 0;
|
|
1383
1510
|
this.segments = [];
|
|
1384
1511
|
this.fmp4Segments = [];
|
|
1385
1512
|
this.initSegment = null;
|
|
1386
1513
|
this._isFMP4 = false;
|
|
1514
|
+
this._isHevc = false;
|
|
1387
1515
|
this.currentPlaylistUrl = "";
|
|
1388
1516
|
this._nalQueue = [];
|
|
1389
1517
|
this._sampleQueue = [];
|
|
@@ -1423,6 +1551,9 @@ class HLSPlayer extends BasePlayer {
|
|
|
1423
1551
|
get isFMP4() {
|
|
1424
1552
|
return this._isFMP4;
|
|
1425
1553
|
}
|
|
1554
|
+
get isHevc() {
|
|
1555
|
+
return this._isHevc;
|
|
1556
|
+
}
|
|
1426
1557
|
get nalQueue() {
|
|
1427
1558
|
return this._nalQueue;
|
|
1428
1559
|
}
|
|
@@ -1690,16 +1821,36 @@ class HLSPlayer extends BasePlayer {
|
|
|
1690
1821
|
const data = new Uint8Array(await response.arrayBuffer());
|
|
1691
1822
|
console.log("[fMP4] Init segment data size:", data.length, "bytes");
|
|
1692
1823
|
const initInfo = this.fmp4Demuxer.parseInitSegment(data);
|
|
1693
|
-
console.log("[fMP4] Parse result:",
|
|
1694
|
-
|
|
1824
|
+
console.log("[fMP4] Parse result:", {
|
|
1825
|
+
isHevc: initInfo?.isHevc,
|
|
1826
|
+
hasAvcC: !!initInfo?.avcC,
|
|
1827
|
+
hasHvcC: !!initInfo?.hvcC,
|
|
1828
|
+
timescale: initInfo?.timescale
|
|
1829
|
+
});
|
|
1830
|
+
this._isHevc = initInfo?.isHevc ?? false;
|
|
1831
|
+
if (initInfo?.hvcC && this._isHevc) {
|
|
1832
|
+
console.log("[fMP4] hvcC size:", initInfo.hvcC.length);
|
|
1833
|
+
await this.initHevcDecoder();
|
|
1834
|
+
this.initDecoderFromHvcC(initInfo.hvcC);
|
|
1835
|
+
console.log("[fMP4] HEVC decoder initialized, timescale:", initInfo.timescale);
|
|
1836
|
+
} else if (initInfo?.avcC) {
|
|
1695
1837
|
console.log("[fMP4] avcC size:", initInfo.avcC.length);
|
|
1696
1838
|
this.initDecoderFromAvcC(initInfo.avcC);
|
|
1697
|
-
console.log("[fMP4]
|
|
1839
|
+
console.log("[fMP4] AVC decoder initialized, timescale:", initInfo.timescale);
|
|
1698
1840
|
} else {
|
|
1699
|
-
console.error("[fMP4] Failed to parse init segment or no avcC found!");
|
|
1700
|
-
throw new Error("Failed to parse fMP4 init segment");
|
|
1841
|
+
console.error("[fMP4] Failed to parse init segment or no avcC/hvcC found!");
|
|
1842
|
+
throw new Error("Failed to parse fMP4 init segment: no valid codec config found");
|
|
1701
1843
|
}
|
|
1702
1844
|
}
|
|
1845
|
+
/**
|
|
1846
|
+
* 初始化 HEVC 解码器
|
|
1847
|
+
*/
|
|
1848
|
+
async initHevcDecoder() {
|
|
1849
|
+
if (this.hevcDecoder) return;
|
|
1850
|
+
this.hevcDecoder = new HEVCDecoder(this.wasmLoader);
|
|
1851
|
+
await this.hevcDecoder.init();
|
|
1852
|
+
console.log("[fMP4] HEVC decoder created");
|
|
1853
|
+
}
|
|
1703
1854
|
/**
|
|
1704
1855
|
* 从 avcC 初始化解码器
|
|
1705
1856
|
*/
|
|
@@ -1744,6 +1895,40 @@ class HLSPlayer extends BasePlayer {
|
|
|
1744
1895
|
this.decoderInitialized = true;
|
|
1745
1896
|
console.log("[fMP4] Decoder initialized from avcC");
|
|
1746
1897
|
}
|
|
1898
|
+
/**
|
|
1899
|
+
* 从 hvcC 初始化 HEVC 解码器
|
|
1900
|
+
*/
|
|
1901
|
+
initDecoderFromHvcC(hvcC) {
|
|
1902
|
+
if (this.decoderInitialized || !this.hevcDecoder) return;
|
|
1903
|
+
let offset = 22;
|
|
1904
|
+
const numOfArrays = hvcC[offset];
|
|
1905
|
+
offset += 1;
|
|
1906
|
+
console.log(`[fMP4] hvcC: numOfArrays=${numOfArrays}`);
|
|
1907
|
+
for (let i = 0; i < numOfArrays && offset < hvcC.length - 3; i++) {
|
|
1908
|
+
const typeCompressed = hvcC[offset];
|
|
1909
|
+
const arrayType = typeCompressed & 63;
|
|
1910
|
+
const numNalus = hvcC[offset + 1] << 8 | hvcC[offset + 2];
|
|
1911
|
+
offset += 3;
|
|
1912
|
+
console.log(`[fMP4] HEVC array: type=${arrayType}, count=${numNalus}`);
|
|
1913
|
+
for (let j = 0; j < numNalus && offset < hvcC.length - 2; j++) {
|
|
1914
|
+
const nalUnitLength = hvcC[offset] << 8 | hvcC[offset + 1];
|
|
1915
|
+
offset += 2;
|
|
1916
|
+
if (offset + nalUnitLength > hvcC.length) break;
|
|
1917
|
+
const nalUnitData = hvcC.slice(offset, offset + nalUnitLength);
|
|
1918
|
+
offset += nalUnitLength;
|
|
1919
|
+
const nalWithStartCode = new Uint8Array(4 + nalUnitLength);
|
|
1920
|
+
nalWithStartCode.set([0, 0, 0, 1], 0);
|
|
1921
|
+
nalWithStartCode.set(nalUnitData, 4);
|
|
1922
|
+
if (arrayType === 32 || arrayType === 33 || arrayType === 34) {
|
|
1923
|
+
this.hevcDecoder.decode({ type: arrayType, data: nalWithStartCode, size: nalWithStartCode.length });
|
|
1924
|
+
const typeName = arrayType === 32 ? "VPS" : arrayType === 33 ? "SPS" : "PPS";
|
|
1925
|
+
console.log(`[fMP4] HEVC ${typeName} decoded, length=${nalUnitLength}`);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
this.decoderInitialized = true;
|
|
1930
|
+
console.log("[fMP4] HEVC decoder initialized from hvcC");
|
|
1931
|
+
}
|
|
1747
1932
|
/**
|
|
1748
1933
|
* 解析 HLS 播放列表
|
|
1749
1934
|
*/
|
|
@@ -1855,7 +2040,8 @@ class HLSPlayer extends BasePlayer {
|
|
|
1855
2040
|
* 解码 fMP4 sample
|
|
1856
2041
|
*/
|
|
1857
2042
|
decodeSample(sample) {
|
|
1858
|
-
|
|
2043
|
+
const decoder = this._isHevc ? this.hevcDecoder : this.decoder;
|
|
2044
|
+
if (!decoder) {
|
|
1859
2045
|
console.warn("[fMP4] Decoder not available");
|
|
1860
2046
|
return null;
|
|
1861
2047
|
}
|
|
@@ -1866,8 +2052,9 @@ class HLSPlayer extends BasePlayer {
|
|
|
1866
2052
|
const data = sample.data;
|
|
1867
2053
|
const hasStartCode = data.length >= 4 && data[0] === 0 && data[1] === 0 && (data[2] === 1 || data[2] === 0 && data[3] === 1);
|
|
1868
2054
|
if (hasStartCode) {
|
|
1869
|
-
|
|
1870
|
-
|
|
2055
|
+
const nalType2 = this._isHevc ? sample.isSync ? HEVC_NAL_TYPE.IDR_W_RADL : 1 : sample.isSync ? 5 : 1;
|
|
2056
|
+
return decoder.decode({
|
|
2057
|
+
type: nalType2,
|
|
1871
2058
|
data,
|
|
1872
2059
|
size: data.length
|
|
1873
2060
|
});
|
|
@@ -1906,8 +2093,9 @@ class HLSPlayer extends BasePlayer {
|
|
|
1906
2093
|
writeOffset += nalLength;
|
|
1907
2094
|
offset += 4 + nalLength;
|
|
1908
2095
|
}
|
|
1909
|
-
const
|
|
1910
|
-
|
|
2096
|
+
const nalType = this._isHevc ? sample.isSync ? HEVC_NAL_TYPE.IDR_W_RADL : 1 : sample.isSync ? 5 : 1;
|
|
2097
|
+
const frame = decoder.decode({
|
|
2098
|
+
type: nalType,
|
|
1911
2099
|
data: annexBData,
|
|
1912
2100
|
size: annexBData.length
|
|
1913
2101
|
});
|
|
@@ -2388,89 +2576,6 @@ class FLVDemuxer {
|
|
|
2388
2576
|
return tag.frameType === FRAME_TYPE_KEYFRAME;
|
|
2389
2577
|
}
|
|
2390
2578
|
}
|
|
2391
|
-
class HEVCDecoder {
|
|
2392
|
-
constructor(wasmLoader) {
|
|
2393
|
-
this.wasmModule = null;
|
|
2394
|
-
this.decoderContext = null;
|
|
2395
|
-
this.wasmModule = wasmLoader.getModule();
|
|
2396
|
-
}
|
|
2397
|
-
async init() {
|
|
2398
|
-
if (!this.wasmModule) {
|
|
2399
|
-
throw new Error("WASM module not loaded");
|
|
2400
|
-
}
|
|
2401
|
-
this.decoderContext = this.wasmModule._create_decoder(173);
|
|
2402
|
-
if (this.decoderContext === 0 || this.decoderContext === null) {
|
|
2403
|
-
throw new Error("Failed to create HEVC decoder context");
|
|
2404
|
-
}
|
|
2405
|
-
console.log("[HEVCDecoder] Initialized with codec_id=173");
|
|
2406
|
-
}
|
|
2407
|
-
decode(nalUnit) {
|
|
2408
|
-
if (this.decoderContext === null || !this.wasmModule) {
|
|
2409
|
-
return null;
|
|
2410
|
-
}
|
|
2411
|
-
const dataPtr = this.wasmModule._malloc(nalUnit.size);
|
|
2412
|
-
this.wasmModule.HEAPU8.set(nalUnit.data, dataPtr);
|
|
2413
|
-
const result = this.wasmModule._decode_video(
|
|
2414
|
-
this.decoderContext,
|
|
2415
|
-
dataPtr,
|
|
2416
|
-
nalUnit.size
|
|
2417
|
-
);
|
|
2418
|
-
this.wasmModule._free(dataPtr);
|
|
2419
|
-
if (result === 1) {
|
|
2420
|
-
const width = this.wasmModule._get_frame_width(this.decoderContext);
|
|
2421
|
-
const height = this.wasmModule._get_frame_height(this.decoderContext);
|
|
2422
|
-
if (width <= 0 || height <= 0) {
|
|
2423
|
-
return null;
|
|
2424
|
-
}
|
|
2425
|
-
const yPtr = this.wasmModule._get_frame_data(this.decoderContext, 0);
|
|
2426
|
-
const uPtr = this.wasmModule._get_frame_data(this.decoderContext, 1);
|
|
2427
|
-
const vPtr = this.wasmModule._get_frame_data(this.decoderContext, 2);
|
|
2428
|
-
const yLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 0);
|
|
2429
|
-
const uLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 1);
|
|
2430
|
-
const vLineSize = this.wasmModule._get_frame_linesize(this.decoderContext, 2);
|
|
2431
|
-
const frameData = this.yuv420pToRgba(
|
|
2432
|
-
yPtr,
|
|
2433
|
-
uPtr,
|
|
2434
|
-
vPtr,
|
|
2435
|
-
width,
|
|
2436
|
-
height,
|
|
2437
|
-
yLineSize,
|
|
2438
|
-
uLineSize,
|
|
2439
|
-
vLineSize
|
|
2440
|
-
);
|
|
2441
|
-
return { width, height, data: frameData };
|
|
2442
|
-
}
|
|
2443
|
-
return null;
|
|
2444
|
-
}
|
|
2445
|
-
yuv420pToRgba(yPtr, uPtr, vPtr, width, height, yLineSize, uLineSize, vLineSize) {
|
|
2446
|
-
const rgba = new Uint8Array(width * height * 4);
|
|
2447
|
-
for (let y = 0; y < height; y++) {
|
|
2448
|
-
for (let x = 0; x < width; x++) {
|
|
2449
|
-
const yIndex = y * yLineSize + x;
|
|
2450
|
-
const uIndex = (y >> 1) * uLineSize + (x >> 1);
|
|
2451
|
-
const vIndex = (y >> 1) * vLineSize + (x >> 1);
|
|
2452
|
-
const yValue = this.wasmModule.HEAPU8[yPtr + yIndex];
|
|
2453
|
-
const uValue = this.wasmModule.HEAPU8[uPtr + uIndex];
|
|
2454
|
-
const vValue = this.wasmModule.HEAPU8[vPtr + vIndex];
|
|
2455
|
-
const c = yValue - 16;
|
|
2456
|
-
const d = uValue - 128;
|
|
2457
|
-
const e = vValue - 128;
|
|
2458
|
-
let r = 298 * c + 409 * e + 128 >> 8;
|
|
2459
|
-
let g = 298 * c - 100 * d - 208 * e + 128 >> 8;
|
|
2460
|
-
let b = 298 * c + 516 * d + 128 >> 8;
|
|
2461
|
-
r = r < 0 ? 0 : r > 255 ? 255 : r;
|
|
2462
|
-
g = g < 0 ? 0 : g > 255 ? 255 : g;
|
|
2463
|
-
b = b < 0 ? 0 : b > 255 ? 255 : b;
|
|
2464
|
-
const rgbaIndex = (y * width + x) * 4;
|
|
2465
|
-
rgba[rgbaIndex] = r;
|
|
2466
|
-
rgba[rgbaIndex + 1] = g;
|
|
2467
|
-
rgba[rgbaIndex + 2] = b;
|
|
2468
|
-
rgba[rgbaIndex + 3] = 255;
|
|
2469
|
-
}
|
|
2470
|
-
}
|
|
2471
|
-
return rgba;
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
2579
|
const CODEC_ID_AVC = 7;
|
|
2475
2580
|
const CODEC_ID_HEVC = 12;
|
|
2476
2581
|
const FLV_PREFETCHER_CONFIG = {
|
|
@@ -2734,8 +2839,8 @@ class FLVPlayer extends BasePlayer {
|
|
|
2734
2839
|
* 开始播放(覆盖基类方法)
|
|
2735
2840
|
*/
|
|
2736
2841
|
async play() {
|
|
2737
|
-
const MIN_BUFFER_SIZE = this.dynamicMinBufferSize;
|
|
2738
|
-
const MAX_WAIT_TIME = 1e4;
|
|
2842
|
+
const MIN_BUFFER_SIZE = this.config.isLive ? 3 : this.dynamicMinBufferSize;
|
|
2843
|
+
const MAX_WAIT_TIME = this.config.isLive ? 3e3 : 1e4;
|
|
2739
2844
|
const startTime = Date.now();
|
|
2740
2845
|
console.log(`[FLVPlayer] Waiting for buffer (target: ${MIN_BUFFER_SIZE} frames)...`);
|
|
2741
2846
|
let aggressiveDecodeCount = 0;
|