sei-nal-unit-generator.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. /**
  2. * Helper functions for creating 608/708 SEI NAL units
  3. */
  4. 'use strict';
  5. var box = require('./mp4-helpers').box;
  6. // Create SEI nal-units from Caption packets
  7. var makeSeiFromCaptionPacket = function(caption) {
  8. return {
  9. pts: caption.pts,
  10. dts: caption.dts,
  11. nalUnitType: 'sei_rbsp',
  12. escapedRBSP: new Uint8Array([
  13. 0x04, // payload_type === user_data_registered_itu_t_t35
  14. 0x0e, // payload_size
  15. 181, // itu_t_t35_country_code
  16. 0x00, 0x31, // itu_t_t35_provider_code
  17. 0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
  18. 0x03, // user_data_type_code, 0x03 is cc_data
  19. // 110 00001
  20. 0xc1, // process_cc_data, cc_count
  21. 0xff, // reserved
  22. // 1111 1100
  23. (0xfc | caption.type), // cc_valid, cc_type (608, field 1)
  24. (caption.ccData & 0xff00) >> 8, // cc_data_1
  25. caption.ccData & 0xff, // cc_data_2 without parity bit set
  26. 0xff // marker_bits
  27. ])
  28. };
  29. };
  30. // Create SEI nal-units from Caption packets
  31. var makeSeiFromMultipleCaptionPackets = function(captionHash) {
  32. var pts = captionHash.pts,
  33. dts = captionHash.dts,
  34. captions = captionHash.captions;
  35. var data = [];
  36. captions.forEach(function(caption) {
  37. data.push(0xfc | caption.type);
  38. data.push((caption.ccData & 0xff00) >> 8);
  39. data.push(caption.ccData & 0xff);
  40. });
  41. return {
  42. pts: pts,
  43. dts: dts,
  44. nalUnitType: 'sei_rbsp',
  45. escapedRBSP: new Uint8Array([
  46. 0x04, // payload_type === user_data_registered_itu_t_t35
  47. (0x0b + (captions.length * 3)), // payload_size
  48. 181, // itu_t_t35_country_code
  49. 0x00, 0x31, // itu_t_t35_provider_code
  50. 0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
  51. 0x03, // user_data_type_code, 0x03 is cc_data
  52. // 110 00001
  53. (0x6 << 5) | captions.length, // process_cc_data, cc_count
  54. 0xff // reserved
  55. ].concat(data).concat([0xff /* marker bits */])
  56. )
  57. };
  58. };
  59. var makeMdatFromCaptionPackets = function(packets) {
  60. var mdat = ['mdat'];
  61. var seis = packets.map(makeSeiFromCaptionPacket);
  62. seis.forEach(function(sei) {
  63. mdat.push(0x00);
  64. mdat.push(0x00);
  65. mdat.push(0x00);
  66. mdat.push(sei.escapedRBSP.length + 1); // nal length
  67. mdat.push(0x06); // declare nal type as SEI
  68. // SEI message
  69. for (var i = 0; i < sei.escapedRBSP.length; i++) {
  70. var byte = sei.escapedRBSP[i];
  71. mdat.push(byte);
  72. }
  73. });
  74. return box.apply(null, mdat);
  75. };
  76. // Returns a ccData byte-pair for a two character string. That is,
  77. // it converts a string like 'hi' into the two-byte number that
  78. // would be parsed back as 'hi' when provided as ccData.
  79. var characters = function(text) {
  80. if (text.length !== 2) {
  81. throw new Error('ccdata must be specified two characters at a time');
  82. }
  83. return (text.charCodeAt(0) << 8) | text.charCodeAt(1);
  84. };
  85. // Returns a ccData byte-pair including
  86. // Header for 708 packet
  87. // Header for the first service block
  88. // seq should increment by 1 for each byte pair mod 3 (0,1,2,0,1,2,...)
  89. // sizeCode is the number of byte pairs in the packet (including header)
  90. // serviceNum is the service number of the first service block
  91. // blockSize is the size of the first service block in bytes (no header)
  92. // If there's only one service block, the blockSize should be (sizeCode-1)*2
  93. var packetHeader708 = function(seq, sizeCode, serviceNum, blockSize) {
  94. var b1 = (seq << 6) | sizeCode;
  95. var b2 = (serviceNum << 5) | blockSize;
  96. return (b1 << 8) | b2;
  97. };
  98. // Returns a ccData byte-pair to execute a 708 DSW command
  99. // Takes an array of window indicies to display
  100. var displayWindows708 = function(windows) {
  101. var cmd = 0x8900;
  102. windows.forEach(function(winIdx) {
  103. cmd |= (0x01 << winIdx);
  104. });
  105. return cmd;
  106. };
  107. module.exports = {
  108. makeSeiFromCaptionPacket: makeSeiFromCaptionPacket,
  109. makeSeiFromMultipleCaptionPackets: makeSeiFromMultipleCaptionPackets,
  110. makeMdatFromCaptionPackets: makeMdatFromCaptionPackets,
  111. characters: characters,
  112. packetHeader708: packetHeader708,
  113. displayWindows708: displayWindows708
  114. };