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