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