From 01affbb93ef685e91ab38b085dfaccb8bb011406 Mon Sep 17 00:00:00 2001 From: Oliver Woodman Date: Thu, 4 Jun 2015 15:46:32 +0100 Subject: [PATCH] Simplify tx3g support. --- .../demo/player/ExtractorRendererBuilder.java | 6 +- .../exoplayer/extractor/mp4/Mp4Extractor.java | 4 +- .../exoplayer/text/TextTrackRenderer.java | 85 ++++++---------- .../exoplayer/text/tx3g/SubtitleData.java | 52 ---------- .../exoplayer/text/tx3g/TextParser.java | 88 ----------------- .../exoplayer/text/tx3g/TextSubtitle.java | 96 ------------------- .../exoplayer/text/tx3g/Tx3gParser.java | 51 ++++++++++ .../exoplayer/text/tx3g/Tx3gSubtitle.java | 69 +++++++++++++ 8 files changed, 156 insertions(+), 295 deletions(-) delete mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java delete mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java delete mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.java create mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java create mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gSubtitle.java diff --git a/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java b/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java index c94aa1e24d..b6534effac 100644 --- a/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java +++ b/demo/src/main/java/com/google/android/exoplayer/demo/player/ExtractorRendererBuilder.java @@ -23,7 +23,7 @@ import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallba import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.text.TextTrackRenderer; -import com.google.android.exoplayer.text.tx3g.TextParser; +import com.google.android.exoplayer.text.tx3g.Tx3gParser; import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer.upstream.DefaultUriDataSource; @@ -64,8 +64,8 @@ public class ExtractorRendererBuilder implements RendererBuilder { player, 50); MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, null, true, player.getMainHandler(), player); - - TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, player.getMainHandler().getLooper(), new TextParser()); + TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, + player.getMainHandler().getLooper(), new Tx3gParser()); // Invoke the callback. TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java index ed0241477a..d7b90a4573 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Mp4Extractor.java @@ -225,8 +225,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { } Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd)); - if (track == null || (track.type != Track.TYPE_AUDIO && track.type != Track.TYPE_VIDEO && - track.type != Track.TYPE_TEXT)) { + if (track == null || track.mediaFormat == null || (track.type != Track.TYPE_AUDIO + && track.type != Track.TYPE_VIDEO && track.type != Track.TYPE_TEXT)) { continue; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java index 4a12b09777..575823070d 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java @@ -55,10 +55,10 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { private boolean inputStreamEnded; private Subtitle subtitle; + private Subtitle nextSubtitle; private SubtitleParserHelper parserHelper; private HandlerThread parserThread; private int nextSubtitleEventIndex; - private boolean textRendererNeedsUpdate; /** * @param source A source from which samples containing subtitle data can be read. @@ -122,14 +122,10 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { inputStreamEnded = false; currentPositionUs = positionUs; source.seekToUs(positionUs); - if (subtitle != null && (positionUs < subtitle.getStartTime() - || subtitle.getLastEventTime() <= positionUs)) { - subtitle = null; - } + subtitle = null; + nextSubtitle = null; parserHelper.flush(); clearTextRenderer(); - syncNextEventIndex(positionUs); - textRendererNeedsUpdate = subtitle != null; } @Override @@ -141,49 +137,49 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { throw new ExoPlaybackException(e); } - if (parserHelper.isParsing()) { - return; - } - - Subtitle dequeuedSubtitle = null; - if (subtitle == null) { + if (nextSubtitle == null) { try { - dequeuedSubtitle = parserHelper.getAndClearResult(); + nextSubtitle = parserHelper.getAndClearResult(); } catch (IOException e) { throw new ExoPlaybackException(e); } } - if (subtitle == null && dequeuedSubtitle != null) { - // We've dequeued a new subtitle. Sync the event index and update the subtitle. - subtitle = dequeuedSubtitle; - syncNextEventIndex(positionUs); - textRendererNeedsUpdate = true; - } else if (subtitle != null) { + boolean textRendererNeedsUpdate = false; + long subtitleNextEventTimeUs = Long.MAX_VALUE; + if (subtitle != null) { // We're iterating through the events in a subtitle. Set textRendererNeedsUpdate if we // advance to the next event. - long nextEventTimeUs = getNextEventTime(); - while (nextEventTimeUs <= positionUs) { + subtitleNextEventTimeUs = getNextEventTime(); + while (subtitleNextEventTimeUs <= positionUs) { nextSubtitleEventIndex++; - nextEventTimeUs = getNextEventTime(); + subtitleNextEventTimeUs = getNextEventTime(); textRendererNeedsUpdate = true; } - if (nextEventTimeUs == Long.MAX_VALUE) { - // We've finished processing the subtitle. - subtitle = null; - } } - // We don't have a subtitle. Try and read the next one from the source, and if we succeed then - // sync and set textRendererNeedsUpdate. - if (!inputStreamEnded && subtitle == null) { + if (subtitleNextEventTimeUs == Long.MAX_VALUE && nextSubtitle != null + && nextSubtitle.getStartTime() <= positionUs) { + // Advance to the next subtitle. Sync the next event index and trigger an update. + subtitle = nextSubtitle; + nextSubtitle = null; + nextSubtitleEventIndex = subtitle.getNextEventTimeIndex(positionUs); + textRendererNeedsUpdate = true; + } + + if (textRendererNeedsUpdate && getState() == TrackRenderer.STATE_STARTED) { + // textRendererNeedsUpdate is set and we're playing. Update the renderer. + updateTextRenderer(subtitle.getCues(positionUs)); + } + + if (!inputStreamEnded && nextSubtitle == null && !parserHelper.isParsing()) { + // Try and read the next subtitle from the source. try { SampleHolder sampleHolder = parserHelper.getSampleHolder(); sampleHolder.clearData(); int result = source.readData(trackIndex, positionUs, formatHolder, sampleHolder, false); if (result == SampleSource.SAMPLE_READ) { parserHelper.startParseOperation(); - textRendererNeedsUpdate = false; } else if (result == SampleSource.END_OF_STREAM) { inputStreamEnded = true; } @@ -191,21 +187,12 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { throw new ExoPlaybackException(e); } } - - // Update the text renderer if we're both playing and textRendererNeedsUpdate is set. - if (textRendererNeedsUpdate && getState() == TrackRenderer.STATE_STARTED) { - textRendererNeedsUpdate = false; - if (subtitle == null) { - clearTextRenderer(); - } else { - updateTextRenderer(positionUs); - } - } } @Override protected void onDisabled() { subtitle = null; + nextSubtitle = null; parserThread.quit(); parserThread = null; parserHelper = null; @@ -236,7 +223,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { @Override protected boolean isEnded() { - return inputStreamEnded && subtitle == null; + return inputStreamEnded && (subtitle == null || getNextEventTime() == Long.MAX_VALUE); } @Override @@ -246,18 +233,13 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { return true; } - private void syncNextEventIndex(long positionUs) { - nextSubtitleEventIndex = subtitle == null ? -1 : subtitle.getNextEventTimeIndex(positionUs); - } - private long getNextEventTime() { return ((nextSubtitleEventIndex == -1) || (nextSubtitleEventIndex >= subtitle.getEventTimeCount())) ? Long.MAX_VALUE : (subtitle.getEventTime(nextSubtitleEventIndex)); } - private void updateTextRenderer(long positionUs) { - List cues = subtitle.getCues(positionUs); + private void updateTextRenderer(List cues) { if (textRendererHandler != null) { textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, cues).sendToTarget(); } else { @@ -266,12 +248,7 @@ public class TextTrackRenderer extends TrackRenderer implements Callback { } private void clearTextRenderer() { - if (textRendererHandler != null) { - textRendererHandler.obtainMessage(MSG_UPDATE_OVERLAY, Collections.emptyList()) - .sendToTarget(); - } else { - invokeRendererInternalCues(Collections.emptyList()); - } + updateTextRenderer(Collections.emptyList()); } @SuppressWarnings("unchecked") diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java deleted file mode 100644 index ce4603b29d..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2014 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.exoplayer.text.tx3g; - -import java.util.Comparator; - -/** - * A representation of a single tx3g. - */ -class SubtitleData implements Comparable , Comparator { - - public final long startTimePosUs; - public final String subtitle; - - SubtitleData(long startTimePosUs, String subtitle) - { - this.startTimePosUs = startTimePosUs; - this.subtitle = subtitle; - } - - @Override - public int compare(SubtitleData o1 , SubtitleData o2) { - if (o1.startTimePosUs < o2.startTimePosUs) - return -1; - if (o1.startTimePosUs > o2.startTimePosUs) - return 1; - return 0; - } - - @Override - public int compareTo(SubtitleData another) { - if (startTimePosUs < another.startTimePosUs) - return -1; - if (startTimePosUs > another.startTimePosUs) - return 1; - return 0; - } -} diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java deleted file mode 100644 index 7f4098d8e5..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2014 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.exoplayer.text.tx3g; - -import com.google.android.exoplayer.text.Subtitle; -import com.google.android.exoplayer.text.SubtitleParser; -import com.google.android.exoplayer.util.MimeTypes; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.List; - -/** - * A simple Text parser that supports tx3g atom. - * - * Only support to parse a single text track at this version , - * since ExtractorSampleSource does not handle multiple audio/video tracks. - * - */ -public class TextParser implements SubtitleParser { - private static final String TAG = "TextParser"; - - private final List subtitleList; - private static final int MAX_SUBTITLE_COUNT = 4; - public TextParser() { - - subtitleList = new LinkedList(); - } - - @Override - public Subtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs) - throws IOException { - - DataInputStream in = new DataInputStream(inputStream); - String text = in.readUTF(); - text = (text == null) ? "" : text; - - SubtitleData cue = new SubtitleData(startTimeUs, text); - - //try to resize the list. - if (subtitleList.size() > 0) { - long lastTimeUs = subtitleList.get(subtitleList.size() - 1).startTimePosUs; - if (startTimeUs < lastTimeUs) { - //when forward seek - subtitleList.clear(); - } - - while (subtitleList.size() > MAX_SUBTITLE_COUNT) { - subtitleList.remove(0); - } - } - subtitleList.add(cue); - - Collections.sort(subtitleList, new Comparator() { - @Override - public int compare(SubtitleData o1 , SubtitleData o2) { - if (o1.startTimePosUs < o2.startTimePosUs) - return -1; - if (o1.startTimePosUs > o2.startTimePosUs) - return 1; - return 0; - } - }); - return new TextSubtitle(subtitleList); - } - - @Override - public boolean canParse(String mimeType) { - return MimeTypes.APPLICATION_TX3G.equals(mimeType); - } -} diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.java deleted file mode 100644 index c59389b4bb..0000000000 --- a/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2014 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.exoplayer.text.tx3g; - -import java.util.ArrayList; -import java.util.List; - -import com.google.android.exoplayer.text.Cue; -import com.google.android.exoplayer.text.Subtitle; - -/** - * A representation of a tx3g subtitle. - */ -public final class TextSubtitle implements Subtitle { - static String TAG = "TextSubtitle"; - private final List text; - - public TextSubtitle(List text) { - this.text = text; - } - - @Override - public long getStartTime() { - return text.get(0).startTimePosUs; - } - - @Override - public int getNextEventTimeIndex(long timeUs) { - - int index = findTheClosed(timeUs); - int next = (index ) < text.size() ? (index ) : -1; - return next; - } - - @Override - public int getEventTimeCount() { - return text.size(); - } - - @Override - public long getEventTime(int index) { - if (index > text.size() - 1) return -1; - - return text.get(index).startTimePosUs; - } - - @Override - public long getLastEventTime() { - return text.get(0).startTimePosUs; - } - - @Override - public List getCues(long timeUs) { - int index = findTheClosed(timeUs); - List list = new ArrayList<>(); - if (index == -1) return null; - - String str = text.get(index).subtitle; - - list.add(new Cue(str)); - return list; - } - - private int findTheClosed(long timeUs) { - //TODO : Time complexity is O(n),not good solution. - - int length = text.size(); - for (int i = 0; i < length ; i++) { - SubtitleData data = text.get(i); - boolean bCheckFront = data.startTimePosUs <= timeUs ; - boolean bCheckEnd = false; - if (i + 1 < length) { - bCheckEnd = text.get(i + 1).startTimePosUs > timeUs ; - } else if (i + 1 == length) { - bCheckEnd = true; - } - - if (bCheckFront && bCheckEnd) - return i; - } - return -1; - } -} diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java new file mode 100644 index 0000000000..54aedc6156 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gParser.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 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.exoplayer.text.tx3g; + +import com.google.android.exoplayer.text.Cue; +import com.google.android.exoplayer.text.Subtitle; +import com.google.android.exoplayer.text.SubtitleParser; +import com.google.android.exoplayer.util.MimeTypes; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * A {@link SubtitleParser} for tx3g. + *

+ * Currently only supports parsing of a single text track + */ +public final class Tx3gParser implements SubtitleParser { + + @Override + public Subtitle parse(InputStream inputStream, String inputEncoding, long startTimeUs) + throws IOException { + DataInputStream dataInputStream = new DataInputStream(inputStream); + try { + String cueText = dataInputStream.readUTF(); + return new Tx3gSubtitle(startTimeUs, new Cue(cueText)); + } finally { + dataInputStream.close(); + } + } + + @Override + public boolean canParse(String mimeType) { + return MimeTypes.APPLICATION_TX3G.equals(mimeType); + } + +} diff --git a/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gSubtitle.java b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gSubtitle.java new file mode 100644 index 0000000000..dfcaf98863 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/Tx3gSubtitle.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 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.exoplayer.text.tx3g; + +import com.google.android.exoplayer.text.Cue; +import com.google.android.exoplayer.text.Subtitle; +import com.google.android.exoplayer.util.Assertions; + +import java.util.Collections; +import java.util.List; + +/** + * A representation of a tx3g subtitle. + */ +/* package */ final class Tx3gSubtitle implements Subtitle { + + private final long startTimeUs; + private final List cues; + + public Tx3gSubtitle(long startTimeUs, Cue cue) { + this.startTimeUs = startTimeUs; + this.cues = Collections.singletonList(cue); + } + + @Override + public long getStartTime() { + return startTimeUs; + } + + @Override + public int getNextEventTimeIndex(long timeUs) { + return timeUs < startTimeUs ? 0 : -1; + } + + @Override + public int getEventTimeCount() { + return 1; + } + + @Override + public long getEventTime(int index) { + Assertions.checkArgument(index == 0); + return startTimeUs; + } + + @Override + public long getLastEventTime() { + return startTimeUs; + } + + @Override + public List getCues(long timeUs) { + return timeUs >= startTimeUs ? cues : Collections.emptyList(); + } + +}