Files
frameworks_base/media/java/android/media/SRTRenderer.java
Dariusz Budner b5f801f458 Do not ignore runID in SRTTrack
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
2019-03-15 14:41:43 -07:00

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;
}
}