mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08: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 (cherry picked from commit 8c0f7827e262c7c4609e0b677a173a1e71ef2bb0)
This commit is contained in:
parent
92dc1d34f3
commit
add9013df4
@ -54,6 +54,9 @@ Release notes
|
||||
a non-empty but invalid license URL.
|
||||
* Fix `setMediaDrmSession failed: session not opened` error when switching
|
||||
between DRM schemes in a playlist (e.g. Widevine to ClearKey).
|
||||
* Text:
|
||||
* CEA-608: Ensure service switch commands on field 2 are handled correctly
|
||||
([#10666](https://github.com/google/ExoPlayer/issues/10666)).
|
||||
* DASH:
|
||||
* Parse `EventStream.presentationTimeOffset` from manifests
|
||||
([#10460](https://github.com/google/ExoPlayer/issues/10460)).
|
||||
|
@ -874,8 +874,8 @@ public final class Cea608Decoder extends CeaDecoder {
|
||||
}
|
||||
|
||||
private static boolean isServiceSwitchCommand(byte cc1) {
|
||||
// cc1 - 0|0|0|1|C|1|0|0
|
||||
return (cc1 & 0xF7) == 0x14;
|
||||
// cc1 - 0|0|0|1|C|1|0|F
|
||||
return (cc1 & 0xF6) == 0x14;
|
||||
}
|
||||
|
||||
private static final class CueBuilder {
|
||||
|
@ -255,6 +255,65 @@ public class Cea608DecoderTest {
|
||||
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) {
|
||||
return new byte[] {
|
||||
UnsignedBytes.checkedCast(header),
|
||||
|
Loading…
x
Reference in New Issue
Block a user