diff --git a/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump b/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump index 13e7a93f19..3e84813162 100644 --- a/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump +++ b/library/src/androidTest/assets/rawcc/sample.rawcc.0.dump @@ -8,7 +8,7 @@ track 0: bitrate = -1 id = null containerMimeType = null - sampleMimeType = application/eia-608 + sampleMimeType = application/cea-608 maxInputSize = -1 width = -1 height = -1 @@ -25,305 +25,605 @@ track 0: language = null drmInitData = - initializationData: - sample count = 75 + sample count = 150 sample 0: time = 37657512133 flags = 1 - data = length 2, hash FFFFF3C1 + data = length 3, hash 7363 sample 1: + time = 37657528822 + flags = 1 + data = length 3, hash 7724 + sample 2: time = 37657545511 flags = 1 - data = length 2, hash FFFFF6CD - sample 2: + data = length 3, hash 766F + sample 3: + time = 37657562177 + flags = 1 + data = length 3, hash 7724 + sample 4: time = 37657578866 flags = 1 - data = length 2, hash FFFFF6DC - sample 3: + data = length 3, hash 767E + sample 5: + time = 37657595555 + flags = 1 + data = length 3, hash 7724 + sample 6: time = 37657612244 flags = 1 - data = length 2, hash FFFFF65B - sample 4: + data = length 15, hash E4359178 + sample 7: + time = 37657628911 + flags = 1 + data = length 3, hash 7724 + sample 8: time = 37657645600 flags = 1 - data = length 2, hash FFFFF6CD - sample 5: + data = length 12, hash 15EBEB66 + sample 9: + time = 37657662288 + flags = 1 + data = length 3, hash 7724 + sample 10: time = 37657678977 flags = 1 - data = length 2, hash FFFFF67B - sample 6: + data = length 3, hash 761D + sample 11: + time = 37657695644 + flags = 1 + data = length 3, hash 7724 + sample 12: time = 37657712333 flags = 1 - data = length 2, hash 2B5 - sample 7: + data = length 30, hash E181418F + sample 13: + time = 37657729022 + flags = 1 + data = length 6, hash 36289CE2 + sample 14: time = 37657745711 flags = 1 - data = length 2, hash F5 - sample 8: + data = length 12, hash 3C304F5B + sample 15: + time = 37657762377 + flags = 1 + data = length 3, hash 7724 + sample 16: time = 37657779066 flags = 1 - data = length 2, hash FFFFF87A - sample 9: + data = length 12, hash 88DD8EF6 + sample 17: + time = 37657795755 + flags = 1 + data = length 3, hash 7724 + sample 18: time = 37657812444 flags = 1 - data = length 2, hash FFFFF698 - sample 10: + data = length 12, hash 8B411833 + sample 19: + time = 37657829111 + flags = 1 + data = length 3, hash 7724 + sample 20: time = 37657845800 flags = 1 - data = length 2, hash 1F4 - sample 11: + data = length 12, hash 742A2DF1 + sample 21: + time = 37657862488 + flags = 1 + data = length 3, hash 7724 + sample 22: time = 37657879177 flags = 1 - data = length 2, hash 803 - sample 12: + data = length 12, hash 9A2ECBEE + sample 23: + time = 37657895844 + flags = 1 + data = length 3, hash 7724 + sample 24: time = 37657912533 flags = 1 - data = length 2, hash 1F8 - sample 13: + data = length 12, hash 562688EA + sample 25: + time = 37657929222 + flags = 1 + data = length 3, hash 7724 + sample 26: time = 37657945911 flags = 1 - data = length 2, hash 117A - sample 14: + data = length 12, hash ADE4B953 + sample 27: + time = 37657962577 + flags = 1 + data = length 3, hash 7724 + sample 28: time = 37657979266 flags = 1 - data = length 2, hash 166 - sample 15: + data = length 12, hash F927E3E5 + sample 29: + time = 37657995955 + flags = 1 + data = length 3, hash 7724 + sample 30: time = 37658012644 flags = 1 - data = length 2, hash 105A - sample 16: + data = length 12, hash EA327945 + sample 31: + time = 37658029311 + flags = 1 + data = length 3, hash 7724 + sample 32: time = 37658046000 flags = 1 - data = length 2, hash FCF - sample 17: + data = length 12, hash 3E5DA13C + sample 33: + time = 37658062688 + flags = 1 + data = length 3, hash 7724 + sample 34: time = 37658079377 flags = 1 - data = length 2, hash 1253 - sample 18: + data = length 12, hash BF646AE3 + sample 35: + time = 37658096044 + flags = 1 + data = length 3, hash 7724 + sample 36: time = 37658112733 flags = 1 - data = length 2, hash 11DA - sample 19: + data = length 12, hash 41E3BA78 + sample 37: + time = 37658129422 + flags = 1 + data = length 3, hash 7724 + sample 38: time = 37658146111 flags = 1 - data = length 2, hash 795 - sample 20: + data = length 12, hash A2945EF6 + sample 39: + time = 37658162777 + flags = 1 + data = length 3, hash 7724 + sample 40: time = 37658179466 flags = 1 - data = length 2, hash 103E - sample 21: + data = length 12, hash 26735812 + sample 41: + time = 37658196155 + flags = 1 + data = length 3, hash 7724 + sample 42: time = 37658212844 flags = 1 - data = length 2, hash 120F - sample 22: + data = length 12, hash DC14D3D8 + sample 43: + time = 37658229511 + flags = 1 + data = length 3, hash 7724 + sample 44: time = 37658246200 flags = 1 - data = length 2, hash FFFFF698 - sample 23: + data = length 12, hash 882191BE + sample 45: + time = 37658262888 + flags = 1 + data = length 3, hash 7724 + sample 46: time = 37658279577 flags = 1 - data = length 2, hash 1F4 - sample 24: + data = length 12, hash 8B4886B1 + sample 47: + time = 37658296244 + flags = 1 + data = length 3, hash 7724 + sample 48: time = 37658312933 flags = 1 - data = length 2, hash FFFFF71B - sample 25: + data = length 12, hash 98D98F96 + sample 49: + time = 37658329622 + flags = 1 + data = length 3, hash 7724 + sample 50: time = 37658346311 flags = 1 - data = length 2, hash F91 - sample 26: + data = length 30, hash CF8E53E3 + sample 51: + time = 37658362977 + flags = 1 + data = length 6, hash 36289CE2 + sample 52: time = 37658379666 flags = 1 - data = length 2, hash 166 - sample 27: + data = length 12, hash F883C9EE + sample 53: + time = 37658396355 + flags = 1 + data = length 3, hash 7724 + sample 54: time = 37658413044 flags = 1 - data = length 2, hash 1023 - sample 28: + data = length 12, hash 6E6B2B9C + sample 55: + time = 37658429711 + flags = 1 + data = length 3, hash 7724 + sample 56: time = 37658446400 flags = 1 - data = length 2, hash 117A - sample 29: + data = length 12, hash B4FE7F08 + sample 57: + time = 37658463088 + flags = 1 + data = length 3, hash 7724 + sample 58: time = 37658479777 flags = 1 - data = length 2, hash 784 - sample 30: + data = length 12, hash 5A1EA7C7 + sample 59: + time = 37658496444 + flags = 1 + data = length 3, hash 7724 + sample 60: time = 37658513133 flags = 1 - data = length 2, hash 1F8 - sample 31: + data = length 12, hash 46BD6CC9 + sample 61: + time = 37658529822 + flags = 1 + data = length 3, hash 7724 + sample 62: time = 37658546511 flags = 1 - data = length 2, hash 10D9 - sample 32: + data = length 12, hash 1B1E2554 + sample 63: + time = 37658563177 + flags = 1 + data = length 3, hash 7724 + sample 64: time = 37658579866 flags = 1 - data = length 2, hash 935 - sample 33: + data = length 12, hash 91FCC537 + sample 65: + time = 37658596555 + flags = 1 + data = length 3, hash 7724 + sample 66: time = 37658613244 flags = 1 - data = length 2, hash 2B5 - sample 34: + data = length 12, hash A9355E1B + sample 67: + time = 37658629911 + flags = 1 + data = length 3, hash 7724 + sample 68: time = 37658646600 flags = 1 - data = length 2, hash F5 - sample 35: + data = length 12, hash 2511F69B + sample 69: + time = 37658663288 + flags = 1 + data = length 3, hash 7724 + sample 70: time = 37658679977 flags = 1 - data = length 2, hash FFFFF87A - sample 36: + data = length 12, hash 90925736 + sample 71: + time = 37658696644 + flags = 1 + data = length 3, hash 7724 + sample 72: time = 37658713333 flags = 1 - data = length 2, hash FFFFF698 - sample 37: + data = length 21, hash 431EEE30 + sample 73: + time = 37658730022 + flags = 1 + data = length 3, hash 7724 + sample 74: time = 37658746711 flags = 1 - data = length 2, hash 1F4 - sample 38: + data = length 12, hash 7BDEF631 + sample 75: + time = 37658763377 + flags = 1 + data = length 3, hash 7724 + sample 76: time = 37658780066 flags = 1 - data = length 2, hash 793 - sample 39: + data = length 12, hash A2EEF59E + sample 77: + time = 37658796755 + flags = 1 + data = length 3, hash 7724 + sample 78: time = 37658813444 flags = 1 - data = length 2, hash FF0 - sample 40: + data = length 12, hash BFC6C022 + sample 79: + time = 37658830111 + flags = 1 + data = length 3, hash 7724 + sample 80: time = 37658846800 flags = 1 - data = length 2, hash 16B - sample 41: + data = length 12, hash CD4D8FCA + sample 81: + time = 37658863488 + flags = 1 + data = length 3, hash 7724 + sample 82: time = 37658880177 flags = 1 - data = length 2, hash 2C0 - sample 42: + data = length 12, hash 2BDE8EFA + sample 83: + time = 37658896844 + flags = 1 + data = length 3, hash 7724 + sample 84: time = 37658913533 flags = 1 - data = length 2, hash FFFFF953 - sample 43: + data = length 12, hash 8C858812 + sample 85: + time = 37658930222 + flags = 1 + data = length 3, hash 7724 + sample 86: time = 37658946911 flags = 1 - data = length 2, hash FFFFF3C1 - sample 44: + data = length 12, hash DE7D0E31 + sample 87: + time = 37658963577 + flags = 1 + data = length 3, hash 7724 + sample 88: time = 37658980266 flags = 1 - data = length 2, hash FFFFF3C1 - sample 45: + data = length 3, hash 7363 + sample 89: + time = 37658996955 + flags = 1 + data = length 3, hash 7724 + sample 90: time = 37659013644 flags = 1 - data = length 2, hash FFFFF3C1 - sample 46: + data = length 3, hash 7363 + sample 91: + time = 37659030311 + flags = 1 + data = length 3, hash 7724 + sample 92: time = 37659047000 flags = 1 - data = length 2, hash FFFFF3C1 - sample 47: + data = length 3, hash 7363 + sample 93: + time = 37659063688 + flags = 1 + data = length 3, hash 7724 + sample 94: time = 37659080377 flags = 1 - data = length 2, hash FFFFF3C1 - sample 48: + data = length 3, hash 7363 + sample 95: + time = 37659097044 + flags = 1 + data = length 3, hash 7724 + sample 96: time = 37659113733 flags = 1 - data = length 2, hash FFFFF3C1 - sample 49: + data = length 3, hash 7363 + sample 97: + time = 37659130422 + flags = 1 + data = length 3, hash 7724 + sample 98: time = 37659147111 flags = 1 - data = length 2, hash FFFFF3C1 - sample 50: + data = length 3, hash 7363 + sample 99: + time = 37659163777 + flags = 1 + data = length 3, hash 7724 + sample 100: time = 37659180466 flags = 1 - data = length 2, hash FFFFF3C1 - sample 51: + data = length 3, hash 7363 + sample 101: + time = 37659197155 + flags = 1 + data = length 3, hash 7724 + sample 102: time = 37659213844 flags = 1 - data = length 2, hash FFFFF3C1 - sample 52: + data = length 3, hash 7363 + sample 103: + time = 37659230511 + flags = 1 + data = length 3, hash 7724 + sample 104: time = 37659247200 flags = 1 - data = length 2, hash FFFFF3C1 - sample 53: + data = length 3, hash 7363 + sample 105: + time = 37659263888 + flags = 1 + data = length 3, hash 7724 + sample 106: time = 37659280577 flags = 1 - data = length 2, hash FFFFF3C1 - sample 54: + data = length 3, hash 7363 + sample 107: + time = 37659297244 + flags = 1 + data = length 3, hash 7724 + sample 108: time = 37659313933 flags = 1 - data = length 2, hash FFFFF3C1 - sample 55: + data = length 3, hash 7363 + sample 109: + time = 37659330622 + flags = 1 + data = length 3, hash 7724 + sample 110: time = 37659347311 flags = 1 - data = length 2, hash FFFFF3C1 - sample 56: + data = length 3, hash 7363 + sample 111: + time = 37659363977 + flags = 1 + data = length 3, hash 7724 + sample 112: time = 37659380666 flags = 1 - data = length 2, hash FFFFF3C1 - sample 57: + data = length 3, hash 7363 + sample 113: + time = 37659397355 + flags = 1 + data = length 3, hash 7724 + sample 114: time = 37659414044 flags = 1 - data = length 2, hash FFFFF3C1 - sample 58: + data = length 3, hash 7363 + sample 115: + time = 37659430711 + flags = 1 + data = length 3, hash 7724 + sample 116: time = 37659447400 flags = 1 - data = length 2, hash FFFFF3C1 - sample 59: + data = length 3, hash 7363 + sample 117: + time = 37659464088 + flags = 1 + data = length 3, hash 7724 + sample 118: time = 37659480777 flags = 1 - data = length 2, hash FFFFF3C1 - sample 60: + data = length 3, hash 7363 + sample 119: + time = 37659497444 + flags = 1 + data = length 3, hash 7724 + sample 120: time = 37659514133 flags = 1 - data = length 2, hash FFFFF3C1 - sample 61: + data = length 3, hash 7363 + sample 121: + time = 37659530822 + flags = 1 + data = length 3, hash 7724 + sample 122: time = 37659547511 flags = 1 - data = length 2, hash FFFFF3C1 - sample 62: + data = length 3, hash 7363 + sample 123: + time = 37659564177 + flags = 1 + data = length 3, hash 7724 + sample 124: time = 37659580866 flags = 1 - data = length 2, hash FFFFF3C1 - sample 63: + data = length 3, hash 7363 + sample 125: + time = 37659597555 + flags = 1 + data = length 3, hash 7724 + sample 126: time = 37659614244 flags = 1 - data = length 2, hash FFFFF6CD - sample 64: + data = length 3, hash 766F + sample 127: + time = 37659630911 + flags = 1 + data = length 3, hash 7724 + sample 128: time = 37659647600 flags = 1 - data = length 2, hash FFFFF6DC - sample 65: + data = length 3, hash 767E + sample 129: + time = 37659664288 + flags = 1 + data = length 3, hash 7724 + sample 130: time = 37659680977 flags = 1 - data = length 2, hash FFFFF65B - sample 66: + data = length 15, hash 191B585A + sample 131: + time = 37659697644 + flags = 1 + data = length 3, hash 7724 + sample 132: time = 37659714333 flags = 1 - data = length 2, hash FFFFF6CD - sample 67: + data = length 12, hash 15EC5FC5 + sample 133: + time = 37659731022 + flags = 1 + data = length 3, hash 7724 + sample 134: time = 37659747711 flags = 1 - data = length 2, hash FFFFF6FF - sample 68: + data = length 3, hash 76A1 + sample 135: + time = 37659764377 + flags = 1 + data = length 3, hash 7724 + sample 136: time = 37659781066 flags = 1 - data = length 2, hash FFFFF6AC - sample 69: + data = length 30, hash E8012479 + sample 137: + time = 37659797755 + flags = 1 + data = length 6, hash 36289D5E + sample 138: time = 37659814444 flags = 1 - data = length 2, hash FFFFF5FE - sample 70: + data = length 12, hash D32F29F3 + sample 139: + time = 37659831111 + flags = 1 + data = length 3, hash 7724 + sample 140: time = 37659847800 flags = 1 - data = length 2, hash FFFFFEF7 - sample 71: + data = length 21, hash 6258623 + sample 141: + time = 37659864488 + flags = 1 + data = length 3, hash 7724 + sample 142: time = 37659881177 flags = 1 - data = length 2, hash 120C - sample 72: + data = length 12, hash FE69ABA2 + sample 143: + time = 37659897844 + flags = 1 + data = length 3, hash 7724 + sample 144: time = 37659914533 flags = 1 - data = length 2, hash 1124 - sample 73: + data = length 12, hash 958D0815 + sample 145: + time = 37659931222 + flags = 1 + data = length 3, hash 7724 + sample 146: time = 37659947911 flags = 1 - data = length 2, hash 1A9 - sample 74: + data = length 12, hash FF57BFD8 + sample 147: + time = 37659964577 + flags = 1 + data = length 3, hash 7724 + sample 148: time = 37659981266 flags = 1 - data = length 2, hash 935 + data = length 12, hash 922122E7 + sample 149: + time = 37659997955 + flags = 1 + data = length 3, hash 7724 tracksEnded = true diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java index be26e7f9d4..0ca0f216c6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java @@ -644,7 +644,7 @@ import java.util.List; 0 /* subsample timing is absolute */); } else if (childAtomType == Atom.TYPE_c608) { out.format = Format.createTextSampleFormat(Integer.toString(trackId), - MimeTypes.APPLICATION_EIA608, null, Format.NO_VALUE, 0, language, drmInitData); + MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, language, drmInitData); out.requiredSampleTransformation = Track.TRANSFORMATION_CEA608_CDAT; } stsd.setPosition(childStartPosition + childAtomSize); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java index 8a63c5557b..ea9458a657 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/rawcc/RawCcExtractor.java @@ -30,7 +30,7 @@ import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** - * Extracts EIA-608 data from a RawCC file + * Extracts CEA data from a RawCC file. */ public final class RawCcExtractor implements Extractor { @@ -68,7 +68,7 @@ public final class RawCcExtractor implements Extractor { trackOutput = extractorOutput.track(0); extractorOutput.endTracks(); - trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_EIA608, + trackOutput.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)); } @@ -154,13 +154,8 @@ public final class RawCcExtractor implements Extractor { dataScratch.reset(); input.readFully(dataScratch.data, 0, 3); - // only accept EIA-608 packets which have validity (6th bit) == 1 and - // type (7-8th bits) == 0; i.e. ccDataPkt[0] == 0bXXXXX100 - int ccValidityAndType = dataScratch.readUnsignedByte() & 0x07; - if (ccValidityAndType == 0x04) { - trackOutput.sampleData(dataScratch, 2); - sampleBytesWritten += 2; - } + trackOutput.sampleData(dataScratch, 3); + sampleBytesWritten += 3; } if (sampleBytesWritten > 0) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 993ec4f872..ce7b7e6383 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -57,7 +57,7 @@ import java.util.List; /** * @param output A {@link TrackOutput} to which H.264 samples should be written. - * @param seiReader A reader for EIA-608 samples in SEI NAL units. + * @param seiReader A reader for CEA-608 samples in SEI NAL units. * @param allowNonIdrKeyframes Whether to treat samples consisting of non-IDR I slices as * synchronization samples (key-frames). * @param detectAccessUnits Whether to split the input stream into access units (samples) based on diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index e7ca3cc625..c8828cefa6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -64,7 +64,7 @@ import java.util.Collections; /** * @param output A {@link TrackOutput} to which H.265 samples should be written. - * @param seiReader A reader for EIA-608 samples in SEI NAL units. + * @param seiReader A reader for CEA-608 samples in SEI NAL units. */ public H265Reader(TrackOutput output, SeiReader seiReader) { super(output); diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java index a921e4c38f..4971c0c2b1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/SeiReader.java @@ -18,12 +18,12 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.TrackOutput; -import com.google.android.exoplayer2.text.eia608.Eia608Decoder; +import com.google.android.exoplayer2.text.cea.Cea608Decoder; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; /** - * Consumes SEI buffers, outputting contained EIA608 messages to a {@link TrackOutput}. + * Consumes SEI buffers, outputting contained CEA-608 messages to a {@link TrackOutput}. */ /* package */ final class SeiReader { @@ -31,7 +31,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; public SeiReader(TrackOutput output) { this.output = output; - output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_EIA608, null, + output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)); } @@ -51,7 +51,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; payloadSize += b; } while (b == 0xFF); // Process the payload. - if (Eia608Decoder.isSeiMessageEia608(payloadType, payloadSize, seiBuffer)) { + if (Cea608Decoder.isSeiMessageCea608(payloadType, payloadSize, seiBuffer)) { // Ignore country_code (1) + provider_code (2) + user_identifier (4) // + user_data_type_code (1). seiBuffer.skipBytes(8); @@ -60,13 +60,13 @@ import com.google.android.exoplayer2.util.ParsableByteArray; seiBuffer.skipBytes(1); int sampleBytes = 0; for (int i = 0; i < ccCount; i++) { - int ccValidityAndType = seiBuffer.readUnsignedByte() & 0x07; - // Check that validity == 1 and type == 0. + int ccValidityAndType = seiBuffer.peekUnsignedByte() & 0x07; + // Check that validity == 1 and type == 0 (i.e. NTSC_CC_FIELD_1). if (ccValidityAndType != 0x04) { - seiBuffer.skipBytes(2); + seiBuffer.skipBytes(3); } else { - sampleBytes += 2; - output.sampleData(seiBuffer, 2); + sampleBytes += 3; + output.sampleData(seiBuffer, 3); } } output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleBytes, 0, null); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java index e0bf7d00e6..143a0e995b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java @@ -654,9 +654,10 @@ public class DashManifestParser extends DefaultHandler } else if (MimeTypes.isVideo(containerMimeType)) { return MimeTypes.getVideoMediaMimeType(codecs); } else if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) { - // We currently only support EIA-608 through RawCC - if (codecs != null && codecs.contains("eia608")) { - return MimeTypes.APPLICATION_EIA608; + // We currently only support CEA-608 through RawCC + if (codecs != null + && (codecs.contains("eia608") || codecs.contains("cea608"))) { + return MimeTypes.APPLICATION_CEA608; } return null; } else if (mimeTypeIsRawText(containerMimeType)) { diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java index 979878a259..1f9fd11c3f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java @@ -589,7 +589,7 @@ import java.util.LinkedList; if (primaryExtractorTrackType == PRIMARY_TYPE_VIDEO) { if (MimeTypes.isAudio(sampleFormat.sampleMimeType)) { trackFormat = muxedAudioFormat; - } else if (MimeTypes.APPLICATION_EIA608.equals(sampleFormat.sampleMimeType)) { + } else if (MimeTypes.APPLICATION_CEA608.equals(sampleFormat.sampleMimeType)) { trackFormat = muxedCaptionFormat; } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index a0757c364a..d5e5267820 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -161,7 +161,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.ParserTTML ({@link TtmlDecoder}) *
  • SubRip ({@link SubripDecoder})
  • *
  • TX3G ({@link Tx3gDecoder})
  • - *
  • Eia608 ({@link Eia608Decoder})
  • + *
  • Cea608 ({@link Cea608Decoder})
  • * */ SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() { @@ -93,8 +93,8 @@ public interface SubtitleDecoderFactory { return Class.forName("com.google.android.exoplayer2.text.subrip.SubripDecoder"); case MimeTypes.APPLICATION_TX3G: return Class.forName("com.google.android.exoplayer2.text.tx3g.Tx3gDecoder"); - case MimeTypes.APPLICATION_EIA608: - return Class.forName("com.google.android.exoplayer2.text.eia608.Eia608Decoder"); + case MimeTypes.APPLICATION_CEA608: + return Class.forName("com.google.android.exoplayer2.text.cea.Cea608Decoder"); default: return null; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java similarity index 78% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java index 50c6d4f6b0..0b497d3646 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java @@ -13,28 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; import android.text.Layout.Alignment; import android.text.TextUtils; -import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.text.Cue; +import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.SubtitleDecoder; -import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleInputBuffer; -import com.google.android.exoplayer2.text.SubtitleOutputBuffer; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableByteArray; -import java.util.LinkedList; -import java.util.TreeSet; /** - * A {@link SubtitleDecoder} for EIA-608 (also known as "line 21 captions" and "CEA-608"). + * A {@link SubtitleDecoder} for CEA-608 (also known as "line 21 captions" and "EIA-608"). */ -public final class Eia608Decoder implements SubtitleDecoder { +public final class Cea608Decoder extends CeaDecoder { - private static final int NUM_INPUT_BUFFERS = 10; - private static final int NUM_OUTPUT_BUFFERS = 2; + private static final int NTSC_CC_FIELD_1 = 0x00; + private static final int CC_VALID_FLAG = 0x04; private static final int PAYLOAD_TYPE_CC = 4; private static final int COUNTRY_CODE = 0xB5; @@ -169,18 +164,10 @@ public final class Eia608Decoder implements SubtitleDecoder { private static final int CUE_POSITION_ANCHOR = Cue.TYPE_UNSET; private static final float CUE_SIZE = 0.8f; - private final LinkedList availableInputBuffers; - private final LinkedList availableOutputBuffers; - private final TreeSet queuedInputBuffers; - private final ParsableByteArray ccData; private final StringBuilder captionStringBuilder; - private long playbackPositionUs; - - private SubtitleInputBuffer dequeuedInputBuffer; - private int captionMode; private int captionRowCount; private String captionString; @@ -191,17 +178,7 @@ public final class Eia608Decoder implements SubtitleDecoder { private byte repeatableControlCc1; private byte repeatableControlCc2; - public Eia608Decoder() { - availableInputBuffers = new LinkedList<>(); - for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { - availableInputBuffers.add(new SubtitleInputBuffer()); - } - availableOutputBuffers = new LinkedList<>(); - for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { - availableOutputBuffers.add(new Eia608SubtitleOutputBuffer(this)); - } - queuedInputBuffers = new TreeSet<>(); - + public Cea608Decoder() { ccData = new ParsableByteArray(); captionStringBuilder = new StringBuilder(); @@ -212,106 +189,20 @@ public final class Eia608Decoder implements SubtitleDecoder { @Override public String getName() { - return "Eia608Decoder"; - } - - @Override - public void setPositionUs(long positionUs) { - playbackPositionUs = positionUs; - } - - @Override - public SubtitleInputBuffer dequeueInputBuffer() throws SubtitleDecoderException { - Assertions.checkState(dequeuedInputBuffer == null); - if (availableInputBuffers.isEmpty()) { - return null; - } - dequeuedInputBuffer = availableInputBuffers.pollFirst(); - return dequeuedInputBuffer; - } - - @Override - public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws SubtitleDecoderException { - Assertions.checkArgument(inputBuffer != null); - Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); - queuedInputBuffers.add(inputBuffer); - dequeuedInputBuffer = null; - } - - @Override - public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException { - if (availableOutputBuffers.isEmpty()) { - return null; - } - - // iterate through all available input buffers whose timestamps are less than or equal - // to the current playback position; processing input buffers for future content should - // be deferred until they would be applicable - while (!queuedInputBuffers.isEmpty() - && queuedInputBuffers.first().timeUs <= playbackPositionUs) { - SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst(); - - // If the input buffer indicates we've reached the end of the stream, we can - // return immediately with an output buffer propagating that - if (inputBuffer.isEndOfStream()) { - SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); - outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); - releaseInputBuffer(inputBuffer); - return outputBuffer; - } - - decode(inputBuffer); - - // check if we have any caption updates to report - if (!TextUtils.equals(captionString, lastCaptionString)) { - lastCaptionString = captionString; - if (!inputBuffer.isDecodeOnly()) { - Cue cue = null; - if (!TextUtils.isEmpty(captionString)) { - cue = new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, - CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE); - } - SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); - outputBuffer.setContent(inputBuffer.timeUs, new Eia608Subtitle(cue), 0); - releaseInputBuffer(inputBuffer); - return outputBuffer; - } - } - - releaseInputBuffer(inputBuffer); - } - - return null; - } - - private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { - inputBuffer.clear(); - availableInputBuffers.add(inputBuffer); - } - - protected void releaseOutputBuffer(SubtitleOutputBuffer outputBuffer) { - outputBuffer.clear(); - availableOutputBuffers.add(outputBuffer); + return "Cea608Decoder"; } @Override public void flush() { + super.flush(); setCaptionMode(CC_MODE_UNKNOWN); captionRowCount = DEFAULT_CAPTIONS_ROW_COUNT; - playbackPositionUs = 0; captionStringBuilder.setLength(0); captionString = null; lastCaptionString = null; repeatableControlSet = false; repeatableControlCc1 = 0; repeatableControlCc2 = 0; - while (!queuedInputBuffers.isEmpty()) { - releaseInputBuffer(queuedInputBuffers.pollFirst()); - } - if (dequeuedInputBuffer != null) { - releaseInputBuffer(dequeuedInputBuffer); - dequeuedInputBuffer = null; - } } @Override @@ -319,14 +210,34 @@ public final class Eia608Decoder implements SubtitleDecoder { // Do nothing } - private void decode(SubtitleInputBuffer inputBuffer) { + @Override + protected boolean isNewSubtitleDataAvailable() { + return !TextUtils.equals(captionString, lastCaptionString); + } + + @Override + protected Subtitle createSubtitle() { + lastCaptionString = captionString; + return new CeaSubtitle(new Cue(captionString, CUE_TEXT_ALIGNMENT, CUE_LINE, CUE_LINE_TYPE, + CUE_LINE_ANCHOR, CUE_POSITION, CUE_POSITION_ANCHOR, CUE_SIZE)); + } + + @Override + protected void decode(SubtitleInputBuffer inputBuffer) { ccData.reset(inputBuffer.data.array(), inputBuffer.data.limit()); boolean captionDataProcessed = false; boolean isRepeatableControl = false; while (ccData.bytesLeft() > 0) { + byte ccTypeAndValid = (byte) (ccData.readUnsignedByte() & 0x07); byte ccData1 = (byte) (ccData.readUnsignedByte() & 0x7F); byte ccData2 = (byte) (ccData.readUnsignedByte() & 0x7F); + // Only examine valid NTSC_CC_FIELD_1 packets + if (ccTypeAndValid != (CC_VALID_FLAG | NTSC_CC_FIELD_1)) { + // TODO: Add support for NTSC_CC_FIELD_2 packets + continue; + } + // Ignore empty captions. if (ccData1 == 0 && ccData2 == 0) { continue; @@ -550,16 +461,16 @@ public final class Eia608Decoder implements SubtitleDecoder { } /** - * Inspects an sei message to determine whether it contains EIA-608. + * 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 EIA-608. + * @return Whether the sei message contains CEA-608. */ - public static boolean isSeiMessageEia608(int payloadType, int payloadLength, + public static boolean isSeiMessageCea608(int payloadType, int payloadLength, ParsableByteArray payload) { if (payloadType != PAYLOAD_TYPE_CC || payloadLength < 8) { return false; diff --git a/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java new file mode 100644 index 0000000000..ae92d7fab8 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 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 com.google.android.exoplayer2.text.cea; + +import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.text.Subtitle; +import com.google.android.exoplayer2.text.SubtitleDecoder; +import com.google.android.exoplayer2.text.SubtitleDecoderException; +import com.google.android.exoplayer2.text.SubtitleInputBuffer; +import com.google.android.exoplayer2.text.SubtitleOutputBuffer; +import com.google.android.exoplayer2.util.Assertions; +import java.util.LinkedList; +import java.util.TreeSet; + +/** + * Base class for subtitle parsers for CEA captions. + */ +/* package */ abstract class CeaDecoder implements SubtitleDecoder { + + private static final int NUM_INPUT_BUFFERS = 10; + private static final int NUM_OUTPUT_BUFFERS = 2; + + private final LinkedList availableInputBuffers; + private final LinkedList availableOutputBuffers; + private final TreeSet queuedInputBuffers; + + private SubtitleInputBuffer dequeuedInputBuffer; + private long playbackPositionUs; + + public CeaDecoder() { + availableInputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_INPUT_BUFFERS; i++) { + availableInputBuffers.add(new SubtitleInputBuffer()); + } + availableOutputBuffers = new LinkedList<>(); + for (int i = 0; i < NUM_OUTPUT_BUFFERS; i++) { + availableOutputBuffers.add(new CeaOutputBuffer(this)); + } + queuedInputBuffers = new TreeSet<>(); + } + + @Override + public abstract String getName(); + + @Override + public void setPositionUs(long positionUs) { + playbackPositionUs = positionUs; + } + + @Override + public SubtitleInputBuffer dequeueInputBuffer() throws SubtitleDecoderException { + Assertions.checkState(dequeuedInputBuffer == null); + if (availableInputBuffers.isEmpty()) { + return null; + } + dequeuedInputBuffer = availableInputBuffers.pollFirst(); + return dequeuedInputBuffer; + } + + @Override + public void queueInputBuffer(SubtitleInputBuffer inputBuffer) throws SubtitleDecoderException { + Assertions.checkArgument(inputBuffer != null); + Assertions.checkArgument(inputBuffer == dequeuedInputBuffer); + queuedInputBuffers.add(inputBuffer); + dequeuedInputBuffer = null; + } + + @Override + public SubtitleOutputBuffer dequeueOutputBuffer() throws SubtitleDecoderException { + if (availableOutputBuffers.isEmpty()) { + return null; + } + + // iterate through all available input buffers whose timestamps are less than or equal + // to the current playback position; processing input buffers for future content should + // be deferred until they would be applicable + while (!queuedInputBuffers.isEmpty() + && queuedInputBuffers.first().timeUs <= playbackPositionUs) { + SubtitleInputBuffer inputBuffer = queuedInputBuffers.pollFirst(); + + // If the input buffer indicates we've reached the end of the stream, we can + // return immediately with an output buffer propagating that + if (inputBuffer.isEndOfStream()) { + SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); + outputBuffer.addFlag(C.BUFFER_FLAG_END_OF_STREAM); + releaseInputBuffer(inputBuffer); + return outputBuffer; + } + + decode(inputBuffer); + + // check if we have any caption updates to report + if (isNewSubtitleDataAvailable()) { + // Even if the subtitle is decode-only; we need to generate it to consume the data so it + // isn't accidentally prepended to the next subtitle + Subtitle subtitle = createSubtitle(); + if (!inputBuffer.isDecodeOnly()) { + SubtitleOutputBuffer outputBuffer = availableOutputBuffers.pollFirst(); + outputBuffer.setContent(inputBuffer.timeUs, subtitle, 0); + releaseInputBuffer(inputBuffer); + return outputBuffer; + } + } + + releaseInputBuffer(inputBuffer); + } + + return null; + } + + private void releaseInputBuffer(SubtitleInputBuffer inputBuffer) { + inputBuffer.clear(); + availableInputBuffers.add(inputBuffer); + } + + protected void releaseOutputBuffer(SubtitleOutputBuffer outputBuffer) { + outputBuffer.clear(); + availableOutputBuffers.add(outputBuffer); + } + + @Override + public void flush() { + playbackPositionUs = 0; + while (!queuedInputBuffers.isEmpty()) { + releaseInputBuffer(queuedInputBuffers.pollFirst()); + } + if (dequeuedInputBuffer != null) { + releaseInputBuffer(dequeuedInputBuffer); + dequeuedInputBuffer = null; + } + } + + @Override + public void release() { + // Do nothing + } + + /** + * Returns whether there is data available to create a new {@link Subtitle}. + */ + protected abstract boolean isNewSubtitleDataAvailable(); + + /** + * Creates a {@link Subtitle} from the available data. + */ + protected abstract Subtitle createSubtitle(); + + /** + * Filters and processes the raw data, providing {@link Subtitle}s via {@link #createSubtitle()} + * when sufficient data has been processed. + */ + protected abstract void decode(SubtitleInputBuffer inputBuffer); + +} diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java similarity index 72% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java index 7e5ba062ab..4cc32bb9e4 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608SubtitleOutputBuffer.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaOutputBuffer.java @@ -13,22 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; -import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.SubtitleOutputBuffer; /** - * A {@link Subtitle} output from an {@link Eia608Decoder}. + * A {@link SubtitleOutputBuffer} for {@link CeaDecoder}s. */ -/* package */ final class Eia608SubtitleOutputBuffer extends SubtitleOutputBuffer { +public final class CeaOutputBuffer extends SubtitleOutputBuffer { - private Eia608Decoder owner; + private final CeaDecoder owner; /** * @param owner The decoder that owns this buffer. */ - public Eia608SubtitleOutputBuffer(Eia608Decoder owner) { + public CeaOutputBuffer(CeaDecoder owner) { super(); this.owner = owner; } diff --git a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java similarity index 86% rename from library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java rename to library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java index 5ec8ca6d95..5becefe106 100644 --- a/library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java +++ b/library/src/main/java/com/google/android/exoplayer2/text/cea/CeaSubtitle.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.android.exoplayer2.text.eia608; +package com.google.android.exoplayer2.text.cea; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Subtitle; @@ -21,16 +21,16 @@ import java.util.Collections; import java.util.List; /** - * A representation of an EIA-608 subtitle. + * A representation of a CEA subtitle. */ -/* package */ final class Eia608Subtitle implements Subtitle { +/* package */ final class CeaSubtitle implements Subtitle { private final List cues; /** * @param cue The subtitle cue. */ - public Eia608Subtitle(Cue cue) { + public CeaSubtitle(Cue cue) { if (cue == null) { cues = Collections.emptyList(); } else { diff --git a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java index f3f63b1a76..561aba0146 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java @@ -64,7 +64,7 @@ public final class MimeTypes { public static final String APPLICATION_MP4 = BASE_TYPE_APPLICATION + "/mp4"; public static final String APPLICATION_WEBM = BASE_TYPE_APPLICATION + "/webm"; public static final String APPLICATION_ID3 = BASE_TYPE_APPLICATION + "/id3"; - public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608"; + public static final String APPLICATION_CEA608 = BASE_TYPE_APPLICATION + "/cea-608"; public static final String APPLICATION_SUBRIP = BASE_TYPE_APPLICATION + "/x-subrip"; public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml"; public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL"; @@ -209,7 +209,7 @@ public final class MimeTypes { return C.TRACK_TYPE_AUDIO; } else if (isVideo(mimeType)) { return C.TRACK_TYPE_VIDEO; - } else if (isText(mimeType) || APPLICATION_EIA608.equals(mimeType) + } else if (isText(mimeType) || APPLICATION_CEA608.equals(mimeType) || APPLICATION_SUBRIP.equals(mimeType) || APPLICATION_TTML.equals(mimeType) || APPLICATION_TX3G.equals(mimeType) || APPLICATION_MP4VTT.equals(mimeType) || APPLICATION_RAWCC.equals(mimeType) || APPLICATION_VOBSUB.equals(mimeType) diff --git a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index e140484531..b306fbf76e 100644 --- a/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -194,6 +194,13 @@ public final class ParsableByteArray { position += length; } + /** + * Peeks at the next byte as an unsigned value. + */ + public int peekUnsignedByte() { + return (data[position] & 0xFF); + } + /** * Reads the next byte as an unsigned value. */