mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Fix Cea608Decoder handling of service switch commands in field 2
From ANSI-CTA-608-E R-2014 section 8.4: > When closed captioning is used on line 21, field 2, it shall conform > to all of the applicable specifications and recommended practices as > defined for field 1 services with the following differences: > 1. The non-printing character of the miscellaneous control-character > pairs that fall in the range of 0x14, 0x20 to 0x14, 0x2F in field 1, > shall be replaced with 0x15, 0x20 to 0x15, 0x2F when used in field > 2. > 2. The non-printing character of the miscellaneous control-character > pairs that fall in the range of 0x1C, 0x20 to 0x1C, 0x2F in field > 1, shall be replaced with 0x1D, 0x20 to 0x1D, 0x2F when used in > field 2. This basically means that `cc1=0x15` in field 2 should be interpreted as `cc1=0x14` in field 1, and same for `0x1D -> 0x1C`. The `isMiscCode` method above already handles this by ignoring the LSB (the only difference between `0x14` and `0x15`, and `0x1C` and `0x1D`) by AND-ing with `0xF6` instead of `0xF7`. This change uses the same trick in `isServiceSwitchCommand`. Issue: google/ExoPlayer#10666 #minor-release PiperOrigin-RevId: 483927506
This commit is contained in:
parent
ad52b68c73
commit
7c6d492ff1
2 changed files with 61 additions and 2 deletions
|
|
@ -872,8 +872,8 @@ public final class Cea608Decoder extends CeaDecoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isServiceSwitchCommand(byte cc1) {
|
private static boolean isServiceSwitchCommand(byte cc1) {
|
||||||
// cc1 - 0|0|0|1|C|1|0|0
|
// cc1 - 0|0|0|1|C|1|0|F
|
||||||
return (cc1 & 0xF7) == 0x14;
|
return (cc1 & 0xF6) == 0x14;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class CueBuilder {
|
private static final class CueBuilder {
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,65 @@ public class Cea608DecoderTest {
|
||||||
assertThat(getOnlyCue(fifthSubtitle).text.toString()).isEqualTo("test subtitle");
|
assertThat(getOnlyCue(fifthSubtitle).text.toString()).isEqualTo("test subtitle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serviceSwitchOnField1Handled() throws Exception {
|
||||||
|
Cea608Decoder decoder =
|
||||||
|
new Cea608Decoder(
|
||||||
|
MimeTypes.APPLICATION_CEA608,
|
||||||
|
/* accessibilityChannel= */ 1, // field 1, channel 1
|
||||||
|
Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS);
|
||||||
|
// field 1 (0xFC header): 'test' then service switch
|
||||||
|
// field 2 (0xFD header): 'wrong!'
|
||||||
|
byte[] sample1 =
|
||||||
|
Bytes.concat(
|
||||||
|
// 'paint on' control character
|
||||||
|
createPacket(0xFC, 0x14, 0x29),
|
||||||
|
createPacket(0xFD, 0x15, 0x29),
|
||||||
|
createPacket(0xFC, 't', 'e'),
|
||||||
|
createPacket(0xFD, 'w', 'r'),
|
||||||
|
createPacket(0xFC, 's', 't'),
|
||||||
|
createPacket(0xFD, 'o', 'n'),
|
||||||
|
// Enter TEXT service
|
||||||
|
createPacket(0xFC, 0x14, 0x2A),
|
||||||
|
createPacket(0xFD, 'g', '!'),
|
||||||
|
createPacket(0xFC, 'X', 'X'),
|
||||||
|
createPacket(0xFD, 0x0, 0x0));
|
||||||
|
|
||||||
|
Subtitle firstSubtitle = checkNotNull(decodeSampleAndCopyResult(decoder, sample1));
|
||||||
|
|
||||||
|
assertThat(getOnlyCue(firstSubtitle).text.toString()).isEqualTo("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/google/ExoPlayer/issues/10666
|
||||||
|
@Test
|
||||||
|
public void serviceSwitchOnField2Handled() throws Exception {
|
||||||
|
Cea608Decoder decoder =
|
||||||
|
new Cea608Decoder(
|
||||||
|
MimeTypes.APPLICATION_CEA608,
|
||||||
|
/* accessibilityChannel= */ 3, // field 2, channel 1
|
||||||
|
Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS);
|
||||||
|
// field 1 (0xFC header): 'wrong!'
|
||||||
|
// field 2 (0xFD header): 'test' then service switch
|
||||||
|
byte[] sample1 =
|
||||||
|
Bytes.concat(
|
||||||
|
// 'paint on' control character
|
||||||
|
createPacket(0xFC, 0x14, 0x29),
|
||||||
|
createPacket(0xFD, 0x15, 0x29),
|
||||||
|
createPacket(0xFC, 'w', 'r'),
|
||||||
|
createPacket(0xFD, 't', 'e'),
|
||||||
|
createPacket(0xFC, 'o', 'n'),
|
||||||
|
createPacket(0xFD, 's', 't'),
|
||||||
|
createPacket(0xFC, 'g', '!'),
|
||||||
|
// Enter TEXT service
|
||||||
|
createPacket(0xFD, 0x15, 0x2A),
|
||||||
|
createPacket(0xFC, 0x0, 0x0),
|
||||||
|
createPacket(0xFD, 'X', 'X'));
|
||||||
|
|
||||||
|
Subtitle firstSubtitle = checkNotNull(decodeSampleAndCopyResult(decoder, sample1));
|
||||||
|
|
||||||
|
assertThat(getOnlyCue(firstSubtitle).text.toString()).isEqualTo("test");
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] createPacket(int header, int cc1, int cc2) {
|
private static byte[] createPacket(int header, int cc1, int cc2) {
|
||||||
return new byte[] {
|
return new byte[] {
|
||||||
UnsignedBytes.checkedCast(header),
|
UnsignedBytes.checkedCast(header),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue