Skip to content

Commit

Permalink
fix: fix default EXT-X-BYTERANGE offset to start after the previous s…
Browse files Browse the repository at this point in the history
…egment (#98)

The default value for EXT-X-BYTERANGE is defined by the spec as the first byte
after the previous segment. Prior to this change, the default set by m3u8-parser
was 0.
  • Loading branch information
gesinger authored Feb 11, 2020
1 parent 4ad5c2d commit 08aca73
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
20 changes: 16 additions & 4 deletions src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export default class Parser extends Stream {
discontinuityStarts: [],
segments: []
};
// keep track of the last seen segment's byte range end, as segments are not required
// to provide the offset, in which case it defaults to the next byte after the
// previous segment
let lastByterangeEnd = 0;

// update the manifest with the m3u8 entry from the parse stream
this.parseStream.on('data', function(entry) {
Expand Down Expand Up @@ -89,16 +93,24 @@ export default class Parser extends Stream {
byterange.length = entry.length;

if (!('offset' in entry)) {
this.trigger('info', {
message: 'defaulting offset to zero'
});
entry.offset = 0;
/*
* From the latest spec (as of this writing):
* https://tools.ietf.org/html/draft-pantos-http-live-streaming-23#section-4.3.2.2
*
* Same text since EXT-X-BYTERANGE's introduction in draft 7:
* https://tools.ietf.org/html/draft-pantos-http-live-streaming-07#section-3.3.1)
*
* "If o [offset] is not present, the sub-range begins at the next byte
* following the sub-range of the previous media segment."
*/
entry.offset = lastByterangeEnd;
}
}
if ('offset' in entry) {
currentUri.byterange = byterange;
byterange.offset = entry.offset;
}
lastByterangeEnd = byterange.offset + byterange.length;
},
endlist() {
this.manifest.endList = true;
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/m3u8/byteRange.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
{
"byterange": {
"length": 713084,
"offset": 0
"offset": 1110328
},
"duration": 10,
"timeline": 0,
Expand Down
56 changes: 56 additions & 0 deletions test/m3u8.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,62 @@ QUnit.test('Widevine #EXT-X-KEY attributes not attached to manifest if KEYFORMAT
assert.notOk(parser.manifest.contentProtection, 'contentProtection not added');
});

QUnit.test('byterange offset defaults to next byte', function(assert) {
const parser = new Parser();

const manifest = [
'#EXTM3U',
'#EXTINF:5,',
'#EXT-X-BYTERANGE:10@5',
'segment.ts',
'#EXTINF:5,',
'#EXT-X-BYTERANGE:20',
'segment.ts',
'#EXTINF:5,',
'#EXT-X-BYTERANGE:30',
'segment.ts',
'#EXTINF:5,',
'segment2.ts',
'#EXT-X-BYTERANGE:15@100',
'segment.ts',
'#EXT-X-BYTERANGE:17',
'segment.ts',
'#EXT-X-ENDLIST'
].join('\n');

parser.push(manifest);

assert.deepEqual(
parser.manifest.segments[0].byterange,
{ length: 10, offset: 5 },
'first segment has correct byterange'
);
assert.deepEqual(
parser.manifest.segments[1].byterange,
{ length: 20, offset: 15 },
'second segment has correct byterange'
);
assert.deepEqual(
parser.manifest.segments[2].byterange,
{ length: 30, offset: 35 },
'third segment has correct byterange'
);
assert.notOk(parser.manifest.segments[3].byterange, 'fourth segment has no byterange');
assert.deepEqual(
parser.manifest.segments[4].byterange,
{ length: 15, offset: 100 },
'fifth segment has correct byterange'
);
// not tested is a segment with no offset coming after a segment that isn't a sub range,
// as the spec requires that a byterange without an offset must follow a segment that
// is a sub range of the same media resource
assert.deepEqual(
parser.manifest.segments[5].byterange,
{ length: 17, offset: 115 },
'sixth segment has correct byterange'
);
});

QUnit.module('m3u8s');

QUnit.test('parses static manifests as expected', function(assert) {
Expand Down

0 comments on commit 08aca73

Please sign in to comment.