Skip to content

Commit

Permalink
Clean up DVB support
Browse files Browse the repository at this point in the history
- Removed the PES_STRIPPED flag. It's unnecessary. We can strip
  PES in the TS extractor instead.
- Made nearly all of the object classes in DvbParser immutable.
  Else it's non-obvious that none of this state can be mutated.
- Made a a lot of the methods in DvbParser static for the same
  reason.
- Removed unnecessary null checks, code that was never executed,
  unused fields etc.
- Add proper flushing of DvbParser, to prevent corrupt output
  following a seek.
  • Loading branch information
ojw28 committed Apr 6, 2017
1 parent 8c05b5b commit 156bc52
Show file tree
Hide file tree
Showing 24 changed files with 1,091 additions and 1,593 deletions.
2 changes: 1 addition & 1 deletion library/core/proguard-rules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
}
-keepclassmembers class com.google.android.exoplayer2.text.dvb.DvbDecoder {
public <init>(java.util.List);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
public void testDecodeEmpty() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
// Assert that the subtitle is empty.
assertEquals(0, subtitle.getEventTimeCount());
assertTrue(subtitle.getCues(0).isEmpty());
Expand All @@ -45,7 +45,7 @@ public void testDecodeEmpty() throws IOException {
public void testDecodeTypical() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_FILE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2);
Expand All @@ -55,7 +55,7 @@ public void testDecodeTypical() throws IOException {
public void testDecodeTypicalWithByteOrderMark() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_WITH_BYTE_ORDER_MARK);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2);
Expand All @@ -65,7 +65,7 @@ public void testDecodeTypicalWithByteOrderMark() throws IOException {
public void testDecodeTypicalExtraBlankLine() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_EXTRA_BLANK_LINE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(6, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0);
assertTypicalCue2(subtitle, 2);
Expand All @@ -76,7 +76,7 @@ public void testDecodeTypicalMissingTimecode() throws IOException {
// Parsing should succeed, parsing the first and third cues only.
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_TIMECODE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2);
Expand All @@ -86,7 +86,7 @@ public void testDecodeTypicalMissingSequence() throws IOException {
// Parsing should succeed, parsing the first and third cues only.
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_MISSING_SEQUENCE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(4, subtitle.getEventTimeCount());
assertTypicalCue1(subtitle, 0);
assertTypicalCue3(subtitle, 2);
Expand All @@ -96,15 +96,15 @@ public void testDecodeTypicalNegativeTimestamps() throws IOException {
// Parsing should succeed, parsing the third cue only.
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), TYPICAL_NEGATIVE_TIMESTAMPS);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertEquals(2, subtitle.getEventTimeCount());
assertTypicalCue3(subtitle, 0);
}

public void testDecodeNoEndTimecodes() throws IOException {
SubripDecoder decoder = new SubripDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), NO_END_TIMECODES_FILE);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length);
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);

// Test event count.
assertEquals(3, subtitle.getEventTimeCount());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ private TtmlNode queryChildrenForTag(TtmlNode node, String tag, int pos) {
private TtmlSubtitle getSubtitle(String file) throws IOException, SubtitleDecoderException {
TtmlDecoder ttmlDecoder = new TtmlDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), file);
return ttmlDecoder.decode(bytes, bytes.length);
return ttmlDecoder.decode(bytes, bytes.length, false);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,22 @@ public final class Mp4WebvttDecoderTest extends TestCase {

public void testSingleCueSample() throws SubtitleDecoderException {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length);
Subtitle result = decoder.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length, false);
Cue expectedCue = new Cue("Hello World"); // Line feed must be trimmed by the decoder
assertMp4WebvttSubtitleEquals(result, expectedCue);
}

public void testTwoCuesSample() throws SubtitleDecoderException {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length);
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length, false);
Cue firstExpectedCue = new Cue("Hello World");
Cue secondExpectedCue = new Cue("Bye Bye");
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
}

public void testNoCueSample() throws SubtitleDecoderException {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length);
Subtitle result = decoder.decode(NO_CUE_SAMPLE, NO_CUE_SAMPLE.length, false);
assertMp4WebvttSubtitleEquals(result);
}

Expand All @@ -105,7 +105,7 @@ public void testNoCueSample() throws SubtitleDecoderException {
public void testSampleWithIncompleteHeader() {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
try {
decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length);
decoder.decode(INCOMPLETE_HEADER_SAMPLE, INCOMPLETE_HEADER_SAMPLE.length, false);
} catch (SubtitleDecoderException e) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void testDecodeEmpty() throws IOException {
WebvttDecoder decoder = new WebvttDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), EMPTY_FILE);
try {
decoder.decode(bytes, bytes.length);
decoder.decode(bytes, bytes.length, false);
fail();
} catch (SubtitleDecoderException expected) {
// Do nothing.
Expand Down Expand Up @@ -194,7 +194,7 @@ private WebvttSubtitle getSubtitleForTestAsset(String asset) throws IOException,
SubtitleDecoderException {
WebvttDecoder decoder = new WebvttDecoder();
byte[] bytes = TestUtil.getByteArray(getInstrumentation(), asset);
return decoder.decode(bytes, bytes.length);
return decoder.decode(bytes, bytes.length, false);
}

private Spanned getUniqueSpanTextAt(WebvttSubtitle sub, long timeUs) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1465,8 +1465,9 @@ public void initializeOutput(ExtractorOutput output, int trackId) throws ParserE
break;
case CODEC_ID_DVBSUB:
mimeType = MimeTypes.APPLICATION_DVBSUBS;
initializationData = Collections.singletonList(new byte[] {
(byte) 0x01, codecPrivate[0], codecPrivate[1], codecPrivate[2], codecPrivate[3]});
// Init data: composition_page (2), ancillary_page (2)
initializationData = Collections.singletonList(new byte[] {codecPrivate[0],
codecPrivate[1], codecPrivate[2], codecPrivate[3]});
break;
default:
throw new ParserException("Unrecognized codec identifier.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
case TsExtractor.TS_STREAM_TYPE_ID3:
return new PesReader(new Id3Reader());
case TsExtractor.TS_STREAM_TYPE_DVBSUBS:
return new PesReader(new DvbSubtitlesReader(esInfo));
return new PesReader(
new DvbSubtitleReader(esInfo.language, esInfo.dvbSubtitleInitializationData));
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright (C) 2017 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.extractor.ts;

import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Collections;
import java.util.List;

/**
* Parses DVB subtitle data and extracts individual frames.
*/
public final class DvbSubtitleReader implements ElementaryStreamReader {

private final String language;
private final List<byte[]> initializationData;

private TrackOutput output;
private boolean writingSample;
private int bytesToCheck;
private int sampleBytesWritten;
private long sampleTimeUs;

/**
* @param language The subtitle language code.
* @param initializationData Initialization data to be included in the track {@link Format}.
*/
public DvbSubtitleReader(String language, byte[] initializationData) {
this.language = language;
this.initializationData = Collections.singletonList(initializationData);
}

@Override
public void seek() {
writingSample = false;
}

@Override
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
idGenerator.generateNewId();
this.output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT);
output.format(Format.createImageSampleFormat(idGenerator.getFormatId(),
MimeTypes.APPLICATION_DVBSUBS, null, Format.NO_VALUE, initializationData, language, null));
}

@Override
public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) {
if (!dataAlignmentIndicator) {
return;
}
writingSample = true;
sampleTimeUs = pesTimeUs;
sampleBytesWritten = 0;
bytesToCheck = 2;
}

@Override
public void packetFinished() {
if (writingSample) {
output.sampleMetadata(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME, sampleBytesWritten, 0, null);
writingSample = false;
}
}

@Override
public void consume(ParsableByteArray data) {
if (writingSample) {
if (bytesToCheck == 2 && !checkNextByte(data, 0x20)) {
// Failed to check data_identifier
return;
}
if (bytesToCheck == 1 && !checkNextByte(data, 0x00)) {
// Check and discard the subtitle_stream_id
return;
}
int bytesAvailable = data.bytesLeft();
output.sampleData(data, bytesAvailable);
sampleBytesWritten += bytesAvailable;
}
}

private boolean checkNextByte(ParsableByteArray data, int expectedValue) {
if (data.bytesLeft() == 0) {
return false;
}
if (data.readUnsignedByte() != expectedValue) {
writingSample = false;
}
bytesToCheck--;
return writingSample;
}

}
Loading

0 comments on commit 156bc52

Please sign in to comment.