Skip to content

Commit

Permalink
preselections metadata (#59)
Browse files Browse the repository at this point in the history
* Bump package version to 0.3.10

* Add meta box

Add a skeleton implementation of 'meta'. This makes the read/write
 editlist.mp4 test fail. This is because The meta subboxes are not yet
 implemented. When neither reader nor writer for meta were implemented,
 the test for reading/writing editlist.mp4 worked, but as soon as meta
 is added as a reader, the subboxes have to be implemented too to not
 fail this test.
 Rather than adding the additional boxes, we remove 'meta' from
 editlist.mp4.

* Add grpl box

* Add utf8string datatype

* Add prsl box

* Add the prsl box, unit tests, and an mp4 file for testing

* Implement FDIS version of prsl

* Make the interleaving tag is optional and contingent on a flag
* Use new test content

* Add labl box

* Implement FDIS version of labl

* read is_group_label from the flags instead of as a separate field
* represent is_group_label as boolean

* Add 'elng' box

* Add 'ardi' box

* Add 'kind' box

* Add 'imda' box

* Update bundles
  • Loading branch information
wschidol authored Sep 30, 2024
1 parent 933f702 commit a99f454
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 6 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,24 @@
Currently a limited set of ISOBMFF boxes is supported:

### ISO/IEC 14496-12:2012 (ISOBMFF)
* ardi
* dinf
* edts
* elst
* elng
* free / skip
* ftyp / styp
* grpl
* hdlr
* imda
* kind
* labl
* mdat
* mdia
* mdhd
* meco
* mehd
* meta
* mfhd
* mfra
* mfro
Expand All @@ -35,6 +42,7 @@ Currently a limited set of ISOBMFF boxes is supported:
* mp4a / enca
* mvex
* mvhd / mfhd
* prsl
* sidx
* ssix
* stbl
Expand Down
88 changes: 86 additions & 2 deletions dist/iso_boxer.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! codem-isoboxer v0.3.8 https://github.com/madebyhiro/codem-isoboxer/blob/master/LICENSE.txt */
/*! codem-isoboxer v0.3.10 https://github.com/madebyhiro/codem-isoboxer/blob/master/LICENSE.txt */
var ISOBoxer = {};

ISOBoxer.parseBuffer = function(arrayBuffer) {
Expand Down Expand Up @@ -253,7 +253,7 @@ ISOBox.create = function(type) {
return newBox;
};

ISOBox.prototype._boxContainers = ['dinf', 'edts', 'mdia', 'meco', 'mfra', 'minf', 'moof', 'moov', 'mvex', 'stbl', 'strk', 'traf', 'trak', 'tref', 'udta', 'vttc', 'sinf', 'schi', 'encv', 'enca'];
ISOBox.prototype._boxContainers = ['dinf', 'edts', 'mdia', 'meco', 'mfra', 'minf', 'moof', 'moov', 'mvex', 'stbl', 'strk', 'traf', 'trak', 'tref', 'udta', 'vttc', 'sinf', 'schi', 'encv', 'enca','meta','grpl','prsl'];

ISOBox.prototype._boxProcessors = {};

Expand Down Expand Up @@ -366,6 +366,8 @@ ISOBox.prototype._readField = function(type, size) {
return this._readData(size);
case 'utf8':
return this._readUTF8String();
case 'utf8string':
return this._readUTF8TerminatedString();
default:
return -1;
}
Expand Down Expand Up @@ -476,6 +478,25 @@ ISOBox.prototype._readUTF8String = function() {
return data ? ISOBoxer.Utils.dataViewToString(data) : data;
};

ISOBox.prototype._readUTF8TerminatedString = function() {
var length = this._raw.byteLength - (this._cursor.offset - this._offset);
var data = null;
if (length > 0) {
data = new DataView(this._raw.buffer, this._cursor.offset, length);

var l;
for (l=0; l<length; l++)
if (data.getUint8(l) === 0)
break;

// remap the Dataview with the actual length
data = new DataView(this._raw.buffer, this._cursor.offset, l);
this._cursor.offset += Math.min(l+1, length);
}

return data ? ISOBoxer.Utils.dataViewToString(data) : data;
};

ISOBox.prototype._parseBox = function() {
this._parsing = true;
this._cursor.offset = this._offset;
Expand Down Expand Up @@ -769,6 +790,12 @@ ISOBox.prototype._writeField = function(type, size, value) {
}
};

// ISO/IEC 14496-12:202x - 12.2.8 Audio rendering indication box
ISOBox.prototype._boxProcessors['ardi'] = function() {
this._procFullBox();
this._procField('audio_rendering_indication', 'uint', 8);
};

// ISO/IEC 14496-15:2014 - avc1/2/3/4, hev1, hvc1, encv
ISOBox.prototype._boxProcessors['avc1'] =
ISOBox.prototype._boxProcessors['avc2'] =
Expand Down Expand Up @@ -814,6 +841,12 @@ ISOBox.prototype._boxProcessors['dref'] = function() {
this._procSubBoxes('entries', this.entry_count);
};

// ISO/IEC 14496-12:202x - 8.4.6 Extended language tag
ISOBox.prototype._boxProcessors['elng'] = function() {
this._procFullBox();
this._procField('extended_language', 'utf8string');
};

// ISO/IEC 14496-12:2012 - 8.6.6 Edit List Box
ISOBox.prototype._boxProcessors['elst'] = function() {
this._procFullBox();
Expand Down Expand Up @@ -876,6 +909,30 @@ ISOBox.prototype._boxProcessors['hdlr'] = function() {
this._procField('name', 'string', -1);
};

// ISO/IEC 14496-12:2012 - 9.1.4.1 Identified media data box
ISOBox.prototype._boxProcessors['imda'] = function() {
this._procField('imda_identifier', 'uint', 32);
// until the end of the box
this._procField('data', 'data', -1);
};

// ISO/IEC 14496-12:202x - 8.10.4 Track kind box
ISOBox.prototype._boxProcessors['kind'] = function() {
this._procFullBox();

this._procField('schemeURI', 'utf8string');
this._procField('value', 'utf8string');
};

// ISO/IEC 14496-12:202x - 8.10.5 Label box
ISOBox.prototype._boxProcessors['labl'] = function() {
this._procFullBox();
this.is_group_label = (this.flags & 0x1) != 0;
this._procField('label_id', 'uint', 16);
this._procField('language', 'utf8string');
this._procField('label', 'utf8string');
};

// ISO/IEC 14496-12:2012 - 8.1.1 Media Data Box
ISOBox.prototype._boxProcessors['mdat'] = function() {
this._procField('data', 'data', -1);
Expand Down Expand Up @@ -909,6 +966,13 @@ ISOBox.prototype._boxProcessors['mehd'] = function() {
this._procField('fragment_duration', 'uint', (this.version == 1) ? 64 : 32);
};

// ISO/IEC 14496-12:202x - 8.11.1 Meta box
ISOBox.prototype._boxProcessors['meta'] = function() {
this._procFullBox();

/* rest of the boxes will be read by _parseContainerBox */
};

// ISO/IEC 14496-12:2012 - 8.8.5 Movie Fragment Header Box
ISOBox.prototype._boxProcessors['mfhd'] = function() {
this._procFullBox();
Expand Down Expand Up @@ -968,6 +1032,21 @@ ISOBox.prototype._boxProcessors['prft'] = function() {
this._procField('media_time', 'uint', (this.version == 1) ? 64 : 32);
};

// ISO/IEC 14496-12:202x - 8.18.4.1 Preselection group box
ISOBox.prototype._boxProcessors['prsl'] = function() {
this._procFullBox();
this._procField('group_id', 'uint', 32);
this._procField('num_entities_in_group', 'uint', 32);
this._procEntries('entities', this.num_entities_in_group, function(entry) {
this._procEntryField(entry, 'entity_id', 'uint', 32);
});
if (this.flags & 0x1000) this._procField('preselection_tag', 'utf8string');
if (this.flags & 0x2000) this._procField('selection_priority', 'uint', 8);
if (this.flags & 0x4000) this._procField('interleaving_tag', 'utf8string');

/* rest of the boxes will be read by _parseContainerBox */
};

//ISO/IEC 23001-7:2011 - 8.1 Protection System Specific Header Box
ISOBox.prototype._boxProcessors['pssh'] = function() {
this._procFullBox();
Expand Down Expand Up @@ -1056,6 +1135,11 @@ ISOBox.prototype._boxProcessors['stsd'] = function() {
this._procSubBoxes('entries', this.entry_count);
};

// ISO/IEC 14496-30:2014 - WebVTT Cue Settings Box.
ISOBox.prototype._boxProcessors['sttg'] = function() {
this._procField('settings', 'utf8');
};

// ISO/IEC 14496-12:2012 - 8.6.1.2 Decoding Time To Sample Box
ISOBox.prototype._boxProcessors['stts'] = function() {
this._procFullBox();
Expand Down
4 changes: 2 additions & 2 deletions dist/iso_boxer.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codem-isoboxer",
"version": "0.3.9",
"version": "0.3.10",
"description": "A lightweight JavaScript MP4 (MPEG-4, ISOBMFF) file/box parser.",
"keywords": [
"mp4",
Expand Down
23 changes: 22 additions & 1 deletion src/iso_box.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ ISOBox.create = function(type) {
return newBox;
};

ISOBox.prototype._boxContainers = ['dinf', 'edts', 'mdia', 'meco', 'mfra', 'minf', 'moof', 'moov', 'mvex', 'stbl', 'strk', 'traf', 'trak', 'tref', 'udta', 'vttc', 'sinf', 'schi', 'encv', 'enca'];
ISOBox.prototype._boxContainers = ['dinf', 'edts', 'mdia', 'meco', 'mfra', 'minf', 'moof', 'moov', 'mvex', 'stbl', 'strk', 'traf', 'trak', 'tref', 'udta', 'vttc', 'sinf', 'schi', 'encv', 'enca','meta','grpl','prsl'];

ISOBox.prototype._boxProcessors = {};

Expand Down Expand Up @@ -145,6 +145,8 @@ ISOBox.prototype._readField = function(type, size) {
return this._readData(size);
case 'utf8':
return this._readUTF8String();
case 'utf8string':
return this._readUTF8TerminatedString();
default:
return -1;
}
Expand Down Expand Up @@ -255,6 +257,25 @@ ISOBox.prototype._readUTF8String = function() {
return data ? ISOBoxer.Utils.dataViewToString(data) : data;
};

ISOBox.prototype._readUTF8TerminatedString = function() {
var length = this._raw.byteLength - (this._cursor.offset - this._offset);
var data = null;
if (length > 0) {
data = new DataView(this._raw.buffer, this._cursor.offset, length);

var l;
for (l=0; l<length; l++)
if (data.getUint8(l) === 0)
break;

// remap the Dataview with the actual length
data = new DataView(this._raw.buffer, this._cursor.offset, l);
this._cursor.offset += Math.min(l+1, length);
}

return data ? ISOBoxer.Utils.dataViewToString(data) : data;
};

ISOBox.prototype._parseBox = function() {
this._parsing = true;
this._cursor.offset = this._offset;
Expand Down
5 changes: 5 additions & 0 deletions src/processors/ardi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// ISO/IEC 14496-12:202x - 12.2.8 Audio rendering indication box
ISOBox.prototype._boxProcessors['ardi'] = function() {
this._procFullBox();
this._procField('audio_rendering_indication', 'uint', 8);
};
5 changes: 5 additions & 0 deletions src/processors/elng.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// ISO/IEC 14496-12:202x - 8.4.6 Extended language tag
ISOBox.prototype._boxProcessors['elng'] = function() {
this._procFullBox();
this._procField('extended_language', 'utf8string');
};
6 changes: 6 additions & 0 deletions src/processors/imda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// ISO/IEC 14496-12:2012 - 9.1.4.1 Identified media data box
ISOBox.prototype._boxProcessors['imda'] = function() {
this._procField('imda_identifier', 'uint', 32);
// until the end of the box
this._procField('data', 'data', -1);
};
7 changes: 7 additions & 0 deletions src/processors/kind.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// ISO/IEC 14496-12:202x - 8.10.4 Track kind box
ISOBox.prototype._boxProcessors['kind'] = function() {
this._procFullBox();

this._procField('schemeURI', 'utf8string');
this._procField('value', 'utf8string');
};
8 changes: 8 additions & 0 deletions src/processors/labl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// ISO/IEC 14496-12:202x - 8.10.5 Label box
ISOBox.prototype._boxProcessors['labl'] = function() {
this._procFullBox();
this.is_group_label = (this.flags & 0x1) != 0;
this._procField('label_id', 'uint', 16);
this._procField('language', 'utf8string');
this._procField('label', 'utf8string');
};
6 changes: 6 additions & 0 deletions src/processors/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// ISO/IEC 14496-12:202x - 8.11.1 Meta box
ISOBox.prototype._boxProcessors['meta'] = function() {
this._procFullBox();

/* rest of the boxes will be read by _parseContainerBox */
};
14 changes: 14 additions & 0 deletions src/processors/prsl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// ISO/IEC 14496-12:202x - 8.18.4.1 Preselection group box
ISOBox.prototype._boxProcessors['prsl'] = function() {
this._procFullBox();
this._procField('group_id', 'uint', 32);
this._procField('num_entities_in_group', 'uint', 32);
this._procEntries('entities', this.num_entities_in_group, function(entry) {
this._procEntryField(entry, 'entity_id', 'uint', 32);
});
if (this.flags & 0x1000) this._procField('preselection_tag', 'utf8string');
if (this.flags & 0x2000) this._procField('selection_priority', 'uint', 8);
if (this.flags & 0x4000) this._procField('interleaving_tag', 'utf8string');

/* rest of the boxes will be read by _parseContainerBox */
};
Binary file added test/fixtures/SRMP_AC4.mp4
Binary file not shown.
Binary file modified test/fixtures/editlist.mp4
Binary file not shown.
65 changes: 65 additions & 0 deletions test/spec/boxes_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,71 @@ describe('ctts box', function() {
});
});

describe('Preselections', function() {
describe('prsl box', function() {
it('should correctly parse the prsl box from sample data', function() {
var parsedFile = loadParsedFixture('./test/fixtures/SRMP_AC4.mp4');
var boxes = parsedFile.fetchAll('prsl');
expect(boxes.length).toEqual(6);
expect(boxes[1].type).toEqual('prsl');
expect(boxes[1].group_id).toEqual(234);
expect(boxes[1].num_entities_in_group).toEqual(1);
expect(boxes[1].entities[0].entity_id).toEqual(1);
expect(boxes[1].preselection_tag).toEqual('1')
expect(boxes[1].selection_priority).toEqual(1)
});
});

describe('labl box', function() {
it('should correctly parse the box from sample data', function() {
var parsedFile = loadParsedFixture('./test/fixtures/SRMP_AC4.mp4');
var box = parsedFile.fetchAll('prsl').filter((b) => b.group_id === 234)[0];
expect(box).toBeDefined()
expect(box.boxes).toBeDefined()
var boxes = box.boxes.filter((b) => b.type === 'labl')
expect(boxes[0].type).toEqual('labl');
expect(boxes[0].is_group_label).toEqual(false);
expect(boxes[0].language.localeCompare('en')).toBe(0);
expect(boxes[0].label).toEqual('Spanish');
expect(boxes[1].type).toEqual('labl');
expect(boxes[1].is_group_label).toEqual(false);
expect(boxes[1].language.localeCompare('es')).toBe(0);
expect(boxes[1].label).toEqual('Español');
});
});

describe('elng box', function() {
it('should correctly parse the box from sample data', function() {
var parsedFile = loadParsedFixture('./test/fixtures/SRMP_AC4.mp4');
var box = parsedFile.fetch('elng');
expect(box.type).toEqual('elng');
expect(box.extended_language.localeCompare('en')).toBe(0);
});
});

describe('ardi box', function() {
it('should correctly parse the box from sample data', function() {
var parsedFile = loadParsedFixture('./test/fixtures/SRMP_AC4.mp4');
var box = parsedFile.fetch('prsl');
expect(box).toBeDefined()
box = box.boxes.find((b) => b.type === 'ardi')
expect(box).toBeDefined()
expect(box.type).toEqual('ardi');
expect(box.audio_rendering_indication <= 4).toBe(true);
});
});

describe('kind box', function() {
it('should correctly parse the box from sample data', function() {
var parsedFile = loadParsedFixture('./test/fixtures/SRMP_AC4.mp4');
var box = parsedFile.fetch('kind');
expect(box.type).toEqual('kind');
expect(box.schemeURI).toEqual('urn:mpeg:dash:role:2011')
expect(box.value).toEqual('main')
});
});
});

describe('Text samples', function() {
describe('vttc box', function() {
it('should correctly parse the box from sample data', function() {
Expand Down

0 comments on commit a99f454

Please sign in to comment.