caption-packet-parser.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /**
  2. * mux.js
  3. *
  4. * Copyright (c) Brightcove
  5. * Licensed Apache-2.0 https://github.com/videojs/mux.js/blob/master/LICENSE
  6. *
  7. * Reads in-band caption information from a video elementary
  8. * stream. Captions must follow the CEA-708 standard for injection
  9. * into an MPEG-2 transport streams.
  10. * @see https://en.wikipedia.org/wiki/CEA-708
  11. * @see https://www.gpo.gov/fdsys/pkg/CFR-2007-title47-vol1/pdf/CFR-2007-title47-vol1-sec15-119.pdf
  12. */
  13. 'use strict';
  14. // Supplemental enhancement information (SEI) NAL units have a
  15. // payload type field to indicate how they are to be
  16. // interpreted. CEAS-708 caption content is always transmitted with
  17. // payload type 0x04.
  18. var USER_DATA_REGISTERED_ITU_T_T35 = 4,
  19. RBSP_TRAILING_BITS = 128;
  20. /**
  21. * Parse a supplemental enhancement information (SEI) NAL unit.
  22. * Stops parsing once a message of type ITU T T35 has been found.
  23. *
  24. * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  25. * @return {object} the parsed SEI payload
  26. * @see Rec. ITU-T H.264, 7.3.2.3.1
  27. */
  28. var parseSei = function(bytes) {
  29. var
  30. i = 0,
  31. result = {
  32. payloadType: -1,
  33. payloadSize: 0
  34. },
  35. payloadType = 0,
  36. payloadSize = 0;
  37. // go through the sei_rbsp parsing each each individual sei_message
  38. while (i < bytes.byteLength) {
  39. // stop once we have hit the end of the sei_rbsp
  40. if (bytes[i] === RBSP_TRAILING_BITS) {
  41. break;
  42. }
  43. // Parse payload type
  44. while (bytes[i] === 0xFF) {
  45. payloadType += 255;
  46. i++;
  47. }
  48. payloadType += bytes[i++];
  49. // Parse payload size
  50. while (bytes[i] === 0xFF) {
  51. payloadSize += 255;
  52. i++;
  53. }
  54. payloadSize += bytes[i++];
  55. // this sei_message is a 608/708 caption so save it and break
  56. // there can only ever be one caption message in a frame's sei
  57. if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
  58. var userIdentifier = String.fromCharCode(
  59. bytes[i + 3],
  60. bytes[i + 4],
  61. bytes[i + 5],
  62. bytes[i + 6]);
  63. if (userIdentifier === 'GA94') {
  64. result.payloadType = payloadType;
  65. result.payloadSize = payloadSize;
  66. result.payload = bytes.subarray(i, i + payloadSize);
  67. break;
  68. } else {
  69. result.payload = void 0;
  70. }
  71. }
  72. // skip the payload and parse the next message
  73. i += payloadSize;
  74. payloadType = 0;
  75. payloadSize = 0;
  76. }
  77. return result;
  78. };
  79. // see ANSI/SCTE 128-1 (2013), section 8.1
  80. var parseUserData = function(sei) {
  81. // itu_t_t35_contry_code must be 181 (United States) for
  82. // captions
  83. if (sei.payload[0] !== 181) {
  84. return null;
  85. }
  86. // itu_t_t35_provider_code should be 49 (ATSC) for captions
  87. if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
  88. return null;
  89. }
  90. // the user_identifier should be "GA94" to indicate ATSC1 data
  91. if (String.fromCharCode(sei.payload[3],
  92. sei.payload[4],
  93. sei.payload[5],
  94. sei.payload[6]) !== 'GA94') {
  95. return null;
  96. }
  97. // finally, user_data_type_code should be 0x03 for caption data
  98. if (sei.payload[7] !== 0x03) {
  99. return null;
  100. }
  101. // return the user_data_type_structure and strip the trailing
  102. // marker bits
  103. return sei.payload.subarray(8, sei.payload.length - 1);
  104. };
  105. // see CEA-708-D, section 4.4
  106. var parseCaptionPackets = function(pts, userData) {
  107. var results = [], i, count, offset, data;
  108. // if this is just filler, return immediately
  109. if (!(userData[0] & 0x40)) {
  110. return results;
  111. }
  112. // parse out the cc_data_1 and cc_data_2 fields
  113. count = userData[0] & 0x1f;
  114. for (i = 0; i < count; i++) {
  115. offset = i * 3;
  116. data = {
  117. type: userData[offset + 2] & 0x03,
  118. pts: pts
  119. };
  120. // capture cc data when cc_valid is 1
  121. if (userData[offset + 2] & 0x04) {
  122. data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
  123. results.push(data);
  124. }
  125. }
  126. return results;
  127. };
  128. var discardEmulationPreventionBytes = function(data) {
  129. var
  130. length = data.byteLength,
  131. emulationPreventionBytesPositions = [],
  132. i = 1,
  133. newLength, newData;
  134. // Find all `Emulation Prevention Bytes`
  135. while (i < length - 2) {
  136. if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
  137. emulationPreventionBytesPositions.push(i + 2);
  138. i += 2;
  139. } else {
  140. i++;
  141. }
  142. }
  143. // If no Emulation Prevention Bytes were found just return the original
  144. // array
  145. if (emulationPreventionBytesPositions.length === 0) {
  146. return data;
  147. }
  148. // Create a new array to hold the NAL unit data
  149. newLength = length - emulationPreventionBytesPositions.length;
  150. newData = new Uint8Array(newLength);
  151. var sourceIndex = 0;
  152. for (i = 0; i < newLength; sourceIndex++, i++) {
  153. if (sourceIndex === emulationPreventionBytesPositions[0]) {
  154. // Skip this byte
  155. sourceIndex++;
  156. // Remove this position index
  157. emulationPreventionBytesPositions.shift();
  158. }
  159. newData[i] = data[sourceIndex];
  160. }
  161. return newData;
  162. };
  163. // exports
  164. module.exports = {
  165. parseSei: parseSei,
  166. parseUserData: parseUserData,
  167. parseCaptionPackets: parseCaptionPackets,
  168. discardEmulationPreventionBytes: discardEmulationPreventionBytes,
  169. USER_DATA_REGISTERED_ITU_T_T35: USER_DATA_REGISTERED_ITU_T_T35
  170. };