/demoApplication/src/main/java/com/sonymobile/android/media/testmediaplayer/subtitles/SubtitleRenderer.java

https://github.com/sonyxperiadev/MultimediaForAndroidLibrary · Java · 239 lines · 180 code · 44 blank · 15 comment · 17 complexity · b21c7ecf3ce41a8900417a53d9489d68 MD5 · raw file

  1. /*
  2. * Copyright (C) 2014 Sony Mobile Communications Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.sonymobile.android.media.testmediaplayer.subtitles;
  17. import com.sonymobile.android.media.MediaPlayer;
  18. import com.sonymobile.android.media.testmediaplayer.PlayerConfiguration;
  19. import com.sonymobile.android.media.testmediaplayer.subtitles.ttml.TtmlData;
  20. import com.sonymobile.android.media.testmediaplayer.subtitles.ttml.TtmlParser;
  21. import com.sonymobile.android.media.testmediaplayer.subtitles.ttml.TtmlSubtitle;
  22. import android.os.Handler;
  23. import android.os.HandlerThread;
  24. import android.os.Looper;
  25. import android.os.Message;
  26. import android.os.SystemClock;
  27. import android.util.Log;
  28. import android.widget.TextView;
  29. import java.io.IOException;
  30. import java.io.InputStream;
  31. import java.util.ArrayList;
  32. import java.util.HashMap;
  33. import java.util.List;
  34. public final class SubtitleRenderer {
  35. private static final boolean LOGS_ENABLED = PlayerConfiguration.DEBUG || false;
  36. private static final int MSG_RENDER_TEXT = 1;
  37. private static final int MSG_RENDER_NOTHING = 2;
  38. private static final int FIFTY_MS = 50;
  39. private static final String TAG = "SubtitleRenderer";
  40. private final TextView mTextView;
  41. private final TtmlParser mTtmlParser;
  42. private long mBaseTimeMs;
  43. private int mSubtitleIndex = 0;
  44. private long mCurrentTimeMs;
  45. private HashMap<Integer, TtmlData> mStringsAtTimes;
  46. private String mCurrentText;
  47. private List<TimeAndPosition> mSeekList;
  48. private final EventHandler mHandler;
  49. private final RenderHandler mRenderer;
  50. private final HandlerThread mHandlerThread;
  51. private final HandlerThread mRenderThread;
  52. private final MediaPlayer mMediaPlayer;
  53. public SubtitleRenderer(TextView textview, Looper looper, MediaPlayer mp) {
  54. mHandlerThread = new HandlerThread("SubtitleHandlerThread");
  55. mHandlerThread.start();
  56. mRenderThread = new HandlerThread("SubtitleRenderThread");
  57. mRenderThread.start();
  58. mTextView = textview;
  59. mHandler = new EventHandler(mHandlerThread.getLooper());
  60. mRenderer = new RenderHandler(looper);
  61. mTtmlParser = new TtmlParser();
  62. mMediaPlayer = mp;
  63. }
  64. public void parseSubtitle(InputStream is) {
  65. try {
  66. mBaseTimeMs = 0;
  67. mSeekList = new ArrayList<>();
  68. TtmlSubtitle ttmlSubtitle = (TtmlSubtitle)mTtmlParser.parse(is, "UTF-8", 0);
  69. mStringsAtTimes = new HashMap<>();
  70. if (LOGS_ENABLED) Log.d(TAG, "eventcounters: " + ttmlSubtitle.getEventTimeCount());
  71. int maxCount = ttmlSubtitle.getEventTimeCount();
  72. for (int i = 0; i < maxCount; i++) {
  73. if (LOGS_ENABLED) Log.d(TAG, "Text: " +
  74. ttmlSubtitle.getText(ttmlSubtitle.getEventTime(i)) + " EventTime: "
  75. + ttmlSubtitle.getEventTime(i) + " NextEvent: "
  76. + ttmlSubtitle.getNextEventTimeIndex(ttmlSubtitle.getEventTime(i)));
  77. mStringsAtTimes.put(i,
  78. new TtmlData(ttmlSubtitle.getText(ttmlSubtitle.getEventTime(i)),
  79. ttmlSubtitle.getEventTime(i) / 1000, ttmlSubtitle
  80. .getNextEventTimeIndex(ttmlSubtitle.getEventTime(i))));
  81. mSeekList.add(new TimeAndPosition(ttmlSubtitle.getEventTime(i) / 1000, mSeekList
  82. .size()));
  83. if (LOGS_ENABLED) Log.d(TAG, "Adding to mSeekList, timeTracker: "
  84. + (ttmlSubtitle.getEventTime(i) / 1000) + " position: "
  85. + (mSeekList.size() - 1));
  86. }
  87. } catch (IOException e) {
  88. if (LOGS_ENABLED) Log.e(TAG, "Error in parsing subtitle " + e.getMessage());
  89. }
  90. }
  91. public void setText(String s) {
  92. mTextView.setText(s);
  93. }
  94. public void startRendering(long mediaPlayerCurrentPosition) {
  95. mBaseTimeMs = SystemClock.uptimeMillis() - mediaPlayerCurrentPosition;
  96. seekTo(mediaPlayerCurrentPosition);
  97. }
  98. public void stopRendering() {
  99. mRenderer.obtainMessage(MSG_RENDER_NOTHING).sendToTarget();
  100. mHandler.removeCallbacksAndMessages(null);
  101. }
  102. public void seekTo(long seekTimeMs) {
  103. mHandler.removeCallbacksAndMessages(null);
  104. mCurrentTimeMs = 0;
  105. for (TimeAndPosition tap : mSeekList) {
  106. if (seekTimeMs < tap.timeMs) {
  107. mSubtitleIndex = tap.position;
  108. mCurrentTimeMs = mBaseTimeMs + tap.timeMs;
  109. mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_RENDER_TEXT), tap.timeMs
  110. - seekTimeMs + SystemClock.uptimeMillis());
  111. break;
  112. }
  113. }
  114. }
  115. public void pause() {
  116. mHandler.removeCallbacksAndMessages(null);
  117. }
  118. public void clearText() {
  119. mRenderer.obtainMessage(MSG_RENDER_NOTHING).sendToTarget();
  120. }
  121. private long checkBaseTime() {
  122. long baseTimeMs = SystemClock.uptimeMillis() - mMediaPlayer.getCurrentPosition();
  123. long diff = Math.abs(mBaseTimeMs - baseTimeMs);
  124. if (diff > FIFTY_MS) {
  125. if (LOGS_ENABLED) Log.d(TAG, "Adjusting baseTime");
  126. mBaseTimeMs = baseTimeMs;
  127. return diff;
  128. }
  129. return 0;
  130. }
  131. private static class TimeAndPosition {
  132. public final long timeMs;
  133. public final int position;
  134. private TimeAndPosition(long time, int pos) {
  135. timeMs = time;
  136. position = pos;
  137. }
  138. }
  139. class EventHandler extends Handler {
  140. public EventHandler(Looper looper) {
  141. super(looper);
  142. }
  143. @Override
  144. public void handleMessage(Message msg) {
  145. switch (msg.what) {
  146. case MSG_RENDER_TEXT: {
  147. TtmlData data = mStringsAtTimes.get(mSubtitleIndex);
  148. long messageDelay = checkBaseTime();
  149. if (data.getNextIndex() != -1) {
  150. mCurrentTimeMs = mBaseTimeMs
  151. + mStringsAtTimes.get(mSubtitleIndex + 1).getDuration();
  152. mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_RENDER_TEXT),
  153. mCurrentTimeMs);
  154. if (LOGS_ENABLED)
  155. Log.d(TAG, "delay posted:"
  156. + (mCurrentTimeMs - SystemClock.uptimeMillis()));
  157. }
  158. mCurrentText = data.getText();
  159. if (LOGS_ENABLED) Log.d(TAG, "Setting text to: " + mCurrentText);
  160. mRenderer.sendMessageAtTime(mRenderer.obtainMessage(MSG_RENDER_TEXT),
  161. SystemClock.uptimeMillis() + messageDelay);
  162. mSubtitleIndex = data.getNextIndex();
  163. break;
  164. }
  165. case MSG_RENDER_NOTHING: {
  166. mRenderer.obtainMessage(MSG_RENDER_NOTHING).sendToTarget();
  167. break;
  168. }
  169. default:
  170. break;
  171. }
  172. }
  173. }
  174. class RenderHandler extends Handler {
  175. public RenderHandler(Looper looper) {
  176. super(looper);
  177. }
  178. @Override
  179. public void handleMessage(Message msg) {
  180. switch (msg.what) {
  181. case MSG_RENDER_TEXT: {
  182. if (LOGS_ENABLED) Log.d(TAG, "Rendering text: " + mCurrentText);
  183. setText(mCurrentText);
  184. break;
  185. }
  186. case MSG_RENDER_NOTHING: {
  187. setText("");
  188. break;
  189. }
  190. default:
  191. break;
  192. }
  193. }
  194. }
  195. }