@give-tech/ec-player 0.0.1-beta.5 → 0.0.1-beta.7

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;
@@ -1334,12 +1460,18 @@ const DEFAULT_HLS_CONFIG = {
1334
1460
  class HLSSegmentPrefetcher extends SegmentPrefetcher {
1335
1461
  constructor(config, callbacks, player) {
1336
1462
  super(config, callbacks);
1463
+ this.currentInitSegmentUri = null;
1337
1464
  this.player = player;
1338
1465
  }
1339
1466
  /**
1340
1467
  * 获取分片数据
1341
1468
  */
1342
1469
  async fetchSegment(segment, index) {
1470
+ if (segment.initSegmentUri && segment.initSegmentUri !== this.currentInitSegmentUri) {
1471
+ console.log("[HLSSegmentPrefetcher] Init segment changed:", segment.initSegmentUri);
1472
+ await this.player.loadNewInitSegment(segment.initSegmentUri);
1473
+ this.currentInitSegmentUri = segment.initSegmentUri;
1474
+ }
1343
1475
  const baseUrl = this.baseUrl;
1344
1476
  const url = segment.uri.startsWith("http") ? segment.uri : baseUrl + segment.uri;
1345
1477
  const headers = {};
@@ -1378,12 +1510,14 @@ class HLSPlayer extends BasePlayer {
1378
1510
  this.pesExtractor = new PESExtractor();
1379
1511
  this.nalParser = new NALParser();
1380
1512
  this.fmp4Demuxer = new fMP4Demuxer();
1513
+ this.hevcDecoder = null;
1381
1514
  this.isPrefetching = false;
1382
1515
  this.currentSegmentIndex = 0;
1383
1516
  this.segments = [];
1384
1517
  this.fmp4Segments = [];
1385
1518
  this.initSegment = null;
1386
1519
  this._isFMP4 = false;
1520
+ this._isHevc = false;
1387
1521
  this.currentPlaylistUrl = "";
1388
1522
  this._nalQueue = [];
1389
1523
  this._sampleQueue = [];
@@ -1423,6 +1557,9 @@ class HLSPlayer extends BasePlayer {
1423
1557
  get isFMP4() {
1424
1558
  return this._isFMP4;
1425
1559
  }
1560
+ get isHevc() {
1561
+ return this._isHevc;
1562
+ }
1426
1563
  get nalQueue() {
1427
1564
  return this._nalQueue;
1428
1565
  }
@@ -1604,7 +1741,9 @@ class HLSPlayer extends BasePlayer {
1604
1741
  const segmentInfos = this.fmp4Segments.map((seg) => ({
1605
1742
  uri: seg.uri,
1606
1743
  duration: seg.duration,
1607
- byteRange: seg.byteRange
1744
+ byteRange: seg.byteRange,
1745
+ initSegmentUri: seg.initSegmentUri
1746
+ // 传递初始化段 URI
1608
1747
  }));
1609
1748
  this.prefetcher.setSegments(segmentInfos, baseUrl);
1610
1749
  } else {
@@ -1690,16 +1829,75 @@ class HLSPlayer extends BasePlayer {
1690
1829
  const data = new Uint8Array(await response.arrayBuffer());
1691
1830
  console.log("[fMP4] Init segment data size:", data.length, "bytes");
1692
1831
  const initInfo = this.fmp4Demuxer.parseInitSegment(data);
1693
- console.log("[fMP4] Parse result:", initInfo);
1694
- if (initInfo?.avcC) {
1832
+ console.log("[fMP4] Parse result:", {
1833
+ isHevc: initInfo?.isHevc,
1834
+ hasAvcC: !!initInfo?.avcC,
1835
+ hasHvcC: !!initInfo?.hvcC,
1836
+ timescale: initInfo?.timescale
1837
+ });
1838
+ this._isHevc = initInfo?.isHevc ?? false;
1839
+ if (initInfo?.hvcC && this._isHevc) {
1840
+ console.log("[fMP4] hvcC size:", initInfo.hvcC.length);
1841
+ await this.initHevcDecoder();
1842
+ this.initDecoderFromHvcC(initInfo.hvcC);
1843
+ console.log("[fMP4] HEVC decoder initialized, timescale:", initInfo.timescale);
1844
+ } else if (initInfo?.avcC) {
1695
1845
  console.log("[fMP4] avcC size:", initInfo.avcC.length);
1696
1846
  this.initDecoderFromAvcC(initInfo.avcC);
1697
- console.log("[fMP4] Init segment loaded, timescale:", initInfo.timescale);
1847
+ console.log("[fMP4] AVC decoder initialized, timescale:", initInfo.timescale);
1848
+ } else {
1849
+ console.error("[fMP4] Failed to parse init segment or no avcC/hvcC found!");
1850
+ throw new Error("Failed to parse fMP4 init segment: no valid codec config found");
1851
+ }
1852
+ }
1853
+ /**
1854
+ * 加载新的初始化段(用于不连续性流)
1855
+ */
1856
+ async loadNewInitSegment(uri) {
1857
+ console.log("[fMP4] Loading new init segment for discontinuity:", uri);
1858
+ this._sampleQueue.length = 0;
1859
+ this._nalQueue.length = 0;
1860
+ this.frameBuffer = [];
1861
+ const url = uri.startsWith("http") ? uri : this.currentPlaylistUrl.substring(0, this.currentPlaylistUrl.lastIndexOf("/") + 1) + uri;
1862
+ const response = await fetch(url);
1863
+ const data = new Uint8Array(await response.arrayBuffer());
1864
+ console.log("[fMP4] New init segment data size:", data.length, "bytes");
1865
+ const initInfo = this.fmp4Demuxer.parseInitSegment(data);
1866
+ console.log("[fMP4] New init segment parse result:", {
1867
+ isHevc: initInfo?.isHevc,
1868
+ hasAvcC: !!initInfo?.avcC,
1869
+ hasHvcC: !!initInfo?.hvcC
1870
+ });
1871
+ const newIsHevc = initInfo?.isHevc ?? false;
1872
+ if (newIsHevc !== this._isHevc) {
1873
+ console.log("[fMP4] Codec type changed from", this._isHevc ? "HEVC" : "AVC", "to", newIsHevc ? "HEVC" : "AVC");
1874
+ this._isHevc = newIsHevc;
1875
+ this.decoderInitialized = false;
1876
+ if (newIsHevc) {
1877
+ await this.initHevcDecoder();
1878
+ }
1879
+ } else {
1880
+ this.decoderInitialized = false;
1881
+ }
1882
+ if (initInfo?.hvcC && this._isHevc) {
1883
+ this.initDecoderFromHvcC(initInfo.hvcC);
1884
+ console.log("[fMP4] HEVC decoder re-initialized for discontinuity");
1885
+ } else if (initInfo?.avcC) {
1886
+ this.initDecoderFromAvcC(initInfo.avcC);
1887
+ console.log("[fMP4] AVC decoder re-initialized for discontinuity");
1698
1888
  } else {
1699
- console.error("[fMP4] Failed to parse init segment or no avcC found!");
1700
- throw new Error("Failed to parse fMP4 init segment");
1889
+ console.error("[fMP4] Failed to parse new init segment!");
1701
1890
  }
1702
1891
  }
1892
+ /**
1893
+ * 初始化 HEVC 解码器
1894
+ */
1895
+ async initHevcDecoder() {
1896
+ if (this.hevcDecoder) return;
1897
+ this.hevcDecoder = new HEVCDecoder(this.wasmLoader);
1898
+ await this.hevcDecoder.init();
1899
+ console.log("[fMP4] HEVC decoder created");
1900
+ }
1703
1901
  /**
1704
1902
  * 从 avcC 初始化解码器
1705
1903
  */
@@ -1744,6 +1942,40 @@ class HLSPlayer extends BasePlayer {
1744
1942
  this.decoderInitialized = true;
1745
1943
  console.log("[fMP4] Decoder initialized from avcC");
1746
1944
  }
1945
+ /**
1946
+ * 从 hvcC 初始化 HEVC 解码器
1947
+ */
1948
+ initDecoderFromHvcC(hvcC) {
1949
+ if (this.decoderInitialized || !this.hevcDecoder) return;
1950
+ let offset = 22;
1951
+ const numOfArrays = hvcC[offset];
1952
+ offset += 1;
1953
+ console.log(`[fMP4] hvcC: numOfArrays=${numOfArrays}`);
1954
+ for (let i = 0; i < numOfArrays && offset < hvcC.length - 3; i++) {
1955
+ const typeCompressed = hvcC[offset];
1956
+ const arrayType = typeCompressed & 63;
1957
+ const numNalus = hvcC[offset + 1] << 8 | hvcC[offset + 2];
1958
+ offset += 3;
1959
+ console.log(`[fMP4] HEVC array: type=${arrayType}, count=${numNalus}`);
1960
+ for (let j = 0; j < numNalus && offset < hvcC.length - 2; j++) {
1961
+ const nalUnitLength = hvcC[offset] << 8 | hvcC[offset + 1];
1962
+ offset += 2;
1963
+ if (offset + nalUnitLength > hvcC.length) break;
1964
+ const nalUnitData = hvcC.slice(offset, offset + nalUnitLength);
1965
+ offset += nalUnitLength;
1966
+ const nalWithStartCode = new Uint8Array(4 + nalUnitLength);
1967
+ nalWithStartCode.set([0, 0, 0, 1], 0);
1968
+ nalWithStartCode.set(nalUnitData, 4);
1969
+ if (arrayType === 32 || arrayType === 33 || arrayType === 34) {
1970
+ this.hevcDecoder.decode({ type: arrayType, data: nalWithStartCode, size: nalWithStartCode.length });
1971
+ const typeName = arrayType === 32 ? "VPS" : arrayType === 33 ? "SPS" : "PPS";
1972
+ console.log(`[fMP4] HEVC ${typeName} decoded, length=${nalUnitLength}`);
1973
+ }
1974
+ }
1975
+ }
1976
+ this.decoderInitialized = true;
1977
+ console.log("[fMP4] HEVC decoder initialized from hvcC");
1978
+ }
1747
1979
  /**
1748
1980
  * 解析 HLS 播放列表
1749
1981
  */
@@ -1776,25 +2008,31 @@ class HLSPlayer extends BasePlayer {
1776
2008
  }
1777
2009
  const isFMP4 = lines.some((line) => line.includes("#EXT-X-MAP:"));
1778
2010
  console.log("[parsePlaylist] isFMP4:", isFMP4, "url:", url);
2011
+ let currentInitSegment;
1779
2012
  for (let i = 0; i < lines.length; i++) {
1780
2013
  const line = lines[i].trim();
1781
2014
  if (line.startsWith("#EXT-X-MAP:")) {
1782
2015
  const mapInfo = line.substring("#EXT-X-MAP:".length);
1783
2016
  const uriMatch = mapInfo.match(/URI="([^"]+)"/);
1784
2017
  if (uriMatch) {
1785
- initSegment = { uri: uriMatch[1] };
2018
+ currentInitSegment = { uri: uriMatch[1] };
1786
2019
  const byteRangeMatch = mapInfo.match(/BYTERANGE="(\d+)@(\d+)"/);
1787
2020
  if (byteRangeMatch) {
1788
- initSegment.byteRange = {
2021
+ currentInitSegment.byteRange = {
1789
2022
  start: parseInt(byteRangeMatch[2]),
1790
2023
  end: parseInt(byteRangeMatch[2]) + parseInt(byteRangeMatch[1]) - 1
1791
2024
  };
1792
2025
  }
2026
+ if (!initSegment) {
2027
+ initSegment = currentInitSegment;
2028
+ }
1793
2029
  }
2030
+ continue;
2031
+ }
2032
+ if (line === "#EXT-X-DISCONTINUITY") {
2033
+ console.log("[parsePlaylist] Found EXT-X-DISCONTINUITY");
2034
+ continue;
1794
2035
  }
1795
- }
1796
- for (let i = 0; i < lines.length; i++) {
1797
- const line = lines[i].trim();
1798
2036
  if (line.startsWith("#EXTINF:")) {
1799
2037
  const duration = parseFloat(line.split(":")[1].split(",")[0]);
1800
2038
  let byteRange;
@@ -1812,7 +2050,13 @@ class HLSPlayer extends BasePlayer {
1812
2050
  const uri = nextLine;
1813
2051
  if (uri && !uri.startsWith("#")) {
1814
2052
  if (isFMP4) {
1815
- fmp4Segments.push({ uri, duration, byteRange });
2053
+ fmp4Segments.push({
2054
+ uri,
2055
+ duration,
2056
+ byteRange,
2057
+ initSegmentUri: currentInitSegment?.uri
2058
+ // 关联当前初始化段
2059
+ });
1816
2060
  } else {
1817
2061
  segments.push({ uri, duration });
1818
2062
  }
@@ -1855,7 +2099,8 @@ class HLSPlayer extends BasePlayer {
1855
2099
  * 解码 fMP4 sample
1856
2100
  */
1857
2101
  decodeSample(sample) {
1858
- if (!this.decoder) {
2102
+ const decoder = this._isHevc ? this.hevcDecoder : this.decoder;
2103
+ if (!decoder) {
1859
2104
  console.warn("[fMP4] Decoder not available");
1860
2105
  return null;
1861
2106
  }
@@ -1866,8 +2111,9 @@ class HLSPlayer extends BasePlayer {
1866
2111
  const data = sample.data;
1867
2112
  const hasStartCode = data.length >= 4 && data[0] === 0 && data[1] === 0 && (data[2] === 1 || data[2] === 0 && data[3] === 1);
1868
2113
  if (hasStartCode) {
1869
- return this.decoder.decode({
1870
- type: sample.isSync ? 5 : 1,
2114
+ const nalType2 = this._isHevc ? sample.isSync ? HEVC_NAL_TYPE.IDR_W_RADL : 1 : sample.isSync ? 5 : 1;
2115
+ return decoder.decode({
2116
+ type: nalType2,
1871
2117
  data,
1872
2118
  size: data.length
1873
2119
  });
@@ -1906,8 +2152,9 @@ class HLSPlayer extends BasePlayer {
1906
2152
  writeOffset += nalLength;
1907
2153
  offset += 4 + nalLength;
1908
2154
  }
1909
- const frame = this.decoder.decode({
1910
- type: sample.isSync ? 5 : 1,
2155
+ const nalType = this._isHevc ? sample.isSync ? HEVC_NAL_TYPE.IDR_W_RADL : 1 : sample.isSync ? 5 : 1;
2156
+ const frame = decoder.decode({
2157
+ type: nalType,
1911
2158
  data: annexBData,
1912
2159
  size: annexBData.length
1913
2160
  });
@@ -2388,89 +2635,6 @@ class FLVDemuxer {
2388
2635
  return tag.frameType === FRAME_TYPE_KEYFRAME;
2389
2636
  }
2390
2637
  }
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
2638
  const CODEC_ID_AVC = 7;
2475
2639
  const CODEC_ID_HEVC = 12;
2476
2640
  const FLV_PREFETCHER_CONFIG = {