mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +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_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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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_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() {}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue