From f42ed8920215c78ee61df924a06d120a3d1c50b3 Mon Sep 17 00:00:00 2001 From: ood_tsen Date: Mon, 1 Jun 2015 19:47:46 +0800 Subject: [PATCH] parse mp4 tx3g --- .../demo/player/ExtractorRendererBuilder.java | 5 + .../google/android/exoplayer/MediaFormat.java | 4 + .../android/exoplayer/extractor/mp4/Atom.java | 2 +- .../exoplayer/extractor/mp4/AtomParsers.java | 2 + .../exoplayer/extractor/mp4/Mp4Extractor.java | 6 +- .../exoplayer/text/tx3g/SubtitleData.java | 65 +++++++++++ .../exoplayer/text/tx3g/TextParser.java | 102 ++++++++++++++++++ .../exoplayer/text/tx3g/TextSubtitle.java | 97 +++++++++++++++++ .../android/exoplayer/util/MimeTypes.java | 1 + 9 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java create mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java create mode 100644 library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.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 6cab32227d..c94aa1e24d 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 @@ -22,6 +22,8 @@ import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilderCallback; 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.upstream.DataSource; import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer.upstream.DefaultUriDataSource; @@ -63,10 +65,13 @@ public class ExtractorRendererBuilder implements RendererBuilder { MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, null, true, player.getMainHandler(), player); + TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, player.getMainHandler().getLooper(), new TextParser()); + // Invoke the callback. TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; + renderers[DemoPlayer.TYPE_TEXT] = textRenderer; callback.onRenderers(null, null, renderers, bandwidthMeter); } diff --git a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java index bb266934f4..3bce2d8902 100644 --- a/library/src/main/java/com/google/android/exoplayer/MediaFormat.java +++ b/library/src/main/java/com/google/android/exoplayer/MediaFormat.java @@ -106,6 +106,10 @@ public class MediaFormat { return createFormatForMimeType(MimeTypes.APPLICATION_TTML); } + public static MediaFormat createTx3GFormat() { + return createFormatForMimeType(MimeTypes.TEXT_TX3G); + } + public static MediaFormat createFormatForMimeType(String mimeType) { return new MediaFormat(mimeType, NO_VALUE, C.UNKNOWN_TIME_US, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null); diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Atom.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Atom.java index a2b3524ddb..55378fa9fa 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Atom.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/Atom.java @@ -91,7 +91,7 @@ import java.util.List; public static final int TYPE_stsz = Util.getIntegerCodeForString("stsz"); public static final int TYPE_stco = Util.getIntegerCodeForString("stco"); public static final int TYPE_co64 = Util.getIntegerCodeForString("co64"); - + public static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g"); public final int type; Atom(int type) { diff --git a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java index 846c71ad9b..418fb3e855 100644 --- a/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java +++ b/library/src/main/java/com/google/android/exoplayer/extractor/mp4/AtomParsers.java @@ -340,6 +340,8 @@ import java.util.List; holder, i); } else if (childAtomType == Atom.TYPE_TTML) { holder.mediaFormat = MediaFormat.createTtmlFormat(); + } else if (childAtomType == Atom.TYPE_tx3g) { + holder.mediaFormat = MediaFormat.createTx3GFormat(); } stsd.setPosition(childStartPosition + childAtomSize); } 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 1c8b2b4faa..ed0241477a 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,7 +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)) { + if (track == null || (track.type != Track.TYPE_AUDIO && track.type != Track.TYPE_VIDEO && + track.type != Track.TYPE_TEXT)) { continue; } @@ -359,7 +360,8 @@ public final class Mp4Extractor implements Extractor, SeekMap { || atom == Atom.TYPE_avc1 || atom == Atom.TYPE_avcC || atom == Atom.TYPE_mp4a || atom == Atom.TYPE_esds || atom == Atom.TYPE_stts || atom == Atom.TYPE_stss || atom == Atom.TYPE_ctts || atom == Atom.TYPE_stsc || atom == Atom.TYPE_stsz - || atom == Atom.TYPE_stco || atom == Atom.TYPE_co64 || atom == Atom.TYPE_tkhd; + || atom == Atom.TYPE_stco || atom == Atom.TYPE_co64 || atom == Atom.TYPE_tkhd + || atom == Atom.TYPE_tx3g; } /** Returns whether the extractor should parse a container atom with type {@code atom}. */ 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 new file mode 100644 index 0000000000..55fd44f81e --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/SubtitleData.java @@ -0,0 +1,65 @@ +package com.google.android.exoplayer.text.tx3g; + +import java.util.Comparator; + +class SubtitleData implements Comparable , Comparator { + + private long mStartTimePosUs; + private long mEndTimePosUs; + private String strSubtitle; + + SubtitleData() + { + mStartTimePosUs = 0l; + mEndTimePosUs = 0l; + strSubtitle = ""; + } + + protected void setStartTimePos(long time) + { + mStartTimePosUs = time; + } + + protected void setEndTimePos(long time) + { + mEndTimePosUs = time; + } + + protected void setSubtitleText(String text) + { + strSubtitle = text; + } + + protected long getStartTimePos() + { + return mStartTimePosUs; + } + + protected long getEndTimePos() + { + return mEndTimePosUs; + } + + protected String getsubtitleText() + { + return strSubtitle; + } + + @Override + public int compare(SubtitleData o1 , SubtitleData o2) { + if (o1.getStartTimePos() < o2.getStartTimePos()) + return -1; + if (o1.getStartTimePos() > o2.getStartTimePos()) + return 1; + return 0; + } + + @Override + public int compareTo(SubtitleData another) { + if (getStartTimePos() < another.getStartTimePos()) + return -1; + if (getStartTimePos() > another.getStartTimePos()) + 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 new file mode 100644 index 0000000000..a802761d6b --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextParser.java @@ -0,0 +1,102 @@ +/* + * 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 android.util.Log; + +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 presentation profile. + *

+ * Supported features in this parser are: + *

    + *
  • content + *
  • core + *
  • presentation + *
  • profile + *
  • structure + *
  • time-offset + *
  • timing + *
  • tickRate + *
  • time-clock-with-frames + *
  • time-clock + *
  • time-offset-with-frames + *
  • time-offset-with-ticks + *
+ *

+ * @see TTML specification + */ +public class TextParser implements SubtitleParser { + private static final String TAG = "TextParser"; + + + private final List mSubtitleList; + + /** + * Equivalent to {@code TtmlParser(true)}. + */ + public TextParser() { + Log.i(TAG,"TextParser "); + mSubtitleList = 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; + Log.i(TAG,"parse(" + text + "," + startTimeUs + ")" ); + + SubtitleData cue = new SubtitleData(); + cue.setSubtitleText(text); + cue.setStartTimePos(startTimeUs); + mSubtitleList.add(cue); + + Collections.sort(mSubtitleList, new Comparator() { + @Override + public int compare(SubtitleData o1 , SubtitleData o2) { + if (o1.getStartTimePos() < o2.getStartTimePos()) + return -1; + if (o1.getStartTimePos() > o2.getStartTimePos()) + return 1; + return 0; + } + }); + TextSubtitle textSubtitle = new TextSubtitle(mSubtitleList); + return textSubtitle; + } + + @Override + public boolean canParse(String mimeType) { + boolean rtn = MimeTypes.TEXT_TX3G.equals(mimeType); + Log.i(TAG,"canParse " + mimeType + "," + rtn); + return rtn; + } +} 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 new file mode 100644 index 0000000000..32f1a76d24 --- /dev/null +++ b/library/src/main/java/com/google/android/exoplayer/text/tx3g/TextSubtitle.java @@ -0,0 +1,97 @@ +/* + * 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 TTML 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).getStartTimePos(); + } + + @Override + public int getNextEventTimeIndex(long timeUs) { + + int index = findTheClosed(timeUs); + int next = (index ) < text.size() ? (index ) : -1; + return next; + } + + @Override + public int getEventTimeCount() { + //LOG.I(TAG,"getEventTimeCount() = " + text.size()); + return text.size(); + } + + @Override + public long getEventTime(int index) { + if (index > text.size() - 1) return -1; + + //LOG.I(TAG,"getEventTime(" + index + ") = " + text.get(index).getStartTimePos()); + return text.get(index).getStartTimePos(); + } + + @Override + public long getLastEventTime() { + return text.get(0).getStartTimePos(); + } + + @Override + public List getCues(long timeUs) { + int index = findTheClosed(timeUs); + List list = new ArrayList<>(); + if (index == -1) return null; + + String str = text.get(index).getsubtitleText(); + + list.add(new Cue(str)); + return list; + } + + private int findTheClosed(long timeUs) { + + int length = text.size(); + for (int i = 0; i < length ; i++) { + SubtitleData data = text.get(i); + boolean bCheckFront = data.getStartTimePos() <= timeUs ; + boolean bCheckEnd = false; + if (i + 1 < length) { + bCheckEnd = text.get(i + 1).getStartTimePos() > 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/util/MimeTypes.java b/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java index 21aa81a965..dea171b6df 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java +++ b/library/src/main/java/com/google/android/exoplayer/util/MimeTypes.java @@ -53,6 +53,7 @@ public class MimeTypes { public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus"; public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt"; + public static final String TEXT_TX3G = BASE_TYPE_TEXT + "/tx3g"; public static final String APPLICATION_ID3 = BASE_TYPE_APPLICATION + "/id3"; public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608";