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 final int _ = 0; 169 for (Cue cue : activeCues) { 170 TextTrackCue ttc = (TextTrackCue) cue; 171 172 Parcel parcel = Parcel.obtain(); 173 parcel.writeInt(KEY_LOCAL_SETTING); 174 parcel.writeInt(KEY_START_TIME); 175 parcel.writeInt((int) cue.mStartTimeMs); 176 177 parcel.writeInt(KEY_STRUCT_TEXT); 178 StringBuilder sb = new StringBuilder(); 179 for (String line : ttc.mStrings) { 180 sb.append(line).append('\n'); 181 } 182 183 byte[] buf = sb.toString().getBytes(); 184 parcel.writeInt(buf.length); 185 parcel.writeByteArray(buf); 186 187 Message msg = mEventHandler.obtainMessage(MEDIA_TIMED_TEXT, _, _, 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