mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add "Webvtt embedded in MP4" support
Issue: #689 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=110003312
This commit is contained in:
parent
63dc769bff
commit
80bd91e636
6 changed files with 173 additions and 0 deletions
|
|
@ -110,6 +110,7 @@ import java.util.List;
|
||||||
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 static final int TYPE_tx3g = Util.getIntegerCodeForString("tx3g");
|
||||||
|
public static final int TYPE_wvtt = Util.getIntegerCodeForString("wvtt");
|
||||||
public static final int TYPE_stpp = Util.getIntegerCodeForString("stpp");
|
public static final int TYPE_stpp = Util.getIntegerCodeForString("stpp");
|
||||||
public static final int TYPE_samr = Util.getIntegerCodeForString("samr");
|
public static final int TYPE_samr = Util.getIntegerCodeForString("samr");
|
||||||
public static final int TYPE_sawb = Util.getIntegerCodeForString("sawb");
|
public static final int TYPE_sawb = Util.getIntegerCodeForString("sawb");
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,9 @@ import java.util.List;
|
||||||
} else if (childAtomType == Atom.TYPE_tx3g) {
|
} else if (childAtomType == Atom.TYPE_tx3g) {
|
||||||
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
|
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
|
||||||
MimeTypes.APPLICATION_TX3G, MediaFormat.NO_VALUE, durationUs, language);
|
MimeTypes.APPLICATION_TX3G, MediaFormat.NO_VALUE, durationUs, language);
|
||||||
|
} else if (childAtomType == Atom.TYPE_wvtt) {
|
||||||
|
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
|
||||||
|
MimeTypes.APPLICATION_MP4VTT, MediaFormat.NO_VALUE, durationUs, language);
|
||||||
} else if (childAtomType == Atom.TYPE_stpp) {
|
} else if (childAtomType == Atom.TYPE_stpp) {
|
||||||
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
|
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
|
||||||
MimeTypes.APPLICATION_TTML, MediaFormat.NO_VALUE, durationUs, language,
|
MimeTypes.APPLICATION_TTML, MediaFormat.NO_VALUE, durationUs, language,
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,13 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
// Parser not found.
|
// Parser not found.
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
DEFAULT_PARSER_CLASSES.add(
|
||||||
|
Class.forName("com.google.android.exoplayer.text.mp4webvtt.Mp4WebvttParser")
|
||||||
|
.asSubclass(SubtitleParser.class));
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
// Parser not found.
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
DEFAULT_PARSER_CLASSES.add(
|
DEFAULT_PARSER_CLASSES.add(
|
||||||
Class.forName("com.google.android.exoplayer.text.subrip.SubripParser")
|
Class.forName("com.google.android.exoplayer.text.subrip.SubripParser")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
/*
|
||||||
|
* 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.mp4webvtt;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.ParserException;
|
||||||
|
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 com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link SubtitleParser} for Webvtt embedded in a Mp4 container file.
|
||||||
|
*/
|
||||||
|
public final class Mp4WebvttParser implements SubtitleParser {
|
||||||
|
|
||||||
|
private static final int BOX_HEADER_SIZE = 8;
|
||||||
|
|
||||||
|
private static final int TYPE_vttc = Util.getIntegerCodeForString("vttc");
|
||||||
|
private static final int TYPE_payl = Util.getIntegerCodeForString("payl");
|
||||||
|
|
||||||
|
private final ParsableByteArray sampleData;
|
||||||
|
private byte[] inputBytesBuffer;
|
||||||
|
|
||||||
|
public Mp4WebvttParser() {
|
||||||
|
sampleData = new ParsableByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canParse(String mimeType) {
|
||||||
|
return MimeTypes.APPLICATION_MP4VTT.equals(mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Subtitle parse(InputStream inputStream) throws IOException {
|
||||||
|
// Webvtt in Mp4 samples have boxes inside of them, so we have to do a traditional box parsing:
|
||||||
|
// first 4 bytes size and then 4 bytes type.
|
||||||
|
int inputStreamByteCount = inputStream.available();
|
||||||
|
if (inputBytesBuffer == null || inputBytesBuffer.length < inputStreamByteCount) {
|
||||||
|
inputBytesBuffer = new byte[inputStreamByteCount];
|
||||||
|
}
|
||||||
|
inputStream.read(inputBytesBuffer, 0, inputStreamByteCount);
|
||||||
|
sampleData.reset(inputBytesBuffer, inputStreamByteCount);
|
||||||
|
List<Cue> resultingCueList = new ArrayList<>();
|
||||||
|
while (sampleData.bytesLeft() > 0) {
|
||||||
|
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
|
||||||
|
throw new ParserException("Incomplete Mp4Webvtt Top Level box header found.");
|
||||||
|
}
|
||||||
|
int boxSize = sampleData.readInt();
|
||||||
|
int boxType = sampleData.readInt();
|
||||||
|
if (boxType == TYPE_vttc) {
|
||||||
|
resultingCueList.add(parseVttCueBox(sampleData));
|
||||||
|
} else {
|
||||||
|
// Peers of the VTTCueBox are still not supported and are skipped.
|
||||||
|
sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Mp4WebvttSubtitle(resultingCueList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Cue parseVttCueBox(ParsableByteArray sampleData) throws IOException {
|
||||||
|
while (sampleData.bytesLeft() > 0) {
|
||||||
|
if (sampleData.bytesLeft() < BOX_HEADER_SIZE) {
|
||||||
|
throw new ParserException("Incomplete vtt cue box header found.");
|
||||||
|
}
|
||||||
|
int boxSize = sampleData.readInt();
|
||||||
|
int boxType = sampleData.readInt();
|
||||||
|
if (boxType == TYPE_payl) {
|
||||||
|
int payloadLength = boxSize - BOX_HEADER_SIZE;
|
||||||
|
String cueText = new String(sampleData.data, sampleData.getPosition(), payloadLength);
|
||||||
|
sampleData.skipBytes(payloadLength);
|
||||||
|
return new Cue(cueText.trim());
|
||||||
|
} else {
|
||||||
|
// Other VTTCueBox children are still not supported and are skipped.
|
||||||
|
sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ParserException("VTTCueBox does not contain mandatory payload box.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* 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.mp4webvtt;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Representation of a Webvtt subtitle embedded in a MP4 container file.
|
||||||
|
*/
|
||||||
|
/* package */ final class Mp4WebvttSubtitle implements Subtitle {
|
||||||
|
|
||||||
|
private final List<Cue> cues;
|
||||||
|
|
||||||
|
public Mp4WebvttSubtitle(List<Cue> cueList) {
|
||||||
|
cues = Collections.unmodifiableList(cueList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNextEventTimeIndex(long timeUs) {
|
||||||
|
return timeUs < 0 ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEventTimeCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEventTime(int index) {
|
||||||
|
Assertions.checkArgument(index == 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLastEventTime() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Cue> getCues(long timeUs) {
|
||||||
|
return timeUs >= 0 ? cues : Collections.<Cue>emptyList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -65,6 +65,7 @@ public final class MimeTypes {
|
||||||
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";
|
public static final String APPLICATION_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
|
||||||
|
public static final String APPLICATION_MP4VTT = BASE_TYPE_APPLICATION + "/x-mp4vtt";
|
||||||
|
|
||||||
private MimeTypes() {}
|
private MimeTypes() {}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue