Merge pull request #509 from Ood-Tsen/tx3g

parse mp4 tx3g
This commit is contained in:
ojw28 2015-06-04 15:07:29 +01:00
commit 254bc5a8cc
9 changed files with 253 additions and 3 deletions

View File

@ -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.demo.player.DemoPlayer.RendererBuilderCallback;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; 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.DataSource;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
@ -63,10 +65,13 @@ public class ExtractorRendererBuilder implements RendererBuilder {
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource, MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
null, true, player.getMainHandler(), player); null, true, player.getMainHandler(), player);
TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player, player.getMainHandler().getLooper(), new TextParser());
// Invoke the callback. // Invoke the callback.
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer; renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
callback.onRenderers(null, null, renderers, bandwidthMeter); callback.onRenderers(null, null, renderers, bandwidthMeter);
} }

View File

@ -106,6 +106,10 @@ public class MediaFormat {
return createFormatForMimeType(MimeTypes.APPLICATION_TTML); return createFormatForMimeType(MimeTypes.APPLICATION_TTML);
} }
public static MediaFormat createTx3GFormat() {
return createFormatForMimeType(MimeTypes.APPLICATION_TX3G);
}
public static MediaFormat createFormatForMimeType(String mimeType) { public static MediaFormat createFormatForMimeType(String mimeType) {
return new MediaFormat(mimeType, NO_VALUE, C.UNKNOWN_TIME_US, NO_VALUE, NO_VALUE, NO_VALUE, return new MediaFormat(mimeType, NO_VALUE, C.UNKNOWN_TIME_US, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, null); NO_VALUE, NO_VALUE, null);

View File

@ -91,7 +91,7 @@ import java.util.List;
public static final int TYPE_stsz = Util.getIntegerCodeForString("stsz"); public static final int TYPE_stsz = Util.getIntegerCodeForString("stsz");
public static final int TYPE_stco = Util.getIntegerCodeForString("stco"); public static final int TYPE_stco = Util.getIntegerCodeForString("stco");
public static final int TYPE_co64 = Util.getIntegerCodeForString("co64"); public static final int TYPE_co64 = Util.getIntegerCodeForString("co64");
public static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g");
public final int type; public final int type;
Atom(int type) { Atom(int type) {

View File

@ -340,6 +340,8 @@ import java.util.List;
holder, i); holder, i);
} else if (childAtomType == Atom.TYPE_TTML) { } else if (childAtomType == Atom.TYPE_TTML) {
holder.mediaFormat = MediaFormat.createTtmlFormat(); holder.mediaFormat = MediaFormat.createTtmlFormat();
} else if (childAtomType == Atom.TYPE_tx3g) {
holder.mediaFormat = MediaFormat.createTx3GFormat();
} }
stsd.setPosition(childStartPosition + childAtomSize); stsd.setPosition(childStartPosition + childAtomSize);
} }

View File

@ -225,7 +225,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
} }
Track track = AtomParsers.parseTrak(atom, moov.getLeafAtomOfType(Atom.TYPE_mvhd)); 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; 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_avc1 || atom == Atom.TYPE_avcC || atom == Atom.TYPE_mp4a
|| atom == Atom.TYPE_esds || atom == Atom.TYPE_stts || atom == Atom.TYPE_stss || 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_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}. */ /** Returns whether the extractor should parse a container atom with type {@code atom}. */

View File

@ -0,0 +1,52 @@
/*
* 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 <SubtitleData>, Comparator<SubtitleData> {
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;
}
}

View File

@ -0,0 +1,88 @@
/*
* 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<SubtitleData> subtitleList;
private static final int MAX_SUBTITLE_COUNT = 4;
public TextParser() {
subtitleList = new LinkedList<SubtitleData>();
}
@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<SubtitleData>() {
@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);
}
}

View File

@ -0,0 +1,96 @@
/*
* 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<SubtitleData> text;
public TextSubtitle(List<SubtitleData> 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<Cue> getCues(long timeUs) {
int index = findTheClosed(timeUs);
List<Cue> 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;
}
}

View File

@ -58,6 +58,7 @@ public class MimeTypes {
public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608"; public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608";
public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml"; public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml";
public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL"; public static final String APPLICATION_M3U8 = BASE_TYPE_APPLICATION + "/x-mpegURL";
public static final String APPLICATION_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
private MimeTypes() {} private MimeTypes() {}