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:
aquilescanta 2015-12-11 09:53:27 -08:00 committed by Oliver Woodman
parent 63dc769bff
commit 80bd91e636
6 changed files with 173 additions and 0 deletions

View file

@ -110,6 +110,7 @@ import java.util.List;
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 static final int TYPE_wvtt = Util.getIntegerCodeForString("wvtt");
public static final int TYPE_stpp = Util.getIntegerCodeForString("stpp");
public static final int TYPE_samr = Util.getIntegerCodeForString("samr");
public static final int TYPE_sawb = Util.getIntegerCodeForString("sawb");

View file

@ -462,6 +462,9 @@ import java.util.List;
} else if (childAtomType == Atom.TYPE_tx3g) {
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
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) {
out.mediaFormat = MediaFormat.createTextFormat(Integer.toString(trackId),
MimeTypes.APPLICATION_TTML, MediaFormat.NO_VALUE, durationUs, language,

View file

@ -87,6 +87,13 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement
} catch (ClassNotFoundException e) {
// 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 {
DEFAULT_PARSER_CLASSES.add(
Class.forName("com.google.android.exoplayer.text.subrip.SubripParser")

View file

@ -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.");
}
}

View file

@ -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();
}
}

View file

@ -65,6 +65,7 @@ public final class MimeTypes {
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_TX3G = BASE_TYPE_APPLICATION + "/x-quicktime-tx3g";
public static final String APPLICATION_MP4VTT = BASE_TYPE_APPLICATION + "/x-mp4vtt";
private MimeTypes() {}