diff --git a/lib/util/ts_parser.js b/lib/util/ts_parser.js index 8f77dc7ad8..50c5a4602c 100644 --- a/lib/util/ts_parser.js +++ b/lib/util/ts_parser.js @@ -23,6 +23,9 @@ shaka.util.TsParser = class { /** @private {boolean} */ this.pmtParsed_ = false; + /** @private {?number} */ + this.videoStartTime_ = null; + /** @private {?number} */ this.videoPid_ = null; @@ -32,6 +35,9 @@ shaka.util.TsParser = class { /** @private {!Array.} */ this.videoData_ = []; + /** @private {?number} */ + this.audioStartTime_ = null; + /** @private {?number} */ this.audioPid_ = null; @@ -55,6 +61,7 @@ shaka.util.TsParser = class { * @return {!shaka.util.TsParser} */ parse(data) { + const timescale = shaka.util.TsParser.Timescale_; const packetLength = shaka.util.TsParser.PacketLength_; // A TS fragment should contain at least 3 TS packets, a PAT, a PMT, and @@ -135,12 +142,28 @@ shaka.util.TsParser = class { this.pmtParsed_ = true; break; } - case this.videoPid_: - this.videoData_.push(data.subarray(offset, start + packetLength)); + case this.videoPid_: { + const videoData = data.subarray(offset, start + packetLength); + if (this.videoStartTime_ == null) { + const pes = this.parsePES(videoData); + if (pes && pes.pts != null) { + this.videoStartTime_ = pes.pts / timescale; + } + } + this.videoData_.push(videoData); break; - case this.audioPid_: - this.audioData_.push(data.subarray(offset, start + packetLength)); + } + case this.audioPid_: { + const audioData = data.subarray(offset, start + packetLength); + if (this.audioStartTime_ == null) { + const pes = this.parsePES(audioData); + if (pes && pes.pts != null) { + this.audioStartTime_ = pes.pts / timescale; + } + } + this.audioData_.push(audioData); break; + } case this.id3Pid_: this.id3Data_.push(data.subarray(offset, start + packetLength)); break; @@ -180,8 +203,8 @@ shaka.util.TsParser = class { audio: -1, video: -1, id3: -1, - audioCodec: 'aac', - videoCodec: 'avc', + audioCodec: '', + videoCodec: '', }; const sectionLength = ((data[offset + 1] & 0x0f) << 8) | data[offset + 2]; const tableEnd = offset + 3 + sectionLength - 4; @@ -201,6 +224,7 @@ shaka.util.TsParser = class { case 0x0f: if (result.audio === -1) { result.audio = pid; + result.audioCodec = 'aac'; } break; // Packetized metadata (ID3) @@ -216,6 +240,7 @@ shaka.util.TsParser = class { case 0x1b: if (result.video === -1) { result.video = pid; + result.videoCodec = 'avc'; } break; // ISO/IEC 11172-3 (MPEG-1 audio) @@ -332,6 +357,30 @@ shaka.util.TsParser = class { return metadata; } + /** + * Return the start time for the audio and video + * + * @return {{audio: ?number, video: ?number}} + */ + getStartTime() { + return { + audio: this.audioStartTime_, + video: this.videoStartTime_, + }; + } + + /** + * Return the audio and video codecs + * + * @return {{audio: ?string, video: ?string}} + */ + getCodecs() { + return { + audio: this.audioCodec_, + video: this.videoCodec_, + }; + } + /** * Check if the passed data corresponds to an MPEG2-TS * @@ -385,6 +434,13 @@ shaka.util.TsParser = class { shaka.util.TsParser.PacketLength_ = 188; +/** + * @const {number} + * @private + */ +shaka.util.TsParser.Timescale_ = 90000; + + /** * @typedef {{ * audio: number, diff --git a/test/util/ts_parser_unit.js b/test/util/ts_parser_unit.js index 3744a6796a..de159829ea 100644 --- a/test/util/ts_parser_unit.js +++ b/test/util/ts_parser_unit.js @@ -48,4 +48,26 @@ describe('TsParser', () => { expect(metadata).toBeTruthy(); expect(metadata.length).toBe(2); }); + + it('get the start time from a TS segment', async () => { + const responses = await Promise.all([ + Util.fetch('/base/test/test/assets/id3-metadata.ts'), + ]); + const tsSegment = BufferUtils.toUint8(responses[0]); + const starttime = new shaka.util.TsParser().parse(tsSegment) + .getStartTime(); + expect(starttime.audio).toBeCloseTo(90019.586, 3); + expect(starttime.video).toBe(null); + }); + + it('get the codecs from a TS segment', async () => { + const responses = await Promise.all([ + Util.fetch('/base/test/test/assets/id3-metadata.ts'), + ]); + const tsSegment = BufferUtils.toUint8(responses[0]); + const codecs = new shaka.util.TsParser().parse(tsSegment) + .getCodecs(); + expect(codecs.audio).toBe('aac'); + expect(codecs.video).toBe(''); + }); });