diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java b/library/extractor/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java index 2925ae7a54..846851eb41 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java @@ -297,71 +297,87 @@ public final class Cea708Decoder extends CeaDecoder { // we have received. } - serviceBlockPacket.reset(currentDtvCcPacket.packetData, currentDtvCcPacket.currentIndex); - - int serviceNumber = serviceBlockPacket.readBits(3); - int blockSize = serviceBlockPacket.readBits(5); - if (serviceNumber == 7) { - // extended service numbers - serviceBlockPacket.skipBits(2); - serviceNumber = serviceBlockPacket.readBits(6); - if (serviceNumber < 7) { - Log.w(TAG, "Invalid extended service number: " + serviceNumber); - } - } - - // Ignore packets in which blockSize is 0 - if (blockSize == 0) { - if (serviceNumber != 0) { - Log.w(TAG, "serviceNumber is non-zero (" + serviceNumber + ") when blockSize is 0"); - } - return; - } - - if (serviceNumber != selectedServiceNumber) { - return; - } - // The cues should be updated if we receive a C0 ETX command, any C1 command, or if after // processing the service block any text has been added to the buffer. See CEA-708-B Section // 8.10.4 for more details. boolean cuesNeedUpdate = false; - int blockEndBitPosition = serviceBlockPacket.getPosition() + (blockSize * 8); - while (serviceBlockPacket.bitsLeft() > 0 - && serviceBlockPacket.getPosition() < blockEndBitPosition) { - int command = serviceBlockPacket.readBits(8); - if (command != COMMAND_EXT1) { - if (command <= GROUP_C0_END) { - handleC0Command(command); - // If the C0 command was an ETX command, the cues are updated in handleC0Command. - } else if (command <= GROUP_G0_END) { - handleG0Character(command); - cuesNeedUpdate = true; - } else if (command <= GROUP_C1_END) { - handleC1Command(command); - cuesNeedUpdate = true; - } else if (command <= GROUP_G1_END) { - handleG1Character(command); - cuesNeedUpdate = true; - } else { - Log.w(TAG, "Invalid base command: " + command); + // Streams with multiple embedded cc tracks (different language tracks) can be delivered + // in the same frame packet, so current serviceBlockPacket buffer can contain information + // for different service numbers. + // xe: consider the following service block 298CFC9818E332731F104220620000. This service + // block must be split into 3 different blocks + // 298CFC9818E332731F10: Information for service number 1 + // 422062: Information for service number 2 + // 0000: Null data + // + // So here we iterate over the full buffer until we found a null block service or untll + // buffer is emptied, and, on each iteration, process only the block related to that + // service block. if the specific service number is not the selected one, then just skip + // it and continue with next service block. + serviceBlockPacket.reset(currentDtvCcPacket.packetData, currentDtvCcPacket.currentIndex); + while (serviceBlockPacket.bitsLeft() > 0) { + int serviceNumber = serviceBlockPacket.readBits(3); + int blockSize = serviceBlockPacket.readBits(5); + if (serviceNumber == 7) { + // extended service numbers + serviceBlockPacket.skipBits(2); + serviceNumber = serviceBlockPacket.readBits(6); + if (serviceNumber < 7) { + Log.w(TAG, "Invalid extended service number: " + serviceNumber); } - } else { - // Read the extended command - command = serviceBlockPacket.readBits(8); - if (command <= GROUP_C2_END) { - handleC2Command(command); - } else if (command <= GROUP_G2_END) { - handleG2Character(command); - cuesNeedUpdate = true; - } else if (command <= GROUP_C3_END) { - handleC3Command(command); - } else if (command <= GROUP_G3_END) { - handleG3Character(command); - cuesNeedUpdate = true; + } + + // Ignore packets in which blockSize is 0 + if (blockSize == 0) { + if (serviceNumber != 0) { + Log.w(TAG, "serviceNumber is non-zero (" + serviceNumber + ") when blockSize is 0"); + } + break; + } + + if (serviceNumber != selectedServiceNumber) { + serviceBlockPacket.skipBytes(blockSize); + continue; + } + + // Process only the information for the current service block (there could be + // more data in the buffer, but it is not part of the current service block). + int endBlockPosition = serviceBlockPacket.getPosition() + (blockSize * 8); + while (serviceBlockPacket.getPosition() < endBlockPosition) { + int command = serviceBlockPacket.readBits(8); + if (command != COMMAND_EXT1) { + if (command <= GROUP_C0_END) { + handleC0Command(command); + // If the C0 command was an ETX command, the cues are updated in handleC0Command. + } else if (command <= GROUP_G0_END) { + handleG0Character(command); + cuesNeedUpdate = true; + } else if (command <= GROUP_C1_END) { + handleC1Command(command); + cuesNeedUpdate = true; + } else if (command <= GROUP_G1_END) { + handleG1Character(command); + cuesNeedUpdate = true; + } else { + Log.w(TAG, "Invalid base command: " + command); + } } else { - Log.w(TAG, "Invalid extended command: " + command); + // Read the extended command + command = serviceBlockPacket.readBits(8); + if (command <= GROUP_C2_END) { + handleC2Command(command); + } else if (command <= GROUP_G2_END) { + handleG2Character(command); + cuesNeedUpdate = true; + } else if (command <= GROUP_C3_END) { + handleC3Command(command); + } else if (command <= GROUP_G3_END) { + handleG3Character(command); + cuesNeedUpdate = true; + } else { + Log.w(TAG, "Invalid extended command: " + command); + } } } }