mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-26 17:46:26 +03:00
Add support for null TS packets in HLS
The timestamp parser in our HLS parser should not fail on null TS packets. These are indicates by a packet ID of 8191 (0x1fff), and according to the spec, should be skipped by any receiver. We will not skip such packets and continue through the segment to find a PES packet with a timestamp. In addition to the comments in #2546, I found this document helpful: https://www.mikrocontroller.net/attachment/27265/mpeg2ts.pdf Closes #2546 Change-Id: Id9dc16160bbde03969199150ca50122f12de77f4
This commit is contained in:
+10
-1
@@ -1966,9 +1966,18 @@ shaka.hls.HlsParser = class {
|
||||
}
|
||||
|
||||
const flagsAndPacketId = reader.readUint16();
|
||||
const packetId = flagsAndPacketId & 0x1fff;
|
||||
if (packetId == 0x1fff) {
|
||||
// A "null" TS packet. Skip this TS packet and try again.
|
||||
skipPacket();
|
||||
continue;
|
||||
}
|
||||
|
||||
const hasPesPacket = flagsAndPacketId & 0x4000;
|
||||
if (!hasPesPacket) {
|
||||
fail();
|
||||
// Not a PES packet yet. Skip this TS packet and try again.
|
||||
skipPacket();
|
||||
continue;
|
||||
}
|
||||
|
||||
const flags = reader.readUint8();
|
||||
|
||||
@@ -1973,6 +1973,8 @@ describe('HlsParser', () => {
|
||||
let segmentDataStartTime;
|
||||
/** @type {!Uint8Array} */
|
||||
let tsSegmentData;
|
||||
/** @type {!Uint8Array} */
|
||||
let nullTsPacketData;
|
||||
|
||||
const master = [
|
||||
'#EXTM3U\n',
|
||||
@@ -2026,6 +2028,10 @@ describe('HlsParser', () => {
|
||||
// 180000 (TS PTS) divided by fixed TS timescale (90000) = 2s.
|
||||
// 2000 (MP4 PTS) divided by parsed MP4 timescale (1000) = 2s.
|
||||
segmentDataStartTime = 2;
|
||||
nullTsPacketData = new Uint8Array([
|
||||
0x47, // TS sync byte (fixed value)
|
||||
0x1f, 0xff, // null packet (packet ID 8191)
|
||||
]);
|
||||
});
|
||||
|
||||
it('parses start time from mp4 segment', async () => {
|
||||
@@ -2091,6 +2097,46 @@ describe('HlsParser', () => {
|
||||
partialEndByte);
|
||||
});
|
||||
|
||||
it('parses start time from ts segments with null packets', async () => {
|
||||
const tsMediaPlaylist = media.replace(/\.mp4/g, '.ts');
|
||||
|
||||
// Each packet is 188 bytes, so allocate space for 3.
|
||||
const tsSegmentWithNullPackets = new Uint8Array(188 * 3);
|
||||
// The first two are "null" packets.
|
||||
tsSegmentWithNullPackets.set(nullTsPacketData, /* offset= */ 0);
|
||||
tsSegmentWithNullPackets.set(nullTsPacketData, /* offset= */ 188);
|
||||
// The third has a timestamp.
|
||||
tsSegmentWithNullPackets.set(tsSegmentData, /* offset= */ 188 * 2);
|
||||
|
||||
fakeNetEngine
|
||||
.setResponseText('test:/master', master)
|
||||
.setResponseText('test:/video', tsMediaPlaylist)
|
||||
.setResponseValue('test:/main.ts', tsSegmentWithNullPackets);
|
||||
|
||||
const expectedRef = ManifestParser.makeReference(
|
||||
/* uri= */ 'test:/main.ts',
|
||||
/* startTime= */ 0,
|
||||
/* endTime= */ 5,
|
||||
/* baseUri= */ '',
|
||||
expectedStartByte,
|
||||
expectedEndByte);
|
||||
// In VOD content, we set the timestampOffset to align the
|
||||
// content to presentation time 0.
|
||||
expectedRef.timestampOffset = -segmentDataStartTime;
|
||||
|
||||
const manifest = await parser.start('test:/master', playerInterface);
|
||||
const video = manifest.variants[0].video;
|
||||
await video.createSegmentIndex();
|
||||
ManifestParser.verifySegmentIndex(video, [expectedRef]);
|
||||
|
||||
// Make sure the segment data was fetched with the correct byte
|
||||
// range.
|
||||
fakeNetEngine.expectRangeRequest(
|
||||
'test:/main.ts',
|
||||
expectedStartByte,
|
||||
partialEndByte);
|
||||
});
|
||||
|
||||
// We want to make sure that we can interrupt the parser while it is getting
|
||||
// the start time. This is a regression test for Issue #1788 where
|
||||
// interrupting the partial network request would be misinterpreted as the
|
||||
|
||||
Reference in New Issue
Block a user