mirror of
https://github.com/androidx/media.git
synced 2025-05-10 09:12:16 +08:00
parse mp4 tx3g
This commit is contained in:
parent
02d5cb8109
commit
f42ed89202
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,10 @@ public class MediaFormat {
|
|||||||
return createFormatForMimeType(MimeTypes.APPLICATION_TTML);
|
return createFormatForMimeType(MimeTypes.APPLICATION_TTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MediaFormat createTx3GFormat() {
|
||||||
|
return createFormatForMimeType(MimeTypes.TEXT_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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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}. */
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.google.android.exoplayer.text.tx3g;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
class SubtitleData implements Comparable <SubtitleData>, Comparator<SubtitleData> {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
* <p>
|
||||||
|
* Supported features in this parser are:
|
||||||
|
* <ul>
|
||||||
|
* <li>content
|
||||||
|
* <li>core
|
||||||
|
* <li>presentation
|
||||||
|
* <li>profile
|
||||||
|
* <li>structure
|
||||||
|
* <li>time-offset
|
||||||
|
* <li>timing
|
||||||
|
* <li>tickRate
|
||||||
|
* <li>time-clock-with-frames
|
||||||
|
* <li>time-clock
|
||||||
|
* <li>time-offset-with-frames
|
||||||
|
* <li>time-offset-with-ticks
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
* @see <a href="http://www.w3.org/TR/ttaf1-dfxp/">TTML specification</a>
|
||||||
|
*/
|
||||||
|
public class TextParser implements SubtitleParser {
|
||||||
|
private static final String TAG = "TextParser";
|
||||||
|
|
||||||
|
|
||||||
|
private final List<SubtitleData> mSubtitleList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@code TtmlParser(true)}.
|
||||||
|
*/
|
||||||
|
public TextParser() {
|
||||||
|
Log.i(TAG,"TextParser ");
|
||||||
|
mSubtitleList = 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;
|
||||||
|
Log.i(TAG,"parse(" + text + "," + startTimeUs + ")" );
|
||||||
|
|
||||||
|
SubtitleData cue = new SubtitleData();
|
||||||
|
cue.setSubtitleText(text);
|
||||||
|
cue.setStartTimePos(startTimeUs);
|
||||||
|
mSubtitleList.add(cue);
|
||||||
|
|
||||||
|
Collections.sort(mSubtitleList, new Comparator<SubtitleData>() {
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
}
|
@ -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<SubtitleData> text;
|
||||||
|
|
||||||
|
public TextSubtitle(List<SubtitleData> 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<Cue> getCues(long timeUs) {
|
||||||
|
int index = findTheClosed(timeUs);
|
||||||
|
List<Cue> 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;
|
||||||
|
}
|
||||||
|
}
|
@ -53,6 +53,7 @@ public class MimeTypes {
|
|||||||
public static final String AUDIO_OPUS = BASE_TYPE_AUDIO + "/opus";
|
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_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_ID3 = BASE_TYPE_APPLICATION + "/id3";
|
||||||
public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608";
|
public static final String APPLICATION_EIA608 = BASE_TYPE_APPLICATION + "/eia-608";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user