Set mRunID parameter when adding cue to the list because of mRunID value depends if past cues are removed from the list during active cues update. Keeping past cues on the list makes it possible to find and set proper active cue after seeking backward. Bug: 68034458 Test: play video with srt subtitle and seek Change-Id: I8439840d5925607127fadc1082913d9d91f688af
204 lines
6.6 KiB
Java
204 lines
6.6 KiB
Java
package android.media;
|
|
|
|
import android.content.Context;
|
|
import android.media.SubtitleController.Renderer;
|
|
import android.os.Handler;
|
|
import android.os.Message;
|
|
import android.os.Parcel;
|
|
import android.util.Log;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.io.Reader;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Vector;
|
|
|
|
/** @hide */
|
|
public class SRTRenderer extends Renderer {
|
|
private final Context mContext;
|
|
private final boolean mRender;
|
|
private final Handler mEventHandler;
|
|
|
|
private WebVttRenderingWidget mRenderingWidget;
|
|
|
|
public SRTRenderer(Context context) {
|
|
this(context, null);
|
|
}
|
|
|
|
SRTRenderer(Context mContext, Handler mEventHandler) {
|
|
this.mContext = mContext;
|
|
this.mRender = (mEventHandler == null);
|
|
this.mEventHandler = mEventHandler;
|
|
}
|
|
|
|
@Override
|
|
public boolean supports(MediaFormat format) {
|
|
if (format.containsKey(MediaFormat.KEY_MIME)) {
|
|
if (!format.getString(MediaFormat.KEY_MIME)
|
|
.equals(MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP)) {
|
|
return false;
|
|
};
|
|
return mRender == (format.getInteger(MediaFormat.KEY_IS_TIMED_TEXT, 0) == 0);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public SubtitleTrack createTrack(MediaFormat format) {
|
|
if (mRender && mRenderingWidget == null) {
|
|
mRenderingWidget = new WebVttRenderingWidget(mContext);
|
|
}
|
|
|
|
if (mRender) {
|
|
return new SRTTrack(mRenderingWidget, format);
|
|
} else {
|
|
return new SRTTrack(mEventHandler, format);
|
|
}
|
|
}
|
|
}
|
|
|
|
class SRTTrack extends WebVttTrack {
|
|
private static final int MEDIA_TIMED_TEXT = 99; // MediaPlayer.MEDIA_TIMED_TEXT
|
|
private static final int KEY_STRUCT_TEXT = 16; // TimedText.KEY_STRUCT_TEXT
|
|
private static final int KEY_START_TIME = 7; // TimedText.KEY_START_TIME
|
|
private static final int KEY_LOCAL_SETTING = 102; // TimedText.KEY_START_TIME
|
|
|
|
private static final String TAG = "SRTTrack";
|
|
private final Handler mEventHandler;
|
|
|
|
SRTTrack(WebVttRenderingWidget renderingWidget, MediaFormat format) {
|
|
super(renderingWidget, format);
|
|
mEventHandler = null;
|
|
}
|
|
|
|
SRTTrack(Handler eventHandler, MediaFormat format) {
|
|
super(null, format);
|
|
mEventHandler = eventHandler;
|
|
}
|
|
|
|
@Override
|
|
protected void onData(SubtitleData data) {
|
|
try {
|
|
TextTrackCue cue = new TextTrackCue();
|
|
cue.mStartTimeMs = data.getStartTimeUs() / 1000;
|
|
cue.mEndTimeMs = (data.getStartTimeUs() + data.getDurationUs()) / 1000;
|
|
|
|
String paragraph;
|
|
paragraph = new String(data.getData(), "UTF-8");
|
|
String[] lines = paragraph.split("\\r?\\n");
|
|
cue.mLines = new TextTrackCueSpan[lines.length][];
|
|
|
|
int i = 0;
|
|
for (String line : lines) {
|
|
TextTrackCueSpan[] span = new TextTrackCueSpan[] {
|
|
new TextTrackCueSpan(line, -1)
|
|
};
|
|
cue.mLines[i++] = span;
|
|
}
|
|
|
|
addCue(cue);
|
|
} catch (UnsupportedEncodingException e) {
|
|
Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onData(byte[] data, boolean eos, long runID) {
|
|
// TODO make reentrant
|
|
try {
|
|
Reader r = new InputStreamReader(new ByteArrayInputStream(data), "UTF-8");
|
|
BufferedReader br = new BufferedReader(r);
|
|
|
|
String header;
|
|
while ((header = br.readLine()) != null) {
|
|
// discard subtitle number
|
|
header = br.readLine();
|
|
if (header == null) {
|
|
break;
|
|
}
|
|
|
|
TextTrackCue cue = new TextTrackCue();
|
|
String[] startEnd = header.split("-->");
|
|
cue.mStartTimeMs = parseMs(startEnd[0]);
|
|
cue.mEndTimeMs = parseMs(startEnd[1]);
|
|
cue.mRunID = runID;
|
|
|
|
String s;
|
|
List<String> paragraph = new ArrayList<String>();
|
|
while (!((s = br.readLine()) == null || s.trim().equals(""))) {
|
|
paragraph.add(s);
|
|
}
|
|
|
|
int i = 0;
|
|
cue.mLines = new TextTrackCueSpan[paragraph.size()][];
|
|
cue.mStrings = paragraph.toArray(new String[0]);
|
|
for (String line : paragraph) {
|
|
TextTrackCueSpan[] span = new TextTrackCueSpan[] {
|
|
new TextTrackCueSpan(line, -1)
|
|
};
|
|
cue.mStrings[i] = line;
|
|
cue.mLines[i++] = span;
|
|
}
|
|
|
|
addCue(cue);
|
|
}
|
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
|
|
} catch (IOException ioe) {
|
|
// shouldn't happen
|
|
Log.e(TAG, ioe.getMessage(), ioe);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void updateView(Vector<Cue> activeCues) {
|
|
if (getRenderingWidget() != null) {
|
|
super.updateView(activeCues);
|
|
return;
|
|
}
|
|
|
|
if (mEventHandler == null) {
|
|
return;
|
|
}
|
|
|
|
for (Cue cue : activeCues) {
|
|
TextTrackCue ttc = (TextTrackCue) cue;
|
|
|
|
Parcel parcel = Parcel.obtain();
|
|
parcel.writeInt(KEY_LOCAL_SETTING);
|
|
parcel.writeInt(KEY_START_TIME);
|
|
parcel.writeInt((int) cue.mStartTimeMs);
|
|
|
|
parcel.writeInt(KEY_STRUCT_TEXT);
|
|
StringBuilder sb = new StringBuilder();
|
|
for (String line : ttc.mStrings) {
|
|
sb.append(line).append('\n');
|
|
}
|
|
|
|
byte[] buf = sb.toString().getBytes();
|
|
parcel.writeInt(buf.length);
|
|
parcel.writeByteArray(buf);
|
|
|
|
Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */,
|
|
parcel);
|
|
mEventHandler.sendMessage(msg);
|
|
}
|
|
activeCues.clear();
|
|
}
|
|
|
|
private static long parseMs(String in) {
|
|
long hours = Long.parseLong(in.split(":")[0].trim());
|
|
long minutes = Long.parseLong(in.split(":")[1].trim());
|
|
long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim());
|
|
long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim());
|
|
|
|
return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies;
|
|
|
|
}
|
|
}
|