13cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihpackage android.media;
23cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
33cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.content.Context;
43cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.media.SubtitleController.Renderer;
53cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.os.Handler;
63cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.os.Message;
73cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.os.Parcel;
83cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport android.util.Log;
93cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
103cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.BufferedReader;
113cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.ByteArrayInputStream;
123cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.IOException;
133cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.InputStreamReader;
143cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.Reader;
153cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.io.UnsupportedEncodingException;
163cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.util.ArrayList;
173cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.util.List;
183cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihimport java.util.Vector;
193cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
203cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih/** @hide */
213cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihpublic class SRTRenderer extends Renderer {
223cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private final Context mContext;
233cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private final boolean mRender;
243cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private final Handler mEventHandler;
253cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
263cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private WebVttRenderingWidget mRenderingWidget;
273cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
283cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    public SRTRenderer(Context context) {
293cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        this(context, null);
303cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
313cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
323cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    SRTRenderer(Context mContext, Handler mEventHandler) {
333cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        this.mContext = mContext;
343cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        this.mRender = (mEventHandler == null);
353cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        this.mEventHandler = mEventHandler;
363cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
373cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
383cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    @Override
393cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    public boolean supports(MediaFormat format) {
403cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (format.containsKey(MediaFormat.KEY_MIME)) {
413cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            if (!format.getString(MediaFormat.KEY_MIME)
423cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    .equals(MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP)) {
433cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                return false;
443cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            };
453cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            return mRender == (format.getInteger(MediaFormat.KEY_IS_TIMED_TEXT, 0) == 0);
463cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
473cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        return false;
483cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
493cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
503cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    @Override
513cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    public SubtitleTrack createTrack(MediaFormat format) {
523cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (mRender && mRenderingWidget == null) {
533cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            mRenderingWidget = new WebVttRenderingWidget(mContext);
543cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
553cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
563cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (mRender) {
573cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            return new SRTTrack(mRenderingWidget, format);
583cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        } else {
593cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            return new SRTTrack(mEventHandler, format);
603cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
613cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
623cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih}
633cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
643cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shihclass SRTTrack extends WebVttTrack {
653cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static final int MEDIA_TIMED_TEXT = 99;   // MediaPlayer.MEDIA_TIMED_TEXT
663cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static final int KEY_STRUCT_TEXT = 16;    // TimedText.KEY_STRUCT_TEXT
673cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static final int KEY_START_TIME = 7;      // TimedText.KEY_START_TIME
683cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static final int KEY_LOCAL_SETTING = 102; // TimedText.KEY_START_TIME
693cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
703cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static final String TAG = "SRTTrack";
713cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private final Handler mEventHandler;
723cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
733cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    SRTTrack(WebVttRenderingWidget renderingWidget, MediaFormat format) {
743cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        super(renderingWidget, format);
753cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        mEventHandler = null;
763cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
773cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
783cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    SRTTrack(Handler eventHandler, MediaFormat format) {
793cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        super(null, format);
803cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        mEventHandler = eventHandler;
813cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
823cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
833cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    @Override
843cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    protected void onData(SubtitleData data) {
853cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        try {
863cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            TextTrackCue cue = new TextTrackCue();
873cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            cue.mStartTimeMs = data.getStartTimeUs() / 1000;
883cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            cue.mEndTimeMs = (data.getStartTimeUs() + data.getDurationUs()) / 1000;
893cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
903cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            String paragraph;
913cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            paragraph = new String(data.getData(), "UTF-8");
923cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            String[] lines = paragraph.split("\\r?\\n");
933cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            cue.mLines = new TextTrackCueSpan[lines.length][];
943cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
953cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            int i = 0;
963cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            for (String line : lines) {
973cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                TextTrackCueSpan[] span = new TextTrackCueSpan[] {
983cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    new TextTrackCueSpan(line, -1)
993cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                };
1003cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                cue.mLines[i++] = span;
1013cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            }
1023cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1033cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            addCue(cue);
1043cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        } catch (UnsupportedEncodingException e) {
1053cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
1063cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1073cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
1083cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1093cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    @Override
1103cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    public void onData(byte[] data, boolean eos, long runID) {
1113cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        // TODO make reentrant
1123cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        try {
1133cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            Reader r = new InputStreamReader(new ByteArrayInputStream(data), "UTF-8");
1143cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            BufferedReader br = new BufferedReader(r);
1153cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1163cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            String header;
1173cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            while ((header = br.readLine()) != null) {
1183cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                // discard subtitle number
1193cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                header  = br.readLine();
1203cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                if (header == null) {
1213cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    break;
1223cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                }
1233cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1243cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                TextTrackCue cue = new TextTrackCue();
1253cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                String[] startEnd = header.split("-->");
1263cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                cue.mStartTimeMs = parseMs(startEnd[0]);
1273cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                cue.mEndTimeMs = parseMs(startEnd[1]);
1283cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1293cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                String s;
1303cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                List<String> paragraph = new ArrayList<String>();
1313cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                while (!((s = br.readLine()) == null || s.trim().equals(""))) {
1323cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    paragraph.add(s);
1333cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                }
1343cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1353cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                int i = 0;
1363cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                cue.mLines = new TextTrackCueSpan[paragraph.size()][];
1373cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                cue.mStrings = paragraph.toArray(new String[0]);
1383cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                for (String line : paragraph) {
1393cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    TextTrackCueSpan[] span = new TextTrackCueSpan[] {
1403cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                            new TextTrackCueSpan(line, -1)
1413cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    };
1423cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    cue.mStrings[i] = line;
1433cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                    cue.mLines[i++] = span;
1443cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                }
1453cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1463cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                addCue(cue);
1473cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            }
1483cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1493cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        } catch (UnsupportedEncodingException e) {
1503cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
1513cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        } catch (IOException ioe) {
1523cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            // shouldn't happen
1533cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            Log.e(TAG, ioe.getMessage(), ioe);
1543cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1553cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
1563cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1573cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    @Override
1583cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    public void updateView(Vector<Cue> activeCues) {
1593cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (getRenderingWidget() != null) {
1603cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            super.updateView(activeCues);
1613cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            return;
1623cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1633cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1643cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        if (mEventHandler == null) {
1653cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            return;
1663cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1673cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1683cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        for (Cue cue : activeCues) {
1693cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            TextTrackCue ttc = (TextTrackCue) cue;
1703cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1713cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            Parcel parcel = Parcel.obtain();
1723cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeInt(KEY_LOCAL_SETTING);
1733cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeInt(KEY_START_TIME);
1743cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeInt((int) cue.mStartTimeMs);
1753cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1763cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeInt(KEY_STRUCT_TEXT);
1773cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            StringBuilder sb = new StringBuilder();
1783cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            for (String line : ttc.mStrings) {
1793cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih                sb.append(line).append('\n');
1803cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            }
1813cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1823cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            byte[] buf = sb.toString().getBytes();
1833cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeInt(buf.length);
1843cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            parcel.writeByteArray(buf);
1853cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
18600d6b601e6257d86b173f743d76396ec3183a5e8Neil Fuller            Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */,
18700d6b601e6257d86b173f743d76396ec3183a5e8Neil Fuller                    parcel);
1883cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih            mEventHandler.sendMessage(msg);
1893cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        }
1903cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        activeCues.clear();
1913cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
1923cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1933cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    private static long parseMs(String in) {
1943cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        long hours = Long.parseLong(in.split(":")[0].trim());
1953cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        long minutes = Long.parseLong(in.split(":")[1].trim());
1963cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim());
1973cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim());
1983cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
1993cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih        return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies;
2003cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih
2013cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih    }
2023cdf7c5b622a8fbb20410736bdab5888d0e1873cRobert Shih}
203