Source: lib/cea/dtvcc_packet_builder.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.cea.DtvccPacketBuilder');
  7. goog.provide('shaka.cea.DtvccPacket');
  8. goog.require('shaka.util.Error');
  9. goog.requireType('shaka.cea.Cea708Service');
  10. /**
  11. * CEA-708 DTVCC Packet Builder.
  12. * Builds packets based on Figure 5 CCP State Table in 5.2 of CEA-708-E.
  13. * Initially, there is no packet. When a DTVCC_PACKET_START payload is received,
  14. * a packet begins construction. The packet is considered "built" once all bytes
  15. * indicated in the header are read, and ignored if a new packet starts building
  16. * before the current packet is finished being built.
  17. */
  18. shaka.cea.DtvccPacketBuilder = class {
  19. /** */
  20. constructor() {
  21. /**
  22. * An array containing built DTVCC packets that are ready to be processed.
  23. * @private {!Array<!shaka.cea.DtvccPacket>}
  24. */
  25. this.builtPackets_ = [];
  26. /**
  27. * Stores the packet data for the current packet being processed, if any.
  28. * @private {?Array<!shaka.cea.Cea708Service.Cea708Byte>}
  29. */
  30. this.currentPacketBeingBuilt_ = null;
  31. /**
  32. * Keeps track of the number of bytes left to add in the current packet.
  33. * @private {number}
  34. */
  35. this.bytesLeftToAddInCurrentPacket_ = 0;
  36. }
  37. /**
  38. * @param {!shaka.cea.Cea708Service.Cea708Byte} cea708Byte
  39. */
  40. addByte(cea708Byte) {
  41. if (cea708Byte.type === shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_START) {
  42. // If there was a packet being built that finished, it would have
  43. // already been added to the built packets when it finished. So if
  44. // there's an open packet at this point, it must be unfinished. As
  45. // per the spec, we don't deal with unfinished packets. So we ignore them.
  46. // A new packet should be opened.
  47. const packetSize = cea708Byte.value & 0x3f;
  48. // As per spec, number of packet data bytes to follow is packetSize*2-1.
  49. this.bytesLeftToAddInCurrentPacket_ = packetSize * 2 - 1;
  50. this.currentPacketBeingBuilt_ = [];
  51. return;
  52. }
  53. if (!this.currentPacketBeingBuilt_) {
  54. // There is no packet open. Then an incoming byte should not
  55. // have come in at all. Ignore it.
  56. return;
  57. }
  58. if (this.bytesLeftToAddInCurrentPacket_ > 0) {
  59. this.currentPacketBeingBuilt_.push(cea708Byte);
  60. this.bytesLeftToAddInCurrentPacket_--;
  61. }
  62. if (this.bytesLeftToAddInCurrentPacket_ === 0) {
  63. // Current packet is complete and ready for processing.
  64. const packet = new shaka.cea.DtvccPacket(this.currentPacketBeingBuilt_);
  65. this.builtPackets_.push(packet);
  66. this.currentPacketBeingBuilt_ = null;
  67. this.bytesLeftToAddInCurrentPacket_ = 0;
  68. }
  69. }
  70. /**
  71. * @return {!Array<!shaka.cea.DtvccPacket>}
  72. */
  73. getBuiltPackets() {
  74. return this.builtPackets_;
  75. }
  76. /** Clear built packets. */
  77. clearBuiltPackets() {
  78. this.builtPackets_ = [];
  79. }
  80. /** Clear built packets and packets in progress. */
  81. clear() {
  82. this.builtPackets_ = [];
  83. this.currentPacketBeingBuilt_ = [];
  84. this.bytesLeftToAddInCurrentPacket_ = 0;
  85. }
  86. };
  87. shaka.cea.DtvccPacket = class {
  88. /**
  89. * @param {!Array<!shaka.cea.Cea708Service.Cea708Byte>} packetData
  90. */
  91. constructor(packetData) {
  92. /**
  93. * Keeps track of the position to read the next byte from in the packet.
  94. * @private {number}
  95. */
  96. this.pos_ = 0;
  97. /**
  98. * Bytes that represent the data in the DTVCC packet.
  99. * @private {!Array<!shaka.cea.Cea708Service.Cea708Byte>}
  100. */
  101. this.packetData_ = packetData;
  102. }
  103. /**
  104. * @return {boolean}
  105. */
  106. hasMoreData() {
  107. return this.pos_ < this.packetData_.length;
  108. }
  109. /**
  110. * @return {number}
  111. */
  112. getPosition() {
  113. return this.pos_;
  114. }
  115. /**
  116. * Reads a byte from the packet. TODO CONSIDER RENAMING THIS TO BLOCK
  117. * @return {!shaka.cea.Cea708Service.Cea708Byte}
  118. * @throws {!shaka.util.Error}
  119. */
  120. readByte() {
  121. if (!this.hasMoreData()) {
  122. throw new shaka.util.Error(
  123. shaka.util.Error.Severity.CRITICAL,
  124. shaka.util.Error.Category.TEXT,
  125. shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS);
  126. }
  127. return this.packetData_[this.pos_++];
  128. }
  129. /**
  130. * Skips the provided number of blocks in the buffer.
  131. * @param {number} numBlocks
  132. * @throws {!shaka.util.Error}
  133. */
  134. skip(numBlocks) {
  135. if (this.pos_ + numBlocks > this.packetData_.length) {
  136. throw new shaka.util.Error(
  137. shaka.util.Error.Severity.CRITICAL,
  138. shaka.util.Error.Category.TEXT,
  139. shaka.util.Error.Code.BUFFER_READ_OUT_OF_BOUNDS);
  140. }
  141. this.pos_ += numBlocks;
  142. }
  143. };
  144. /**
  145. * @const {number}
  146. */
  147. shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_DATA = 2;
  148. /**
  149. * @const {number}
  150. */
  151. shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_START = 3;