Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ jobs:
CI_TEST_TYPE: ${{matrix.test-type}}
runs-on: ${{matrix.os}}
steps:
- name: Disable apparmor because it breaks Chromium headless
run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

- name: checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: read node version from .nvmrc
run: echo "NVMRC=$(cat .nvmrc)" >> $GITHUB_OUTPUT
Expand All @@ -47,7 +50,7 @@ jobs:
run: pulseaudio -D

- name: setup node
uses: actions/setup-node@v3
uses: actions/setup-node@v5
with:
node-version: '${{steps.nvm.outputs.NVMRC}}'
cache: npm
Expand Down
44 changes: 44 additions & 0 deletions lib/mp4/probe.js
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,50 @@ getTracks = function(init) {
// and are using the default
track.codec = 'mp4a.40.2';
}
} else if(/^h(vc|ev)1$/i.test(track.codec)) {
/*
Using just hvc1 or hev1 as codec string is not sufficient as canPlayType won't accept it.
It must be CODEC.PROFILE.COMPATIBILTY.LEVEL.CONSTRAINTS
See these for approach:
https://www.etsi.org/deliver/etsi_ts/103200_103299/103285/01.01.01_60/ts_103285v010101p.pdf
https://github.com/wader/fq/blob/ad0c6ecd1b79cea97bbb3048106ce309e0ec7ce0/format/mpeg/hevc_dcr.go#L24
*/
codecConfig = codecBox.subarray(78);
codecConfigType = parseType$1(codecConfig.subarray(4, 8));

if (codecConfigType === 'hvcC' && codecConfig.length > 21) {
// These stored in one byte as int(2), int(1), int(5)
const profileSpaceTierIDC = codecConfig[9];
const generalProfileSpace = ['', 'A', 'B', 'C'][profileSpaceTierIDC >> 6];
const generalTierFlag = (profileSpaceTierIDC & 0x20) ? 'H' : 'L';
const generalProfileIDC = profileSpaceTierIDC & 0x1f;

const generalLevelIDC = codecConfig[20];

// general_profile_compatibility_flags, but in reverse bit order, in a hexadecimal representation
const profileCompatibility = parseInt(Array.from(codecConfig.subarray(10, 14)).map(
byte => byte.toString(2).padStart(8,'0')
).join('').split('').reverse().join(''), 2).toString(16);

// hexadecimal representation of the general_constraint_indicator_flags.
// Each byte isseparated by a '.', and trailing zero bytes may be omitted.
const contraintsBytes = Array.from(codecConfig.subarray(14, 20)).map(
byte => byte.toString(16).toUpperCase().padStart(2, '0')
);

// Trailing zeros removed from joined string. Leave one zero byte if there
// are multiple, as all examples are like this
track.codec = [
track.codec,
generalProfileSpace + parseInt(generalProfileIDC, 2).toString(10),
profileCompatibility,
generalTierFlag + generalLevelIDC,
...contraintsBytes
].join('.').replace(/(\.00)+\.00$/, '');
} else {
// This is a fallback. Show a warning here?
track.codec = 'hvc1.1.6.L93.B0';
}
} else {
// flac, opus, etc
track.codec = track.codec.toLowerCase();
Expand Down