summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java138
1 files changed, 138 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java
new file mode 100644
index 0000000000..ced169ba17
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/text/cea/CeaUtil.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mozilla.thirdparty.com.google.android.exoplayer2.text.cea;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.extractor.TrackOutput;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Log;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+
+/** Utility methods for handling CEA-608/708 messages. Defined in A/53 Part 4:2009. */
+public final class CeaUtil {
+
+ private static final String TAG = "CeaUtil";
+
+ public static final int USER_DATA_IDENTIFIER_GA94 = 0x47413934;
+ public static final int USER_DATA_TYPE_CODE_MPEG_CC = 0x3;
+
+ private static final int PAYLOAD_TYPE_CC = 4;
+ private static final int COUNTRY_CODE = 0xB5;
+ private static final int PROVIDER_CODE_ATSC = 0x31;
+ private static final int PROVIDER_CODE_DIRECTV = 0x2F;
+
+ /**
+ * Consumes the unescaped content of an SEI NAL unit, writing the content of any CEA-608 messages
+ * as samples to all of the provided outputs.
+ *
+ * @param presentationTimeUs The presentation time in microseconds for any samples.
+ * @param seiBuffer The unescaped SEI NAL unit data, excluding the NAL unit start code and type.
+ * @param outputs The outputs to which any samples should be written.
+ */
+ public static void consume(long presentationTimeUs, ParsableByteArray seiBuffer,
+ TrackOutput[] outputs) {
+ while (seiBuffer.bytesLeft() > 1 /* last byte will be rbsp_trailing_bits */) {
+ int payloadType = readNon255TerminatedValue(seiBuffer);
+ int payloadSize = readNon255TerminatedValue(seiBuffer);
+ int nextPayloadPosition = seiBuffer.getPosition() + payloadSize;
+ // Process the payload.
+ if (payloadSize == -1 || payloadSize > seiBuffer.bytesLeft()) {
+ // This might occur if we're trying to read an encrypted SEI NAL unit.
+ Log.w(TAG, "Skipping remainder of malformed SEI NAL unit.");
+ nextPayloadPosition = seiBuffer.limit();
+ } else if (payloadType == PAYLOAD_TYPE_CC && payloadSize >= 8) {
+ int countryCode = seiBuffer.readUnsignedByte();
+ int providerCode = seiBuffer.readUnsignedShort();
+ int userIdentifier = 0;
+ if (providerCode == PROVIDER_CODE_ATSC) {
+ userIdentifier = seiBuffer.readInt();
+ }
+ int userDataTypeCode = seiBuffer.readUnsignedByte();
+ if (providerCode == PROVIDER_CODE_DIRECTV) {
+ seiBuffer.skipBytes(1); // user_data_length.
+ }
+ boolean messageIsSupportedCeaCaption =
+ countryCode == COUNTRY_CODE
+ && (providerCode == PROVIDER_CODE_ATSC || providerCode == PROVIDER_CODE_DIRECTV)
+ && userDataTypeCode == USER_DATA_TYPE_CODE_MPEG_CC;
+ if (providerCode == PROVIDER_CODE_ATSC) {
+ messageIsSupportedCeaCaption &= userIdentifier == USER_DATA_IDENTIFIER_GA94;
+ }
+ if (messageIsSupportedCeaCaption) {
+ consumeCcData(presentationTimeUs, seiBuffer, outputs);
+ }
+ }
+ seiBuffer.setPosition(nextPayloadPosition);
+ }
+ }
+
+ /**
+ * Consumes caption data (cc_data), writing the content as samples to all of the provided outputs.
+ *
+ * @param presentationTimeUs The presentation time in microseconds for any samples.
+ * @param ccDataBuffer The buffer containing the caption data.
+ * @param outputs The outputs to which any samples should be written.
+ */
+ public static void consumeCcData(
+ long presentationTimeUs, ParsableByteArray ccDataBuffer, TrackOutput[] outputs) {
+ // First byte contains: reserved (1), process_cc_data_flag (1), zero_bit (1), cc_count (5).
+ int firstByte = ccDataBuffer.readUnsignedByte();
+ boolean processCcDataFlag = (firstByte & 0x40) != 0;
+ if (!processCcDataFlag) {
+ // No need to process.
+ return;
+ }
+ int ccCount = firstByte & 0x1F;
+ ccDataBuffer.skipBytes(1); // Ignore em_data
+ // Each data packet consists of 24 bits: marker bits (5) + cc_valid (1) + cc_type (2)
+ // + cc_data_1 (8) + cc_data_2 (8).
+ int sampleLength = ccCount * 3;
+ int sampleStartPosition = ccDataBuffer.getPosition();
+ for (TrackOutput output : outputs) {
+ ccDataBuffer.setPosition(sampleStartPosition);
+ output.sampleData(ccDataBuffer, sampleLength);
+ output.sampleMetadata(
+ presentationTimeUs,
+ C.BUFFER_FLAG_KEY_FRAME,
+ sampleLength,
+ /* offset= */ 0,
+ /* encryptionData= */ null);
+ }
+ }
+
+ /**
+ * Reads a value from the provided buffer consisting of zero or more 0xFF bytes followed by a
+ * terminating byte not equal to 0xFF. The returned value is ((0xFF * N) + T), where N is the
+ * number of 0xFF bytes and T is the value of the terminating byte.
+ *
+ * @param buffer The buffer from which to read the value.
+ * @return The read value, or -1 if the end of the buffer is reached before a value is read.
+ */
+ private static int readNon255TerminatedValue(ParsableByteArray buffer) {
+ int b;
+ int value = 0;
+ do {
+ if (buffer.bytesLeft() == 0) {
+ return -1;
+ }
+ b = buffer.readUnsignedByte();
+ value += b;
+ } while (b == 0xFF);
+ return value;
+ }
+
+ private CeaUtil() {}
+
+}