Skip to content

Commit

Permalink
feat: add EventStream support
Browse files Browse the repository at this point in the history
  • Loading branch information
adrums86 committed Mar 7, 2023
1 parent 3fc0486 commit f1a09ca
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 2 deletions.
42 changes: 41 additions & 1 deletion src/inheritAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,45 @@ export const parseCaptionServiceMetadata = (service) => {
}
};

/**
* A map callback that will parse all event stream data for a collection of periods
* DASH ISO_IEC_23009 5.10.2.2
* https://dashif-documents.azurewebsites.net/Events/master/event.html#mpd-event-timing
*
* @param {PeriodInformation} period object containing necessary period information
* @return a collection of parsed eventstream event objects with
*/
export const toEventStream = (period) => {
// get all EventStreams tags and parse attributes and children
return findChildren(period.node, 'EventStream').flatMap((eventStream) => {
const eventStreamAttributes = parseAttributes(eventStream);
const schemeIdUri = eventStreamAttributes.schemeIdUri;
// schemeIdUri is mandatory for EventStream tags

if (!schemeIdUri) {
return;
}

// find all Events per EventStream tag and map to return objects
return findChildren(eventStream, 'Event').map((event) => {
const eventAttributes = parseAttributes(event);
const presentationTime = eventAttributes.presentationTime || 0;
const timescale = eventStreamAttributes.timescale || 1;
const duration = eventAttributes.duration || 0;
const start = (presentationTime / timescale) + period.attributes.start;

return {
schemeIdUri,
value: eventStreamAttributes.value,
id: eventAttributes.id,
start,
end: start + (duration / timescale),
messageData: eventAttributes.messageData
};
});
});
};

/**
* Maps an AdaptationSet node to a list of Representation information objects
*
Expand Down Expand Up @@ -531,6 +570,7 @@ export const inheritAttributes = (mpd, options = {}) => {

return {
locations: mpdAttributes.locations,
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls)))
representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))),
eventStreamInfo: flatten(periods.map(toEventStream))
};
};
13 changes: 13 additions & 0 deletions src/parseAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,19 @@ export const parsers = {
return parseInt(value, 10);
},

/**
* Specifies the presentationTime.
*
* @param {string} value
* value of the attribute as a string
*
* @return {number}
* The parsed presentationTime
*/
presentationTime(value) {
return parseInt(value, 10);
},

/**
* Default parser for all other attributes. Acts as a no-op and just returns the value
* as a string
Expand Down
129 changes: 128 additions & 1 deletion test/inheritAttributes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import {
buildBaseUrls,
parseCaptionServiceMetadata,
getSegmentInformation,
getPeriodStart
getPeriodStart,
toEventStream
} from '../src/inheritAttributes';
import { stringToMpdXml } from '../src/stringToMpdXml';
import errors from '../src/errors';
import QUnit from 'qunit';
import { toPlaylists } from '../src/toPlaylists';
import decodeB64ToUint8Array from '@videojs/vhs-utils/es/decode-b64-to-uint8-array';
import { findChildren } from '../src/utils/xml';

QUnit.module('buildBaseUrls');

Expand Down Expand Up @@ -544,6 +546,7 @@ QUnit.test('end to end - basic', function(assert) {
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -618,6 +621,7 @@ QUnit.test('end to end - basic dynamic', function(assert) {
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -699,6 +703,7 @@ QUnit.test('end to end - basic multiperiod', function(assert) {
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -785,6 +790,7 @@ QUnit.test('end to end - inherits BaseURL from all levels', function(assert) {
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -861,6 +867,7 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) {
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -1028,6 +1035,7 @@ QUnit.test(
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -1140,6 +1148,7 @@ QUnit.test(
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -1263,6 +1272,7 @@ QUnit.test(
`), { NOW });

const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -2106,6 +2116,7 @@ QUnit.test('keySystem info for representation - lowercase UUIDs', function(asser

// inconsistent quoting because of quote-props
const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -2192,6 +2203,7 @@ QUnit.test('keySystem info for representation - uppercase UUIDs', function(asser

// inconsistent quoting because of quote-props
const expected = {
eventStreamInfo: [],
locations: undefined,
representationInfo: [{
attributes: {
Expand Down Expand Up @@ -2238,3 +2250,118 @@ QUnit.test('keySystem info for representation - uppercase UUIDs', function(asser
assert.equal(actual.representationInfo.length, 1);
assert.deepEqual(actual, expected);
});

QUnit.test('gets EventStream data from toEventStream', function(assert) {
const mpd = stringToMpdXml(`
<MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
<Period id="dai_pod-0001065804-ad-1" start="PT17738H17M14.156S" duration="PT9.977S">
<BaseURL>https://www.example.com/base/</BaseURL>
<SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
<EventStream schemeIdUri="urn:google:dai:2018" timescale="1000">
<Event presentationTime="100" duration="0" id="0" messageData="foo"/>
<Event presentationTime="900" duration="0" id="5" messageData="bar"/>
<Event presentationTime="1900" duration="0" id="6" messageData="foo_bar"/>
</EventStream>
</Period>
</MPD>`);
const expected = [
{
end: 2.1,
id: '0',
messageData: 'foo',
schemeIdUri: 'urn:google:dai:2018',
start: 2.1,
value: undefined
},
{
end: 2.9,
id: '5',
messageData: 'bar',
schemeIdUri: 'urn:google:dai:2018',
start: 2.9,
value: undefined
},
{
end: 3.9,
id: '6',
messageData: 'foo_bar',
schemeIdUri: 'urn:google:dai:2018',
start: 3.9,
value: undefined
}
];

const firstPeriod = { node: findChildren(mpd, 'Period')[0], attributes: { start: 2 } };
const eventStreams = toEventStream(firstPeriod);

assert.deepEqual(eventStreams, expected, 'getEventStreams returns the expected object');
});

QUnit.test('cannot get EventStream data from toEventStream with no schemeIdUri', function(assert) {
const mpd = stringToMpdXml(`
<MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
<Period id="dai_pod-0001065804-ad-1" start="PT17738H17M14.156S" duration="PT9.977S">
<BaseURL>https://www.example.com/base/</BaseURL>
<SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
<EventStream timescale="1000">
<Event presentationTime="100" duration="0" id="0" messageData="foo"/>
<Event presentationTime="900" duration="0" id="5" messageData="bar"/>
<Event presentationTime="1900" duration="0" id="6" messageData="foo_bar"/>
</EventStream>
</Period>
</MPD>`);

const firstPeriod = { node: findChildren(mpd, 'Period')[0], attributes: { start: 2} };
const eventStreams = toEventStream(firstPeriod);

assert.deepEqual(eventStreams, [undefined], 'getEventStreams returns the expected object');
});

QUnit.test('gets EventStreamInfo from inheritAttributes', function(assert) {
const mpd = stringToMpdXml(`
<MPD mediaPresentationDuration="PT30S" xmlns:cenc="urn:mpeg:cenc:2013">
<Period id="dai_pod-0001065804-ad-1" start="PT0H0M14.9S" duration="PT9.977S">
<BaseURL>https://www.example.com/base/</BaseURL>
<SegmentTemplate media="$RepresentationID$/$Number$.mp4" initialization="$RepresentationID$/init.mp4"/>
<EventStream schemeIdUri="urn:google:dai:2018" timescale="1000">
<Event presentationTime="100" duration="0" id="0" messageData="foo"/>
<Event presentationTime="1100" duration="0" id="5" messageData="bar"/>
<Event presentationTime="2100" duration="0" id="6" messageData="foo_bar"/>
</EventStream>
</Period>
</MPD>`);
const expected = {
eventStreamInfo: [
{
end: 15,
id: '0',
messageData: 'foo',
schemeIdUri: 'urn:google:dai:2018',
start: 15,
value: undefined
},
{
end: 16,
id: '5',
messageData: 'bar',
schemeIdUri: 'urn:google:dai:2018',
start: 16,
value: undefined
},
{
end: 17,
id: '6',
messageData: 'foo_bar',
schemeIdUri: 'urn:google:dai:2018',
start: 17,
value: undefined
}
],
locations: undefined,
representationInfo: []
};

const eventStreams = inheritAttributes(mpd);

assert.deepEqual(eventStreams, expected, 'getEventStreams returns the expected object');
});

0 comments on commit f1a09ca

Please sign in to comment.