diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaUtil.java index ddb3804c3e..0022d37d6c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaUtil.java @@ -19,18 +19,19 @@ import android.util.Log; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableByteArray; +import com.google.android.exoplayer2.util.Util; -/** - * Utility methods for handling CEA-608/708 messages. - */ +/** 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"; private static final int PAYLOAD_TYPE_CC = 4; private static final int COUNTRY_CODE = 0xB5; - private static final int PROVIDER_CODE = 0x31; - private static final int USER_ID = 0x47413934; // "GA94" + private static final int PROVIDER_CODE_ATSC = 0x31; + private static final int PROVIDER_CODE_DIRECTV = 0x2F; + private static final int USER_ID_GA94 = Util.getIntegerCodeForString("GA94"); + private static final int USER_ID_DTG1 = Util.getIntegerCodeForString("DTG1"); private static final int USER_DATA_TYPE_CODE = 0x3; /** @@ -46,33 +47,49 @@ public final class CeaUtil { 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."); seiBuffer.setPosition(seiBuffer.limit()); - } else if (isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) { - // Ignore country_code (1) + provider_code (2) + user_identifier (4) - // + user_data_type_code (1). - seiBuffer.skipBytes(8); - // Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1). - int ccCount = seiBuffer.readUnsignedByte() & 0x1F; - // Ignore em_data (1) - seiBuffer.skipBytes(1); - // 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 = seiBuffer.getPosition(); - for (TrackOutput output : outputs) { - seiBuffer.setPosition(sampleStartPosition); - output.sampleData(seiBuffer, sampleLength); - output.sampleMetadata(presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null); + } 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; + if (providerCode == PROVIDER_CODE_ATSC) { + messageIsSupportedCeaCaption &= + userIdentifier == USER_ID_GA94 || userIdentifier == USER_ID_DTG1; + } + if (messageIsSupportedCeaCaption) { + // Ignore first three bits: reserved (1) + process_cc_data_flag (1) + zero_bit (1). + int ccCount = seiBuffer.readUnsignedByte() & 0x1F; + // Ignore em_data (1) + seiBuffer.skipBytes(1); + // 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 = seiBuffer.getPosition(); + for (TrackOutput output : outputs) { + seiBuffer.setPosition(sampleStartPosition); + output.sampleData(seiBuffer, sampleLength); + output.sampleMetadata( + presentationTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleLength, 0, null); + } } - // Ignore trailing information in SEI, if any. - seiBuffer.skipBytes(payloadSize - (10 + ccCount * 3)); - } else { - seiBuffer.skipBytes(payloadSize); } + seiBuffer.setPosition(nextPayloadPosition); } } @@ -97,31 +114,6 @@ public final class CeaUtil { return value; } - /** - * Inspects an sei message to determine whether it contains CEA-608. - *

- * The position of {@code payload} is left unchanged. - * - * @param payloadType The payload type of the message. - * @param payloadLength The length of the payload. - * @param payload A {@link ParsableByteArray} containing the payload. - * @return Whether the sei message contains CEA-608. - */ - private static boolean isSeiMessageCea608(int payloadType, int payloadLength, - ParsableByteArray payload) { - if (payloadType != PAYLOAD_TYPE_CC || payloadLength < 8) { - return false; - } - int startPosition = payload.getPosition(); - int countryCode = payload.readUnsignedByte(); - int providerCode = payload.readUnsignedShort(); - int userIdentifier = payload.readInt(); - int userDataTypeCode = payload.readUnsignedByte(); - payload.setPosition(startPosition); - return countryCode == COUNTRY_CODE && providerCode == PROVIDER_CODE - && userIdentifier == USER_ID && userDataTypeCode == USER_DATA_TYPE_CODE; - } - private CeaUtil() {} }