@give-tech/ec-player 0.0.1-beta.5 → 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.
@@ -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;CAClB;AAMD,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,IAAI,CAA0B;IAEtC;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAuBtD;;OAEG;IACH,OAAO,CAAC,SAAS;IAkBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAoBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAkBjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAoBjB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAiCxD;;OAEG;IACH,OAAO,CAAC,SAAS;IA8BjB;;OAEG;IACH,OAAO,CAAC,SAAS;IA+BjB;;OAEG;IACH,OAAO,CAAC,SAAS;IASjB;;OAEG;IACH,OAAO,CAAC,SAAS;IAiEjB;;OAEG;IACH,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,WAAW,EAAE;IA4CtF;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,OAAO,IAAI,UAAU,GAAG,IAAI;CAG7B;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAuBzD"}
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:", initInfo);
1694
- if (initInfo?.avcC) {
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] Init segment loaded, timescale:", initInfo.timescale);
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
- if (!this.decoder) {
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
- return this.decoder.decode({
1870
- type: sample.isSync ? 5 : 1,
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 frame = this.decoder.decode({
1910
- type: sample.isSync ? 5 : 1,
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 = {