Home Reference Source

src/demux/tsdemuxer.js

  1. /**
  2. * highly optimized TS demuxer:
  3. * parse PAT, PMT
  4. * extract PES packet from audio and video PIDs
  5. * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet
  6. * trigger the remuxer upon parsing completion
  7. * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource.
  8. * it also controls the remuxing process :
  9. * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state.
  10. */
  11.  
  12. import * as ADTS from './adts';
  13. import MpegAudio from './mpegaudio';
  14. import Event from '../events';
  15. import ExpGolomb from './exp-golomb';
  16. import SampleAesDecrypter from './sample-aes';
  17. // import Hex from '../utils/hex';
  18. import { logger } from '../utils/logger';
  19. import { ErrorTypes, ErrorDetails } from '../errors';
  20. import { utf8ArrayToStr } from './id3';
  21.  
  22. // We are using fixed track IDs for driving the MP4 remuxer
  23. // instead of following the TS PIDs.
  24. // There is no reason not to do this and some browsers/SourceBuffer-demuxers
  25. // may not like if there are TrackID "switches"
  26. // See https://github.com/video-dev/hls.js/issues/1331
  27. // Here we are mapping our internal track types to constant MP4 track IDs
  28. // With MSE currently one can only have one track of each, and we are muxing
  29. // whatever video/audio rendition in them.
  30. const RemuxerTrackIdConfig = {
  31. video: 1,
  32. audio: 2,
  33. id3: 3,
  34. text: 4
  35. };
  36.  
  37. class TSDemuxer {
  38. constructor (observer, remuxer, config, typeSupported) {
  39. this.observer = observer;
  40. this.config = config;
  41. this.typeSupported = typeSupported;
  42. this.remuxer = remuxer;
  43. this.sampleAes = null;
  44. this.pmtUnknownTypes = {};
  45. }
  46.  
  47. setDecryptData (decryptdata) {
  48. if ((decryptdata != null) && (decryptdata.key != null) && (decryptdata.method === 'SAMPLE-AES')) {
  49. this.sampleAes = new SampleAesDecrypter(this.observer, this.config, decryptdata, this.discardEPB);
  50. } else {
  51. this.sampleAes = null;
  52. }
  53. }
  54.  
  55. static probe (data) {
  56. const syncOffset = TSDemuxer._syncOffset(data);
  57. if (syncOffset < 0) {
  58. return false;
  59. } else {
  60. if (syncOffset) {
  61. logger.warn(`MPEG2-TS detected but first sync word found @ offset ${syncOffset}, junk ahead ?`);
  62. }
  63.  
  64. return true;
  65. }
  66. }
  67.  
  68. static _syncOffset (data) {
  69. // scan 1000 first bytes
  70. const scanwindow = Math.min(1000, data.length - 3 * 188);
  71. let i = 0;
  72. while (i < scanwindow) {
  73. // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47
  74. if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) {
  75. return i;
  76. } else {
  77. i++;
  78. }
  79. }
  80. return -1;
  81. }
  82.  
  83. /**
  84. * Creates a track model internal to demuxer used to drive remuxing input
  85. *
  86. * @param {string} type 'audio' | 'video' | 'id3' | 'text'
  87. * @param {number} duration
  88. * @return {object} TSDemuxer's internal track model
  89. */
  90. static createTrack (type, duration) {
  91. return {
  92. container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined,
  93. type,
  94. id: RemuxerTrackIdConfig[type],
  95. pid: -1,
  96. inputTimeScale: 90000,
  97. sequenceNumber: 0,
  98. samples: [],
  99. dropped: type === 'video' ? 0 : undefined,
  100. isAAC: type === 'audio' ? true : undefined,
  101. duration: type === 'audio' ? duration : undefined
  102. };
  103. }
  104.  
  105. /**
  106. * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start)
  107. * Resets all internal track instances of the demuxer.
  108. *
  109. * @override Implements generic demuxing/remuxing interface (see DemuxerInline)
  110. * @param {object} initSegment
  111. * @param {string} audioCodec
  112. * @param {string} videoCodec
  113. * @param {number} duration (in TS timescale = 90kHz)
  114. */
  115. resetInitSegment (initSegment, audioCodec, videoCodec, duration) {
  116. this.pmtParsed = false;
  117. this._pmtId = -1;
  118. this.pmtUnknownTypes = {};
  119.  
  120. this._avcTrack = TSDemuxer.createTrack('video', duration);
  121. this._audioTrack = TSDemuxer.createTrack('audio', duration);
  122. this._id3Track = TSDemuxer.createTrack('id3', duration);
  123. this._txtTrack = TSDemuxer.createTrack('text', duration);
  124.  
  125. // flush any partial content
  126. this.aacOverFlow = null;
  127. this.aacLastPTS = null;
  128. this.avcSample = null;
  129. this.audioCodec = audioCodec;
  130. this.videoCodec = videoCodec;
  131. this._duration = duration;
  132. }
  133.  
  134. /**
  135. *
  136. * @override
  137. */
  138. resetTimeStamp () {}
  139.  
  140. // feed incoming data to the front of the parsing pipeline
  141. append (data, timeOffset, contiguous, accurateTimeOffset) {
  142. let start, len = data.length, stt, pid, atf, offset, pes,
  143. unknownPIDs = false;
  144. this.pmtUnknownTypes = {};
  145. this.contiguous = contiguous;
  146. let pmtParsed = this.pmtParsed,
  147. avcTrack = this._avcTrack,
  148. audioTrack = this._audioTrack,
  149. id3Track = this._id3Track,
  150. avcId = avcTrack.pid,
  151. audioId = audioTrack.pid,
  152. id3Id = id3Track.pid,
  153. pmtId = this._pmtId,
  154. avcData = avcTrack.pesData,
  155. audioData = audioTrack.pesData,
  156. id3Data = id3Track.pesData,
  157. parsePAT = this._parsePAT,
  158. parsePMT = this._parsePMT.bind(this),
  159. parsePES = this._parsePES,
  160. parseAVCPES = this._parseAVCPES.bind(this),
  161. parseAACPES = this._parseAACPES.bind(this),
  162. parseMPEGPES = this._parseMPEGPES.bind(this),
  163. parseID3PES = this._parseID3PES.bind(this);
  164.  
  165. const syncOffset = TSDemuxer._syncOffset(data);
  166.  
  167. // don't parse last TS packet if incomplete
  168. len -= (len + syncOffset) % 188;
  169.  
  170. // loop through TS packets
  171. for (start = syncOffset; start < len; start += 188) {
  172. if (data[start] === 0x47) {
  173. stt = !!(data[start + 1] & 0x40);
  174. // pid is a 13-bit field starting at the last bit of TS[1]
  175. pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
  176. atf = (data[start + 3] & 0x30) >> 4;
  177. // if an adaption field is present, its length is specified by the fifth byte of the TS packet header.
  178. if (atf > 1) {
  179. offset = start + 5 + data[start + 4];
  180. // continue if there is only adaptation field
  181. if (offset === (start + 188)) {
  182. continue;
  183. }
  184. } else {
  185. offset = start + 4;
  186. }
  187. switch (pid) {
  188. case avcId:
  189. if (stt) {
  190. if (avcData && (pes = parsePES(avcData))) {
  191. parseAVCPES(pes, false);
  192. }
  193.  
  194. avcData = { data: [], size: 0 };
  195. }
  196. if (avcData) {
  197. avcData.data.push(data.subarray(offset, start + 188));
  198. avcData.size += start + 188 - offset;
  199. }
  200. break;
  201. case audioId:
  202. if (stt) {
  203. if (audioData && (pes = parsePES(audioData))) {
  204. if (audioTrack.isAAC) {
  205. parseAACPES(pes);
  206. } else {
  207. parseMPEGPES(pes);
  208. }
  209. }
  210. audioData = { data: [], size: 0 };
  211. }
  212. if (audioData) {
  213. audioData.data.push(data.subarray(offset, start + 188));
  214. audioData.size += start + 188 - offset;
  215. }
  216. break;
  217. case id3Id:
  218. if (stt) {
  219. if (id3Data && (pes = parsePES(id3Data))) {
  220. parseID3PES(pes);
  221. }
  222.  
  223. id3Data = { data: [], size: 0 };
  224. }
  225. if (id3Data) {
  226. id3Data.data.push(data.subarray(offset, start + 188));
  227. id3Data.size += start + 188 - offset;
  228. }
  229. break;
  230. case 0:
  231. if (stt) {
  232. offset += data[offset] + 1;
  233. }
  234.  
  235. pmtId = this._pmtId = parsePAT(data, offset);
  236. break;
  237. case pmtId:
  238. if (stt) {
  239. offset += data[offset] + 1;
  240. }
  241.  
  242. let parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null);
  243.  
  244. // only update track id if track PID found while parsing PMT
  245. // this is to avoid resetting the PID to -1 in case
  246. // track PID transiently disappears from the stream
  247. // this could happen in case of transient missing audio samples for example
  248. // NOTE this is only the PID of the track as found in TS,
  249. // but we are not using this for MP4 track IDs.
  250. avcId = parsedPIDs.avc;
  251. if (avcId > 0) {
  252. avcTrack.pid = avcId;
  253. }
  254.  
  255. audioId = parsedPIDs.audio;
  256. if (audioId > 0) {
  257. audioTrack.pid = audioId;
  258. audioTrack.isAAC = parsedPIDs.isAAC;
  259. }
  260. id3Id = parsedPIDs.id3;
  261. if (id3Id > 0) {
  262. id3Track.pid = id3Id;
  263. }
  264.  
  265. if (unknownPIDs && !pmtParsed) {
  266. logger.log('reparse from beginning');
  267. unknownPIDs = false;
  268. // we set it to -188, the += 188 in the for loop will reset start to 0
  269. start = syncOffset - 188;
  270. }
  271. pmtParsed = this.pmtParsed = true;
  272. break;
  273. case 17:
  274. case 0x1fff:
  275. break;
  276. default:
  277. unknownPIDs = true;
  278. break;
  279. }
  280. } else {
  281. this.observer.trigger(Event.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' });
  282. }
  283. }
  284. // try to parse last PES packets
  285. if (avcData && (pes = parsePES(avcData))) {
  286. parseAVCPES(pes, true);
  287. avcTrack.pesData = null;
  288. } else {
  289. // either avcData null or PES truncated, keep it for next frag parsing
  290. avcTrack.pesData = avcData;
  291. }
  292.  
  293. if (audioData && (pes = parsePES(audioData))) {
  294. if (audioTrack.isAAC) {
  295. parseAACPES(pes);
  296. } else {
  297. parseMPEGPES(pes);
  298. }
  299.  
  300. audioTrack.pesData = null;
  301. } else {
  302. if (audioData && audioData.size) {
  303. logger.log('last AAC PES packet truncated,might overlap between fragments');
  304. }
  305.  
  306. // either audioData null or PES truncated, keep it for next frag parsing
  307. audioTrack.pesData = audioData;
  308. }
  309.  
  310. if (id3Data && (pes = parsePES(id3Data))) {
  311. parseID3PES(pes);
  312. id3Track.pesData = null;
  313. } else {
  314. // either id3Data null or PES truncated, keep it for next frag parsing
  315. id3Track.pesData = id3Data;
  316. }
  317.  
  318. if (this.sampleAes == null) {
  319. this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
  320. } else {
  321. this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
  322. }
  323. }
  324.  
  325. decryptAndRemux (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
  326. if (audioTrack.samples && audioTrack.isAAC) {
  327. let localthis = this;
  328. this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () {
  329. localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  330. });
  331. } else {
  332. this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  333. }
  334. }
  335.  
  336. decryptAndRemuxAvc (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
  337. if (videoTrack.samples) {
  338. let localthis = this;
  339. this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () {
  340. localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  341. });
  342. } else {
  343. this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
  344. }
  345. }
  346.  
  347. destroy () {
  348. this._initPTS = this._initDTS = undefined;
  349. this._duration = 0;
  350. }
  351.  
  352. _parsePAT (data, offset) {
  353. // skip the PSI header and parse the first PMT entry
  354. return (data[offset + 10] & 0x1F) << 8 | data[offset + 11];
  355. // logger.log('PMT PID:' + this._pmtId);
  356. }
  357.  
  358. _trackUnknownPmt (type, logLevel, message) {
  359. // Only log unknown and unsupported stream types once per append or stream (by resetting this.pmtUnknownTypes)
  360. // For more information on elementary stream types see:
  361. // https://en.wikipedia.org/wiki/Program-specific_information#Elementary_stream_types
  362. const result = this.pmtUnknownTypes[type] || 0;
  363. if (result === 0) {
  364. this.pmtUnknownTypes[type] = 0;
  365. logLevel.call(logger, message);
  366. }
  367. this.pmtUnknownTypes[type]++;
  368. return result;
  369. }
  370.  
  371. _parsePMT (data, offset, mpegSupported, isSampleAes) {
  372. let sectionLength, tableEnd, programInfoLength, pid, result = { audio: -1, avc: -1, id3: -1, isAAC: true };
  373. sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2];
  374. tableEnd = offset + 3 + sectionLength - 4;
  375. // to determine where the table is, we have to figure out how
  376. // long the program info descriptors are
  377. programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11];
  378. // advance the offset to the first entry in the mapping table
  379. offset += 12 + programInfoLength;
  380. while (offset < tableEnd) {
  381. pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2];
  382. switch (data[offset]) {
  383. case 0xcf: // SAMPLE-AES AAC
  384. if (!isSampleAes) {
  385. this._trackUnknownPmt(data[offset], logger.warn, 'ADTS AAC with AES-128-CBC frame encryption found in unencrypted stream');
  386. break;
  387. }
  388. /* falls through */
  389.  
  390. // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
  391. case 0x0f:
  392. // logger.log('AAC PID:' + pid);
  393. if (result.audio === -1) {
  394. result.audio = pid;
  395. }
  396.  
  397. break;
  398.  
  399. // Packetized metadata (ID3)
  400. case 0x15:
  401. // logger.log('ID3 PID:' + pid);
  402. if (result.id3 === -1) {
  403. result.id3 = pid;
  404. }
  405.  
  406. break;
  407.  
  408. case 0xdb: // SAMPLE-AES AVC
  409. if (!isSampleAes) {
  410. this._trackUnknownPmt(data[offset], logger.warn, 'H.264 with AES-128-CBC slice encryption found in unencrypted stream');
  411. break;
  412. }
  413. /* falls through */
  414.  
  415. // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
  416. case 0x1b:
  417. // logger.log('AVC PID:' + pid);
  418. if (result.avc === -1) {
  419. result.avc = pid;
  420. }
  421.  
  422. break;
  423.  
  424. // ISO/IEC 11172-3 (MPEG-1 audio)
  425. // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
  426. case 0x03:
  427. case 0x04:
  428. // logger.log('MPEG PID:' + pid);
  429. if (!mpegSupported) {
  430. this._trackUnknownPmt(data[offset], logger.warn, 'MPEG audio found, not supported in this browser');
  431. } else if (result.audio === -1) {
  432. result.audio = pid;
  433. result.isAAC = false;
  434. }
  435. break;
  436.  
  437. case 0x24:
  438. this._trackUnknownPmt(data[offset], logger.warn, 'Unsupported HEVC stream type found');
  439. break;
  440.  
  441. default:
  442. this._trackUnknownPmt(data[offset], logger.log, 'Unknown stream type:' + data[offset]);
  443. break;
  444. }
  445. // move to the next table entry
  446. // skip past the elementary stream descriptors, if present
  447. offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5;
  448. }
  449. return result;
  450. }
  451.  
  452. _parsePES (stream) {
  453. let i = 0, frag, pesFlags, pesPrefix, pesLen, pesHdrLen, pesData, pesPts, pesDts, payloadStartOffset, data = stream.data;
  454. // safety check
  455. if (!stream || stream.size === 0) {
  456. return null;
  457. }
  458.  
  459. // we might need up to 19 bytes to read PES header
  460. // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes
  461. // usually only one merge is needed (and this is rare ...)
  462. while (data[0].length < 19 && data.length > 1) {
  463. let newData = new Uint8Array(data[0].length + data[1].length);
  464. newData.set(data[0]);
  465. newData.set(data[1], data[0].length);
  466. data[0] = newData;
  467. data.splice(1, 1);
  468. }
  469. // retrieve PTS/DTS from first fragment
  470. frag = data[0];
  471. pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2];
  472. if (pesPrefix === 1) {
  473. pesLen = (frag[4] << 8) + frag[5];
  474. // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated
  475. // minus 6 : PES header size
  476. if (pesLen && pesLen > stream.size - 6) {
  477. return null;
  478. }
  479.  
  480. pesFlags = frag[7];
  481. if (pesFlags & 0xC0) {
  482. /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
  483. as PTS / DTS is 33 bit we cannot use bitwise operator in JS,
  484. as Bitwise operators treat their operands as a sequence of 32 bits */
  485. pesPts = (frag[9] & 0x0E) * 536870912 +// 1 << 29
  486. (frag[10] & 0xFF) * 4194304 +// 1 << 22
  487. (frag[11] & 0xFE) * 16384 +// 1 << 14
  488. (frag[12] & 0xFF) * 128 +// 1 << 7
  489. (frag[13] & 0xFE) / 2;
  490.  
  491. if (pesFlags & 0x40) {
  492. pesDts = (frag[14] & 0x0E) * 536870912 +// 1 << 29
  493. (frag[15] & 0xFF) * 4194304 +// 1 << 22
  494. (frag[16] & 0xFE) * 16384 +// 1 << 14
  495. (frag[17] & 0xFF) * 128 +// 1 << 7
  496. (frag[18] & 0xFE) / 2;
  497.  
  498. if (pesPts - pesDts > 60 * 90000) {
  499. logger.warn(`${Math.round((pesPts - pesDts) / 90000)}s delta between PTS and DTS, align them`);
  500. pesPts = pesDts;
  501. }
  502. } else {
  503. pesDts = pesPts;
  504. }
  505. }
  506. pesHdrLen = frag[8];
  507. // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension
  508. payloadStartOffset = pesHdrLen + 9;
  509.  
  510. if (stream.size <= payloadStartOffset) {
  511. return null;
  512. }
  513. stream.size -= payloadStartOffset;
  514. // reassemble PES packet
  515. pesData = new Uint8Array(stream.size);
  516. for (let j = 0, dataLen = data.length; j < dataLen; j++) {
  517. frag = data[j];
  518. let len = frag.byteLength;
  519. if (payloadStartOffset) {
  520. if (payloadStartOffset > len) {
  521. // trim full frag if PES header bigger than frag
  522. payloadStartOffset -= len;
  523. continue;
  524. } else {
  525. // trim partial frag if PES header smaller than frag
  526. frag = frag.subarray(payloadStartOffset);
  527. len -= payloadStartOffset;
  528. payloadStartOffset = 0;
  529. }
  530. }
  531. pesData.set(frag, i);
  532. i += len;
  533. }
  534. if (pesLen) {
  535. // payload size : remove PES header + PES extension
  536. pesLen -= pesHdrLen + 3;
  537. }
  538. return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen };
  539. } else {
  540. return null;
  541. }
  542. }
  543.  
  544. pushAccesUnit (avcSample, avcTrack) {
  545. if (avcSample.units.length && avcSample.frame) {
  546. const samples = avcTrack.samples;
  547. const nbSamples = samples.length;
  548. // if sample does not have PTS/DTS, patch with last sample PTS/DTS
  549. if (isNaN(avcSample.pts)) {
  550. if (nbSamples) {
  551. const lastSample = samples[nbSamples - 1];
  552. avcSample.pts = lastSample.pts;
  553. avcSample.dts = lastSample.dts;
  554. } else {
  555. // dropping samples, no timestamp found
  556. avcTrack.dropped++;
  557. return;
  558. }
  559. }
  560. // only push AVC sample if starting with a keyframe is not mandatory OR
  561. // if keyframe already found in this fragment OR
  562. // keyframe found in last fragment (track.sps) AND
  563. // samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous
  564. if (!this.config.forceKeyFrameOnDiscontinuity ||
  565. avcSample.key === true ||
  566. (avcTrack.sps && (nbSamples || this.contiguous))) {
  567. avcSample.id = nbSamples;
  568. samples.push(avcSample);
  569. } else {
  570. // dropped samples, track it
  571. avcTrack.dropped++;
  572. }
  573. }
  574. if (avcSample.debug.length) {
  575. logger.log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug);
  576. }
  577. }
  578.  
  579. _parseAVCPES (pes, last) {
  580. // logger.log('parse new PES');
  581. let track = this._avcTrack,
  582. units = this._parseAVCNALu(pes.data),
  583. debug = false,
  584. expGolombDecoder,
  585. avcSample = this.avcSample,
  586. push,
  587. spsfound = false,
  588. i,
  589. pushAccesUnit = this.pushAccesUnit.bind(this),
  590. createAVCSample = function (key, pts, dts, debug) {
  591. return { key: key, pts: pts, dts: dts, units: [], debug: debug };
  592. };
  593. // free pes.data to save up some memory
  594. pes.data = null;
  595.  
  596. // if new NAL units found and last sample still there, let's push ...
  597. // this helps parsing streams with missing AUD (only do this if AUD never found)
  598. if (avcSample && units.length && !track.audFound) {
  599. pushAccesUnit(avcSample, track);
  600. avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, '');
  601. }
  602.  
  603. units.forEach(unit => {
  604. switch (unit.type) {
  605. // NDR
  606. case 1:
  607. push = true;
  608. if (!avcSample) {
  609. avcSample = this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');
  610. }
  611.  
  612. if (debug) {
  613. avcSample.debug += 'NDR ';
  614. }
  615.  
  616. avcSample.frame = true;
  617. let data = unit.data;
  618. // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
  619. if (spsfound && data.length > 4) {
  620. // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
  621. let sliceType = new ExpGolomb(data).readSliceType();
  622. // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
  623. // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
  624. // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
  625. // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
  626. // if (sliceType === 2 || sliceType === 7) {
  627. if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
  628. avcSample.key = true;
  629. }
  630. }
  631. break;
  632. // IDR
  633. case 5:
  634. push = true;
  635. // handle PES not starting with AUD
  636. if (!avcSample) {
  637. avcSample = this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');
  638. }
  639.  
  640. if (debug) {
  641. avcSample.debug += 'IDR ';
  642. }
  643.  
  644. avcSample.key = true;
  645. avcSample.frame = true;
  646. break;
  647. // SEI
  648. case 6:
  649. push = true;
  650. if (debug && avcSample) {
  651. avcSample.debug += 'SEI ';
  652. }
  653.  
  654. expGolombDecoder = new ExpGolomb(this.discardEPB(unit.data));
  655.  
  656. // skip frameType
  657. expGolombDecoder.readUByte();
  658.  
  659. var payloadType = 0;
  660. var payloadSize = 0;
  661. var endOfCaptions = false;
  662. var b = 0;
  663.  
  664. while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) {
  665. payloadType = 0;
  666. do {
  667. b = expGolombDecoder.readUByte();
  668. payloadType += b;
  669. } while (b === 0xFF);
  670.  
  671. // Parse payload size.
  672. payloadSize = 0;
  673. do {
  674. b = expGolombDecoder.readUByte();
  675. payloadSize += b;
  676. } while (b === 0xFF);
  677.  
  678. // TODO: there can be more than one payload in an SEI packet...
  679. // TODO: need to read type and size in a while loop to get them all
  680. if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) {
  681. endOfCaptions = true;
  682.  
  683. let countryCode = expGolombDecoder.readUByte();
  684.  
  685. if (countryCode === 181) {
  686. let providerCode = expGolombDecoder.readUShort();
  687.  
  688. if (providerCode === 49) {
  689. let userStructure = expGolombDecoder.readUInt();
  690.  
  691. if (userStructure === 0x47413934) {
  692. let userDataType = expGolombDecoder.readUByte();
  693.  
  694. // Raw CEA-608 bytes wrapped in CEA-708 packet
  695. if (userDataType === 3) {
  696. let firstByte = expGolombDecoder.readUByte();
  697. let secondByte = expGolombDecoder.readUByte();
  698.  
  699. let totalCCs = 31 & firstByte;
  700. let byteArray = [firstByte, secondByte];
  701.  
  702. for (i = 0; i < totalCCs; i++) {
  703. // 3 bytes per CC
  704. byteArray.push(expGolombDecoder.readUByte());
  705. byteArray.push(expGolombDecoder.readUByte());
  706. byteArray.push(expGolombDecoder.readUByte());
  707. }
  708.  
  709. this._insertSampleInOrder(this._txtTrack.samples, { type: 3, pts: pes.pts, bytes: byteArray });
  710. }
  711. }
  712. }
  713. }
  714. } else if (payloadType === 5 && expGolombDecoder.bytesAvailable !== 0) {
  715. endOfCaptions = true;
  716.  
  717. if (payloadSize > 16) {
  718. const uuidStrArray = [];
  719. for (i = 0; i < 16; i++) {
  720. uuidStrArray.push(expGolombDecoder.readUByte().toString(16));
  721.  
  722. if (i === 3 || i === 5 || i === 7 || i === 9) {
  723. uuidStrArray.push('-');
  724. }
  725. }
  726. const length = payloadSize - 16;
  727. const userDataPayloadBytes = new Uint8Array(length);
  728. for (i = 0; i < length; i++) {
  729. userDataPayloadBytes[i] = expGolombDecoder.readUByte();
  730. }
  731.  
  732. this._insertSampleInOrder(this._txtTrack.samples, {
  733. pts: pes.pts,
  734. payloadType: payloadType,
  735. uuid: uuidStrArray.join(''),
  736. userDataBytes: userDataPayloadBytes,
  737. userData: utf8ArrayToStr(userDataPayloadBytes.buffer)
  738. });
  739. }
  740. } else if (payloadSize < expGolombDecoder.bytesAvailable) {
  741. for (i = 0; i < payloadSize; i++) {
  742. expGolombDecoder.readUByte();
  743. }
  744. }
  745. }
  746. break;
  747. // SPS
  748. case 7:
  749. push = true;
  750. spsfound = true;
  751. if (debug && avcSample) {
  752. avcSample.debug += 'SPS ';
  753. }
  754.  
  755. if (!track.sps) {
  756. expGolombDecoder = new ExpGolomb(unit.data);
  757. let config = expGolombDecoder.readSPS();
  758. track.width = config.width;
  759. track.height = config.height;
  760. track.pixelRatio = config.pixelRatio;
  761. track.sps = [unit.data];
  762. track.duration = this._duration;
  763. let codecarray = unit.data.subarray(1, 4);
  764. let codecstring = 'avc1.';
  765. for (i = 0; i < 3; i++) {
  766. let h = codecarray[i].toString(16);
  767. if (h.length < 2) {
  768. h = '0' + h;
  769. }
  770.  
  771. codecstring += h;
  772. }
  773. track.codec = codecstring;
  774. }
  775. break;
  776. // PPS
  777. case 8:
  778. push = true;
  779. if (debug && avcSample) {
  780. avcSample.debug += 'PPS ';
  781. }
  782.  
  783. if (!track.pps) {
  784. track.pps = [unit.data];
  785. }
  786.  
  787. break;
  788. // AUD
  789. case 9:
  790. push = false;
  791. track.audFound = true;
  792. if (avcSample) {
  793. pushAccesUnit(avcSample, track);
  794. }
  795.  
  796. avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : '');
  797. break;
  798. // Filler Data
  799. case 12:
  800. push = false;
  801. break;
  802. default:
  803. push = false;
  804. if (avcSample) {
  805. avcSample.debug += 'unknown NAL ' + unit.type + ' ';
  806. }
  807.  
  808. break;
  809. }
  810. if (avcSample && push) {
  811. let units = avcSample.units;
  812. units.push(unit);
  813. }
  814. });
  815. // if last PES packet, push samples
  816. if (last && avcSample) {
  817. pushAccesUnit(avcSample, track);
  818. this.avcSample = null;
  819. }
  820. }
  821.  
  822. _insertSampleInOrder (arr, data) {
  823. let len = arr.length;
  824. if (len > 0) {
  825. if (data.pts >= arr[len - 1].pts) {
  826. arr.push(data);
  827. } else {
  828. for (let pos = len - 1; pos >= 0; pos--) {
  829. if (data.pts < arr[pos].pts) {
  830. arr.splice(pos, 0, data);
  831. break;
  832. }
  833. }
  834. }
  835. } else {
  836. arr.push(data);
  837. }
  838. }
  839.  
  840. _getLastNalUnit () {
  841. let avcSample = this.avcSample, lastUnit;
  842. // try to fallback to previous sample if current one is empty
  843. if (!avcSample || avcSample.units.length === 0) {
  844. let track = this._avcTrack, samples = track.samples;
  845. avcSample = samples[samples.length - 1];
  846. }
  847. if (avcSample) {
  848. let units = avcSample.units;
  849. lastUnit = units[units.length - 1];
  850. }
  851. return lastUnit;
  852. }
  853.  
  854. _parseAVCNALu (array) {
  855. let i = 0, len = array.byteLength, value, overflow, track = this._avcTrack, state = track.naluState || 0, lastState = state;
  856. let units = [], unit, unitType, lastUnitStart = -1, lastUnitType;
  857. // logger.log('PES:' + Hex.hexDump(array));
  858.  
  859. if (state === -1) {
  860. // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
  861. lastUnitStart = 0;
  862. // NALu type is value read from offset 0
  863. lastUnitType = array[0] & 0x1f;
  864. state = 0;
  865. i = 1;
  866. }
  867.  
  868. while (i < len) {
  869. value = array[i++];
  870. // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
  871. if (!state) {
  872. state = value ? 0 : 1;
  873. continue;
  874. }
  875. if (state === 1) {
  876. state = value ? 0 : 2;
  877. continue;
  878. }
  879. // here we have state either equal to 2 or 3
  880. if (!value) {
  881. state = 3;
  882. } else if (value === 1) {
  883. if (lastUnitStart >= 0) {
  884. unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType };
  885. // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
  886. units.push(unit);
  887. } else {
  888. // lastUnitStart is undefined => this is the first start code found in this PES packet
  889. // first check if start code delimiter is overlapping between 2 PES packets,
  890. // ie it started in last packet (lastState not zero)
  891. // and ended at the beginning of this PES packet (i <= 4 - lastState)
  892. let lastUnit = this._getLastNalUnit();
  893. if (lastUnit) {
  894. if (lastState && (i <= 4 - lastState)) {
  895. // start delimiter overlapping between PES packets
  896. // strip start delimiter bytes from the end of last NAL unit
  897. // check if lastUnit had a state different from zero
  898. if (lastUnit.state) {
  899. // strip last bytes
  900. lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
  901. }
  902. }
  903. // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
  904. overflow = i - state - 1;
  905. if (overflow > 0) {
  906. // logger.log('first NALU found with overflow:' + overflow);
  907. let tmp = new Uint8Array(lastUnit.data.byteLength + overflow);
  908. tmp.set(lastUnit.data, 0);
  909. tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength);
  910. lastUnit.data = tmp;
  911. }
  912. }
  913. }
  914. // check if we can read unit type
  915. if (i < len) {
  916. unitType = array[i] & 0x1f;
  917. // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
  918. lastUnitStart = i;
  919. lastUnitType = unitType;
  920. state = 0;
  921. } else {
  922. // not enough byte to read unit type. let's read it on next PES parsing
  923. state = -1;
  924. }
  925. } else {
  926. state = 0;
  927. }
  928. }
  929. if (lastUnitStart >= 0 && state >= 0) {
  930. unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state };
  931. units.push(unit);
  932. // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
  933. }
  934. // no NALu found
  935. if (units.length === 0) {
  936. // append pes.data to previous NAL unit
  937. let lastUnit = this._getLastNalUnit();
  938. if (lastUnit) {
  939. let tmp = new Uint8Array(lastUnit.data.byteLength + array.byteLength);
  940. tmp.set(lastUnit.data, 0);
  941. tmp.set(array, lastUnit.data.byteLength);
  942. lastUnit.data = tmp;
  943. }
  944. }
  945. track.naluState = state;
  946. return units;
  947. }
  948.  
  949. /**
  950. * remove Emulation Prevention bytes from a RBSP
  951. */
  952. discardEPB (data) {
  953. let length = data.byteLength,
  954. EPBPositions = [],
  955. i = 1,
  956. newLength, newData;
  957.  
  958. // Find all `Emulation Prevention Bytes`
  959. while (i < length - 2) {
  960. if (data[i] === 0 &&
  961. data[i + 1] === 0 &&
  962. data[i + 2] === 0x03) {
  963. EPBPositions.push(i + 2);
  964. i += 2;
  965. } else {
  966. i++;
  967. }
  968. }
  969.  
  970. // If no Emulation Prevention Bytes were found just return the original
  971. // array
  972. if (EPBPositions.length === 0) {
  973. return data;
  974. }
  975.  
  976. // Create a new array to hold the NAL unit data
  977. newLength = length - EPBPositions.length;
  978. newData = new Uint8Array(newLength);
  979. let sourceIndex = 0;
  980.  
  981. for (i = 0; i < newLength; sourceIndex++, i++) {
  982. if (sourceIndex === EPBPositions[0]) {
  983. // Skip this byte
  984. sourceIndex++;
  985. // Remove this position index
  986. EPBPositions.shift();
  987. }
  988. newData[i] = data[sourceIndex];
  989. }
  990. return newData;
  991. }
  992.  
  993. _parseAACPES (pes) {
  994. let track = this._audioTrack,
  995. data = pes.data,
  996. pts = pes.pts,
  997. startOffset = 0,
  998. aacOverFlow = this.aacOverFlow,
  999. aacLastPTS = this.aacLastPTS,
  1000. frameDuration, frameIndex, offset, stamp, len;
  1001. if (aacOverFlow) {
  1002. let tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
  1003. tmp.set(aacOverFlow, 0);
  1004. tmp.set(data, aacOverFlow.byteLength);
  1005. // logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`);
  1006. data = tmp;
  1007. }
  1008. // look for ADTS header (0xFFFx)
  1009. for (offset = startOffset, len = data.length; offset < len - 1; offset++) {
  1010. if (ADTS.isHeader(data, offset)) {
  1011. break;
  1012. }
  1013. }
  1014. // if ADTS header does not start straight from the beginning of the PES payload, raise an error
  1015. if (offset) {
  1016. let reason, fatal;
  1017. if (offset < len - 1) {
  1018. reason = `AAC PES did not start with ADTS header,offset:${offset}`;
  1019. fatal = false;
  1020. } else {
  1021. reason = 'no ADTS header found in AAC PES';
  1022. fatal = true;
  1023. }
  1024. logger.warn(`parsing error:${reason}`);
  1025. this.observer.trigger(Event.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason });
  1026. if (fatal) {
  1027. return;
  1028. }
  1029. }
  1030.  
  1031. ADTS.initTrackConfig(track, this.observer, data, offset, this.audioCodec);
  1032. frameIndex = 0;
  1033. frameDuration = ADTS.getFrameDuration(track.samplerate);
  1034.  
  1035. // if last AAC frame is overflowing, we should ensure timestamps are contiguous:
  1036. // first sample PTS should be equal to last sample PTS + frameDuration
  1037. if (aacOverFlow && aacLastPTS) {
  1038. let newPTS = aacLastPTS + frameDuration;
  1039. if (Math.abs(newPTS - pts) > 1) {
  1040. logger.log(`AAC: align PTS for overlapping frames by ${Math.round((newPTS - pts) / 90)}`);
  1041. pts = newPTS;
  1042. }
  1043. }
  1044.  
  1045. // scan for aac samples
  1046. while (offset < len) {
  1047. if (ADTS.isHeader(data, offset)) {
  1048. if ((offset + 5) < len) {
  1049. const frame = ADTS.appendFrame(track, data, offset, pts, frameIndex);
  1050. if (frame) {
  1051. offset += frame.length;
  1052. stamp = frame.sample.pts;
  1053. frameIndex++;
  1054. continue;
  1055. }
  1056. }
  1057. // We are at an ADTS header, but do not have enough data for a frame
  1058. // Remaining data will be added to aacOverFlow
  1059. break;
  1060. } else {
  1061. // nothing found, keep looking
  1062. offset++;
  1063. }
  1064. }
  1065.  
  1066. if (offset < len) {
  1067. aacOverFlow = data.subarray(offset, len);
  1068. // logger.log(`AAC: overflow detected:${len-offset}`);
  1069. } else {
  1070. aacOverFlow = null;
  1071. }
  1072.  
  1073. this.aacOverFlow = aacOverFlow;
  1074. this.aacLastPTS = stamp;
  1075. }
  1076.  
  1077. _parseMPEGPES (pes) {
  1078. let data = pes.data;
  1079. let length = data.length;
  1080. let frameIndex = 0;
  1081. let offset = 0;
  1082. let pts = pes.pts;
  1083.  
  1084. while (offset < length) {
  1085. if (MpegAudio.isHeader(data, offset)) {
  1086. let frame = MpegAudio.appendFrame(this._audioTrack, data, offset, pts, frameIndex);
  1087. if (frame) {
  1088. offset += frame.length;
  1089. frameIndex++;
  1090. } else {
  1091. // logger.log('Unable to parse Mpeg audio frame');
  1092. break;
  1093. }
  1094. } else {
  1095. // nothing found, keep looking
  1096. offset++;
  1097. }
  1098. }
  1099. }
  1100.  
  1101. _parseID3PES (pes) {
  1102. this._id3Track.samples.push(pes);
  1103. }
  1104. }
  1105.  
  1106. export default TSDemuxer;