Skip to content

Commit 18784f3

Browse files
author
vladimir.vyatkin
committed
Process HEVC in mpeg-ts container
1 parent a30d0e1 commit 18784f3

File tree

10 files changed

+1003
-25
lines changed

10 files changed

+1003
-25
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ HLS.js is written in [ECMAScript6] (`*.js`) and [TypeScript] (`*.ts`) (strongly
3737
- Fragmented MP4 container
3838
- MPEG-2 TS container
3939
- ITU-T Rec. H.264 and ISO/IEC 14496-10 Elementary Stream
40+
- ITU-T Rec. H.265 and ISO/IEC 23008-2 Elementary Stream
4041
- ISO/IEC 13818-7 ADTS AAC Elementary Stream
4142
- ISO/IEC 11172-3 / ISO/IEC 13818-3 (MPEG-1/2 Audio Layer III) Elementary Stream
4243
- ATSC A/52 / AC-3 / Dolby Digital Elementary Stream
@@ -319,6 +320,8 @@ _Keep in mind that if the intention is to support HLS on multiple platforms, bey
319320

320321
Find a support matrix of the MediaSource API here: https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
321322

323+
Decoding of HEVC stream is available in Safari, MS Edge and another WebKit based browsers. In case of MS Edge and Google Chrome it completely relies on GPU capabilities of the client device. Call `Hls.isHEVCSupported()` to check this feature is enabled either via MediaSource or native HLS support.
324+
322325
## Using HLS.js
323326

324327
### Installation

api-extractor/report/hls.js.api.md

+2
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,8 @@ class Hls implements HlsEventEmitter {
15751575
// Warning: (ae-setter-with-docs) The doc comment for the property "firstLevel" must appear on the getter, not the setter.
15761576
set firstLevel(newLevel: number);
15771577
get forceStartLoad(): boolean;
1578+
// (undocumented)
1579+
static isHEVCSupported(): boolean;
15781580
static isSupported(): boolean;
15791581
get latency(): number;
15801582
// (undocumented)

src/demux/tsdemuxer.ts

+50-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* highly optimized TS demuxer:
33
* parse PAT, PMT
44
* extract PES packet from audio and video PIDs
5-
* extract AVC/H264 NAL units and AAC/ADTS samples from PES packet
5+
* extract AVC/H264 (or HEVC/H265) NAL units and AAC/ADTS samples from PES packet
66
* trigger the remuxer upon parsing completion
77
* it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource.
88
* it also controls the remuxing process :
@@ -14,6 +14,7 @@ import * as MpegAudio from './audio/mpegaudio';
1414
import * as AC3 from './audio/ac3-demuxer';
1515
import BaseVideoParser from './video/base-video-parser';
1616
import AvcVideoParser from './video/avc-video-parser';
17+
import HevcVideoParser from './video/hevc-video-parser';
1718
import SampleAesDecrypter from './sample-aes';
1819
import { Events } from '../events';
1920
import { appendUint8Array, RemuxerTrackIdConfig } from '../utils/mp4-tools';
@@ -75,7 +76,7 @@ class TSDemuxer implements Demuxer {
7576
private _txtTrack?: DemuxedUserdataTrack;
7677
private aacOverFlow: AudioFrame | null = null;
7778
private remainderData: Uint8Array | null = null;
78-
private videoParser: BaseVideoParser;
79+
private videoParser: BaseVideoParser | null;
7980

8081
constructor(
8182
observer: HlsEventEmitter,
@@ -85,7 +86,7 @@ class TSDemuxer implements Demuxer {
8586
this.observer = observer;
8687
this.config = config;
8788
this.typeSupported = typeSupported;
88-
this.videoParser = new AvcVideoParser();
89+
this.videoParser = null;
8990
}
9091

9192
static probe(data: Uint8Array) {
@@ -291,13 +292,25 @@ class TSDemuxer implements Demuxer {
291292
case videoPid:
292293
if (stt) {
293294
if (videoData && (pes = parsePES(videoData))) {
294-
this.videoParser.parsePES(
295-
videoTrack,
296-
textTrack,
297-
pes,
298-
false,
299-
this._duration,
300-
);
295+
if (this.videoParser === null) {
296+
switch (videoTrack.segmentCodec) {
297+
case 'avc':
298+
this.videoParser = new AvcVideoParser();
299+
break;
300+
case 'hevc':
301+
this.videoParser = new HevcVideoParser();
302+
break;
303+
}
304+
}
305+
if (this.videoParser !== null) {
306+
this.videoParser.parsePES(
307+
videoTrack,
308+
textTrack,
309+
pes,
310+
false,
311+
this._duration,
312+
);
313+
}
301314
}
302315

303316
videoData = { data: [], size: 0 };
@@ -469,14 +482,26 @@ class TSDemuxer implements Demuxer {
469482
// try to parse last PES packets
470483
let pes: PES | null;
471484
if (videoData && (pes = parsePES(videoData))) {
472-
this.videoParser.parsePES(
473-
videoTrack as DemuxedVideoTrack,
474-
textTrack as DemuxedUserdataTrack,
475-
pes,
476-
true,
477-
this._duration,
478-
);
479-
videoTrack.pesData = null;
485+
if (this.videoParser === null) {
486+
switch (videoTrack.segmentCodec) {
487+
case 'avc':
488+
this.videoParser = new AvcVideoParser();
489+
break;
490+
case 'hevc':
491+
this.videoParser = new HevcVideoParser();
492+
break;
493+
}
494+
}
495+
if (this.videoParser !== null) {
496+
this.videoParser.parsePES(
497+
videoTrack as DemuxedVideoTrack,
498+
textTrack as DemuxedUserdataTrack,
499+
pes,
500+
true,
501+
this._duration,
502+
);
503+
videoTrack.pesData = null;
504+
}
480505
} else {
481506
// either avcData null or PES truncated, keep it for next frag parsing
482507
videoTrack.pesData = videoData;
@@ -873,8 +898,13 @@ function parsePMT(
873898
case 0x87:
874899
logger.warn('Unsupported EC-3 in M2TS found');
875900
break;
876-
case 0x24:
877-
logger.warn('Unsupported HEVC in M2TS found');
901+
902+
case 0x24: // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
903+
if (result.videoPid === -1) {
904+
result.videoPid = pid;
905+
result.segmentVideoCodec = 'hevc';
906+
logger.log('HEVC in M2TS found');
907+
}
878908
break;
879909

880910
default:

0 commit comments

Comments
 (0)