mp4-helpers.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /**
  2. * Helper functions for creating test MP4 data.
  3. */
  4. 'use strict';
  5. // ----------------------
  6. // Box Generation Helpers
  7. // ----------------------
  8. var typeBytes = function(type) {
  9. return [
  10. type.charCodeAt(0),
  11. type.charCodeAt(1),
  12. type.charCodeAt(2),
  13. type.charCodeAt(3)
  14. ];
  15. };
  16. var box = function(type) {
  17. var
  18. array = Array.prototype.slice.call(arguments, 1),
  19. result = [],
  20. size,
  21. i;
  22. // "unwrap" any arrays that were passed as arguments
  23. // e.g. box('etc', 1, [2, 3], 4) -> box('etc', 1, 2, 3, 4)
  24. for (i = 0; i < array.length; i++) {
  25. if (array[i] instanceof Array) {
  26. array.splice.apply(array, [i, 1].concat(array[i]));
  27. }
  28. }
  29. size = 8 + array.length;
  30. result[0] = (size & 0xFF000000) >> 24;
  31. result[1] = (size & 0x00FF0000) >> 16;
  32. result[2] = (size & 0x0000FF00) >> 8;
  33. result[3] = size & 0xFF;
  34. result = result.concat(typeBytes(type));
  35. result = result.concat(array);
  36. return result;
  37. };
  38. var unityMatrix = unityMatrix = [
  39. 0, 0, 0x10, 0,
  40. 0, 0, 0, 0,
  41. 0, 0, 0, 0,
  42. 0, 0, 0, 0,
  43. 0, 0, 0x10, 0,
  44. 0, 0, 0, 0,
  45. 0, 0, 0, 0,
  46. 0, 0, 0, 0,
  47. 0x40, 0, 0, 0
  48. ];
  49. // ------------
  50. // Example Data
  51. // ------------
  52. var sampleMoov =
  53. box('moov',
  54. box('mvhd',
  55. 0x01, // version 1
  56. 0x00, 0x00, 0x00, // flags
  57. 0x00, 0x00, 0x00, 0x00,
  58. 0x00, 0x00, 0x00, 0x01, // creation_time
  59. 0x00, 0x00, 0x00, 0x00,
  60. 0x00, 0x00, 0x00, 0x02, // modification_time
  61. 0x00, 0x00, 0x03, 0xe8, // timescale = 1000
  62. 0x00, 0x00, 0x00, 0x00,
  63. 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
  64. 0x00, 0x01, 0x00, 0x00, // 1.0 rate
  65. 0x01, 0x00, // 1.0 volume
  66. 0x00, 0x00, // reserved
  67. 0x00, 0x00, 0x00, 0x00, // reserved
  68. 0x00, 0x00, 0x00, 0x00, // reserved
  69. unityMatrix,
  70. 0x00, 0x00, 0x00, 0x00,
  71. 0x00, 0x00, 0x00, 0x00,
  72. 0x00, 0x00, 0x00, 0x00,
  73. 0x00, 0x00, 0x00, 0x00,
  74. 0x00, 0x00, 0x00, 0x00,
  75. 0x00, 0x00, 0x00, 0x00, // pre_defined
  76. 0x00, 0x00, 0x00, 0x02), // next_track_ID
  77. box('trak',
  78. box('tkhd',
  79. 0x01, // version 1
  80. 0x00, 0x00, 0x00, // flags
  81. 0x00, 0x00, 0x00, 0x00,
  82. 0x00, 0x00, 0x00, 0x02, // creation_time
  83. 0x00, 0x00, 0x00, 0x00,
  84. 0x00, 0x00, 0x00, 0x03, // modification_time
  85. 0x00, 0x00, 0x00, 0x01, // track_ID
  86. 0x00, 0x00, 0x00, 0x00, // reserved
  87. 0x00, 0x00, 0x00, 0x00,
  88. 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
  89. 0x00, 0x00, 0x00, 0x00,
  90. 0x00, 0x00, 0x00, 0x00, // reserved
  91. 0x00, 0x00, // layer
  92. 0x00, 0x00, // alternate_group
  93. 0x00, 0x00, // non-audio track volume
  94. 0x00, 0x00, // reserved
  95. unityMatrix,
  96. 0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
  97. 0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
  98. box('edts',
  99. box('elst',
  100. 0x00, // version
  101. 0x00, 0x00, 0x00, // flags
  102. 0x00, 0x00, 0x00, 0x01, // entry_count
  103. 0x00, 0x00, 0x00, 0x00, // segment_duration
  104. 0x00, 0x00, 0x04, 0x00, // media_time
  105. 0x00, 0x01, 0x80, 0x00)), // media_rate
  106. box('mdia',
  107. box('mdhd',
  108. 0x01, // version 1
  109. 0x00, 0x00, 0x00, // flags
  110. 0x00, 0x00, 0x00, 0x00,
  111. 0x00, 0x00, 0x00, 0x02, // creation_time
  112. 0x00, 0x00, 0x00, 0x00,
  113. 0x00, 0x00, 0x00, 0x03, // modification_time
  114. 0x00, 0x01, 0x5f, 0x90, // timescale = 90000
  115. 0x00, 0x00, 0x00, 0x00,
  116. 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
  117. 0x15, 0xc7, // 'eng' language
  118. 0x00, 0x00),
  119. box('hdlr',
  120. 0x01, // version 1
  121. 0x00, 0x00, 0x00, // flags
  122. 0x00, 0x00, 0x00, 0x00, // pre_defined
  123. typeBytes('vide'), // handler_type
  124. 0x00, 0x00, 0x00, 0x00, // reserved
  125. 0x00, 0x00, 0x00, 0x00, // reserved
  126. 0x00, 0x00, 0x00, 0x00, // reserved
  127. typeBytes('one'), 0x00), // name
  128. box('minf',
  129. box('dinf',
  130. box('dref',
  131. 0x01, // version 1
  132. 0x00, 0x00, 0x00, // flags
  133. 0x00, 0x00, 0x00, 0x01, // entry_count
  134. box('url ',
  135. 0x00, // version
  136. 0x00, 0x00, 0x01))), // flags
  137. box('stbl',
  138. box('stsd',
  139. 0x01, // version 1
  140. 0x00, 0x00, 0x00, // flags
  141. 0x00, 0x00, 0x00, 0x00, // entry_count
  142. box('avc1',
  143. 0x00, 0x00, 0x00, 0x00, // box content
  144. 0x00, 0x00, 0x00, 0x00, // box content
  145. 0x00, 0x00, 0x00, 0x00, // box content
  146. 0x00, 0x00, 0x00, 0x00, // box content
  147. 0x00, 0x00, 0x00, 0x00, // box content
  148. 0x00, 0x00, 0x00, 0x00, // box content
  149. 0x00, 0x00, 0x00, 0x00, // box content
  150. 0x00, 0x00, 0x00, 0x00, // box content
  151. 0x00, 0x00, 0x00, 0x00, // box content
  152. 0x00, 0x00, 0x00, 0x00, // box content
  153. 0x00, 0x00, 0x00, 0x00, // box content
  154. 0x00, 0x00, 0x00, 0x00, // box content
  155. 0x00, 0x00, 0x00, 0x00, // box content
  156. 0x00, 0x00, 0x00, 0x00, // box content
  157. 0x00, 0x00, 0x00, 0x00, // box content
  158. 0x00, 0x00, 0x00, 0x00, // box content
  159. 0x00, 0x00, 0x00, 0x00, // box content
  160. 0x00, 0x00, 0x00, 0x00, // box content
  161. 0x00, 0x00, 0x00, 0x00, // box content
  162. 0x00, 0x00, 0x00, 0x00, // box content
  163. 0x00, 0x00, // box content
  164. typeBytes('avcC'), // codec profile type
  165. 0x00, 0x4d, 0x40, 0x0d)), // codec parameters
  166. box('stts',
  167. 0x01, // version 1
  168. 0x00, 0x00, 0x00, // flags
  169. 0x00, 0x00, 0x00, 0x01, // entry_count
  170. 0x00, 0x00, 0x00, 0x01, // sample_count
  171. 0x00, 0x00, 0x00, 0x01), // sample_delta
  172. box('stsc',
  173. 0x01, // version 1
  174. 0x00, 0x00, 0x00, // flags
  175. 0x00, 0x00, 0x00, 0x01, // entry_count
  176. 0x00, 0x00, 0x00, 0x02, // first_chunk
  177. 0x00, 0x00, 0x00, 0x03, // samples_per_chunk
  178. 0x00, 0x00, 0x00, 0x01), // sample_description_index
  179. box('stco',
  180. 0x01, // version 1
  181. 0x00, 0x00, 0x00, // flags
  182. 0x00, 0x00, 0x00, 0x01, // entry_count
  183. 0x00, 0x00, 0x00, 0x01), // chunk_offset
  184. box('stss',
  185. 0x00, // version 0
  186. 0x00, 0x00, 0x00, // flags
  187. 0x00, 0x00, 0x00, 0x01, // entry_count
  188. 0x00, 0x00, 0x00, 0x01), // sync_sample
  189. box('ctts',
  190. 0x00, // version 0
  191. 0x00, 0x00, 0x00, // flags
  192. 0x00, 0x00, 0x00, 0x01, // entry_count
  193. 0x00, 0x00, 0x00, 0x01, // sample_count
  194. 0x00, 0x00, 0x00, 0x01))))), // sample_offset
  195. box('trak',
  196. box('tkhd',
  197. 0x01, // version 1
  198. 0x00, 0x00, 0x00, // flags
  199. 0x00, 0x00, 0x00, 0x00,
  200. 0x00, 0x00, 0x00, 0x02, // creation_time
  201. 0x00, 0x00, 0x00, 0x00,
  202. 0x00, 0x00, 0x00, 0x03, // modification_time
  203. 0x00, 0x00, 0x00, 0x02, // track_ID
  204. 0x00, 0x00, 0x00, 0x00, // reserved
  205. 0x00, 0x00, 0x00, 0x00,
  206. 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
  207. 0x00, 0x00, 0x00, 0x00,
  208. 0x00, 0x00, 0x00, 0x00, // reserved
  209. 0x00, 0x00, // layer
  210. 0x00, 0x00, // alternate_group
  211. 0x00, 0x00, // non-audio track volume
  212. 0x00, 0x00, // reserved
  213. unityMatrix,
  214. 0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
  215. 0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
  216. box('edts',
  217. box('elst',
  218. 0x01, // version
  219. 0x00, 0x00, 0x00, // flags
  220. 0x00, 0x00, 0x00, 0x01, // entry_count
  221. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment_duration
  222. 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // media_time
  223. 0x00, 0x01, 0x80, 0x00)), // media_rate
  224. box('mdia',
  225. box('mdhd',
  226. 0x01, // version 1
  227. 0x00, 0x00, 0x00, // flags
  228. 0x00, 0x00, 0x00, 0x00,
  229. 0x00, 0x00, 0x00, 0x02, // creation_time
  230. 0x00, 0x00, 0x00, 0x00,
  231. 0x00, 0x00, 0x00, 0x03, // modification_time
  232. 0x00, 0x01, 0x5f, 0x90, // timescale = 90000
  233. 0x00, 0x00, 0x00, 0x00,
  234. 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
  235. 0x15, 0xc7, // 'eng' language
  236. 0x00, 0x00),
  237. box('hdlr',
  238. 0x01, // version 1
  239. 0x00, 0x00, 0x00, // flags
  240. 0x00, 0x00, 0x00, 0x00, // pre_defined
  241. typeBytes('soun'), // handler_type
  242. 0x00, 0x00, 0x00, 0x00, // reserved
  243. 0x00, 0x00, 0x00, 0x00, // reserved
  244. 0x00, 0x00, 0x00, 0x00, // reserved
  245. typeBytes('one'), 0x00), // name
  246. box('minf',
  247. box('dinf',
  248. box('dref',
  249. 0x01, // version 1
  250. 0x00, 0x00, 0x00, // flags
  251. 0x00, 0x00, 0x00, 0x01, // entry_count
  252. box('url ',
  253. 0x00, // version
  254. 0x00, 0x00, 0x01))), // flags
  255. box('stbl',
  256. box('stsd',
  257. 0x01, // version 1
  258. 0x00, 0x00, 0x00, // flags
  259. 0x00, 0x00, 0x00, 0x00, // entry_count
  260. box('mp4a',
  261. 0x00, 0x00, 0x00, 0x00, // box content
  262. 0x00, 0x00, 0x00, 0x00, // box content
  263. 0x00, 0x00, 0x00, 0x00, // box content
  264. 0x00, 0x00, 0x00, 0x00, // box content
  265. 0x00, 0x00, 0x00, 0x00, // box content
  266. 0x00, 0x00, 0x00, 0x00, // box content
  267. 0x00, 0x00, 0x00, 0x00, // box content
  268. 0x00, 0x00, 0x00, 0x00, // box content
  269. typeBytes('esds'), // codec profile type
  270. 0x00, 0x00, 0x00, 0x00, // box content
  271. 0x00, 0x00, 0x00, 0x00, // box content
  272. 0x00, 0x00, 0x00, // box content
  273. 0x40, 0x0a, // codec params
  274. 0x00, 0x00, 0x00, 0x00,
  275. 0x00, 0x00, 0x00, 0x00,
  276. 0x00, 0x00, 0x00, 0x00,
  277. 0x00, 0x00, 0x00, 0x00)), // codec params
  278. box('stts',
  279. 0x01, // version 1
  280. 0x00, 0x00, 0x00, // flags
  281. 0x00, 0x00, 0x00, 0x01, // entry_count
  282. 0x00, 0x00, 0x00, 0x01, // sample_count
  283. 0x00, 0x00, 0x00, 0x01), // sample_delta
  284. box('stsc',
  285. 0x01, // version 1
  286. 0x00, 0x00, 0x00, // flags
  287. 0x00, 0x00, 0x00, 0x01, // entry_count
  288. 0x00, 0x00, 0x00, 0x02, // first_chunk
  289. 0x00, 0x00, 0x00, 0x03, // samples_per_chunk
  290. 0x00, 0x00, 0x00, 0x01), // sample_description_index
  291. box('ctts',
  292. 0x01, // version 1
  293. 0x00, 0x00, 0x00, // flags
  294. 0x00, 0x00, 0x00, 0x01, // entry_count
  295. 0x00, 0x00, 0x00, 0x01, // sample_count
  296. 0xff, 0xff, 0xff, 0xff), // sample_offset
  297. box('stco',
  298. 0x01, // version 1
  299. 0x00, 0x00, 0x00, // flags
  300. 0x00, 0x00, 0x00, 0x01, // entry_count
  301. 0x00, 0x00, 0x00, 0x01)))))); // chunk_offset
  302. /**
  303. * Generates generic emsg box data for both v0 and v1 boxes concats the messageData
  304. * and returns the result as a Uint8Array. Passing any version other than 0 or 1 will
  305. * return an invalid EMSG box.
  306. */
  307. var generateEmsgBoxData = function(version, messageData) {
  308. var emsgProps;
  309. if (version === 0) {
  310. emsgProps = new Uint8Array([
  311. 0x00, // version
  312. 0x00, 0x00, 0x00, //flags
  313. 0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
  314. 0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00, // foo.bar.value\0
  315. 0x00, 0x00, 0x00, 0x64, // timescale = 100
  316. 0x00, 0x00, 0x03, 0xE8, // presentation_time_delta = 1000
  317. 0x00, 0x00, 0x00, 0x00, // event_duration = 0
  318. 0x00, 0x00, 0x00, 0x01 // id = 1
  319. ]);
  320. } else if (version === 1) {
  321. emsgProps = new Uint8Array([
  322. 0x01, // version
  323. 0x00, 0x00, 0x00, //flags
  324. 0x00, 0x00, 0x00, 0x64, // timescale = 100
  325. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x10, // presentation_time = 10000
  326. 0x00, 0x00, 0x00, 0x01, // event_duration = 1
  327. 0x00, 0x00, 0x00, 0x02, // id = 2
  328. 0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
  329. 0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
  330. ]);
  331. } else if (version === 2) {
  332. // Invalid version only
  333. emsgProps = new Uint8Array([
  334. 0x02, // version
  335. 0x00, 0x00, 0x00, //flags
  336. 0x00, 0x00, 0x00, 0x64, // timescale = 100
  337. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x10, // presentation_time = 10000
  338. 0x00, 0x00, 0x00, 0x01, // event_duration = 1
  339. 0x00, 0x00, 0x00, 0x02, // id = 2
  340. 0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
  341. 0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
  342. ]);
  343. } else {
  344. emsgProps = new Uint8Array([
  345. // malformed emsg data
  346. 0x00, 0x00, 0x00, 0x64, // timescale = 100
  347. // no presentation_time
  348. 0x00, 0x00, 0x00, 0x01, // event_duration = 1
  349. // no id
  350. 0x75, 0x72, 0x6E, 0x3A, 0x66, 0x6F, 0x6F, 0x3A, 0x62, 0x61, 0x72, 0x3A, 0x32, 0x30, 0x32, 0x33, 0x00, // urn:foo:bar:2023\0
  351. 0x66, 0x6F, 0x6F, 0x2E, 0x62, 0x61, 0x72, 0x2E, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x00 // foo.bar.value\0
  352. ]);
  353. }
  354. // concat the props and messageData
  355. var retArr = new Uint8Array(emsgProps.length + messageData.length);
  356. retArr.set(emsgProps);
  357. retArr.set(messageData, emsgProps.length);
  358. return retArr;
  359. };
  360. module.exports = {
  361. typeBytes,
  362. sampleMoov,
  363. unityMatrix,
  364. box,
  365. generateEmsgBoxData
  366. };