Source: lib/util/mp4_generator.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.Mp4Generator');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.util.ManifestParserUtils');
  9. goog.require('shaka.util.Platform');
  10. goog.require('shaka.util.Uint8ArrayUtils');
  11. shaka.util.Mp4Generator = class {
  12. /**
  13. * @param {!Array<shaka.util.Mp4Generator.StreamInfo>} streamInfos
  14. */
  15. constructor(streamInfos) {
  16. shaka.util.Mp4Generator.initStaticProperties_();
  17. /** @private {!Array<shaka.util.Mp4Generator.StreamInfo>} */
  18. this.streamInfos_ = streamInfos;
  19. }
  20. /**
  21. * Generate a Init Segment (MP4).
  22. *
  23. * @return {!Uint8Array}
  24. */
  25. initSegment() {
  26. const Mp4Generator = shaka.util.Mp4Generator;
  27. const movie = this.moov_();
  28. const length = Mp4Generator.FTYP_.byteLength + movie.byteLength;
  29. const result = new Uint8Array(length);
  30. result.set(Mp4Generator.FTYP_);
  31. result.set(movie, Mp4Generator.FTYP_.byteLength);
  32. return result;
  33. }
  34. /**
  35. * Generate a MOOV box
  36. *
  37. * @return {!Uint8Array}
  38. * @private
  39. */
  40. moov_() {
  41. goog.asserts.assert(this.streamInfos_.length > 0,
  42. 'StreamInfos must have elements');
  43. const Mp4Generator = shaka.util.Mp4Generator;
  44. const trakArrays = [];
  45. for (const streamInfo of this.streamInfos_) {
  46. trakArrays.push(this.trak_(streamInfo));
  47. }
  48. const traks = shaka.util.Uint8ArrayUtils.concat(...trakArrays);
  49. const firstStreamInfo = this.streamInfos_[0];
  50. return Mp4Generator.box('moov',
  51. this.mvhd_(firstStreamInfo),
  52. traks,
  53. this.mvex_(),
  54. this.pssh_(firstStreamInfo));
  55. }
  56. /**
  57. * Generate a MVHD box
  58. *
  59. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  60. * @return {!Uint8Array}
  61. * @private
  62. */
  63. mvhd_(streamInfo) {
  64. const Mp4Generator = shaka.util.Mp4Generator;
  65. const duration = streamInfo.duration * streamInfo.timescale;
  66. const upperWordDuration =
  67. Math.floor(duration / (Mp4Generator.UINT32_MAX_ + 1));
  68. const lowerWordDuration =
  69. Math.floor(duration % (Mp4Generator.UINT32_MAX_ + 1));
  70. const bytes = new Uint8Array([
  71. 0x01, // version 1
  72. 0x00, 0x00, 0x00, // flags
  73. 0x00, 0x00, 0x00, 0x00,
  74. 0x00, 0x00, 0x00, 0x02, // creation_time
  75. 0x00, 0x00, 0x00, 0x00,
  76. 0x00, 0x00, 0x00, 0x03, // modification_time
  77. ...this.breakNumberIntoBytes_(streamInfo.timescale, 4), // timescale
  78. ...this.breakNumberIntoBytes_(upperWordDuration, 4),
  79. ...this.breakNumberIntoBytes_(lowerWordDuration, 4), // duration
  80. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  81. 0x01, 0x00, // 1.0 volume
  82. 0x00, 0x00, // reserved
  83. 0x00, 0x00, 0x00, 0x00, // reserved
  84. 0x00, 0x00, 0x00, 0x00, // reserved
  85. 0x00, 0x01, 0x00, 0x00,
  86. 0x00, 0x00, 0x00, 0x00,
  87. 0x00, 0x00, 0x00, 0x00,
  88. 0x00, 0x00, 0x00, 0x00,
  89. 0x00, 0x01, 0x00, 0x00,
  90. 0x00, 0x00, 0x00, 0x00,
  91. 0x00, 0x00, 0x00, 0x00,
  92. 0x00, 0x00, 0x00, 0x00,
  93. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  94. 0x00, 0x00, 0x00, 0x00,
  95. 0x00, 0x00, 0x00, 0x00,
  96. 0x00, 0x00, 0x00, 0x00,
  97. 0x00, 0x00, 0x00, 0x00,
  98. 0x00, 0x00, 0x00, 0x00,
  99. 0x00, 0x00, 0x00, 0x00, // pre_defined
  100. 0xff, 0xff, 0xff, 0xff, // next_track_ID
  101. ]);
  102. return Mp4Generator.box('mvhd', bytes);
  103. }
  104. /**
  105. * Generate a TRAK box
  106. *
  107. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  108. * @return {!Uint8Array}
  109. * @private
  110. */
  111. trak_(streamInfo) {
  112. const Mp4Generator = shaka.util.Mp4Generator;
  113. return Mp4Generator.box('trak',
  114. this.tkhd_(streamInfo), this.mdia_(streamInfo));
  115. }
  116. /**
  117. * Generate a TKHD box
  118. *
  119. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  120. * @return {!Uint8Array}
  121. * @private
  122. */
  123. tkhd_(streamInfo) {
  124. const Mp4Generator = shaka.util.Mp4Generator;
  125. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  126. const id = streamInfo.id + 1;
  127. let width = streamInfo.stream.width || 0;
  128. let height = streamInfo.stream.height || 0;
  129. if (streamInfo.type == ContentType.AUDIO) {
  130. width = 0;
  131. height = 0;
  132. }
  133. const duration = streamInfo.duration * streamInfo.timescale;
  134. const upperWordDuration =
  135. Math.floor(duration / (Mp4Generator.UINT32_MAX_ + 1));
  136. const lowerWordDuration =
  137. Math.floor(duration % (Mp4Generator.UINT32_MAX_ + 1));
  138. const bytes = new Uint8Array([
  139. 0x01, // version 1
  140. 0x00, 0x00, 0x07, // flags
  141. 0x00, 0x00, 0x00, 0x00,
  142. 0x00, 0x00, 0x00, 0x02, // creation_time
  143. 0x00, 0x00, 0x00, 0x00,
  144. 0x00, 0x00, 0x00, 0x03, // modification_time
  145. ...this.breakNumberIntoBytes_(id, 4), // track_ID
  146. 0x00, 0x00, 0x00, 0x00, // reserved
  147. ...this.breakNumberIntoBytes_(upperWordDuration, 4),
  148. ...this.breakNumberIntoBytes_(lowerWordDuration, 4), // duration
  149. 0x00, 0x00, 0x00, 0x00,
  150. 0x00, 0x00, 0x00, 0x00, // reserved
  151. 0x00, 0x00, // layer
  152. 0x00, 0x00, // alternate_group
  153. 0x00, 0x00, // non-audio track volume
  154. 0x00, 0x00, // reserved
  155. 0x00, 0x01, 0x00, 0x00,
  156. 0x00, 0x00, 0x00, 0x00,
  157. 0x00, 0x00, 0x00, 0x00,
  158. 0x00, 0x00, 0x00, 0x00,
  159. 0x00, 0x01, 0x00, 0x00,
  160. 0x00, 0x00, 0x00, 0x00,
  161. 0x00, 0x00, 0x00, 0x00,
  162. 0x00, 0x00, 0x00, 0x00,
  163. 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
  164. ...this.breakNumberIntoBytes_(width, 2),
  165. 0x00, 0x00, // width
  166. ...this.breakNumberIntoBytes_(height, 2),
  167. 0x00, 0x00, // height
  168. ]);
  169. return Mp4Generator.box('tkhd', bytes);
  170. }
  171. /**
  172. * Generate a MDIA box
  173. *
  174. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  175. * @return {!Uint8Array}
  176. * @private
  177. */
  178. mdia_(streamInfo) {
  179. const Mp4Generator = shaka.util.Mp4Generator;
  180. return Mp4Generator.box('mdia', this.mdhd_(streamInfo),
  181. this.hdlr_(streamInfo), this.minf_(streamInfo));
  182. }
  183. /**
  184. * Generate a MDHD box
  185. *
  186. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  187. * @return {!Uint8Array}
  188. * @private
  189. */
  190. mdhd_(streamInfo) {
  191. const Mp4Generator = shaka.util.Mp4Generator;
  192. const duration = streamInfo.duration * streamInfo.timescale;
  193. const upperWordDuration =
  194. Math.floor(duration / (Mp4Generator.UINT32_MAX_ + 1));
  195. const lowerWordDuration =
  196. Math.floor(duration % (Mp4Generator.UINT32_MAX_ + 1));
  197. const language = streamInfo.stream.language;
  198. const languageNumber = ((language.charCodeAt(0) - 0x60) << 10) |
  199. ((language.charCodeAt(1) - 0x60) << 5) |
  200. ((language.charCodeAt(2) - 0x60));
  201. const bytes = new Uint8Array([
  202. 0x01, // version 1
  203. 0x00, 0x00, 0x00, // flags
  204. 0x00, 0x00, 0x00, 0x00,
  205. 0x00, 0x00, 0x00, 0x02, // creation_time
  206. 0x00, 0x00, 0x00, 0x00,
  207. 0x00, 0x00, 0x00, 0x03, // modification_time
  208. ...this.breakNumberIntoBytes_(streamInfo.timescale, 4), // timescale
  209. ...this.breakNumberIntoBytes_(upperWordDuration, 4),
  210. ...this.breakNumberIntoBytes_(lowerWordDuration, 4), // duration
  211. ...this.breakNumberIntoBytes_(languageNumber, 2), // language
  212. 0x00, 0x00,
  213. ]);
  214. return Mp4Generator.box('mdhd', bytes);
  215. }
  216. /**
  217. * Generate a HDLR box
  218. *
  219. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  220. * @return {!Uint8Array}
  221. * @private
  222. */
  223. hdlr_(streamInfo) {
  224. const Mp4Generator = shaka.util.Mp4Generator;
  225. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  226. let bytes = new Uint8Array([]);
  227. switch (streamInfo.type) {
  228. case ContentType.VIDEO:
  229. bytes = Mp4Generator.HDLR_TYPES_.video;
  230. break;
  231. case ContentType.AUDIO:
  232. bytes = Mp4Generator.HDLR_TYPES_.audio;
  233. break;
  234. }
  235. return Mp4Generator.box('hdlr', bytes);
  236. }
  237. /**
  238. * Generate a MINF box
  239. *
  240. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  241. * @return {!Uint8Array}
  242. * @private
  243. */
  244. minf_(streamInfo) {
  245. const Mp4Generator = shaka.util.Mp4Generator;
  246. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  247. switch (streamInfo.type) {
  248. case ContentType.VIDEO:
  249. return Mp4Generator.box(
  250. 'minf', Mp4Generator.box('vmhd', Mp4Generator.VMHD_),
  251. Mp4Generator.DINF_, this.stbl_(streamInfo));
  252. case ContentType.AUDIO:
  253. return Mp4Generator.box(
  254. 'minf', Mp4Generator.box('smhd', Mp4Generator.SMHD_),
  255. Mp4Generator.DINF_, this.stbl_(streamInfo));
  256. }
  257. return new Uint8Array([]);
  258. }
  259. /**
  260. * Generate a STBL box
  261. *
  262. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  263. * @return {!Uint8Array}
  264. * @private
  265. */
  266. stbl_(streamInfo) {
  267. const Mp4Generator = shaka.util.Mp4Generator;
  268. return Mp4Generator.box(
  269. 'stbl',
  270. this.stsd_(streamInfo),
  271. Mp4Generator.box('stts', Mp4Generator.STTS_),
  272. Mp4Generator.box('stsc', Mp4Generator.STSC_),
  273. Mp4Generator.box('stsz', Mp4Generator.STSZ_),
  274. Mp4Generator.box('stco', Mp4Generator.STCO_));
  275. }
  276. /**
  277. * Generate a STSD box
  278. *
  279. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  280. * @return {!Uint8Array}
  281. * @private
  282. */
  283. stsd_(streamInfo) {
  284. const Mp4Generator = shaka.util.Mp4Generator;
  285. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  286. let audioCodec = 'aac';
  287. if (streamInfo.codecs.includes('mp3')) {
  288. audioCodec = 'mp3';
  289. } else if (streamInfo.codecs.includes('ac-3')) {
  290. if (shaka.util.Platform.requiresEC3InitSegments()) {
  291. audioCodec = 'ec-3';
  292. } else {
  293. audioCodec = 'ac-3';
  294. }
  295. } else if (streamInfo.codecs.includes('ec-3')) {
  296. audioCodec = 'ec-3';
  297. } else if (streamInfo.codecs.includes('opus')) {
  298. audioCodec = 'opus';
  299. }
  300. let bytes = new Uint8Array([]);
  301. switch (streamInfo.type) {
  302. case ContentType.VIDEO:
  303. if (streamInfo.codecs.includes('avc1')) {
  304. bytes = this.avc1_(streamInfo);
  305. } else if (streamInfo.codecs.includes('hvc1')) {
  306. bytes = this.hvc1_(streamInfo);
  307. }
  308. break;
  309. case ContentType.AUDIO:
  310. if (audioCodec == 'mp3') {
  311. bytes = this.mp3_(streamInfo);
  312. } else if (audioCodec == 'ac-3') {
  313. bytes = this.ac3_(streamInfo);
  314. } else if (audioCodec == 'ec-3') {
  315. bytes = this.ec3_(streamInfo);
  316. } else if (audioCodec == 'opus') {
  317. bytes = this.opus_(streamInfo);
  318. } else {
  319. bytes = this.mp4a_(streamInfo);
  320. }
  321. break;
  322. }
  323. return Mp4Generator.box('stsd', Mp4Generator.STSD_, bytes);
  324. }
  325. /**
  326. * Generate a AVC1 box
  327. *
  328. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  329. * @return {!Uint8Array}
  330. * @private
  331. */
  332. avc1_(streamInfo) {
  333. const Mp4Generator = shaka.util.Mp4Generator;
  334. const width = streamInfo.stream.width || 0;
  335. const height = streamInfo.stream.height || 0;
  336. let avcCBox;
  337. if (streamInfo.videoConfig.byteLength > 0) {
  338. avcCBox = Mp4Generator.box('avcC', streamInfo.videoConfig);
  339. } else {
  340. avcCBox = Mp4Generator.box('avcC', this.avcC_(streamInfo));
  341. }
  342. const avc1Bytes = new Uint8Array([
  343. 0x00, 0x00, 0x00,
  344. 0x00, 0x00, 0x00, // reserved
  345. 0x00, 0x01, // data_reference_index
  346. 0x00, 0x00, // pre_defined
  347. 0x00, 0x00, // reserved
  348. 0x00, 0x00, 0x00, 0x00,
  349. 0x00, 0x00, 0x00, 0x00,
  350. 0x00, 0x00, 0x00, 0x00, // pre_defined
  351. ...this.breakNumberIntoBytes_(width, 2), // width
  352. ...this.breakNumberIntoBytes_(height, 2), // height
  353. 0x00, 0x48, 0x00, 0x00, // horizontal resolution
  354. 0x00, 0x48, 0x00, 0x00, // vertical resolution
  355. 0x00, 0x00, 0x00, 0x00, // reserved
  356. 0x00, 0x01, // frame_count
  357. 0x13,
  358. 0x76, 0x69, 0x64, 0x65,
  359. 0x6f, 0x6a, 0x73, 0x2d,
  360. 0x63, 0x6f, 0x6e, 0x74,
  361. 0x72, 0x69, 0x62, 0x2d,
  362. 0x68, 0x6c, 0x73, 0x00,
  363. 0x00, 0x00, 0x00, 0x00,
  364. 0x00, 0x00, 0x00, 0x00,
  365. 0x00, 0x00, 0x00, // compressor name
  366. 0x00, 0x18, // depth = 24
  367. 0x11, 0x11, // pre_defined = -1
  368. ]);
  369. let boxName = 'avc1';
  370. const paspBox = this.pasp_(streamInfo);
  371. let sinfBox = new Uint8Array([]);
  372. if (streamInfo.encrypted) {
  373. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  374. boxName = 'encv';
  375. }
  376. return Mp4Generator.box(boxName, avc1Bytes, avcCBox, paspBox, sinfBox);
  377. }
  378. /**
  379. * Generate a AVCC box
  380. *
  381. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  382. * @return {!Uint8Array}
  383. * @private
  384. */
  385. avcC_(streamInfo) {
  386. const NALU_TYPE_SPS = 7;
  387. const NALU_TYPE_PPS = 8;
  388. // length = 7 by default (0 SPS and 0 PPS)
  389. let avcCLength = 7;
  390. // First get all SPS and PPS from nalus
  391. const sps = [];
  392. const pps = [];
  393. let AVCProfileIndication = 0;
  394. let AVCLevelIndication = 0;
  395. let profileCompatibility = 0;
  396. for (let i = 0; i < streamInfo.videoNalus.length; i++) {
  397. const naluBytes = this.hexStringToBuffer_(streamInfo.videoNalus[i]);
  398. const naluType = naluBytes[0] & 0x1F;
  399. switch (naluType) {
  400. case NALU_TYPE_SPS:
  401. sps.push(naluBytes);
  402. // 2 = sequenceParameterSetLength field length
  403. avcCLength += naluBytes.length + 2;
  404. break;
  405. case NALU_TYPE_PPS:
  406. pps.push(naluBytes);
  407. // 2 = pictureParameterSetLength field length
  408. avcCLength += naluBytes.length + 2;
  409. break;
  410. default:
  411. break;
  412. }
  413. }
  414. // Get profile and level from SPS
  415. if (sps.length > 0) {
  416. AVCProfileIndication = sps[0][1];
  417. profileCompatibility = sps[0][2];
  418. AVCLevelIndication = sps[0][3];
  419. }
  420. // Generate avcC buffer
  421. const avcCBytes = new Uint8Array(avcCLength);
  422. let i = 0;
  423. // configurationVersion = 1
  424. avcCBytes[i++] = 1;
  425. avcCBytes[i++] = AVCProfileIndication;
  426. avcCBytes[i++] = profileCompatibility;
  427. avcCBytes[i++] = AVCLevelIndication;
  428. // '11111' + lengthSizeMinusOne = 3
  429. avcCBytes[i++] = 0xFF;
  430. // '111' + numOfSequenceParameterSets
  431. avcCBytes[i++] = 0xE0 | sps.length;
  432. for (let n = 0; n < sps.length; n++) {
  433. avcCBytes[i++] = (sps[n].length & 0xFF00) >> 8;
  434. avcCBytes[i++] = (sps[n].length & 0x00FF);
  435. avcCBytes.set(sps[n], i);
  436. i += sps[n].length;
  437. }
  438. // numOfPictureParameterSets
  439. avcCBytes[i++] = pps.length;
  440. for (let n = 0; n < pps.length; n++) {
  441. avcCBytes[i++] = (pps[n].length & 0xFF00) >> 8;
  442. avcCBytes[i++] = (pps[n].length & 0x00FF);
  443. avcCBytes.set(pps[n], i);
  444. i += pps[n].length;
  445. }
  446. return avcCBytes;
  447. }
  448. /**
  449. * Generate a HVC1 box
  450. *
  451. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  452. * @return {!Uint8Array}
  453. * @private
  454. */
  455. hvc1_(streamInfo) {
  456. const Mp4Generator = shaka.util.Mp4Generator;
  457. const width = streamInfo.stream.width || 0;
  458. const height = streamInfo.stream.height || 0;
  459. let hvcCBox = new Uint8Array([]);
  460. if (streamInfo.videoConfig.byteLength > 0) {
  461. hvcCBox = Mp4Generator.box('hvcC', streamInfo.videoConfig);
  462. }
  463. const hvc1Bytes = new Uint8Array([
  464. 0x00, 0x00, 0x00,
  465. 0x00, 0x00, 0x00, // reserved
  466. 0x00, 0x01, // data_reference_index
  467. 0x00, 0x00, // pre_defined
  468. 0x00, 0x00, // reserved
  469. 0x00, 0x00, 0x00, 0x00,
  470. 0x00, 0x00, 0x00, 0x00,
  471. 0x00, 0x00, 0x00, 0x00, // pre_defined
  472. ...this.breakNumberIntoBytes_(width, 2), // width
  473. ...this.breakNumberIntoBytes_(height, 2), // height
  474. 0x00, 0x48, 0x00, 0x00, // horizontal resolution
  475. 0x00, 0x48, 0x00, 0x00, // vertical resolution
  476. 0x00, 0x00, 0x00, 0x00, // reserved
  477. 0x00, 0x01, // frame_count
  478. 0x13,
  479. 0x76, 0x69, 0x64, 0x65,
  480. 0x6f, 0x6a, 0x73, 0x2d,
  481. 0x63, 0x6f, 0x6e, 0x74,
  482. 0x72, 0x69, 0x62, 0x2d,
  483. 0x68, 0x6c, 0x73, 0x00,
  484. 0x00, 0x00, 0x00, 0x00,
  485. 0x00, 0x00, 0x00, 0x00,
  486. 0x00, 0x00, 0x00, // compressor name
  487. 0x00, 0x18, // depth = 24
  488. 0x11, 0x11, // pre_defined = -1
  489. ]);
  490. let boxName = 'hvc1';
  491. const paspBox = this.pasp_(streamInfo);
  492. let sinfBox = new Uint8Array([]);
  493. if (streamInfo.encrypted) {
  494. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  495. boxName = 'encv';
  496. }
  497. return Mp4Generator.box(boxName, hvc1Bytes, hvcCBox, paspBox, sinfBox);
  498. }
  499. /**
  500. * Generate a PASP box
  501. *
  502. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  503. * @return {!Uint8Array}
  504. * @private
  505. */
  506. pasp_(streamInfo) {
  507. if (!streamInfo.hSpacing && !streamInfo.vSpacing) {
  508. return new Uint8Array([]);
  509. }
  510. const Mp4Generator = shaka.util.Mp4Generator;
  511. const hSpacing = streamInfo.hSpacing;
  512. const vSpacing = streamInfo.vSpacing;
  513. const bytes = new Uint8Array([
  514. ...this.breakNumberIntoBytes_(hSpacing, 4), // hSpacing
  515. ...this.breakNumberIntoBytes_(vSpacing, 4), // vSpacing
  516. ]);
  517. return Mp4Generator.box('pasp', bytes);
  518. }
  519. /**
  520. * Generate STSD bytes
  521. *
  522. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  523. * @return {!Uint8Array}
  524. * @private
  525. */
  526. audioStsd_(streamInfo) {
  527. const channelsCount = streamInfo.stream.channelsCount || 2;
  528. const audioSamplingRate = streamInfo.stream.audioSamplingRate || 44100;
  529. const bytes = new Uint8Array([
  530. 0x00, 0x00, 0x00, // reserved
  531. 0x00, 0x00, 0x00, // reserved
  532. 0x00, 0x01, // data_reference_index
  533. 0x00, 0x00, 0x00, 0x00,
  534. 0x00, 0x00, 0x00, 0x00, // reserved
  535. 0x00,
  536. channelsCount, // channel count
  537. 0x00, 0x10, // sampleSize:16bits
  538. 0x00, 0x00, 0x00, 0x00, // reserved2
  539. ...this.breakNumberIntoBytes_(audioSamplingRate, 2), // Sample Rate
  540. 0x00, 0x00,
  541. ]);
  542. return bytes;
  543. }
  544. /**
  545. * Generate a .MP3 box
  546. *
  547. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  548. * @return {!Uint8Array}
  549. * @private
  550. */
  551. mp3_(streamInfo) {
  552. const Mp4Generator = shaka.util.Mp4Generator;
  553. return Mp4Generator.box('.mp3', this.audioStsd_(streamInfo));
  554. }
  555. /**
  556. * Generate a AC-3 box
  557. *
  558. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  559. * @return {!Uint8Array}
  560. * @private
  561. */
  562. ac3_(streamInfo) {
  563. const Mp4Generator = shaka.util.Mp4Generator;
  564. const dac3Box = Mp4Generator.box('dac3', streamInfo.audioConfig);
  565. let boxName = 'ac-3';
  566. let sinfBox = new Uint8Array([]);
  567. if (streamInfo.encrypted) {
  568. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  569. boxName = 'enca';
  570. }
  571. return Mp4Generator.box(boxName,
  572. this.audioStsd_(streamInfo), dac3Box, sinfBox);
  573. }
  574. /**
  575. * Generate a EC-3 box
  576. *
  577. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  578. * @return {!Uint8Array}
  579. * @private
  580. */
  581. ec3_(streamInfo) {
  582. const Mp4Generator = shaka.util.Mp4Generator;
  583. const dec3Box = Mp4Generator.box('dec3', streamInfo.audioConfig);
  584. let boxName = 'ec-3';
  585. let sinfBox = new Uint8Array([]);
  586. if (streamInfo.encrypted) {
  587. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  588. boxName = 'enca';
  589. }
  590. return Mp4Generator.box(boxName,
  591. this.audioStsd_(streamInfo), dec3Box, sinfBox);
  592. }
  593. /**
  594. * Generate a Opus box
  595. *
  596. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  597. * @return {!Uint8Array}
  598. * @private
  599. */
  600. opus_(streamInfo) {
  601. const Mp4Generator = shaka.util.Mp4Generator;
  602. const dopsBox = Mp4Generator.box('dOps', streamInfo.audioConfig);
  603. let boxName = 'Opus';
  604. let sinfBox = new Uint8Array([]);
  605. if (streamInfo.encrypted) {
  606. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  607. boxName = 'enca';
  608. }
  609. return Mp4Generator.box(boxName,
  610. this.audioStsd_(streamInfo), dopsBox, sinfBox);
  611. }
  612. /**
  613. * Generate a MP4A box
  614. *
  615. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  616. * @return {!Uint8Array}
  617. * @private
  618. */
  619. mp4a_(streamInfo) {
  620. const Mp4Generator = shaka.util.Mp4Generator;
  621. let esdsBox;
  622. if (streamInfo.audioConfig.byteLength > 0) {
  623. esdsBox = Mp4Generator.box('esds', streamInfo.audioConfig);
  624. } else {
  625. esdsBox = Mp4Generator.box('esds', this.esds_(streamInfo));
  626. }
  627. let boxName = 'mp4a';
  628. let sinfBox = new Uint8Array([]);
  629. if (streamInfo.encrypted) {
  630. sinfBox = this.sinf(streamInfo.stream, streamInfo.codecs);
  631. boxName = 'enca';
  632. }
  633. return Mp4Generator.box(boxName,
  634. this.audioStsd_(streamInfo), esdsBox, sinfBox);
  635. }
  636. /**
  637. * Generate a ESDS box
  638. *
  639. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  640. * @return {!Uint8Array}
  641. * @private
  642. */
  643. esds_(streamInfo) {
  644. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  645. const id = streamInfo.id + 1;
  646. const channelsCount = streamInfo.stream.channelsCount || 2;
  647. const audioSamplingRate = streamInfo.stream.audioSamplingRate || 44100;
  648. const audioCodec = shaka.util.ManifestParserUtils.guessCodecs(
  649. ContentType.AUDIO, streamInfo.codecs.split(','));
  650. const samplingFrequencyIndex = {
  651. 96000: 0x0,
  652. 88200: 0x1,
  653. 64000: 0x2,
  654. 48000: 0x3,
  655. 44100: 0x4,
  656. 32000: 0x5,
  657. 24000: 0x6,
  658. 22050: 0x7,
  659. 16000: 0x8,
  660. 12000: 0x9,
  661. 11025: 0xA,
  662. 8000: 0xB,
  663. 7350: 0xC,
  664. };
  665. let indexFreq = samplingFrequencyIndex[audioSamplingRate];
  666. // In HE AAC Sampling frequence equals to SamplingRate * 2
  667. if (audioCodec === 'mp4a.40.5' || audioCodec === 'mp4a.40.29') {
  668. indexFreq = samplingFrequencyIndex[audioSamplingRate * 2];
  669. }
  670. const audioObjectType = parseInt(audioCodec.split('.').pop(), 10);
  671. return new Uint8Array([
  672. 0x00, // version
  673. 0x00, 0x00, 0x00, // flags
  674. // ES_Descriptor
  675. 0x03, // tag, ES_DescriptionTag
  676. 0x19, // length
  677. ...this.breakNumberIntoBytes_(id, 2), // ES_ID
  678. 0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority
  679. // DecoderConfigDescriptor
  680. 0x04, // tag, DecoderConfigDescriptionTag
  681. 0x11, // length
  682. 0x40, // object type
  683. 0x15, // streamType
  684. 0x00, 0x00, 0x00, // bufferSizeDB
  685. 0x00, 0x00, 0x00, 0x00, // maxBitrate
  686. 0x00, 0x00, 0x00, 0x00, // avgBitrate
  687. // DecoderSpecificInfo
  688. 0x05, // tag, DecoderSpecificInfoTag
  689. 0x02, // length
  690. // ISO/IEC 14496-3, AudioSpecificConfig
  691. // for samplingFrequencyIndex see
  692. // ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
  693. (audioObjectType << 3) | (indexFreq >>> 1),
  694. (indexFreq << 7) | (channelsCount << 3),
  695. 0x06, 0x01, 0x02, // GASpecificConfig
  696. ]);
  697. }
  698. /**
  699. * Generate a MVEX box
  700. *
  701. * @return {!Uint8Array}
  702. * @private
  703. */
  704. mvex_() {
  705. const Mp4Generator = shaka.util.Mp4Generator;
  706. const trexArrays = [];
  707. for (const streamInfo of this.streamInfos_) {
  708. trexArrays.push(this.trex_(streamInfo));
  709. }
  710. const trexs = shaka.util.Uint8ArrayUtils.concat(...trexArrays);
  711. return Mp4Generator.box('mvex', trexs);
  712. }
  713. /**
  714. * Generate a TREX box
  715. *
  716. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  717. * @return {!Uint8Array}
  718. * @private
  719. */
  720. trex_(streamInfo) {
  721. const Mp4Generator = shaka.util.Mp4Generator;
  722. const id = streamInfo.id + 1;
  723. const bytes = new Uint8Array([
  724. 0x00, // version 0
  725. 0x00, 0x00, 0x00, // flags
  726. ...this.breakNumberIntoBytes_(id, 4), // track_ID
  727. 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
  728. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  729. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  730. 0x00, 0x00, 0x00, 0x00, // default_sample_flags
  731. ]);
  732. return Mp4Generator.box('trex', bytes);
  733. }
  734. /**
  735. * Generate a PSSH box
  736. *
  737. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  738. * @return {!Uint8Array}
  739. * @private
  740. */
  741. pssh_(streamInfo) {
  742. const initDatas = [];
  743. if (!streamInfo.encrypted) {
  744. return new Uint8Array([]);
  745. }
  746. for (const drmInfo of streamInfo.stream.drmInfos) {
  747. if (!drmInfo.initData) {
  748. continue;
  749. }
  750. for (const initData of drmInfo.initData) {
  751. initDatas.push(initData.initData);
  752. }
  753. }
  754. const boxes = shaka.util.Uint8ArrayUtils.concat(...initDatas);
  755. return boxes;
  756. }
  757. /**
  758. * Generate a SINF box
  759. *
  760. * @param {!shaka.extern.Stream} stream
  761. * @param {string} codecs
  762. * @return {!Uint8Array}
  763. */
  764. sinf(stream, codecs) {
  765. const Mp4Generator = shaka.util.Mp4Generator;
  766. return Mp4Generator.box('sinf',
  767. this.frma_(codecs),
  768. this.schm_(stream),
  769. this.schi_(stream));
  770. }
  771. /**
  772. * Generate a FRMA box
  773. *
  774. * @param {string} codecs
  775. * @return {!Uint8Array}
  776. * @private
  777. */
  778. frma_(codecs) {
  779. const codec = codecs.split('.')[0];
  780. const Mp4Generator = shaka.util.Mp4Generator;
  781. const codecNumber = this.stringToCharCode_(codec);
  782. const bytes = new Uint8Array([
  783. ...this.breakNumberIntoBytes_(codecNumber, 4),
  784. ]);
  785. return Mp4Generator.box('frma', bytes);
  786. }
  787. /**
  788. * Generate a SCHM box
  789. *
  790. * @param {!shaka.extern.Stream} stream
  791. * @return {!Uint8Array}
  792. * @private
  793. */
  794. schm_(stream) {
  795. let encryptionScheme = 'cenc';
  796. const drmInfo = stream.drmInfos[0];
  797. if (drmInfo && drmInfo.encryptionScheme) {
  798. encryptionScheme = drmInfo.encryptionScheme;
  799. }
  800. const Mp4Generator = shaka.util.Mp4Generator;
  801. const schemeNumber = this.stringToCharCode_(encryptionScheme);
  802. const bytes = new Uint8Array([
  803. 0x00, // version 0
  804. 0x00, 0x00, 0x00, // flags
  805. ...this.breakNumberIntoBytes_(schemeNumber, 4), // Scheme
  806. 0x00, 0x01, 0x00, 0x00, // Scheme version: 1.0
  807. ]);
  808. return Mp4Generator.box('schm', bytes);
  809. }
  810. /**
  811. * Generate a SCHI box
  812. *
  813. * @param {!shaka.extern.Stream} stream
  814. * @return {!Uint8Array}
  815. * @private
  816. */
  817. schi_(stream) {
  818. const Mp4Generator = shaka.util.Mp4Generator;
  819. return Mp4Generator.box('schi', this.tenc_(stream));
  820. }
  821. /**
  822. * Generate a TENC box
  823. *
  824. * @param {!shaka.extern.Stream} stream
  825. * @return {!Uint8Array}
  826. * @private
  827. */
  828. tenc_(stream) {
  829. // Default key ID: all zeros (dummy)
  830. let defaultKeyId = new Uint8Array([
  831. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  832. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  833. ]);
  834. for (const drmInfo of stream.drmInfos) {
  835. if (drmInfo && drmInfo.keyIds && drmInfo.keyIds.size) {
  836. for (const keyId of drmInfo.keyIds) {
  837. defaultKeyId = this.hexStringToBuffer_(keyId);
  838. }
  839. }
  840. }
  841. const Mp4Generator = shaka.util.Mp4Generator;
  842. const bytes = new Uint8Array([
  843. 0x00, // version 0
  844. 0x00, 0x00, 0x00, // flags
  845. 0x00, 0x00, // Reserved fields
  846. 0x01, // Default protected: true
  847. 0x08, // Default per-sample IV size: 8
  848. ]);
  849. goog.asserts.assert(defaultKeyId, 'Default KID should be non-null');
  850. return Mp4Generator.box('tenc', bytes, defaultKeyId);
  851. }
  852. /**
  853. * Generate a Segment Data (MP4).
  854. *
  855. * @return {!Uint8Array}
  856. */
  857. segmentData() {
  858. const segmentDataArray = [];
  859. for (const streamInfo of this.streamInfos_) {
  860. segmentDataArray.push(
  861. ...[this.moof_(streamInfo), this.mdat_(streamInfo)]);
  862. }
  863. const result = shaka.util.Uint8ArrayUtils.concat(...segmentDataArray);
  864. return result;
  865. }
  866. /**
  867. * Generate a MOOF box
  868. *
  869. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  870. * @return {!Uint8Array}
  871. * @private
  872. */
  873. moof_(streamInfo) {
  874. const Mp4Generator = shaka.util.Mp4Generator;
  875. return Mp4Generator.box('moof',
  876. this.mfhd_(streamInfo), this.traf_(streamInfo));
  877. }
  878. /**
  879. * Generate a MOOF box
  880. *
  881. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  882. * @return {!Uint8Array}
  883. * @private
  884. */
  885. mfhd_(streamInfo) {
  886. const Mp4Generator = shaka.util.Mp4Generator;
  887. const sequenceNumber =
  888. streamInfo.data ? streamInfo.data.sequenceNumber : 0;
  889. const bytes = new Uint8Array([
  890. 0x00, // version 0
  891. 0x00, 0x00, 0x00, // flags
  892. ...this.breakNumberIntoBytes_(sequenceNumber, 4),
  893. ]);
  894. return Mp4Generator.box('mfhd', bytes);
  895. }
  896. /**
  897. * Generate a TRAF box
  898. *
  899. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  900. * @return {!Uint8Array}
  901. * @private
  902. */
  903. traf_(streamInfo) {
  904. const Mp4Generator = shaka.util.Mp4Generator;
  905. const sampleDependencyTable = this.sdtp_(streamInfo);
  906. const offset = sampleDependencyTable.length +
  907. 32 + // tfhd
  908. 20 + // tfdt
  909. 8 + // traf header
  910. 16 + // mfhd
  911. 8 + // moof header
  912. 8; // mdat header;
  913. return Mp4Generator.box('traf',
  914. this.tfhd_(streamInfo),
  915. this.tfdt_(streamInfo),
  916. this.trun_(streamInfo, offset),
  917. sampleDependencyTable);
  918. }
  919. /**
  920. * Generate a SDTP box
  921. *
  922. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  923. * @return {!Uint8Array}
  924. * @private
  925. */
  926. sdtp_(streamInfo) {
  927. const Mp4Generator = shaka.util.Mp4Generator;
  928. const samples = streamInfo.data ? streamInfo.data.samples : [];
  929. const bytes = new Uint8Array(4 + samples.length);
  930. // leave the full box header (4 bytes) all zero
  931. // write the sample table
  932. for (let i = 0; i < samples.length; i++) {
  933. const flags = samples[i].flags;
  934. bytes[i + 4] = (flags.dependsOn << 4) |
  935. (flags.isDependedOn << 2) |
  936. flags.hasRedundancy;
  937. }
  938. return Mp4Generator.box('sdtp', bytes);
  939. }
  940. /**
  941. * Generate a TFHD box
  942. *
  943. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  944. * @return {!Uint8Array}
  945. * @private
  946. */
  947. tfhd_(streamInfo) {
  948. const Mp4Generator = shaka.util.Mp4Generator;
  949. const id = streamInfo.id + 1;
  950. const bytes = new Uint8Array([
  951. 0x00, // version 0
  952. 0x00, 0x00, 0x3a, // flags
  953. ...this.breakNumberIntoBytes_(id, 4), // track_ID
  954. 0x00, 0x00, 0x00, 0x01, // sample_description_index
  955. 0x00, 0x00, 0x00, 0x00, // default_sample_duration
  956. 0x00, 0x00, 0x00, 0x00, // default_sample_size
  957. 0x00, 0x00, 0x00, 0x00, // default_sample_flags
  958. ]);
  959. return Mp4Generator.box('tfhd', bytes);
  960. }
  961. /**
  962. * Generate a TFDT box
  963. *
  964. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  965. * @return {!Uint8Array}
  966. * @private
  967. */
  968. tfdt_(streamInfo) {
  969. const Mp4Generator = shaka.util.Mp4Generator;
  970. const baseMediaDecodeTime =
  971. streamInfo.data ? streamInfo.data.baseMediaDecodeTime : 0;
  972. const upperWordBaseMediaDecodeTime =
  973. Math.floor(baseMediaDecodeTime / (Mp4Generator.UINT32_MAX_ + 1));
  974. const lowerWordBaseMediaDecodeTime =
  975. Math.floor(baseMediaDecodeTime % (Mp4Generator.UINT32_MAX_ + 1));
  976. const bytes = new Uint8Array([
  977. 0x01, // version 1
  978. 0x00, 0x00, 0x00, // flags
  979. ...this.breakNumberIntoBytes_(upperWordBaseMediaDecodeTime, 4),
  980. ...this.breakNumberIntoBytes_(lowerWordBaseMediaDecodeTime, 4),
  981. ]);
  982. return Mp4Generator.box('tfdt', bytes);
  983. }
  984. /**
  985. * Generate a TRUN box
  986. *
  987. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  988. * @param {number} offset
  989. * @return {!Uint8Array}
  990. * @private
  991. */
  992. trun_(streamInfo, offset) {
  993. const ContentType = shaka.util.ManifestParserUtils.ContentType;
  994. const Mp4Generator = shaka.util.Mp4Generator;
  995. const samples = streamInfo.data ? streamInfo.data.samples : [];
  996. const samplesLength = samples.length;
  997. const bytesLen = 12 + 16 * samplesLength;
  998. const bytes = new Uint8Array(bytesLen);
  999. offset += 8 + bytesLen;
  1000. const isVideo = streamInfo.type === ContentType.VIDEO;
  1001. bytes.set(
  1002. [
  1003. // version 1 for video with signed-int sample_composition_time_offset
  1004. isVideo ? 0x01 : 0x00,
  1005. 0x00, 0x0f, 0x01, // flags
  1006. ...this.breakNumberIntoBytes_(samplesLength, 4), // sample_count
  1007. ...this.breakNumberIntoBytes_(offset, 4), // data_offset
  1008. ],
  1009. 0,
  1010. );
  1011. for (let i = 0; i < samplesLength; i++) {
  1012. const sample = samples[i];
  1013. const duration = this.breakNumberIntoBytes_(sample.duration, 4);
  1014. const size = this.breakNumberIntoBytes_(sample.size, 4);
  1015. const flags = sample.flags;
  1016. const cts = this.breakNumberIntoBytes_(sample.cts, 4);
  1017. bytes.set(
  1018. [
  1019. ...duration, // sample_duration
  1020. ...size, // sample_size
  1021. (flags.isLeading << 2) | flags.dependsOn,
  1022. (flags.isDependedOn << 6) | (flags.hasRedundancy << 4) |
  1023. flags.isNonSync,
  1024. flags.degradPrio & (0xf0 << 8),
  1025. flags.degradPrio & 0x0f, // sample_flags
  1026. ...cts, // sample_composition_time_offset
  1027. ],
  1028. 12 + 16 * i,
  1029. );
  1030. }
  1031. return Mp4Generator.box('trun', bytes);
  1032. }
  1033. /**
  1034. * Generate a MDAT box
  1035. *
  1036. * @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
  1037. * @return {!Uint8Array}
  1038. * @private
  1039. */
  1040. mdat_(streamInfo) {
  1041. const Mp4Generator = shaka.util.Mp4Generator;
  1042. const samples = streamInfo.data ? streamInfo.data.samples : [];
  1043. const allData = samples.map((sample) => sample.data);
  1044. const bytes = shaka.util.Uint8ArrayUtils.concat(...allData);
  1045. return Mp4Generator.box('mdat', bytes);
  1046. }
  1047. /**
  1048. * @param {number} number
  1049. * @param {number} numBytes
  1050. * @return {!Array<number>}
  1051. * @private
  1052. */
  1053. breakNumberIntoBytes_(number, numBytes) {
  1054. const bytes = [];
  1055. for (let byte = numBytes - 1; byte >= 0; byte--) {
  1056. bytes.push((number >> (8 * byte)) & 0xff);
  1057. }
  1058. return bytes;
  1059. }
  1060. /**
  1061. * Convert a hex string to buffer.
  1062. *
  1063. * @param {string} str
  1064. * @return {Uint8Array}
  1065. * @private
  1066. */
  1067. hexStringToBuffer_(str) {
  1068. const buf = new Uint8Array(str.length / 2);
  1069. for (let i = 0; i < str.length / 2; i += 1) {
  1070. buf[i] = parseInt(String(str[i * 2] + str[i * 2 + 1]), 16);
  1071. }
  1072. return buf;
  1073. }
  1074. /**
  1075. * Convert a string to char code.
  1076. *
  1077. * @param {string} str
  1078. * @return {number}
  1079. * @private
  1080. */
  1081. stringToCharCode_(str) {
  1082. let code = 0;
  1083. for (let i = 0; i < str.length; i += 1) {
  1084. code |= str.charCodeAt(i) << ((str.length - i - 1) * 8);
  1085. }
  1086. return code;
  1087. }
  1088. /**
  1089. * @private
  1090. */
  1091. static initStaticProperties_() {
  1092. const Mp4Generator = shaka.util.Mp4Generator;
  1093. if (Mp4Generator.initialized_) {
  1094. return;
  1095. }
  1096. Mp4Generator.initialized_ = true;
  1097. const majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
  1098. const avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
  1099. const minorVersion = new Uint8Array([0, 0, 0, 1]);
  1100. Mp4Generator.FTYP_ = Mp4Generator.box(
  1101. 'ftyp', majorBrand, minorVersion, majorBrand, avc1Brand);
  1102. const drefBox = Mp4Generator.box('dref', Mp4Generator.DREF_);
  1103. Mp4Generator.DINF_ = Mp4Generator.box('dinf', drefBox);
  1104. }
  1105. /**
  1106. * Generate a box
  1107. *
  1108. * @param {string} boxName
  1109. * @param {...!Uint8Array} payload
  1110. * @return {!Uint8Array}
  1111. */
  1112. static box(boxName, ...payload) {
  1113. let type = shaka.util.Mp4Generator.BOX_TYPES_.get(boxName);
  1114. if (!type) {
  1115. type = [
  1116. boxName.charCodeAt(0),
  1117. boxName.charCodeAt(1),
  1118. boxName.charCodeAt(2),
  1119. boxName.charCodeAt(3),
  1120. ];
  1121. shaka.util.Mp4Generator.BOX_TYPES_.set(boxName, type);
  1122. }
  1123. // make the header for the box
  1124. let size = 8;
  1125. // calculate the total size we need to allocate
  1126. for (let i = payload.length - 1; i >= 0; i--) {
  1127. size += payload[i].byteLength;
  1128. }
  1129. const result = new Uint8Array(size);
  1130. result[0] = (size >> 24) & 0xff;
  1131. result[1] = (size >> 16) & 0xff;
  1132. result[2] = (size >> 8) & 0xff;
  1133. result[3] = size & 0xff;
  1134. result.set(type, 4);
  1135. // copy the payload into the result
  1136. for (let i = 0, pointer = 8; i < payload.length; i++) {
  1137. // copy payload[i] array @ offset pointer
  1138. result.set(payload[i], pointer);
  1139. pointer += payload[i].byteLength;
  1140. }
  1141. return result;
  1142. }
  1143. };
  1144. /**
  1145. * @private {boolean}
  1146. */
  1147. shaka.util.Mp4Generator.initialized_ = false;
  1148. /**
  1149. * @private {number}
  1150. */
  1151. shaka.util.Mp4Generator.UINT32_MAX_ = Math.pow(2, 32) - 1;
  1152. /**
  1153. * @private {!Map<string, !Array<number>>}
  1154. */
  1155. shaka.util.Mp4Generator.BOX_TYPES_ = new Map();
  1156. /**
  1157. * @private {{video: !Uint8Array, audio: !Uint8Array}}
  1158. */
  1159. shaka.util.Mp4Generator.HDLR_TYPES_ = {
  1160. video: new Uint8Array([
  1161. 0x00, // version 0
  1162. 0x00, 0x00, 0x00, // flags
  1163. 0x00, 0x00, 0x00, 0x00, // pre_defined
  1164. 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
  1165. 0x00, 0x00, 0x00, 0x00, // reserved
  1166. 0x00, 0x00, 0x00, 0x00, // reserved
  1167. 0x00, 0x00, 0x00, 0x00, // reserved
  1168. 0x56, 0x69, 0x64, 0x65,
  1169. 0x6f, 0x48, 0x61, 0x6e,
  1170. 0x64, 0x6c, 0x65, 0x72, 0x00, // name: 'VideoHandler'
  1171. ]),
  1172. audio: new Uint8Array([
  1173. 0x00, // version 0
  1174. 0x00, 0x00, 0x00, // flags
  1175. 0x00, 0x00, 0x00, 0x00, // pre_defined
  1176. 0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
  1177. 0x00, 0x00, 0x00, 0x00, // reserved
  1178. 0x00, 0x00, 0x00, 0x00, // reserved
  1179. 0x00, 0x00, 0x00, 0x00, // reserved
  1180. 0x53, 0x6f, 0x75, 0x6e,
  1181. 0x64, 0x48, 0x61, 0x6e,
  1182. 0x64, 0x6c, 0x65, 0x72, 0x00, // name: 'SoundHandler'
  1183. ]),
  1184. };
  1185. /**
  1186. * @private {!Uint8Array}
  1187. */
  1188. shaka.util.Mp4Generator.STTS_ = new Uint8Array([
  1189. 0x00, // version
  1190. 0x00, 0x00, 0x00, // flags
  1191. 0x00, 0x00, 0x00, 0x00, // entry_count
  1192. ]);
  1193. /**
  1194. * @private {!Uint8Array}
  1195. */
  1196. shaka.util.Mp4Generator.STSC_ = new Uint8Array([
  1197. 0x00, // version
  1198. 0x00, 0x00, 0x00, // flags
  1199. 0x00, 0x00, 0x00, 0x00, // entry_count
  1200. ]);
  1201. /**
  1202. * @private {!Uint8Array}
  1203. */
  1204. shaka.util.Mp4Generator.STCO_ = new Uint8Array([
  1205. 0x00, // version
  1206. 0x00, 0x00, 0x00, // flags
  1207. 0x00, 0x00, 0x00, 0x00, // entry_count
  1208. ]);
  1209. /**
  1210. * @private {!Uint8Array}
  1211. */
  1212. shaka.util.Mp4Generator.STSZ_ = new Uint8Array([
  1213. 0x00, // version
  1214. 0x00, 0x00, 0x00, // flags
  1215. 0x00, 0x00, 0x00, 0x00, // sample_size
  1216. 0x00, 0x00, 0x00, 0x00, // sample_count
  1217. ]);
  1218. /**
  1219. * @private {!Uint8Array}
  1220. */
  1221. shaka.util.Mp4Generator.VMHD_ = new Uint8Array([
  1222. 0x00, // version
  1223. 0x00, 0x00, 0x01, // flags
  1224. 0x00, 0x00, // graphics mode
  1225. 0x00, 0x00,
  1226. 0x00, 0x00,
  1227. 0x00, 0x00, // op color
  1228. ]);
  1229. /**
  1230. * @private {!Uint8Array}
  1231. */
  1232. shaka.util.Mp4Generator.SMHD_ = new Uint8Array([
  1233. 0x00, // version
  1234. 0x00, 0x00, 0x00, // flags
  1235. 0x00, 0x00, // balance, 0 means centered
  1236. 0x00, 0x00, // reserved
  1237. ]);
  1238. /**
  1239. * @private {!Uint8Array}
  1240. */
  1241. shaka.util.Mp4Generator.STSD_ = new Uint8Array([
  1242. 0x00, // version 0
  1243. 0x00, 0x00, 0x00, // flags
  1244. 0x00, 0x00, 0x00, 0x01, // entry_count
  1245. ]);
  1246. /**
  1247. * @private {!Uint8Array}
  1248. */
  1249. shaka.util.Mp4Generator.FTYP_ = new Uint8Array([]);
  1250. /**
  1251. * @private {!Uint8Array}
  1252. */
  1253. shaka.util.Mp4Generator.DREF_ = new Uint8Array([
  1254. 0x00, // version 0
  1255. 0x00, 0x00, 0x00, // flags
  1256. 0x00, 0x00, 0x00, 0x01, // entry_count
  1257. 0x00, 0x00, 0x00, 0x0c, // entry_size
  1258. 0x75, 0x72, 0x6c, 0x20, // 'url' type
  1259. 0x00, // version 0
  1260. 0x00, 0x00, 0x01, // entry_flags
  1261. ]);
  1262. /**
  1263. * @private {!Uint8Array}
  1264. */
  1265. shaka.util.Mp4Generator.DINF_ = new Uint8Array([]);
  1266. /**
  1267. * @typedef {{
  1268. * id: number,
  1269. * type: string,
  1270. * codecs: string,
  1271. * encrypted: boolean,
  1272. * timescale: number,
  1273. * duration: number,
  1274. * videoNalus: !Array<string>,
  1275. * audioConfig: !Uint8Array,
  1276. * videoConfig: !Uint8Array,
  1277. * hSpacing: number,
  1278. * vSpacing: number,
  1279. * data: ?shaka.util.Mp4Generator.Data,
  1280. * stream: !shaka.extern.Stream
  1281. * }}
  1282. *
  1283. * @property {number} id
  1284. * A unique ID
  1285. * @property {string} type
  1286. * Indicate the content type: 'video' or 'audio'.
  1287. * @property {string} codecs
  1288. * <i>Defaults to '' (i.e., unknown / not needed).</i> <br>
  1289. * The Stream's codecs, e.g., 'avc1.4d4015' or 'vp9'<br>
  1290. * See {@link https://tools.ietf.org/html/rfc6381}
  1291. * @property {boolean} encrypted
  1292. * Indicate if the stream is encrypted.
  1293. * @property {number} timescale
  1294. * The Stream's timescale.
  1295. * @property {number} duration
  1296. * The Stream's duration.
  1297. * @property {!Array<string>} videoNalus
  1298. * The stream's video nalus.
  1299. * @property {!Uint8Array} audioConfig
  1300. * The stream's audio config.
  1301. * @property {!Uint8Array} videoConfig
  1302. * The stream's video config.
  1303. * @property {number} hSpacing
  1304. * The stream's video horizontal spacing of pixels.
  1305. * @property {number} vSpacing
  1306. * The stream's video vertical spacing of pixels.
  1307. * @property {?shaka.util.Mp4Generator.Data} data
  1308. * The stream's data.
  1309. * @property {!shaka.extern.Stream} stream
  1310. * The Stream.
  1311. */
  1312. shaka.util.Mp4Generator.StreamInfo;
  1313. /**
  1314. * @typedef {{
  1315. * sequenceNumber: number,
  1316. * baseMediaDecodeTime: number,
  1317. * samples: !Array<shaka.util.Mp4Generator.Mp4Sample>
  1318. * }}
  1319. *
  1320. * @property {number} sequenceNumber
  1321. * The sequence number.
  1322. * @property {number} baseMediaDecodeTime
  1323. * The base media decode time.
  1324. * @property {!Array<shaka.util.Mp4Generator.Mp4Sample>} samples
  1325. * The data samples.
  1326. */
  1327. shaka.util.Mp4Generator.Data;
  1328. /**
  1329. * @typedef {{
  1330. * data: !Uint8Array,
  1331. * size: number,
  1332. * duration: number,
  1333. * cts: number,
  1334. * flags: !shaka.util.Mp4Generator.Mp4SampleFlags
  1335. * }}
  1336. *
  1337. * @property {!Uint8Array} data
  1338. * The sample data.
  1339. * @property {number} size
  1340. * The sample size.
  1341. * @property {number} duration
  1342. * The sample duration.
  1343. * @property {number} cts
  1344. * The sample composition time.
  1345. * @property {!shaka.util.Mp4Generator.Mp4SampleFlags} flags
  1346. * The sample flags.
  1347. */
  1348. shaka.util.Mp4Generator.Mp4Sample;
  1349. /**
  1350. * @typedef {{
  1351. * isLeading: number,
  1352. * isDependedOn: number,
  1353. * hasRedundancy: number,
  1354. * degradPrio: number,
  1355. * dependsOn: number,
  1356. * isNonSync: number
  1357. * }}
  1358. *
  1359. * @property {number} isLeading
  1360. * @property {number} isDependedOn
  1361. * @property {number} hasRedundancy
  1362. * @property {number} degradPrio
  1363. * @property {number} dependsOn
  1364. * @property {number} isNonSync
  1365. */
  1366. shaka.util.Mp4Generator.Mp4SampleFlags;