PageRenderTime 76ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/usbtuner/src/com/android/usbtuner/tvinput/TunerSessionWorker.java

https://bitbucket.org/TinkerBoard_Android/packages-apps-tv
Java | 1455 lines | 1270 code | 99 blank | 86 comment | 313 complexity | 72b367ee341dc7f941c89f6f624cfa65 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright (C) 2015 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of 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,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.usbtuner.tvinput;
  17. import android.content.ContentResolver;
  18. import android.content.ContentUris;
  19. import android.content.Context;
  20. import android.database.Cursor;
  21. import android.media.MediaDataSource;
  22. import android.media.MediaFormat;
  23. import android.media.PlaybackParams;
  24. import android.media.tv.TvContentRating;
  25. import android.media.tv.TvContract;
  26. import android.media.tv.TvInputManager;
  27. import android.media.tv.TvTrackInfo;
  28. import android.net.Uri;
  29. import android.os.Handler;
  30. import android.os.HandlerThread;
  31. import android.os.Message;
  32. import android.os.SystemClock;
  33. import android.text.Html;
  34. import android.util.Log;
  35. import android.util.Pair;
  36. import android.util.Size;
  37. import android.util.SparseArray;
  38. import android.view.Surface;
  39. import android.view.accessibility.CaptioningManager;
  40. import com.google.android.exoplayer.audio.AudioCapabilities;
  41. import com.android.tv.common.TvContentRatingCache;
  42. import com.android.usbtuner.FileDataSource;
  43. import com.android.usbtuner.InputStreamSource;
  44. import com.android.usbtuner.TunerHal;
  45. import com.android.usbtuner.UsbTunerDataSource;
  46. import com.android.usbtuner.data.Cea708Data;
  47. import com.android.usbtuner.data.Channel;
  48. import com.android.usbtuner.data.PsipData.EitItem;
  49. import com.android.usbtuner.data.PsipData.TvTracksInterface;
  50. import com.android.usbtuner.data.Track.AtscAudioTrack;
  51. import com.android.usbtuner.data.Track.AtscCaptionTrack;
  52. import com.android.usbtuner.data.TunerChannel;
  53. import com.android.usbtuner.exoplayer.MpegTsPassthroughAc3RendererBuilder;
  54. import com.android.usbtuner.exoplayer.MpegTsPlayer;
  55. import com.android.usbtuner.exoplayer.cache.CacheManager;
  56. import com.android.usbtuner.exoplayer.cache.DvrStorageManager;
  57. import com.android.usbtuner.util.IsoUtils;
  58. import com.android.usbtuner.util.StatusTextUtils;
  59. import junit.framework.Assert;
  60. import java.io.File;
  61. import java.io.FileNotFoundException;
  62. import java.io.IOException;
  63. import java.util.ArrayList;
  64. import java.util.Iterator;
  65. import java.util.List;
  66. import java.util.Objects;
  67. import java.util.concurrent.CountDownLatch;
  68. /**
  69. * {@link TunerSessionWorker} implements a handler thread which processes TV input jobs
  70. * such as handling {@link ExoPlayer}, managing a tuner device, trickplay, and so on.
  71. */
  72. public class TunerSessionWorker implements PlaybackCacheListener,
  73. MpegTsPlayer.VideoEventListener, MpegTsPlayer.Listener, EventDetector.EventListener,
  74. ChannelDataManager.ProgramInfoListener, Handler.Callback {
  75. private static final String TAG = "TunerSessionWorker";
  76. private static final boolean DEBUG = false;
  77. private static final boolean ENABLE_PROFILER = true;
  78. private static final String PLAY_FROM_CHANNEL = "channel";
  79. // Public messages
  80. public static final int MSG_SELECT_TRACK = 1;
  81. public static final int MSG_SET_CAPTION_ENABLED = 2;
  82. public static final int MSG_SET_SURFACE = 3;
  83. public static final int MSG_SET_STREAM_VOLUME = 4;
  84. public static final int MSG_TIMESHIFT_PAUSE = 5;
  85. public static final int MSG_TIMESHIFT_RESUME = 6;
  86. public static final int MSG_TIMESHIFT_SEEK_TO = 7;
  87. public static final int MSG_TIMESHIFT_SET_PLAYBACKPARAMS = 8;
  88. public static final int MSG_AUDIO_CAPABILITIES_CHANGED = 9;
  89. public static final int MSG_UNBLOCKED_RATING = 10;
  90. // Private messages
  91. private static final int MSG_TUNE = 1000;
  92. private static final int MSG_RELEASE = 1001;
  93. private static final int MSG_RETRY_PLAYBACK = 1002;
  94. private static final int MSG_START_PLAYBACK = 1003;
  95. private static final int MSG_PLAYBACK_STATE_CHANGED = 1004;
  96. private static final int MSG_PLAYBACK_ERROR = 1005;
  97. private static final int MSG_PLAYBACK_VIDEO_SIZE_CHANGED = 1006;
  98. private static final int MSG_AUDIO_UNPLAYABLE = 1007;
  99. private static final int MSG_UPDATE_PROGRAM = 1008;
  100. private static final int MSG_SCHEDULE_OF_PROGRAMS = 1009;
  101. private static final int MSG_UPDATE_CHANNEL_INFO = 1010;
  102. private static final int MSG_TRICKPLAY = 1011;
  103. private static final int MSG_DRAWN_TO_SURFACE = 1012;
  104. private static final int MSG_PARENTAL_CONTROLS = 1013;
  105. private static final int MSG_RESCHEDULE_PROGRAMS = 1014;
  106. private static final int MSG_CACHE_START_TIME_CHANGED = 1015;
  107. private static final int MSG_CHECK_SIGNAL = 1016;
  108. private static final int MSG_DISCOVER_CAPTION_SERVICE_NUMBER = 1017;
  109. private static final int MSG_RECOVER_STOPPED_PLAYBACK = 1018;
  110. private static final int MSG_CACHE_STATE_CHANGED = 1019;
  111. private static final int MSG_PROGRAM_DATA_RESULT = 1020;
  112. private static final int MSG_STOP_TUNE = 1021;
  113. private static final int TS_PACKET_SIZE = 188;
  114. private static final int CHECK_NO_SIGNAL_INITIAL_DELAY_MS = 4000;
  115. private static final int CHECK_NO_SIGNAL_PERIOD_MS = 500;
  116. private static final int RECOVER_STOPPED_PLAYBACK_PERIOD_MS = 2500;
  117. private static final int PARENTAL_CONTROLS_INTERVAL_MS = 5000;
  118. private static final int RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS = 4000;
  119. private static final int RESCHEDULE_PROGRAMS_INTERVAL_MS = 10000;
  120. private static final int RESCHEDULE_PROGRAMS_TOLERANCE_MS = 2000;
  121. private static final int MAX_RETRY_COUNT = 2;
  122. // Some examples of the track ids of the audio tracks, "a0", "a1", "a2".
  123. // The number after prefix is being used for indicating a index of the given audio track.
  124. private static final String AUDIO_TRACK_PREFIX = "a";
  125. // Some examples of the tracks id of the caption tracks, "s1", "s2", "s3".
  126. // The number after prefix is being used for indicating a index of a caption service number
  127. // of the given caption track.
  128. private static final String SUBTITLE_TRACK_PREFIX = "s";
  129. private static final int TRACK_PREFIX_SIZE = 1;
  130. private static final String VIDEO_TRACK_ID = "v";
  131. private static final long CACHE_UNDERFLOW_BUFFER_MS = 5000;
  132. // Actual interval would be divided by the speed.
  133. private static final int TRICKPLAY_SEEK_INTERVAL_MS = 2000;
  134. private static final int MIN_TRICKPLAY_SEEK_INTERVAL_MS = 500;
  135. private final Context mContext;
  136. private final ChannelDataManager mChannelDataManager;
  137. private final TunerHal mTunerHal;
  138. private UsbTunerDataSource mTunerSource;
  139. private FileDataSource mFileSource;
  140. private InputStreamSource mSource;
  141. private Surface mSurface;
  142. private int mPlayerGeneration;
  143. private int mPreparingGeneration;
  144. private int mEndedGeneration;
  145. private volatile MpegTsPlayer mPlayer;
  146. private volatile TunerChannel mChannel;
  147. private String mRecordingId;
  148. private volatile Long mRecordingDuration;
  149. private final Handler mHandler;
  150. private int mRetryCount;
  151. private float mVolume;
  152. private final ArrayList<TvTrackInfo> mTvTracks;
  153. private SparseArray<AtscAudioTrack> mAudioTrackMap;
  154. private SparseArray<AtscCaptionTrack> mCaptionTrackMap;
  155. private AtscCaptionTrack mCaptionTrack;
  156. private boolean mCaptionEnabled;
  157. private volatile long mRecordStartTimeMs;
  158. private volatile long mCacheStartTimeMs;
  159. private PlaybackParams mPlaybackParams = new PlaybackParams();
  160. private boolean mPlayerStarted = false;
  161. private boolean mReportedDrawnToSurface = false;
  162. private boolean mReportedSignalAvailable = false;
  163. private EitItem mProgram;
  164. private List<EitItem> mPrograms;
  165. private TvInputManager mTvInputManager;
  166. private boolean mChannelBlocked;
  167. private TvContentRating mUnblockedContentRating;
  168. private long mLastPositionMs;
  169. private AudioCapabilities mAudioCapabilities;
  170. private final CountDownLatch mReleaseLatch = new CountDownLatch(1);
  171. private long mLastLimitInBytes = 0L;
  172. private long mLastPositionInBytes = 0L;
  173. private final CacheManager mCacheManager;
  174. private final TvContentRatingCache mTvContentRatingCache = TvContentRatingCache.getInstance();
  175. private final TunerSession mSession;
  176. public TunerSessionWorker(Context context, ChannelDataManager channelDataManager,
  177. CacheManager cacheManager, TunerSession tunerSession) {
  178. mContext = context;
  179. mTunerHal = TunerHal.createInstance(context);
  180. if (mTunerHal == null) {
  181. throw new RuntimeException("Failed to open a DVB device");
  182. }
  183. // HandlerThread should be set up before it is registered as a listener in the all other
  184. // components.
  185. HandlerThread handlerThread = new HandlerThread(TAG);
  186. handlerThread.start();
  187. mHandler = new Handler(handlerThread.getLooper(), this);
  188. mSession = tunerSession;
  189. mChannelDataManager = channelDataManager;
  190. // TODO: need to refactor it for multi-tuner support.
  191. mChannelDataManager.setListener(this);
  192. mChannelDataManager.checkDataVersion(mContext);
  193. mTvInputManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
  194. mTunerSource = new UsbTunerDataSource(mTunerHal, this);
  195. mFileSource = new FileDataSource(this);
  196. mVolume = 1.0f;
  197. mTvTracks = new ArrayList<>();
  198. mAudioTrackMap = new SparseArray<>();
  199. mCaptionTrackMap = new SparseArray<>();
  200. CaptioningManager captioningManager =
  201. (CaptioningManager) context.getSystemService(Context.CAPTIONING_SERVICE);
  202. mCaptionEnabled = captioningManager.isEnabled();
  203. mPlaybackParams.setSpeed(1.0f);
  204. mCacheManager = cacheManager;
  205. }
  206. // Public methods
  207. public void tune(Uri channelUri) {
  208. if (mSurface != null) { // To avoid removing MSG_SET_SURFACE
  209. mHandler.removeCallbacksAndMessages(null);
  210. }
  211. sendMessage(MSG_TUNE, channelUri);
  212. }
  213. public void stopTune() {
  214. mHandler.removeCallbacksAndMessages(null);
  215. sendMessage(MSG_STOP_TUNE);
  216. }
  217. public TunerChannel getCurrentChannel() {
  218. return mChannel;
  219. }
  220. public long getStartPosition() {
  221. return mCacheStartTimeMs;
  222. }
  223. private String getRecordingPath() {
  224. return Uri.parse(mRecordingId).getPath();
  225. }
  226. public Long getDurationForRecording() {
  227. return mRecordingDuration;
  228. }
  229. private Long getDurationForRecording(String recordingId) {
  230. try {
  231. DvrStorageManager storageManager =
  232. new DvrStorageManager(new File(getRecordingPath()), false);
  233. Pair<String, MediaFormat> trackInfo = null;
  234. try {
  235. trackInfo = storageManager.readTrackInfoFile(false);
  236. } catch (FileNotFoundException e) {
  237. }
  238. if (trackInfo == null) {
  239. trackInfo = storageManager.readTrackInfoFile(true);
  240. }
  241. Long durationUs = trackInfo.second.getLong(MediaFormat.KEY_DURATION);
  242. // we need duration by milli for trickplay notification.
  243. return durationUs != null ? durationUs / 1000 : null;
  244. } catch (IOException e) {
  245. Log.e(TAG, "meta file for recording was not found: " + recordingId);
  246. return null;
  247. }
  248. }
  249. public long getCurrentPosition() {
  250. // TODO: More precise time may be necessary.
  251. MpegTsPlayer mpegTsPlayer = mPlayer;
  252. long currentTime = mpegTsPlayer != null
  253. ? mRecordStartTimeMs + mpegTsPlayer.getCurrentPosition() : mRecordStartTimeMs;
  254. if (DEBUG) {
  255. long systemCurrentTime = System.currentTimeMillis();
  256. Log.d(TAG, "currentTime = " + currentTime
  257. + " ; System.currentTimeMillis() = " + systemCurrentTime
  258. + " ; diff = " + (currentTime - systemCurrentTime));
  259. }
  260. return currentTime;
  261. }
  262. public void sendMessage(int messageType) {
  263. mHandler.sendEmptyMessage(messageType);
  264. }
  265. public void sendMessage(int messageType, Object object) {
  266. mHandler.obtainMessage(messageType, object).sendToTarget();
  267. }
  268. public void sendMessage(int messageType, int arg1, int arg2, Object object) {
  269. mHandler.obtainMessage(messageType, arg1, arg2, object).sendToTarget();
  270. }
  271. public void release() {
  272. mHandler.removeCallbacksAndMessages(null);
  273. mHandler.sendEmptyMessage(MSG_RELEASE);
  274. try {
  275. mReleaseLatch.await();
  276. } catch (InterruptedException e) {
  277. Log.e(TAG, "Couldn't wait for finish of MSG_RELEASE", e);
  278. } finally {
  279. mHandler.getLooper().quitSafely();
  280. }
  281. }
  282. // MpegTsPlayer.Listener
  283. @Override
  284. public void onStateChanged(int generation, boolean playWhenReady, int playbackState) {
  285. sendMessage(MSG_PLAYBACK_STATE_CHANGED, generation, playbackState, playWhenReady);
  286. }
  287. @Override
  288. public void onError(int generation, Exception e) {
  289. sendMessage(MSG_PLAYBACK_ERROR, generation, 0, e);
  290. }
  291. @Override
  292. public void onVideoSizeChanged(int generation, int width, int height, float pixelWidthHeight) {
  293. sendMessage(MSG_PLAYBACK_VIDEO_SIZE_CHANGED, generation, 0, new Size(width, height));
  294. }
  295. @Override
  296. public void onDrawnToSurface(MpegTsPlayer player, Surface surface) {
  297. sendMessage(MSG_DRAWN_TO_SURFACE, player);
  298. }
  299. @Override
  300. public void onAudioUnplayable(int generation) {
  301. sendMessage(MSG_AUDIO_UNPLAYABLE, generation);
  302. }
  303. // MpegTsPlayer.VideoEventListener
  304. @Override
  305. public void onEmitCaptionEvent(Cea708Data.CaptionEvent event) {
  306. mSession.sendUiMessage(TunerSession.MSG_UI_PROCESS_CAPTION_TRACK, event);
  307. }
  308. @Override
  309. public void onDiscoverCaptionServiceNumber(int serviceNumber) {
  310. sendMessage(MSG_DISCOVER_CAPTION_SERVICE_NUMBER, serviceNumber);
  311. }
  312. // ChannelDataManager.ProgramInfoListener
  313. @Override
  314. public void onProgramsArrived(TunerChannel channel, List<EitItem> programs) {
  315. sendMessage(MSG_SCHEDULE_OF_PROGRAMS, new Pair<>(channel, programs));
  316. }
  317. @Override
  318. public void onChannelArrived(TunerChannel channel) {
  319. sendMessage(MSG_UPDATE_CHANNEL_INFO, channel);
  320. }
  321. @Override
  322. public void onRescanNeeded() {
  323. mSession.sendUiMessage(TunerSession.MSG_UI_TOAST_RESCAN_NEEDED);
  324. }
  325. @Override
  326. public void onRequestProgramsResponse(TunerChannel channel, List<EitItem> programs) {
  327. sendMessage(MSG_PROGRAM_DATA_RESULT, new Pair<>(channel, programs));
  328. }
  329. // PlaybackCacheListener
  330. @Override
  331. public void onCacheStartTimeChanged(long startTimeMs) {
  332. sendMessage(MSG_CACHE_START_TIME_CHANGED, startTimeMs);
  333. }
  334. @Override
  335. public void onCacheStateChanged(boolean available) {
  336. sendMessage(MSG_CACHE_STATE_CHANGED, available);
  337. }
  338. @Override
  339. public void onDiskTooSlow() {
  340. sendMessage(MSG_RETRY_PLAYBACK, mPlayer);
  341. }
  342. // EventDetector.EventListener
  343. @Override
  344. public void onChannelDetected(TunerChannel channel, boolean channelArrivedAtFirstTime) {
  345. mChannelDataManager.notifyChannelDetected(channel, channelArrivedAtFirstTime);
  346. }
  347. @Override
  348. public void onEventDetected(TunerChannel channel, List<EitItem> items) {
  349. mChannelDataManager.notifyEventDetected(channel, items);
  350. }
  351. private long parseChannel(Uri uri) {
  352. try {
  353. List<String> paths = uri.getPathSegments();
  354. if (paths.size() > 1 && paths.get(0).equals(PLAY_FROM_CHANNEL)) {
  355. return ContentUris.parseId(uri);
  356. }
  357. } catch (UnsupportedOperationException | NumberFormatException e) {
  358. }
  359. return -1;
  360. }
  361. private static class RecordedProgram {
  362. private long mChannelId;
  363. private String mDataUri;
  364. private static final String[] PROJECTION = {
  365. TvContract.Programs.COLUMN_CHANNEL_ID,
  366. TvContract.RecordedPrograms.COLUMN_RECORDING_DATA_URI,
  367. };
  368. public RecordedProgram(Cursor cursor) {
  369. int index = 0;
  370. mChannelId = cursor.getLong(index++);
  371. mDataUri = cursor.getString(index++);
  372. }
  373. public RecordedProgram(long channelId, String dataUri) {
  374. mChannelId = channelId;
  375. mDataUri = dataUri;
  376. }
  377. public static RecordedProgram onQuery(Cursor c) {
  378. RecordedProgram recording = null;
  379. if (c != null && c.moveToNext()) {
  380. recording = new RecordedProgram(c);
  381. }
  382. return recording;
  383. }
  384. public String getDataUri() {
  385. return mDataUri;
  386. }
  387. }
  388. private RecordedProgram getRecordedProgram(Uri recordedUri) {
  389. ContentResolver resolver = mContext.getContentResolver();
  390. try(Cursor c = resolver.query(recordedUri, RecordedProgram.PROJECTION, null, null, null)) {
  391. if (c != null) {
  392. RecordedProgram result = RecordedProgram.onQuery(c);
  393. if (DEBUG) {
  394. Log.d(TAG, "Finished query for " + this);
  395. }
  396. return result;
  397. } else {
  398. if (c == null) {
  399. Log.e(TAG, "Unknown query error for " + this);
  400. } else {
  401. if (DEBUG) {
  402. Log.d(TAG, "Canceled query for " + this);
  403. }
  404. }
  405. return null;
  406. }
  407. }
  408. }
  409. private String parseRecording(Uri uri) {
  410. RecordedProgram recording = getRecordedProgram(uri);
  411. if (recording != null) {
  412. return recording.getDataUri();
  413. }
  414. return null;
  415. }
  416. @Override
  417. public boolean handleMessage(Message msg) {
  418. switch (msg.what) {
  419. case MSG_TUNE: {
  420. if (DEBUG) Log.d(TAG, "MSG_TUNE");
  421. // When sequential tuning messages arrived, it skips middle tuning messages in order
  422. // to change to the last requested channel quickly.
  423. if (mHandler.hasMessages(MSG_TUNE)) {
  424. return true;
  425. }
  426. Uri channelUri = (Uri) msg.obj;
  427. String recording = null;
  428. long channelId = parseChannel(channelUri);
  429. TunerChannel channel = (channelId == -1) ? null
  430. : mChannelDataManager.getChannel(channelId);
  431. if (channelId == -1) {
  432. recording = parseRecording(channelUri);
  433. }
  434. if (channel == null && recording == null) {
  435. Log.w(TAG, "onTune() is failed. Can't find channel for " + channelUri);
  436. stopTune();
  437. mSession.notifyVideoUnavailable(
  438. TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
  439. return true;
  440. }
  441. mHandler.removeCallbacksAndMessages(null);
  442. if (channel != null) {
  443. mChannelDataManager.requestProgramsData(channel);
  444. }
  445. prepareTune(channel, recording);
  446. // TODO: Need to refactor. notifyContentAllowed() should not be called if parental
  447. // control is turned on.
  448. mSession.notifyContentAllowed();
  449. resetPlayback();
  450. resetTvTracks();
  451. mHandler.sendEmptyMessageDelayed(MSG_RESCHEDULE_PROGRAMS,
  452. RESCHEDULE_PROGRAMS_INITIAL_DELAY_MS);
  453. mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL,
  454. CHECK_NO_SIGNAL_INITIAL_DELAY_MS);
  455. return true;
  456. }
  457. case MSG_STOP_TUNE: {
  458. if (DEBUG) {
  459. Log.d(TAG, "MSG_STOP_TUNE");
  460. }
  461. mChannel = null;
  462. stopPlayback();
  463. stopCaptionTrack();
  464. resetTvTracks();
  465. mTunerHal.stopTune();
  466. mSource = null;
  467. mSession.notifyVideoUnavailable(
  468. TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
  469. return true;
  470. }
  471. case MSG_RELEASE: {
  472. if (DEBUG) {
  473. Log.d(TAG, "MSG_RELEASE");
  474. }
  475. mHandler.removeCallbacksAndMessages(null);
  476. stopPlayback();
  477. stopCaptionTrack();
  478. try {
  479. mTunerHal.close();
  480. } catch (Exception ex) {
  481. Log.e(TAG, "Error on closing tuner HAL.", ex);
  482. }
  483. mSource = null;
  484. mReleaseLatch.countDown();
  485. return true;
  486. }
  487. case MSG_RETRY_PLAYBACK: {
  488. if (mPlayer == msg.obj) {
  489. mHandler.removeMessages(MSG_RETRY_PLAYBACK);
  490. mRetryCount++;
  491. if (DEBUG) {
  492. Log.d(TAG, "MSG_RETRY_PLAYBACK " + mRetryCount);
  493. }
  494. if (mRetryCount <= MAX_RETRY_COUNT) {
  495. resetPlayback();
  496. } else {
  497. // When it reaches this point, it may be due to an error that occurred in
  498. // the tuner device. Calling stopPlayback() and TunerHal.stopTune()
  499. // resets the tuner device to recover from the error.
  500. stopPlayback();
  501. stopCaptionTrack();
  502. mTunerHal.stopTune();
  503. mSession.notifyVideoUnavailable(
  504. TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
  505. // After MAX_RETRY_COUNT, give some delay of an empirically chosen value
  506. // before recovering the playback.
  507. mHandler.sendEmptyMessageDelayed(MSG_RECOVER_STOPPED_PLAYBACK,
  508. RECOVER_STOPPED_PLAYBACK_PERIOD_MS);
  509. }
  510. }
  511. return true;
  512. }
  513. case MSG_RECOVER_STOPPED_PLAYBACK: {
  514. if (DEBUG) {
  515. Log.d(TAG, "MSG_RECOVER_STOPPED_PLAYBACK");
  516. }
  517. resetPlayback();
  518. return true;
  519. }
  520. case MSG_START_PLAYBACK: {
  521. if (DEBUG) {
  522. Log.d(TAG, "MSG_START_PLAYBACK");
  523. }
  524. if (mChannel != null || mRecordingId != null) {
  525. startPlayback(msg.obj);
  526. }
  527. return true;
  528. }
  529. case MSG_PLAYBACK_STATE_CHANGED: {
  530. int generation = msg.arg1;
  531. int playbackState = msg.arg2;
  532. boolean playWhenReady = (boolean) msg.obj;
  533. if (DEBUG) {
  534. Log.d(TAG, "ExoPlayer state change: " + generation + " "
  535. + playbackState + " " + playWhenReady);
  536. }
  537. // Generation starts from 1 not 0.
  538. if (playbackState == MpegTsPlayer.STATE_READY
  539. && mPreparingGeneration == mPlayerGeneration) {
  540. if (DEBUG) {
  541. Log.d(TAG, "ExoPlayer ready: " + mPlayerGeneration);
  542. }
  543. // mPreparingGeneration was set to mPlayerGeneration in order to indicate that
  544. // ExoPlayer is in its preparing status when MpegTsPlayer::prepare() was called.
  545. // Now MpegTsPlayer::prepare() is finished. Clear preparing state in order to
  546. // ensure another DO_START_PLAYBACK will not be sent for same generation.
  547. mPreparingGeneration = 0;
  548. sendMessage(MSG_START_PLAYBACK, mPlayer);
  549. } else if (playbackState == MpegTsPlayer.STATE_ENDED
  550. && mEndedGeneration != generation) {
  551. // Final status
  552. // notification of STATE_ENDED from MpegTsPlayer will be ignored afterwards.
  553. mEndedGeneration = generation;
  554. Log.i(TAG, "Player ended: end of stream " + generation);
  555. sendMessage(MSG_RETRY_PLAYBACK, mPlayer);
  556. }
  557. return true;
  558. }
  559. case MSG_PLAYBACK_ERROR: {
  560. int generation = msg.arg1;
  561. Exception exception = (Exception) msg.obj;
  562. Log.i(TAG, "ExoPlayer Error: " + generation + " " + mPlayerGeneration);
  563. if (generation != mPlayerGeneration) {
  564. return true;
  565. }
  566. mHandler.obtainMessage(MSG_RETRY_PLAYBACK, mPlayer).sendToTarget();
  567. return true;
  568. }
  569. case MSG_PLAYBACK_VIDEO_SIZE_CHANGED: {
  570. int generation = msg.arg1;
  571. Size size = (Size) msg.obj;
  572. if (generation != mPlayerGeneration) {
  573. return true;
  574. }
  575. if (mChannel != null && mChannel.hasVideo()) {
  576. updateVideoTrack(size.getWidth(), size.getHeight());
  577. }
  578. if (mRecordingId != null) {
  579. updateVideoTrack(size.getWidth(), size.getHeight());
  580. }
  581. return true;
  582. }
  583. case MSG_AUDIO_UNPLAYABLE: {
  584. int generation = (int) msg.obj;
  585. if (mPlayer == null || generation != mPlayerGeneration) {
  586. return true;
  587. }
  588. Log.i(TAG, "AC3 audio cannot be played due to device limitation");
  589. mSession.sendUiMessage(
  590. TunerSession.MSG_UI_SHOW_AUDIO_UNPLAYABLE);
  591. return true;
  592. }
  593. case MSG_UPDATE_PROGRAM: {
  594. if (mChannel != null) {
  595. EitItem program = (EitItem) msg.obj;
  596. updateTvTracks(program);
  597. mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
  598. }
  599. return true;
  600. }
  601. case MSG_SCHEDULE_OF_PROGRAMS: {
  602. mHandler.removeMessages(MSG_UPDATE_PROGRAM);
  603. Pair<TunerChannel, List<EitItem>> pair =
  604. (Pair<TunerChannel, List<EitItem>>) msg.obj;
  605. TunerChannel channel = pair.first;
  606. if (mChannel == null) {
  607. return true;
  608. }
  609. if (mChannel != null && mChannel.compareTo(channel) != 0) {
  610. return true;
  611. }
  612. mPrograms = pair.second;
  613. EitItem currentProgram = getCurrentProgram();
  614. if (currentProgram == null) {
  615. mProgram = null;
  616. }
  617. long currentTimeMs = getCurrentPosition();
  618. if (mPrograms != null) {
  619. for (EitItem item : mPrograms) {
  620. if (currentProgram != null && currentProgram.compareTo(item) == 0) {
  621. if (DEBUG) {
  622. Log.d(TAG, "Update current TvTracks " + item);
  623. }
  624. if (mProgram != null && mProgram.compareTo(item) == 0) {
  625. continue;
  626. }
  627. mProgram = item;
  628. updateTvTracks(item);
  629. } else if (item.getStartTimeUtcMillis() > currentTimeMs) {
  630. if (DEBUG) {
  631. Log.d(TAG, "Update next TvTracks " + item + " "
  632. + (item.getStartTimeUtcMillis() - currentTimeMs));
  633. }
  634. mHandler.sendMessageDelayed(
  635. mHandler.obtainMessage(MSG_UPDATE_PROGRAM, item),
  636. item.getStartTimeUtcMillis() - currentTimeMs);
  637. }
  638. }
  639. }
  640. mHandler.sendEmptyMessage(MSG_PARENTAL_CONTROLS);
  641. return true;
  642. }
  643. case MSG_UPDATE_CHANNEL_INFO: {
  644. TunerChannel channel = (TunerChannel) msg.obj;
  645. if (mChannel != null && mChannel.compareTo(channel) == 0) {
  646. updateChannelInfo(channel);
  647. }
  648. return true;
  649. }
  650. case MSG_PROGRAM_DATA_RESULT: {
  651. TunerChannel channel = (TunerChannel) ((Pair) msg.obj).first;
  652. // If there already exists, skip it since real-time data is a top priority,
  653. if (mChannel != null && mChannel.compareTo(channel) == 0
  654. && mPrograms == null && mProgram == null) {
  655. sendMessage(MSG_SCHEDULE_OF_PROGRAMS, msg.obj);
  656. }
  657. return true;
  658. }
  659. case MSG_DRAWN_TO_SURFACE: {
  660. if (mPlayer == msg.obj && mSurface != null && mPlayerStarted) {
  661. if (DEBUG) {
  662. Log.d(TAG, "MSG_DRAWN_TO_SURFACE");
  663. }
  664. mCacheStartTimeMs = mRecordStartTimeMs =
  665. (mRecordingId != null) ? 0 : System.currentTimeMillis();
  666. mSession.notifyVideoAvailable();
  667. mReportedDrawnToSurface = true;
  668. // If surface is drawn successfully, it means that the playback was brought back
  669. // to normal and therefore, the playback recovery status will be reset through
  670. // setting a zero value to the retry count.
  671. // TODO: Consider audio only channels for detecting playback status changes to
  672. // be normal.
  673. mRetryCount = 0;
  674. if (mCaptionEnabled && mCaptionTrack != null) {
  675. startCaptionTrack();
  676. } else {
  677. stopCaptionTrack();
  678. }
  679. }
  680. return true;
  681. }
  682. case MSG_TRICKPLAY: {
  683. doTrickplay(msg.arg1);
  684. return true;
  685. }
  686. case MSG_RESCHEDULE_PROGRAMS: {
  687. doReschedulePrograms();
  688. return true;
  689. }
  690. case MSG_PARENTAL_CONTROLS: {
  691. doParentalControls();
  692. mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
  693. mHandler.sendEmptyMessageDelayed(MSG_PARENTAL_CONTROLS,
  694. PARENTAL_CONTROLS_INTERVAL_MS);
  695. return true;
  696. }
  697. case MSG_UNBLOCKED_RATING: {
  698. mUnblockedContentRating = (TvContentRating) msg.obj;
  699. doParentalControls();
  700. mHandler.removeMessages(MSG_PARENTAL_CONTROLS);
  701. mHandler.sendEmptyMessageDelayed(MSG_PARENTAL_CONTROLS,
  702. PARENTAL_CONTROLS_INTERVAL_MS);
  703. return true;
  704. }
  705. case MSG_DISCOVER_CAPTION_SERVICE_NUMBER: {
  706. int serviceNumber = (int) msg.obj;
  707. doDiscoverCaptionServiceNumber(serviceNumber);
  708. return true;
  709. }
  710. case MSG_SELECT_TRACK: {
  711. if (mChannel != null) {
  712. doSelectTrack(msg.arg1, (String) msg.obj);
  713. } else if (mRecordingId != null) {
  714. // TODO : mChannel == null && mRecordingId != null
  715. Log.d(TAG, "track selected for recording");
  716. }
  717. return true;
  718. }
  719. case MSG_SET_CAPTION_ENABLED: {
  720. mCaptionEnabled = (boolean) msg.obj;
  721. if (mCaptionEnabled) {
  722. startCaptionTrack();
  723. } else {
  724. stopCaptionTrack();
  725. }
  726. return true;
  727. }
  728. case MSG_TIMESHIFT_PAUSE: {
  729. doTimeShiftPause();
  730. return true;
  731. }
  732. case MSG_TIMESHIFT_RESUME: {
  733. doTimeShiftResume();
  734. return true;
  735. }
  736. case MSG_TIMESHIFT_SEEK_TO: {
  737. doTimeShiftSeekTo((long) msg.obj);
  738. return true;
  739. }
  740. case MSG_TIMESHIFT_SET_PLAYBACKPARAMS: {
  741. doTimeShiftSetPlaybackParams((PlaybackParams) msg.obj);
  742. return true;
  743. }
  744. case MSG_AUDIO_CAPABILITIES_CHANGED: {
  745. AudioCapabilities capabilities = (AudioCapabilities) msg.obj;
  746. if (DEBUG) {
  747. Log.d(TAG, "MSG_AUDIO_CAPABILITIES_CHANGED " + capabilities);
  748. }
  749. if (capabilities == null) {
  750. return true;
  751. }
  752. if (!capabilities.equals(mAudioCapabilities)) {
  753. // HDMI supported encodings are changed. restart player.
  754. mAudioCapabilities = capabilities;
  755. resetPlayback();
  756. }
  757. return true;
  758. }
  759. case MSG_SET_SURFACE: {
  760. Surface surface = (Surface) msg.obj;
  761. if (DEBUG) {
  762. Log.d(TAG, "MSG_SET_SURFACE " + surface);
  763. }
  764. if (surface != null && !surface.isValid()) {
  765. Log.w(TAG, "Ignoring invalid surface.");
  766. return true;
  767. }
  768. mSurface = surface;
  769. resetPlayback();
  770. return true;
  771. }
  772. case MSG_SET_STREAM_VOLUME: {
  773. mVolume = (float) msg.obj;
  774. if (mPlayer != null && mPlayer.isPlaying()) {
  775. mPlayer.setVolume(mVolume);
  776. }
  777. return true;
  778. }
  779. case MSG_CACHE_START_TIME_CHANGED: {
  780. if (mPlayer == null) {
  781. return true;
  782. }
  783. mCacheStartTimeMs = (long) msg.obj;
  784. if (!hasEnoughBackwardCache()
  785. && (!mPlayer.isPlaying() || mPlaybackParams.getSpeed() < 1.0f)) {
  786. mPlayer.setPlayWhenReady(true);
  787. mPlayer.setAudioTrack(true);
  788. mPlaybackParams.setSpeed(1.0f);
  789. }
  790. return true;
  791. }
  792. case MSG_CACHE_STATE_CHANGED: {
  793. boolean available = (boolean) msg.obj;
  794. mSession.notifyTimeShiftStatusChanged(available
  795. ? TvInputManager.TIME_SHIFT_STATUS_AVAILABLE
  796. : TvInputManager.TIME_SHIFT_STATUS_UNAVAILABLE);
  797. return true;
  798. }
  799. case MSG_CHECK_SIGNAL: {
  800. if (mChannel == null) {
  801. return true;
  802. }
  803. long limitInBytes = mSource != null ? mSource.getLimit() : 0L;
  804. long positionInBytes = mSource != null ? mSource.getPosition() : 0L;
  805. if (UsbTunerDebug.ENABLED) {
  806. UsbTunerDebug.calculateDiff();
  807. mSession.sendUiMessage(TunerSession.MSG_UI_SET_STATUS_TEXT,
  808. Html.fromHtml(
  809. StatusTextUtils.getStatusWarningInHTML(
  810. (limitInBytes - mLastLimitInBytes)
  811. / TS_PACKET_SIZE,
  812. UsbTunerDebug.getVideoFrameDrop(),
  813. UsbTunerDebug.getBytesInQueue(),
  814. UsbTunerDebug.getAudioPositionUs(),
  815. UsbTunerDebug.getAudioPositionUsRate(),
  816. UsbTunerDebug.getAudioPtsUs(),
  817. UsbTunerDebug.getAudioPtsUsRate(),
  818. UsbTunerDebug.getVideoPtsUs(),
  819. UsbTunerDebug.getVideoPtsUsRate()
  820. )));
  821. }
  822. if (DEBUG) {
  823. Log.d(TAG, String.format("MSG_CHECK_SIGNAL position: %d, limit: %d",
  824. positionInBytes, limitInBytes));
  825. }
  826. mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_MESSAGE);
  827. if (mSource != null && mChannel.getType() == Channel.TYPE_TUNER
  828. && positionInBytes == mLastPositionInBytes
  829. && limitInBytes == mLastLimitInBytes) {
  830. mSession.notifyVideoUnavailable(
  831. TvInputManager.VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL);
  832. mReportedSignalAvailable = false;
  833. } else {
  834. if (mReportedDrawnToSurface && !mReportedSignalAvailable) {
  835. mSession.notifyVideoAvailable();
  836. mReportedSignalAvailable = true;
  837. }
  838. }
  839. mLastLimitInBytes = limitInBytes;
  840. mLastPositionInBytes = positionInBytes;
  841. mHandler.sendEmptyMessageDelayed(MSG_CHECK_SIGNAL,
  842. CHECK_NO_SIGNAL_PERIOD_MS);
  843. return true;
  844. }
  845. default: {
  846. Log.w(TAG, "Unhandled message code: " + msg.what);
  847. return false;
  848. }
  849. }
  850. }
  851. // Private methods
  852. private void doSelectTrack(int type, String trackId) {
  853. int numTrackId = trackId != null
  854. ? Integer.parseInt(trackId.substring(TRACK_PREFIX_SIZE)) : -1;
  855. if (type == TvTrackInfo.TYPE_AUDIO) {
  856. if (trackId == null) {
  857. return;
  858. }
  859. AtscAudioTrack audioTrack = mAudioTrackMap.get(numTrackId);
  860. if (audioTrack == null) {
  861. return;
  862. }
  863. int oldAudioPid = mChannel.getAudioPid();
  864. mChannel.selectAudioTrack(audioTrack.index);
  865. int newAudioPid = mChannel.getAudioPid();
  866. if (oldAudioPid != newAudioPid) {
  867. // TODO: Implement a switching between tracks more smoothly.
  868. resetPlayback();
  869. }
  870. mSession.notifyTrackSelected(type, trackId);
  871. } else if (type == TvTrackInfo.TYPE_SUBTITLE) {
  872. if (trackId == null) {
  873. mSession.notifyTrackSelected(type, null);
  874. mCaptionTrack = null;
  875. stopCaptionTrack();
  876. return;
  877. }
  878. for (TvTrackInfo track : mTvTracks) {
  879. if (track.getId().equals(trackId)) {
  880. // The service number of the caption service is used for track id of a
  881. // subtitle track. Passes the following track id on to TsParser.
  882. mSession.notifyTrackSelected(type, trackId);
  883. mCaptionTrack = mCaptionTrackMap.get(numTrackId);
  884. startCaptionTrack();
  885. return;
  886. }
  887. }
  888. }
  889. }
  890. private MpegTsPlayer createPlayer(AudioCapabilities capabilities, CacheManager cacheManager) {
  891. if (capabilities == null) {
  892. Log.w(TAG, "No Audio Capabilities");
  893. }
  894. ++mPlayerGeneration;
  895. MpegTsPlayer player = new MpegTsPlayer(mPlayerGeneration,
  896. new MpegTsPassthroughAc3RendererBuilder(mContext, cacheManager, this),
  897. mHandler, capabilities, this);
  898. Log.i(TAG, "Passthrough AC3 renderer");
  899. if (DEBUG) Log.d(TAG, "ExoPlayer created: " + mPlayerGeneration);
  900. return player;
  901. }
  902. private void startCaptionTrack() {
  903. if (mCaptionEnabled && mCaptionTrack != null) {
  904. mSession.sendUiMessage(
  905. TunerSession.MSG_UI_START_CAPTION_TRACK, mCaptionTrack);
  906. if (mPlayer != null) {
  907. mPlayer.setCaptionServiceNumber(mCaptionTrack.serviceNumber);
  908. }
  909. }
  910. }
  911. private void stopCaptionTrack() {
  912. if (mPlayer != null) {
  913. mPlayer.setCaptionServiceNumber(Cea708Data.EMPTY_SERVICE_NUMBER);
  914. }
  915. mSession.sendUiMessage(TunerSession.MSG_UI_STOP_CAPTION_TRACK);
  916. }
  917. private void resetTvTracks() {
  918. mTvTracks.clear();
  919. mAudioTrackMap.clear();
  920. mCaptionTrackMap.clear();
  921. mSession.sendUiMessage(TunerSession.MSG_UI_RESET_CAPTION_TRACK);
  922. mSession.notifyTracksChanged(mTvTracks);
  923. }
  924. private void updateTvTracks(TvTracksInterface tvTracksInterface) {
  925. if (DEBUG) {
  926. Log.d(TAG, "UpdateTvTracks " + tvTracksInterface);
  927. }
  928. List<AtscAudioTrack> audioTracks = tvTracksInterface.getAudioTracks();
  929. List<AtscCaptionTrack> captionTracks = tvTracksInterface.getCaptionTracks();
  930. if (audioTracks != null && !audioTracks.isEmpty()) {
  931. updateAudioTracks(audioTracks);
  932. }
  933. if (captionTracks == null || captionTracks.isEmpty()) {
  934. if (tvTracksInterface.hasCaptionTrack()) {
  935. updateCaptionTracks(captionTracks);
  936. }
  937. } else {
  938. updateCaptionTracks(captionTracks);
  939. }
  940. }
  941. private void removeTvTracks(int trackType) {
  942. Iterator<TvTrackInfo> iterator = mTvTracks.iterator();
  943. while (iterator.hasNext()) {
  944. TvTrackInfo tvTrackInfo = iterator.next();
  945. if (tvTrackInfo.getType() == trackType) {
  946. iterator.remove();
  947. }
  948. }
  949. }
  950. private void updateVideoTrack(int width, int height) {
  951. removeTvTracks(TvTrackInfo.TYPE_VIDEO);
  952. mTvTracks.add(new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID)
  953. .setVideoWidth(width).setVideoHeight(height).build());
  954. mSession.notifyTracksChanged(mTvTracks);
  955. mSession.notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, VIDEO_TRACK_ID);
  956. }
  957. private void updateAudioTracks(List<AtscAudioTrack> audioTracks) {
  958. if (DEBUG) {
  959. Log.d(TAG, "Update AudioTracks " + audioTracks);
  960. }
  961. removeTvTracks(TvTrackInfo.TYPE_AUDIO);
  962. mAudioTrackMap.clear();
  963. if (audioTracks != null) {
  964. int index = 0;
  965. for (AtscAudioTrack audioTrack : audioTracks) {
  966. String language = audioTrack.language;
  967. if (language == null && mChannel.getAudioTracks() != null
  968. && mChannel.getAudioTracks().size() == audioTracks.size()) {
  969. // If a language is not present, use a language field in PMT section parsed.
  970. language = mChannel.getAudioTracks().get(index).language;
  971. }
  972. // Save the index to the audio track.
  973. // Later, when a audio track is selected, Both an audio pid and its audio stream
  974. // type reside in the selected index position of the tuner channel's audio data.
  975. audioTrack.index = index;
  976. TvTrackInfo.Builder builder = new TvTrackInfo.Builder(
  977. TvTrackInfo.TYPE_AUDIO, AUDIO_TRACK_PREFIX + index);
  978. if (IsoUtils.isValidIso3Language(language)) {
  979. builder.setLanguage(language);
  980. }
  981. if (audioTrack.channelCount != 0) {
  982. builder.setAudioChannelCount(audioTrack.channelCount);
  983. }
  984. if (audioTrack.sampleRate != 0) {
  985. builder.setAudioSampleRate(audioTrack.sampleRate);
  986. }
  987. TvTrackInfo track = builder.build();
  988. mTvTracks.add(track);
  989. mAudioTrackMap.put(index, audioTrack);
  990. ++index;
  991. }
  992. }
  993. mSession.notifyTracksChanged(mTvTracks);
  994. }
  995. private void updateCaptionTracks(List<AtscCaptionTrack> captionTracks) {
  996. if (DEBUG) {
  997. Log.d(TAG, "Update CaptionTrack " + captionTracks);
  998. }
  999. removeTvTracks(TvTrackInfo.TYPE_SUBTITLE);
  1000. mCaptionTrackMap.clear();
  1001. if (captionTracks != null) {
  1002. for (AtscCaptionTrack captionTrack : captionTracks) {
  1003. if (mCaptionTrackMap.indexOfKey(captionTrack.serviceNumber) >= 0) {
  1004. continue;
  1005. }
  1006. String language = captionTrack.language;
  1007. // The service number of the caption service is used for track id of a subtitle.
  1008. // Later, when a subtitle is chosen, track id will be passed on to TsParser.
  1009. TvTrackInfo.Builder builder =
  1010. new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE,
  1011. SUBTITLE_TRACK_PREFIX + captionTrack.serviceNumber);
  1012. if (IsoUtils.isValidIso3Language(language)) {
  1013. builder.setLanguage(language);
  1014. }
  1015. mTvTracks.add(builder.build());
  1016. mCaptionTrackMap.put(captionTrack.serviceNumber, captionTrack);
  1017. }
  1018. }
  1019. mSession.notifyTracksChanged(mTvTracks);
  1020. }
  1021. private void updateChannelInfo(TunerChannel channel) {
  1022. if (DEBUG) {
  1023. Log.d(TAG, String.format("Channel Info (old) videoPid: %d audioPid: %d " +
  1024. "audioSize: %d", mChannel.getVideoPid(), mChannel.getAudioPid(),
  1025. mChannel.getAudioPids().size()));
  1026. }
  1027. // The list of the audio tracks resided in a channel is often changed depending on a
  1028. // program being on the air. So, we should update the streaming PIDs and types of the
  1029. // tuned channel according to the newly received channel data.
  1030. int oldVideoPid = mChannel.getVideoPid();
  1031. int oldAudioPid = mChannel.getAudioPid();
  1032. List<Integer> audioPids = channel.getAudioPids();
  1033. List<Integer> audioStreamTypes = channel.getAudioStreamTypes();
  1034. int size = audioPids.size();
  1035. mChannel.setVideoPid(channel.getVideoPid());
  1036. mChannel.setAudioPids(audioPids);
  1037. mChannel.setAudioStreamTypes(audioStreamTypes);
  1038. updateTvTracks(mChannel);
  1039. int index = audioPids.isEmpty() ? -1 : 0;
  1040. for (int i = 0; i < size; ++i) {
  1041. if (audioPids.get(i) == oldAudioPid) {
  1042. index = i;
  1043. break;
  1044. }
  1045. }
  1046. mChannel.selectAudioTrack(index);
  1047. mSession.notifyTrackSelected(TvTrackInfo.TYPE_AUDIO,
  1048. index == -1 ? null : AUDIO_TRACK_PREFIX + index);
  1049. // Reset playback if there is a change in the listening streaming PIDs.
  1050. if (oldVideoPid != mChannel.getVideoPid()
  1051. || oldAudioPid != mChannel.getAudioPid()) {
  1052. // TODO: Implement a switching between tracks more smoothly.
  1053. resetPlayback();
  1054. }
  1055. if (DEBUG) {
  1056. Log.d(TAG, String.format("Channel Info (new) videoPid: %d audioPid: %d " +
  1057. " audioSize: %d", mChannel.getVideoPid(), mChannel.getAudioPid(),
  1058. mChannel.getAudioPids().size()));
  1059. }
  1060. }
  1061. private void stopPlayback() {
  1062. if (mPlayer != null) {
  1063. if (mSource != null) {
  1064. mSource.stopStream();
  1065. }
  1066. mPlayer.setPlayWhenReady(false);
  1067. mPlayer.release();
  1068. mPlayer = null;
  1069. mPlaybackParams.setSpeed(1.0f);
  1070. mPlayerStarted = false;
  1071. mReportedDrawnToSurface = false;
  1072. mReportedSignalAvailable = false;
  1073. mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_AUDIO_UNPLAYABLE);
  1074. }
  1075. }
  1076. private void startPlayback(Object playerObj) {
  1077. // TODO: provide hasAudio()/hasVideo() for play recordings.
  1078. if (mPlayer == null || mPlayer != playerObj) {
  1079. return;
  1080. }
  1081. if (mChannel != null && !mChannel.hasAudio()) {
  1082. // A channel needs to have a audio stream at least to play in exoPlayer.
  1083. mSession.notifyVideoUnavailable(
  1084. TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN);
  1085. return;
  1086. }
  1087. if (mSurface != null && !mPlayerStarted) {
  1088. mPlayer.setSurface(mSurface);
  1089. mPlayer.setPlayWhenReady(true);
  1090. mPlayer.setVolume(mVolume);
  1091. if (mChannel != null && !mChannel.hasVideo() && mChannel.hasAudio()) {
  1092. mSession.notifyVideoUnavailable(
  1093. TvInputManager.VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY);
  1094. } else {
  1095. mSession.notifyVideoUnavailable(
  1096. TvInputManager.VIDEO_UNAVAILABLE_REASON_BUFFERING);
  1097. }
  1098. mSession.sendUiMessage(TunerSession.MSG_UI_HIDE_MESSAGE);
  1099. mPlayerStarted = true;
  1100. }
  1101. }
  1102. private void playFromChannel(long timestamp) {
  1103. long oldTimestamp;
  1104. mSource = null;
  1105. if (mChannel.getType() == Channel.TYPE_TUNER) {
  1106. mSource = mTunerSource;
  1107. } else if (mChannel.getType() == Channel.TYPE_FILE) {
  1108. mSource = mFileSource;
  1109. }
  1110. Assert.assertNotNull(mSource);
  1111. if (mSource.tuneToChannel(mChannel)) {
  1112. if (ENABLE_PROFILER) {
  1113. oldTimestamp = timestamp;
  1114. timestamp = SystemClock.elapsedRealtime();
  1115. Log.i(TAG, "[Profiler] …

Large files files are truncated, but you can click here to view the full file