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";