Skip to content

Commit

Permalink
use regular expressions for checking (parsing later). add support for…
Browse files Browse the repository at this point in the history
… LCEVC
  • Loading branch information
paulhiggs committed Jul 19, 2024
1 parent bbcdc7a commit db3e5f2
Show file tree
Hide file tree
Showing 16 changed files with 385 additions and 65 deletions.
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"editor.tabSize": 2,
"editor.detectIndentation": false,
"editor.detectIndentation": true,
"editor.renderWhitespace": "all",
"editor.insertSpaces": true,
"editor.insertSpaces": false,
"files.eol": "\n",

"prettier.singleQuote": true,
"prettier.useTabs": true,
"prettier.tabWidth": 2,
"prettier.printWidth": 160
}
}
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ supports
* AV1 (av01)
* E-AC3 (ec-3) - no processing required
* VP9 - see https://www.webmproject.org/vp9/mp4/
* EVC - refer annex E.9 of ISO/IEC 14496-15:2019 Amd.2
* VVC - refer annex E.6 of ISO/IEC 14496-15:2019 Amd.2
* EVC - refer annex E.9 of ISO/IEC 14496-15:2022
* VVC - refer annex E.6 of ISO/IEC 14496-15:2022
* MPEG-H
* AVS3
* AVS3 video and audio
* AVS2 audio
* LCEVC - refer to ISO/IEC 14496-15:2022 Amd.1

## Installation

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codec-string",
"version": "0.1.2",
"version": "0.1.3",
"description": "decode the codec= string in a media mime type",
"type": "module",
"engines": {
Expand Down
8 changes: 4 additions & 4 deletions src/decode-ac4.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ import { hexDigits } from './utils.js';
import { normal, error, warning } from './decode.js';
import { DVBclassification } from './dvb-mapping.js';
import { simpleHTML } from './formatters.js';

const AC4format = 'ac-4.<bitstream_version>.<presentation_version>.<mdcompat>';
import { expressions } from './regular_expressions.js';

// eslint-disable-next-line no-unused-vars
export function decodeEAC3(val) {
Expand Down Expand Up @@ -116,9 +115,10 @@ const decodeMDcompat = (mdcompat, pres_version) => {
};

export function decodeAC4(val) {
if (!expressions.AC4.regex.test(val)) return [error('Regex mismatch!'), error(expressions.AC4.format)];
const parts = val.split('.');

if (parts.length != 4) return [error(`AC-4 format is "${AC4format}"`)];
// neither of these checks should fail as the number of parts and the format are checked in the regupar expression
if (parts.length != 4) return [error(`AC-4 format is "${expressions.AC4.format}"`)];
if (!hexDigits(parts[1]) || !hexDigits(parts[2]) || !hexDigits(parts[3])) return [error('parameters contain non-hex digits')];

const coding_params = { type: 'audio', codec: parts[0] },
Expand Down
23 changes: 13 additions & 10 deletions src/decode-av1.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@ const DEBUGGING = false;
import { sscanf } from './sscanf-func.js';
import { normal, error, warning } from './decode.js';
import { simpleHTML } from './formatters.js';

const AV1format =
'<sample entry 4CC>.<profile>.<level><tier>.<bitDepth>.<monochrome>.<chromaSubsampling>.<colorPrimaries>.<transferCharacteristics>.<matrixCoefficients>.<videoFullRangeFlag></videoFullRangeFlag>';
import { expressions } from './regular_expressions.js';

export function decodeAV1(val) {
// defined in https://aomediacodec.github.io/av1-isobmff/#codecsparam
/*
<sample entry 4CC>.<profile>.<level><tier>.<bitDepth>.<monochrome>.<chromaSubsampling>.
<colorPrimaries>.<transferCharacteristics>.<matrixCoefficients>.<videoFullRangeFlag>
*/
if (!expressions.AV1.regex.text(val))
return [error('Regex mismatch!'), error(expressions.AV1.format), error(expressions.AV1.description)];

const parts = val.split('.');
if (parts.length < 4) return [error(`invalid format "${AV1format}"`)];
// this checks should not fail as the number of parts and the format are checked in the regupar expression
if (parts.length < 4) return [error(`invalid format "${expressions.AV1.format}"`)];

const res = [];

Expand Down Expand Up @@ -164,14 +166,14 @@ export function decodeAV1(val) {
res.push(error(`unknown tier (${levelAndTier[1]})`));
}

switch (parts[3]) {
case '08':
switch (parseInt(parts[3])) {
case 8:
res.push(normal('8 bit'));
break;
case '10':
case 10:
res.push(normal('10 bit'));
break;
case '12':
case 12:
res.push(normal('12 bit'));
break;
default:
Expand Down Expand Up @@ -390,18 +392,19 @@ export function decodeAV1(val) {
}
else res.push({ default: '1 (ITU-R BT.709)' });

const _dflt_videoFullRangeFlag = '0 (studio swing representation)';
if (parts.length > 9) {
switch (parts[9]) {
case '0':
res.push(normal('0 (studio swing representation)'));
res.push(normal(_dflt_videoFullRangeFlag));
break;
case '1':
res.push(normal('1 (full swing representation)'));
break;
default:
res.push(error(`unknown value for video_full_range (${parts[9]})`));
}
} else res.push({ default: '0 (studio swing representation)' });
} else res.push({ default: _dflt_videoFullRangeFlag });
return res;
}

Expand Down
17 changes: 13 additions & 4 deletions src/decode-avc.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,23 +37,32 @@ import { sscanf } from './sscanf-func.js';
import { normal, error } from './decode.js';
import { DVBclassification } from './dvb-mapping.js';
import { simpleHTML } from './formatters.js';
import { expressions } from './regular_expressions.js';

/**
* AVCregex
* 4 ASCII Printable characters (see https://en.wikipedia.org/wiki/FourCC)
* followed by a period and then 6 hexadecimal characters
*/

export function decodeAVC(val) {
// regex from DVB TM-STREAM0087: /avc[1-4]\.[a-fA-F\d]{6}/

export function decodeAVC(val) {

function AVCconstraint(val, constraint) {
// constraint 012345--
// bit 76543210
if (constraint < 0 || constraint > 5) return false;
return val & Math.pow(2, 7 - constraint) ? true : false;
}

if (!expressions.AVC.regex.test(val))
return [error('Regex mismatch!'), error(`${val.substring(0,4)}.${expressions.AVC.format_suffix}`), error(expressions.AV1.description)];

const parts = val.split('.');

// the following tests should not fail as the format is checked with the regular expression
if (parts.length != 2) return [error('invalid format')];

if (parts[1].length != 6) return [error(`invalid parameters length (${parts[1].length}) - should be 6`)];

if (!hexDigits(parts[1])) return [error('parameters contains non-hex digits')];

const prof = sscanf(parts[1], '%2x%2x%2x'),
Expand Down
39 changes: 25 additions & 14 deletions src/decode-avs.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { hexDigits } from './utils.js';
import { normal, warning, error } from './decode.js';
import { DVBclassification } from './dvb-mapping.js';
import { simpleHTML } from './formatters.js';
import { expressions } from './regular_expressions.js';

const avs3 = {
profileMain8: 0x20,
Expand Down Expand Up @@ -166,8 +167,8 @@ const AVS3profiles = [
];

const AVS3ProfileStr = (prof) => {
const t = AVS3profiles.find((e) => (e.val = prof));
return t ? t.str : null;
const t = AVS3profiles.find((e) => (e.val == prof));
return t ? `${t.str} (${prof})` : null;
};

const AVS3levels = [
Expand Down Expand Up @@ -214,15 +215,20 @@ const AVS3levels = [
{ val: avs3.level10_6_120, str: '10.6.120' },
];
const AVS3LevelStr = (lev) => {
const t = AVS3levels.find((e) => (e.val = lev));
return t ? t.str : null;
const t = AVS3levels.find((e) => (e.val == lev));
return t ? `${t.str} (${lev})` : null;
};

export function decodeAVS3(val) {
if (!expressions.AVS3video.regex.test(val))
return [error('Regex mismatch!'), error(expressions.AVS3video.format), error(expressions.AVS3video.description)];

const parts = val.split('.');
// the following check should not occur due to regular expression checking
if (parts.length != 3) return [error('AVS3 codec requires 3 parts')];

const argErrs = [];
// the following checks should not occur due to regular expression checking
if (!hexDigits(parts[1])) argErrs.push(error(`profile_id not expressed in hexadecimal (${parts[1]})`));
if (!hexDigits(parts[2])) argErrs.push(error(`level_id not expressed in hexadecimal (${parts[2]})`));
if (argErrs.length) return argErrs;
Expand Down Expand Up @@ -263,12 +269,15 @@ export function decodeAVS3(val) {
}

export function decodeAVS3audio(val) {

if (!expressions.AVS3audio.regex.test(val))
return [error('Regex mismatch!'), error(expressions.AVS3audio.format), error(expressions.AVS3audio.description)];

const parts = val.split('.');
if (parts.length != 2) return [error('AVS3 audio codec requires 2 parts')];

const argErrs = [];
if (!hexDigits(parts[1])) argErrs.push(error(`audio_codec_id not expressed in hexadecimal (${parts[1]})`));
if (argErrs.length) return argErrs;
// the following checks should not occur due to regular expression checking
if (parts.length != 2) return [error('AVS3 audio codec requires 2 parts')];
if (!hexDigits(parts[1])) return [error(`audio_codec_id not expressed in hexadecimal (${parts[1]})`)];

const coding_params = { type: 'audio', codec: parts[0] };
const res = [];
Expand Down Expand Up @@ -296,12 +305,14 @@ export function decodeAVS3audio(val) {
}

export function decodeAVS2audio(val) {
if (!expressions.AVS2audio.regex.test(val))
return [error('Regex mismatch!'), error(expressions.AVS2audio.format), error(expressions.AVS2audio.description)];

const parts = val.split('.');
if (parts.length != 2) return [error('AVS2 audio codec requires 2 parts')];

const argErrs = [];
if (!hexDigits(parts[1])) argErrs.push(error(`audio_codec_id not expressed in hexadecimal (${parts[1]})`));
if (argErrs.length) return argErrs;
// the following checks should not occur due to regular expression checking
if (parts.length != 2) return [error('AVS2 audio codec requires 2 parts')];
if (!hexDigits(parts[1])) return [error(`audio_codec_id not expressed in hexadecimal (${parts[1]})`)];

const coding_params = { type: 'audio', codec: parts[0] };
const res = [];
Expand Down Expand Up @@ -330,8 +341,8 @@ function outputHTML(label, messages) {
}

export function registerAVS3(addHandler) {
addHandler(['avs3'], 'AVS3 Video', decodeAVS3, outputHTML);
addHandler(['lav3'], 'AVS3 Library Track', decodeAVS3, outputHTML);
addHandler('avs3', 'AVS3 Video', decodeAVS3, outputHTML);
addHandler('lav3', 'AVS3 Library Track', decodeAVS3, outputHTML);
addHandler('av3a', 'AVS3 Audio', decodeAVS3audio, outputHTML);
addHandler('cavs', 'AVS2 Audio', decodeAVS2audio, outputHTML);
}
26 changes: 13 additions & 13 deletions src/decode-evc.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
*/

// see annex E.9 of ISO/IEC 14496-15:2019 Amd.2 "Carriage of VVC and EVC in ISOBMFF" (w19454)
// see annex E.9 of ISO/IEC 14496-15:2022 "Carriage of network abstraction layer (NAL) unit structured video in the ISO base media file format"
// and ISO/IEC 23091-2 (MDS19669_WG05_N00011)

const DEBUGGING = false;
Expand Down Expand Up @@ -466,19 +466,17 @@ export function decodeEVC(val) {
return null;
}

const parts = val.split('.');

const KEY_PROFILE = 'vprf',
KEY_LEVEL = 'vlev',
KEY_TOOLSET_HIGH = 'vtoh',
KEY_TOOLSET_LOW = 'vtol';
const KEY_BIT_DEPTH = 'vbit',
KEY_TOOLSET_LOW = 'vtol',
KEY_BIT_DEPTH = 'vbit',
KEY_CHROMA = 'vcss',
KEY_PRIMARIES = 'vcpr';
const KEY_XFER_CHAR = 'vtrc',
KEY_PRIMARIES = 'vcpr',
KEY_XFER_CHAR = 'vtrc',
KEY_MATRIX_COEFF = 'vmac',
KEY_FULL_RANGE = 'vfrf';
const KEY_FRAME_PACK = 'vfpq',
KEY_FULL_RANGE = 'vfrf',
KEY_FRAME_PACK = 'vfpq',
KEY_INTERPRETATION = 'vpci',
KEY_SAR = 'vsar';

Expand Down Expand Up @@ -594,7 +592,7 @@ export function decodeEVC(val) {
},
];

const res = [];
const res = [], parts = val.split('.');

for (let i = 1; i < parts.length; i++) {
const key = parts[i].substring(0, 4);
Expand Down Expand Up @@ -679,7 +677,7 @@ export function decodeEVC(val) {
break;
}
default:
res.push(error('invalid key specified (' + key + ')'));
res.push(warning('unrecognised key (' + key + ')'));
break;
}
}
Expand All @@ -701,7 +699,7 @@ function evcHTML(label, messages) {
// const TABLE_STYLE = '<style>table {border-collapse: collapse;border: 1px solid black;} th, td {text-align: left; padding: 8px;} tr:nth-child(even) {background-color: #f2f2f2;}</style>';
const TABLE_STYLE =
'<style>table {border-collapse: collapse;border: none;} th, td {text-align: left; padding: 8px;} ' +
'tr {border-bottom: 1pt solid black;} tr:nth-child(even) {background-color: #f2f2f2;}</style > ';
'tr {border-bottom: 1pt solid black;} tr:nth-child(even) {background-color: #f2f2f2;}</style>';
function printHex3(value) {
let enc = value.toString(16);
while (enc.length < 6) enc = '0' + enc;
Expand Down Expand Up @@ -762,7 +760,9 @@ function evcHTML(label, messages) {
});
if (tools.length) res += cell(tools, 1, 2);
res += cell(toolset?.is_default ? dflt('default') : '');
} else if (msg?.error) res += cell(err(msg.error), 4);
}
else if (msg?.error) res += cell(err(msg.error), 4);
else if (msg?.warning) res += cell(warn(msg.warning), 4);
else res += cell(err(`invalid element ${JSON.stringify(msg)}`), 4);
res += '</tr>';
});
Expand Down
Loading

0 comments on commit db3e5f2

Please sign in to comment.