Commit ac988740 by Julian Cable

move header into track.output.format object and then use it in the decoder create method.

This doesn't make a stateless decoder but it is a step in the right direction.
Note that subtitles are not rendering. Is there something wrong with the timecodes?
parent ef2d7c7f
...@@ -19,7 +19,7 @@ buildscript { ...@@ -19,7 +19,7 @@ buildscript {
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.novoda:bintray-release:0.3.4' classpath 'com.novoda:bintray-release:0.3.4'
} }
} }
......
...@@ -352,6 +352,10 @@ ...@@ -352,6 +352,10 @@
"name": "Misc", "name": "Misc",
"samples": [ "samples": [
{ {
"name": "Watashi ga Motete Dousunda - 01 SSA",
"uri": "http://192.168.1.133:8080/Watashi ga Motete Dousunda - 01.mkv"
},
{
"name": "Dizzy", "name": "Dizzy",
"uri": "http://html5demos.com/assets/dizzy.mp4" "uri": "http://html5demos.com/assets/dizzy.mp4"
}, },
......
...@@ -305,9 +305,18 @@ public final class Format implements Parcelable { ...@@ -305,9 +305,18 @@ public final class Format implements Parcelable {
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs, public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate, @C.SelectionFlags int selectionFlags, String language, int bitrate, @C.SelectionFlags int selectionFlags, String language,
int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs) { int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs) {
return createTextSampleFormat(id, sampleMimeType, codecs, bitrate, null, selectionFlags, language,
NO_VALUE, drmInitData, subsampleOffsetUs);
}
public static Format createTextSampleFormat(String id, String sampleMimeType, String codecs,
int bitrate,
List<byte[]> initializationData,
@C.SelectionFlags int selectionFlags, String language,
int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs) {
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE, return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, null, NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
drmInitData, null); drmInitData, null);
} }
......
...@@ -1110,13 +1110,8 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1110,13 +1110,8 @@ public final class MatroskaExtractor implements Extractor {
} }
private void writeSSASample(Track track) { private void writeSSASample(Track track) {
StringBuffer s = new StringBuffer(); String s = SSADecoder.buildDialogue(track.ssaSample, blockDurationUs);
if(track.ssaHeader != null) { ParsableByteArray out = new ParsableByteArray(s.getBytes());
// header contains the original format but the Matroska encoder changes this.
SSADecoder.writeMangledHeader(s, track.ssaHeader);
}
SSADecoder.buildDialogue(s, track.ssaSample, blockDurationUs);
ParsableByteArray out = new ParsableByteArray(s.toString().getBytes());
track.output.sampleData(out, out.limit()); track.output.sampleData(out, out.limit());
sampleBytesWritten += out.limit(); sampleBytesWritten += out.limit();
} }
...@@ -1382,7 +1377,6 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1382,7 +1377,6 @@ public final class MatroskaExtractor implements Extractor {
public boolean flagForced; public boolean flagForced;
public boolean flagDefault = true; public boolean flagDefault = true;
private String language = "eng"; private String language = "eng";
public byte[] ssaHeader = null;
public String ssaSample = null; public String ssaSample = null;
// Set when the output is initialized. nalUnitLengthFieldLength is only set for H264/H265. // Set when the output is initialized. nalUnitLengthFieldLength is only set for H264/H265.
...@@ -1504,7 +1498,7 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1504,7 +1498,7 @@ public final class MatroskaExtractor implements Extractor {
break; break;
case CODEC_ID_ASS: case CODEC_ID_ASS:
mimeType = MimeTypes.TEXT_SSA; mimeType = MimeTypes.TEXT_SSA;
ssaHeader = codecPrivate; initializationData = Collections.singletonList(codecPrivate);
break; break;
case CODEC_ID_VOBSUB: case CODEC_ID_VOBSUB:
mimeType = MimeTypes.APPLICATION_VOBSUB; mimeType = MimeTypes.APPLICATION_VOBSUB;
...@@ -1548,8 +1542,7 @@ public final class MatroskaExtractor implements Extractor { ...@@ -1548,8 +1542,7 @@ public final class MatroskaExtractor implements Extractor {
Format.NO_VALUE, initializationData, language, drmInitData); Format.NO_VALUE, initializationData, language, drmInitData);
} else if (MimeTypes.TEXT_SSA.equals(mimeType)) { } else if (MimeTypes.TEXT_SSA.equals(mimeType)) {
format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null, format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null,
Format.NO_VALUE, selectionFlags, language, drmInitData); Format.NO_VALUE, initializationData, selectionFlags, language, Format.NO_VALUE, drmInitData, Format.NO_VALUE);
output.track(number).sampleData(new ParsableByteArray(codecPrivate), codecPrivate.length);
} else { } else {
throw new ParserException("Unexpected MIME type."); throw new ParserException("Unexpected MIME type.");
} }
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.text.cea.Cea608Decoder; import com.google.android.exoplayer2.text.cea.Cea608Decoder;
import com.google.android.exoplayer2.text.ssa.SSADecoder;
import com.google.android.exoplayer2.text.subrip.SubripDecoder; import com.google.android.exoplayer2.text.subrip.SubripDecoder;
import com.google.android.exoplayer2.text.ttml.TtmlDecoder; import com.google.android.exoplayer2.text.ttml.TtmlDecoder;
import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder; import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder;
...@@ -24,6 +25,8 @@ import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder; ...@@ -24,6 +25,8 @@ import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder;
import com.google.android.exoplayer2.text.webvtt.WebvttDecoder; import com.google.android.exoplayer2.text.webvtt.WebvttDecoder;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import static android.R.attr.mimeType;
/** /**
* A factory for {@link SubtitleDecoder} instances. * A factory for {@link SubtitleDecoder} instances.
*/ */
...@@ -75,10 +78,15 @@ public interface SubtitleDecoderFactory { ...@@ -75,10 +78,15 @@ public interface SubtitleDecoderFactory {
if (clazz == null) { if (clazz == null) {
throw new IllegalArgumentException("Attempted to create decoder for unsupported format"); throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
} }
if(clazz == SSADecoder.class) {
return clazz.asSubclass(SubtitleDecoder.class).getConstructor(byte[].class)
.newInstance(format.initializationData.get(0));
}
if (clazz == Cea608Decoder.class) { if (clazz == Cea608Decoder.class) {
return clazz.asSubclass(SubtitleDecoder.class).getConstructor(String.class, Integer.TYPE) return clazz.asSubclass(SubtitleDecoder.class).getConstructor(String.class, Integer.TYPE)
.newInstance(format.sampleMimeType, format.accessibilityChannel); .newInstance(format.sampleMimeType, format.accessibilityChannel);
} else { }
else {
return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance(); return clazz.asSubclass(SubtitleDecoder.class).getConstructor().newInstance();
} }
} catch (Exception e) { } catch (Exception e) {
......
...@@ -11,7 +11,9 @@ import java.util.HashMap; ...@@ -11,7 +11,9 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import static android.R.attr.data;
import static android.R.attr.subtitle; import static android.R.attr.subtitle;
import static android.R.attr.x;
/** /**
* Created by cablej01 on 26/12/2016. * Created by cablej01 on 26/12/2016.
...@@ -58,21 +60,33 @@ import static android.R.attr.subtitle; ...@@ -58,21 +60,33 @@ import static android.R.attr.subtitle;
than the previous one, the value passed to getCues will go down. than the previous one, the value passed to getCues will go down.
*/ */
/**
* A {@link SimpleSubtitleDecoder} for ASS/SSA.
*/
public class SSADecoder extends SimpleSubtitleDecoder { public class SSADecoder extends SimpleSubtitleDecoder {
private static final String TAG = "SSADecoder"; private static final String TAG = "SSADecoder";
private static String defaultDialogueFormat = "Start, End, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text"; private static String defaultDialogueFormat = "Start, End, ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text";
private static String defaultStyleFormat = "Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding"; private static String defaultStyleFormat = "Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding";
private String[] dialogueFormat; private String[] dialogueFormat;
private String[] styleFormat; private String[] styleFormat;
private Map<String,Style> styles = new HashMap<>(); private Map<String,Style> styles = new HashMap<>();
// 0,0,Watamote-internal/narrator,Serinuma,0000,0000,0000, ,The prince should be with the princess.
// , ,style ,name ,L ,R, V ,Effect, text
public SSADecoder() { public SSADecoder() {
super("SSADecoder"); super("SSADecoder");
dialogueFormat = parseKeys(defaultDialogueFormat); dialogueFormat = parseKeys(defaultDialogueFormat);
styleFormat = parseKeys(defaultStyleFormat); styleFormat = parseKeys(defaultStyleFormat);
} }
public SSADecoder(byte[] header) {
super("SSADecoder");
styleFormat = parseKeys(defaultStyleFormat);
decodeHeader(header, header.length);
// put back the Matroska format
dialogueFormat = parseKeys(defaultDialogueFormat);
}
/** /**
* Decodes data into a {@link SSASubtitle}. * Decodes data into a {@link SSASubtitle}.
* *
...@@ -183,22 +197,8 @@ public class SSADecoder extends SimpleSubtitleDecoder { ...@@ -183,22 +197,8 @@ public class SSADecoder extends SimpleSubtitleDecoder {
return result; return result;
} }
public static void writeMangledHeader(StringBuffer s, byte[] data){ public static String buildDialogue(String data, long durationUs) {
// header contains the original format but the Matroska encoder changes this. StringBuffer s = new StringBuffer();
// we won't need anything after the [Events] line
try {
String header = new String(data, "UTF-8").split("\\[Events]")[0];
s.append(header);
}
catch (UnsupportedEncodingException e) {
// we know this can't happen
}
s.append("[Events]\n");
s.append(defaultDialogueFormat);
s.append("\n");
}
public static void buildDialogue(StringBuffer s, String data, long durationUs) {
s.append("Dialogue: "); s.append("Dialogue: ");
s.append(SSADecoder.formatTimeCode(0)); // blockTimeUs s.append(SSADecoder.formatTimeCode(0)); // blockTimeUs
s.append(","); s.append(",");
...@@ -210,6 +210,7 @@ public class SSADecoder extends SimpleSubtitleDecoder { ...@@ -210,6 +210,7 @@ public class SSADecoder extends SimpleSubtitleDecoder {
s.append(","); s.append(",");
s.append(data); s.append(data);
s.append("\n"); s.append("\n");
return s.toString();
} }
public static String formatTimeCode(long tc_us) { public static String formatTimeCode(long tc_us) {
......
...@@ -11,6 +11,8 @@ import java.util.Collections; ...@@ -11,6 +11,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static android.R.attr.start;
/** /**
* Created by cablej01 on 26/12/2016. * Created by cablej01 on 26/12/2016.
*/ */
...@@ -91,7 +93,8 @@ public class SSASubtitle implements Subtitle { ...@@ -91,7 +93,8 @@ public class SSASubtitle implements Subtitle {
String effect = ev.get("effect"); String effect = ev.get("effect");
String text = ev.get("text").replaceAll("\\\\N", "\n"); String text = ev.get("text").replaceAll("\\\\N", "\n");
String simpleText = text.replaceAll("\\{[^{]*\\}", ""); String simpleText = text.replaceAll("\\{[^{]*\\}", "");
Cue cue = new SSACue(text, style, layer, effect); //Cue cue = new SSACue(text, style, layer, effect);
Cue cue = new Cue(simpleText);
long start = SSADecoder.parseTimecode(ev.get("start")); long start = SSADecoder.parseTimecode(ev.get("start"));
cueTimesUs.add(start); cueTimesUs.add(start);
cues.add(cue); cues.add(cue);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment