Home | History | Annotate | Download | only in media
      1 package android.media;
      2 
      3 import android.content.Context;
      4 import android.media.SubtitleController.Renderer;
      5 import android.os.Handler;
      6 import android.os.Message;
      7 import android.os.Parcel;
      8 import android.util.Log;
      9 
     10 import java.io.BufferedReader;
     11 import java.io.ByteArrayInputStream;
     12 import java.io.IOException;
     13 import java.io.InputStreamReader;
     14 import java.io.Reader;
     15 import java.io.UnsupportedEncodingException;
     16 import java.util.ArrayList;
     17 import java.util.List;
     18 import java.util.Vector;
     19 
     20 /** @hide */
     21 public class SRTRenderer extends Renderer {
     22     private final Context mContext;
     23     private final boolean mRender;
     24     private final Handler mEventHandler;
     25 
     26     private WebVttRenderingWidget mRenderingWidget;
     27 
     28     public SRTRenderer(Context context) {
     29         this(context, null);
     30     }
     31 
     32     SRTRenderer(Context mContext, Handler mEventHandler) {
     33         this.mContext = mContext;
     34         this.mRender = (mEventHandler == null);
     35         this.mEventHandler = mEventHandler;
     36     }
     37 
     38     @Override
     39     public boolean supports(MediaFormat format) {
     40         if (format.containsKey(MediaFormat.KEY_MIME)) {
     41             if (!format.getString(MediaFormat.KEY_MIME)
     42                     .equals(MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP)) {
     43                 return false;
     44             };
     45             return mRender == (format.getInteger(MediaFormat.KEY_IS_TIMED_TEXT, 0) == 0);
     46         }
     47         return false;
     48     }
     49 
     50     @Override
     51     public SubtitleTrack createTrack(MediaFormat format) {
     52         if (mRender && mRenderingWidget == null) {
     53             mRenderingWidget = new WebVttRenderingWidget(mContext);
     54         }
     55 
     56         if (mRender) {
     57             return new SRTTrack(mRenderingWidget, format);
     58         } else {
     59             return new SRTTrack(mEventHandler, format);
     60         }
     61     }
     62 }
     63 
     64 class SRTTrack extends WebVttTrack {
     65     private static final int MEDIA_TIMED_TEXT = 99;   // MediaPlayer.MEDIA_TIMED_TEXT
     66     private static final int KEY_STRUCT_TEXT = 16;    // TimedText.KEY_STRUCT_TEXT
     67     private static final int KEY_START_TIME = 7;      // TimedText.KEY_START_TIME
     68     private static final int KEY_LOCAL_SETTING = 102; // TimedText.KEY_START_TIME
     69 
     70     private static final String TAG = "SRTTrack";
     71     private final Handler mEventHandler;
     72 
     73     SRTTrack(WebVttRenderingWidget renderingWidget, MediaFormat format) {
     74         super(renderingWidget, format);
     75         mEventHandler = null;
     76     }
     77 
     78     SRTTrack(Handler eventHandler, MediaFormat format) {
     79         super(null, format);
     80         mEventHandler = eventHandler;
     81     }
     82 
     83     @Override
     84     protected void onData(SubtitleData data) {
     85         try {
     86             TextTrackCue cue = new TextTrackCue();
     87             cue.mStartTimeMs = data.getStartTimeUs() / 1000;
     88             cue.mEndTimeMs = (data.getStartTimeUs() + data.getDurationUs()) / 1000;
     89 
     90             String paragraph;
     91             paragraph = new String(data.getData(), "UTF-8");
     92             String[] lines = paragraph.split("\\r?\\n");
     93             cue.mLines = new TextTrackCueSpan[lines.length][];
     94 
     95             int i = 0;
     96             for (String line : lines) {
     97                 TextTrackCueSpan[] span = new TextTrackCueSpan[] {
     98                     new TextTrackCueSpan(line, -1)
     99                 };
    100                 cue.mLines[i++] = span;
    101             }
    102 
    103             addCue(cue);
    104         } catch (UnsupportedEncodingException e) {
    105             Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
    106         }
    107     }
    108 
    109     @Override
    110     public void onData(byte[] data, boolean eos, long runID) {
    111         // TODO make reentrant
    112         try {
    113             Reader r = new InputStreamReader(new ByteArrayInputStream(data), "UTF-8");
    114             BufferedReader br = new BufferedReader(r);
    115 
    116             String header;
    117             while ((header = br.readLine()) != null) {
    118                 // discard subtitle number
    119                 header  = br.readLine();
    120                 if (header == null) {
    121                     break;
    122                 }
    123 
    124                 TextTrackCue cue = new TextTrackCue();
    125                 String[] startEnd = header.split("-->");
    126                 cue.mStartTimeMs = parseMs(startEnd[0]);
    127                 cue.mEndTimeMs = parseMs(startEnd[1]);
    128 
    129                 String s;
    130                 List<String> paragraph = new ArrayList<String>();
    131                 while (!((s = br.readLine()) == null || s.trim().equals(""))) {
    132                     paragraph.add(s);
    133                 }
    134 
    135                 int i = 0;
    136                 cue.mLines = new TextTrackCueSpan[paragraph.size()][];
    137                 cue.mStrings = paragraph.toArray(new String[0]);
    138                 for (String line : paragraph) {
    139                     TextTrackCueSpan[] span = new TextTrackCueSpan[] {
    140                             new TextTrackCueSpan(line, -1)
    141                     };
    142                     cue.mStrings[i] = line;
    143                     cue.mLines[i++] = span;
    144                 }
    145 
    146                 addCue(cue);
    147             }
    148 
    149         } catch (UnsupportedEncodingException e) {
    150             Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e);
    151         } catch (IOException ioe) {
    152             // shouldn't happen
    153             Log.e(TAG, ioe.getMessage(), ioe);
    154         }
    155     }
    156 
    157     @Override
    158     public void updateView(Vector<Cue> activeCues) {
    159         if (getRenderingWidget() != null) {
    160             super.updateView(activeCues);
    161             return;
    162         }
    163 
    164         if (mEventHandler == null) {
    165             return;
    166         }
    167 
    168         for (Cue cue : activeCues) {
    169             TextTrackCue ttc = (TextTrackCue) cue;
    170 
    171             Parcel parcel = Parcel.obtain();
    172             parcel.writeInt(KEY_LOCAL_SETTING);
    173             parcel.writeInt(KEY_START_TIME);
    174             parcel.writeInt((int) cue.mStartTimeMs);
    175 
    176             parcel.writeInt(KEY_STRUCT_TEXT);
    177             StringBuilder sb = new StringBuilder();
    178             for (String line : ttc.mStrings) {
    179                 sb.append(line).append('\n');
    180             }
    181 
    182             byte[] buf = sb.toString().getBytes();
    183             parcel.writeInt(buf.length);
    184             parcel.writeByteArray(buf);
    185 
    186             Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, 0 /* arg1 */, 0 /* arg2 */,
    187                     parcel);
    188             mEventHandler.sendMessage(msg);
    189         }
    190         activeCues.clear();
    191     }
    192 
    193     private static long parseMs(String in) {
    194         long hours = Long.parseLong(in.split(":")[0].trim());
    195         long minutes = Long.parseLong(in.split(":")[1].trim());
    196         long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim());
    197         long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim());
    198 
    199         return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies;
    200 
    201     }
    202 }
    203