Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
brendanlong committed Apr 9, 2014
0 parents commit 5a5be57
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
10 changes: 10 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Copyright (c) 2014, CableLabs, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
122 changes: 122 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# MPEG-TS Sections in JavaScript

Author: Brendan Long <[email protected]>

This library decodes [MPEG-TS program-specific information][mpegts-psi] into JSON. It's intended to be used with [HTML5 DataCues][datacue], but will work in any case where you have an `ArrayBuffer`.

## Using this library

To use, copy mpegtssections.js into your application's script directory, then add something like this to the `<head>` of your HTML page(s):

<script src="your-script-directory/mpegtssections.js"></script>

You can then use `MpegTs` methods in your JavaScript code.

## API Documentation

### Data Structures

The following uses [Web IDL][webidl] syntax to describe the resulting data structures. The attribute names are taken directly from the MPEG-TS spec.

#### Generic MPEG-TS Section

interface MpegTsSyntaxSection {
attribute unsigned short table_id_extension;
attribute octet version_number;
attribute boolean current_next_indicator;
attribute octet section_number;
attribute octet last_section_number;
attribute unsigned long crc32;
}

interface MpegTsSection {
attribute octet table_id;
attribute unsigned short section_length;
attribute MpegTsSyntaxSection? syntax_section;
}

interface MpegTsDescriptor {
attribute octet tag;
attribute octet length; // in bytes
attribute ArrayBuffer data;
}

#### Program Association Section

See Table 2-25 - Program association section

interface MpegTsPatProgramInfo {
attribute unsigned short program_number;

attribute unsigned short? network_PID; // if program_number == 0
attribute unsigned short? program_map_PID; // if program_number != 0
}

interface MpegTsPat implements MpegTsSection {
attribute unsigned short transport_stream_id;
attribute MpegTsPatProgramInfo[] program_info;
}

#### Conditional Access Table

See Table 2-27 - Conditional access section.

interface MpegTsCat implements MpegTsSection {
attribute MpegTsDescriptor[] descriptors;
}

#### Program Map Table

See Table 2-28 - Transport Stream program map section.

interface MpegTsElementaryStream {
attribute octet stream_type;
attribute unsigned short elementary_PID;
attribute unsigned short ES_info_length;
attribute MpegTsDescriptor[] descriptors;
}

interface MpegTsPmt implements MpegTsSection {
attribute unsigned short? PCR_PID; // 8191 maps to null
attribute octet program_info_length;
attribute MpegTsDescriptor[] descriptors;
attribute MpegTsElementaryStreamData[] stream_info;
}

#### Private Section

See Table 2-30 - Private Section

interface MpegTsPrivateSection implements MpegTsSection {
attribute boolean private_indicator;
attribute unsigned short private_section_length;
attribute MpegTsSyntaxSection? syntax_section;
ArrayBuffer private_data;
}

#### Transport Stream Description

See Table 2-30-1 - The Transport Stream Description Table

interface MpegTsDescriptionSection implements MpegTsSection {
attribute MpegTsDescriptor[] descriptors;
}

#### Functions

`String MpegTs.decodeTable(ArrayBuffer buf, boolean checkReservedBits)`

If `buf` is a PSI table (starting with the `table_id`), it will be decoded into the most appropriate type, using the following algorithm:

1. If there are any serious problems with the data (lengths are wrong), throw an exception.
2. If the `table_id` is 0, return an `MpegTsPat`.
3. If the `table_id` is 1, return an `MpegTsCat`.
4. If the `table_id` is 2, return an `MpegTsPmt`.
5. If the `table_id` is 3, return an `MpegTsDescriptionSection`.
6. If the `table_id` is >= 128, return an `MpegTsPrivateSection`.
7. If the `sectionSyntaxIndicator` is `true`, return an `MpegTsTableWithSyntaxSection`.
8. Return an `MpegTsTable`.

[datacue]: http://www.w3.org/html/wg/drafts/html/CR/embedded-content-0.html#datacue
[mpegts-psi]: http://en.wikipedia.org/wiki/Program-specific_information
[webidl]: http://www.w3.org/TR/WebIDL/
149 changes: 149 additions & 0 deletions lib/mpegtssections.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* Copyright (c) 2014, CableLabs, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint browser: true, node: true, bitwise: true, plusplus: true, vars: true,
indent: 4, maxlen: 80 */
(function (exports) {
"use strict";

/* Constants */
var TableIds = {
PROGRAM_ASSOCIATION_SECTION: 0,
CONDITIONAL_ACCESS_SECTION: 1,
TS_PROGRAM_MAP_SECTION: 2,
TS_DESCRIPTION_SECTION: 3,
ISO_IEC_14496_SCENE_DESCRIPTION_SECTION: 4,
ISO_IEC_14496_OBJECT_DESCRIPTION_SECTION: 5
};

var ReservedPids = {
PROGRAM_ASSOCIATION: 0,
CONDITIONAL_ACCESS: 1,
TRANSPORT_STREAM_DESCRIPTION: 2
};

/* Errors */
function defineError(name, defaultMessage) {
function NewError(message) {
this.name = name;
this.message = message;
}
NewError.prototype = new Error();
NewError.prototype.constructor = NewError;
return NewError;
}
var BadSizeError = defineError("BadSizeError");
var MissingSyntaxSectionError = defineError("MissingSyntaxSectionError");

/* Functions */
function decodeSection(buf) {
if (!buf || !(buf instanceof ArrayBuffer)) {
throw new TypeError("Expected an ArrayBuffer as the first " +
"argument but got " + typeof buf + ": " + buf);
}

if (buf.byteLength < 3) {
throw new BadSizeError("MPEG-TS sections must be at least 3 " +
"bytes long, but got buffer with length " + buf.byteLength);
}

var view = new DataView(buf);
// check CRC32?

var section = {
table_id: view.getUint8(0),
section_length: view.getUint16(1) & 0xFFF
};

// if section_syntax_indicator is set, parse the syntax section
if ((view.getUint8(1) & 0x80) >> 7) {
if (buf.byteLength < 7) {
throw new BadSizeError("section_syntax_indicator is 1, but " +
"the buffer is not long enough to contain a valid " +
"syntax section");
}

section.syntax_section = {
table_id_extension: view.getUint16(3),
version_number: (view.getUint8(5) & 0x3E) >> 1,
current_next_indicator: view.getUint8(5) & 1,
section_number: view.getUint8(6),
last_section_number: view.getUint8(7),
CRC: view.getUint32(buf.byteLength - 4) // check this
};
} else {
section.syntax_section = null;
}

switch (section.table_id) {
case TableIds.PROGRAM_ASSOCIATION_SECTION:
if (!section.syntax_section) {
throw new MissingSyntaxSectionError("Program access section " +
"requires a syntax section, but " +
"section_syntax_indicator is 0");
}
section.transport_stream_id = view.getUint16(3);
// program info
break;
case TableIds.CONDITIONAL_ACCESS_SECTION:
case TableIds.TS_DESCRIPTION_SECTION:
// descriptors
break;
case TableIds.TS_PROGRAM_MAP_SECTION:
section.program_number = view.getUint16(3);
section.PCR_PID = view.getUint16(8) & 0x1FFF;
section.program_info_length = view.getUint16(10) & 0xFFF;
section.streams = [];

var stream_start = 12 + section.program_info_length;
var streams_end = buf.byteLength - 4 - 5;
var i = 0;
while (stream_start <= streams_end) {
section.streams[i] = {
stream_type: view.getUint8(stream_start),
elementary_PID: view.getUint16(stream_start + 1) & 0x1FFF,
ES_info_length: view.getUint16(stream_start + 3) & 0xFFF
};
stream_start += 5 + section.streams[i].ES_info_length;
++i;
}
break;
default:
if (section.table_id >= 128) {
// private data
}
break;
}
return section;
}

exports.TableIds = TableIds;
exports.ReservedPids = ReservedPids;

exports.BadSizeError = BadSizeError;
exports.MissingSyntaxSectionError = MissingSyntaxSectionError;

exports.decodeSection = decodeSection;
}(typeof exports === 'undefined' ? this.MpegTs = {} : exports));
25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"author": {
"name": "Brendan Long",
"email": "[email protected]",
"url": "https://www.brendanlong.com"
},
"bugs": "https://github.com/cablelabs/mpegtssections-js/issues",
"devDependencies": {
"jshint": "2.5.x",
"nodeunit": "0.8.x"
},
"homepage": "https://github.com/cablelabs/mpegtssections-js",
"license": "BSD-2-Clause",
"main": "./lib/mpegtssections",
"name": "mpegtssections",
"repository": {
"type": "git",
"url": "https://github.com/cablelabs/mpegtssections-js.git"
},
"scripts": {
"hint": "./node_modules/.bin/jshint --show-non-errors lib/mpegtssections.js test/tests.js",
"test": "./node_modules/.bin/nodeunit test"
},
"version": "1.0.0"
}
64 changes: 64 additions & 0 deletions test/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/* Copyright (c) 2014, CableLabs, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint browser: true, node: true, plusplus: true, vars: true, indent: 4 */
"use strict";
var MpegTs = require('../lib/mpegtssections');

exports.TestBufArgumentNull = function(test) {
test.throws(function() {
MpegTs.decodeSection(null);
}, TypeError);
test.done();
};

exports.TestBufArgumentUndefined = function(test) {
test.throws(function() {
MpegTs.decodeSection();
}, TypeError);
test.done();
};

exports.testBufArgumentWrongType = function(test) {
test.throws(function() {
MpegTs.decodeSection([5]);
}, TypeError);
test.done();
};

exports.testBufArgumentTooSmall = function(test) {
test.throws(function() {
var data = new Uint8Array([1, 2]).buffer;
MpegTs.decodeSection(data);
}, MpegTs.BadSizeError);
test.done();
};

exports.TestUserPrivateData = function(test) {
var data = new Uint8Array([227, 64, 136, 251, 251, 0, 59, 176, 126, 0, 1, 193, 0, 0, 17, 3, 16, 2, 128, 0, 0, 1, 255, 0, 0, 105, 0, 0, 0, 1, 3, 216, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 12, 1, 60, 59, 67, 97, 98, 108, 101, 108, 97, 98, 115, 95, 78, 97, 116, 105, 111, 110, 97, 108, 95, 101, 116, 118, 95, 115, 116, 114, 101, 97, 109, 95, 99, 111, 110, 102, 105, 103, 47, 109, 97, 105, 110, 97, 112, 112, 47, 49, 46, 48, 47, 109, 97, 105, 110, 95, 112, 114, 46, 112, 114, 0, 15, 14, 105, 98, 46, 116, 118, 119, 111, 114, 107, 115, 46, 99, 111, 109, 225, 54, 136, 221, 188, 252, 142, 137]).buffer;
var section = MpegTs.decodeSection(data);
test.ok(section, "Section should not be null");
test.done();
};

0 comments on commit 5a5be57

Please sign in to comment.