diff options
author | Xin Li <delphij@google.com> | 2021-08-14 06:31:01 +0000 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2021-08-14 06:31:01 +0000 |
commit | 2ec8408e77758597061356e1644b11bb50608515 (patch) | |
tree | f3b83727e120cc8fe898c5b216081ef989d20670 | |
parent | f724f148987c687ca7244a34da5109a24e64b391 (diff) | |
parent | e7b4ffb232e400a8257b7da1e9dec80c4820e8e4 (diff) | |
download | TV-2ec8408e77758597061356e1644b11bb50608515.tar.gz |
Merge sc-dev-plus-aosp-without-vendor@7634622
Merged-In: I3dd531d7307ea651c40da1c1f99613688e91fa46
Change-Id: I0ccf1a78a2c6109579912a644ac41ebc5f8ea8e7
-rw-r--r-- | AndroidManifest.xml | 9 | ||||
-rw-r--r-- | com.android.tv.xml | 3 | ||||
-rw-r--r-- | lint-baseline.xml | 16 | ||||
-rw-r--r-- | src/com/android/tv/MainActivity.java | 7 | ||||
-rw-r--r-- | src/com/android/tv/util/GtvUtils.java | 56 | ||||
-rw-r--r-- | tests/common/Android.mk | 4 | ||||
-rw-r--r-- | tests/input/Android.mk | 4 | ||||
-rw-r--r-- | tuner/sampletunertvinput/AndroidManifest.xml | 6 | ||||
-rw-r--r-- | tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml | 6 | ||||
-rw-r--r-- | tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java | 385 |
10 files changed, 464 insertions, 32 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 854f2883..2cff92d3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -34,6 +34,7 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_TV_LISTINGS"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> + <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/> <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/> @@ -115,6 +116,7 @@ <category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> + <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name="com.android.tv.MainActivity" @@ -165,7 +167,12 @@ <activity android:name="com.android.tv.SelectInputActivity" android:configChanges="keyboard|keyboardHidden" android:launchMode="singleTask" - android:theme="@style/Theme.SelectInputActivity"/> + android:theme="@style/Theme.SelectInputActivity"> + <intent-filter> + <action android:name="com.android.tv.action.VIEW_INPUTS" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> <activity android:name="com.android.tv.onboarding.OnboardingActivity" android:configChanges="keyboard|keyboardHidden" android:launchMode="singleTop" diff --git a/com.android.tv.xml b/com.android.tv.xml index 245d275d..5ac6c0c3 100644 --- a/com.android.tv.xml +++ b/com.android.tv.xml @@ -1,5 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <permissions> + <feature name="com.google.android.tv.installed" /> + <privapp-permissions package="com.android.tv"> <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/> <permission name="android.permission.DVB_DEVICE"/> @@ -7,6 +9,7 @@ <permission name="android.permission.HDMI_CEC"/> <permission name="android.permission.MODIFY_PARENTAL_CONTROLS"/> <permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/> + <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/> <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/> </privapp-permissions> diff --git a/lint-baseline.xml b/lint-baseline.xml index 2a8be311..d91a1894 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -118,7 +118,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="529" + line="534" column="28"/> </issue> @@ -129,7 +129,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="997" + line="1002" column="28"/> </issue> @@ -140,7 +140,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="1024" + line="1029" column="48"/> </issue> @@ -151,7 +151,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="1032" + line="1037" column="28"/> </issue> @@ -162,7 +162,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="1060" + line="1065" column="28"/> </issue> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="1539" + line="1544" column="35"/> </issue> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="2397" + line="2402" column="27"/> </issue> @@ -195,7 +195,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> <location file="packages/apps/TV/src/com/android/tv/MainActivity.java" - line="2808" + line="2813" column="27"/> </issue> diff --git a/src/com/android/tv/MainActivity.java b/src/com/android/tv/MainActivity.java index 9c615b93..8dbafe47 100644 --- a/src/com/android/tv/MainActivity.java +++ b/src/com/android/tv/MainActivity.java @@ -149,6 +149,7 @@ import com.android.tv.ui.sidepanel.parentalcontrols.RatingsFragment; import com.android.tv.util.AsyncDbTask; import com.android.tv.util.AsyncDbTask.DbExecutor; import com.android.tv.util.CaptionSettings; +import com.android.tv.util.GtvUtils; import com.android.tv.util.OnboardingUtils; import com.android.tv.util.SetupUtils; import com.android.tv.util.TvInputManagerHelper; @@ -456,7 +457,11 @@ public class MainActivity extends Activity } @Override - public void onChannelChanged(Channel previousChannel, Channel currentChannel) {} + public void onChannelChanged(Channel previousChannel, Channel currentChannel) { + if (currentChannel != null) { + GtvUtils.broadcastInputId(MainActivity.this, currentChannel.getInputId()); + } + } }; private final Runnable mRestoreMainViewRunnable = this::restoreMainTvView; diff --git a/src/com/android/tv/util/GtvUtils.java b/src/com/android/tv/util/GtvUtils.java new file mode 100644 index 00000000..eb50e062 --- /dev/null +++ b/src/com/android/tv/util/GtvUtils.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tv.util; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.util.Log; + +/** A utility class for Google TV */ +public class GtvUtils { + private static final String TAG = "GtvUtils"; + private static final String AMATI_FEATURE = "com.google.android.feature.AMATI_EXPERIENCE"; + private static final String PERMISSION_WRITE_EPG_DATA = + "com.android.providers.tv.permission.WRITE_EPG_DATA"; + private static final String ACTION_INPUT_SELECTED = "android.apps.tv.launcherx.INPUT_SELECTED"; + private static final String EXTRA_INPUT_ID = "extra_input_id"; + private static final String LAUNCHERX_PACKAGE_NAME = "com.google.android.apps.tv.launcherx"; + private static Boolean mEnabled = null; + + private static boolean isEnabled(Context context) { + if (mEnabled == null) { + PackageManager pm = context.getPackageManager(); + mEnabled = pm.hasSystemFeature(AMATI_FEATURE); + } + return mEnabled; + } + + /** Broadcasts the intent with inputId to the Launcher */ + public static void broadcastInputId(Context context, String inputId) { + if (isEnabled(context)) { + if (inputId == null) { + Log.e(TAG, "Will not broadcast inputId because it is null"); + } else { + Intent intent = new Intent(ACTION_INPUT_SELECTED); + intent.putExtra(EXTRA_INPUT_ID, inputId); + intent.setPackage(LAUNCHERX_PACKAGE_NAME); + context.sendBroadcast(intent, PERMISSION_WRITE_EPG_DATA); + } + } + } +} diff --git a/tests/common/Android.mk b/tests/common/Android.mk index b74a65d3..7a232ff7 100644 --- a/tests/common/Android.mk +++ b/tests/common/Android.mk @@ -17,6 +17,10 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ mockito-robolectric-prebuilt \ tv-test-common \ +# Disable dexpreopt and <uses-library> check for test. +LOCAL_ENFORCE_USES_LIBRARIES := false +LOCAL_DEX_PREOPT := false + LOCAL_INSTRUMENTATION_FOR := LiveTv LOCAL_MODULE_TAGS := optional diff --git a/tests/input/Android.mk b/tests/input/Android.mk index c15e4a49..4011dec8 100644 --- a/tests/input/Android.mk +++ b/tests/input/Android.mk @@ -16,6 +16,10 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ tv-test-common \ tv-common +# Disable dexpreopt and <uses-library> check for test. +LOCAL_ENFORCE_USES_LIBRARIES := false +LOCAL_DEX_PREOPT := false + LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/../common/res $(LOCAL_PATH)/res LOCAL_AAPT_FLAGS := --auto-add-overlay \ --extra-packages com.android.tv.testing diff --git a/tuner/sampletunertvinput/AndroidManifest.xml b/tuner/sampletunertvinput/AndroidManifest.xml index d282889a..8b25d0bf 100644 --- a/tuner/sampletunertvinput/AndroidManifest.xml +++ b/tuner/sampletunertvinput/AndroidManifest.xml @@ -43,7 +43,8 @@ android:theme="@android:style/Theme.Holo.Light.NoActionBar" android:appComponentFactory="android.support.v4.app.CoreComponentFactory" > <uses-library android:name="com.android.libraries.tv.tvsystem" android:required="false" /> - <activity android:name=".SampleTunerTvInputSetupActivity" > + <activity android:name=".SampleTunerTvInputSetupActivity" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> @@ -51,7 +52,8 @@ <service android:name=".SampleTunerTvInputService" android:permission="android.permission.BIND_TV_INPUT" android:label="@string/sample_tuner_tv_input" - android:process="com.android.tv.samples.sampletunertvinput"> + android:process="com.android.tv.samples.sampletunertvinput" + android:exported="true"> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> diff --git a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml index 8fc96b2a..909e2431 100644 --- a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml +++ b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/AndroidManifest.xml @@ -39,7 +39,8 @@ android:icon="@mipmap/ic_launcher" android:theme="@android:style/Theme.Holo.Light.NoActionBar" android:appComponentFactory="android.support.v4.app.CoreComponentFactory" > - <activity android:name=".SampleTunerTvInputSetupActivity" > + <activity android:name=".SampleTunerTvInputSetupActivity" + android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> @@ -47,7 +48,8 @@ <service android:name=".SampleTunerTvInputService" android:permission="android.permission.BIND_TV_INPUT" android:label="@string/sample_tuner_tv_input" - android:process="com.android.tv.samples.sampletunertvinput"> + android:process="com.android.tv.samples.sampletunertvinput" + android:exported="true"> <intent-filter> <action android:name="android.media.tv.TvInputService" /> </intent-filter> diff --git a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java index 6ac95353..e86ced14 100644 --- a/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java +++ b/tuner/sampletunertvinput/src/com/android/tv/samples/sampletunertvinput/SampleTunerTvInputService.java @@ -1,13 +1,39 @@ package com.android.tv.samples.sampletunertvinput; +import static android.media.tv.TvInputManager.VIDEO_UNAVAILABLE_REASON_UNKNOWN; + import android.content.Context; +import android.media.MediaCodec; +import android.media.MediaCodec.BufferInfo; +import android.media.MediaFormat; +import android.media.tv.tuner.dvr.DvrPlayback; +import android.media.tv.tuner.dvr.DvrSettings; +import android.media.tv.tuner.filter.AvSettings; +import android.media.tv.tuner.filter.Filter; +import android.media.tv.tuner.filter.FilterCallback; +import android.media.tv.tuner.filter.FilterEvent; +import android.media.tv.tuner.filter.MediaEvent; +import android.media.tv.tuner.filter.TsFilterConfiguration; import android.media.tv.tuner.frontend.AtscFrontendSettings; +import android.media.tv.tuner.frontend.DvbtFrontendSettings; import android.media.tv.tuner.frontend.FrontendSettings; +import android.media.tv.tuner.frontend.OnTuneEventListener; import android.media.tv.tuner.Tuner; import android.media.tv.TvInputService; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.ParcelFileDescriptor; import android.util.Log; import android.view.Surface; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; /** SampleTunerTvInputService */ @@ -15,8 +41,39 @@ public class SampleTunerTvInputService extends TvInputService { private static final String TAG = "SampleTunerTvInput"; private static final boolean DEBUG = true; + private static final int AUDIO_TPID = 257; + private static final int VIDEO_TPID = 256; + private static final int STATUS_MASK = 0xf; + private static final int LOW_THRESHOLD = 0x1000; + private static final int HIGH_THRESHOLD = 0x07fff; + private static final int FREQUENCY = 578000; + private static final int FILTER_BUFFER_SIZE = 16000000; + private static final int DVR_BUFFER_SIZE = 4000000; + private static final int INPUT_FILE_MAX_SIZE = 700000; + private static final int PACKET_SIZE = 188; + + private static final int TIMEOUT_US = 100000; + private static final boolean SAVE_DATA = true; + private static final String ES_PATH = "/data/local/tmp/test.es"; + private static final MediaFormat VIDEO_FORMAT; + + static { + // format extracted for the specific input file + VIDEO_FORMAT = MediaFormat.createVideoFormat("video/avc", 320, 240); + VIDEO_FORMAT.setInteger(MediaFormat.KEY_TRACK_ID, 1); + VIDEO_FORMAT.setLong(MediaFormat.KEY_DURATION, 9933333); + VIDEO_FORMAT.setInteger(MediaFormat.KEY_LEVEL, 32); + VIDEO_FORMAT.setInteger(MediaFormat.KEY_PROFILE, 65536); + ByteBuffer csd = ByteBuffer.wrap( + new byte[] {0, 0, 0, 1, 103, 66, -64, 20, -38, 5, 7, -24, 64, 0, 0, 3, 0, 64, 0, + 0, 15, 35, -59, 10, -88}); + VIDEO_FORMAT.setByteBuffer("csd-0", csd); + csd = ByteBuffer.wrap(new byte[] {0, 0, 0, 1, 104, -50, 60, -128}); + VIDEO_FORMAT.setByteBuffer("csd-1", csd); + } + public static final String INPUT_ID = - "com.android.tv.samples.sampletunertvinput/.SampleTunerTvInputService"; + "com.android.tv.samples.sampletunertvinput/.SampleTunerTvInputService"; private String mSessionId; @Override @@ -36,9 +93,19 @@ public class SampleTunerTvInputService extends TvInputService { class TvInputSessionImpl extends Session { - private Surface surface; private final Context mContext; - Tuner tuner; + private Handler mHandler; + + private Surface mSurface; + private Filter mAudioFilter; + private Filter mVideoFilter; + private DvrPlayback mDvr; + private Tuner mTuner; + private MediaCodec mMediaCodec; + private Thread mDecoderThread; + private Deque<MediaEvent> mDataQueue; + private List<MediaEvent> mSavedData; + private boolean mDataReady = false; public TvInputSessionImpl(Context context) { @@ -51,6 +118,28 @@ public class SampleTunerTvInputService extends TvInputService { if (DEBUG) { Log.d(TAG, "onRelease"); } + if (mDecoderThread != null) { + mDecoderThread.interrupt(); + mDecoderThread = null; + } + if (mMediaCodec != null) { + mMediaCodec.release(); + mMediaCodec = null; + } + if (mAudioFilter != null) { + mAudioFilter.close(); + } + if (mVideoFilter != null) { + mVideoFilter.close(); + } + if (mDvr != null) { + mDvr.close(); + } + if (mTuner != null) { + mTuner.close(); + } + mDataQueue = null; + mSavedData = null; } @Override @@ -58,7 +147,7 @@ public class SampleTunerTvInputService extends TvInputService { if (DEBUG) { Log.d(TAG, "onSetSurface"); } - this.surface = surface; + this.mSurface = surface; return true; } @@ -74,20 +163,16 @@ public class SampleTunerTvInputService extends TvInputService { if (DEBUG) { Log.d(TAG, "onTune " + uri); } - tuner = new Tuner(mContext, mSessionId, - TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); - - int feCount = tuner.getFrontendIds().size(); - if (feCount <= 0) return false; - - AtscFrontendSettings settings = - AtscFrontendSettings - .builder() - .setFrequency(2000) - .setModulation(AtscFrontendSettings.MODULATION_AUTO) - .build(); - tuner.tune(settings); - + if (!initCodec()) { + Log.e(TAG, "null codec!"); + return false; + } + mHandler = new Handler(); + mDecoderThread = + new Thread( + this::decodeInternal, + "sample-tuner-tis-decoder-thread"); + mDecoderThread.start(); return true; } @@ -97,5 +182,269 @@ public class SampleTunerTvInputService extends TvInputService { Log.d(TAG, "onSetCaptionEnabled " + b); } } + + private Filter audioFilter() { + Filter audioFilter = mTuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_AUDIO, + FILTER_BUFFER_SIZE, new HandlerExecutor(mHandler), + new FilterCallback() { + @Override + public void onFilterEvent(Filter filter, FilterEvent[] events) { + if (DEBUG) { + Log.d(TAG, "onFilterEvent audio, size=" + events.length); + } + for (int i = 0; i < events.length; i++) { + if (DEBUG) { + Log.d(TAG, "events[" + i + "] is " + + events[i].getClass().getSimpleName()); + } + } + } + + @Override + public void onFilterStatusChanged(Filter filter, int status) { + if (DEBUG) { + Log.d(TAG, "onFilterEvent audio, status=" + status); + } + } + }); + AvSettings settings = + AvSettings.builder(Filter.TYPE_TS, true).setPassthrough(false).build(); + audioFilter.configure( + TsFilterConfiguration.builder().setTpid(AUDIO_TPID) + .setSettings(settings).build()); + return audioFilter; + } + + private Filter videoFilter() { + Filter videoFilter = mTuner.openFilter(Filter.TYPE_TS, Filter.SUBTYPE_VIDEO, + FILTER_BUFFER_SIZE, new HandlerExecutor(mHandler), + new FilterCallback() { + @Override + public void onFilterEvent(Filter filter, FilterEvent[] events) { + if (DEBUG) { + Log.d(TAG, "onFilterEvent video, size=" + events.length); + } + for (int i = 0; i < events.length; i++) { + if (DEBUG) { + Log.d(TAG, "events[" + i + "] is " + + events[i].getClass().getSimpleName()); + } + if (events[i] instanceof MediaEvent) { + MediaEvent me = (MediaEvent) events[i]; + mDataQueue.add(me); + if (SAVE_DATA) { + mSavedData.add(me); + } + } + } + } + + @Override + public void onFilterStatusChanged(Filter filter, int status) { + if (DEBUG) { + Log.d(TAG, "onFilterEvent video, status=" + status); + if (status == Filter.STATUS_DATA_READY) { + mDataReady = true; + } + } + } + }); + AvSettings settings = + AvSettings.builder(Filter.TYPE_TS, false).setPassthrough(false).build(); + videoFilter.configure( + TsFilterConfiguration.builder().setTpid(VIDEO_TPID) + .setSettings(settings).build()); + return videoFilter; + } + + private DvrPlayback dvrPlayback() { + DvrPlayback dvr = mTuner.openDvrPlayback(DVR_BUFFER_SIZE, new HandlerExecutor(mHandler), + status -> { + if (DEBUG) { + Log.d(TAG, "onPlaybackStatusChanged status=" + status); + } + }); + int res = dvr.configure( + DvrSettings.builder() + .setStatusMask(STATUS_MASK) + .setLowThreshold(LOW_THRESHOLD) + .setHighThreshold(HIGH_THRESHOLD) + .setDataFormat(DvrSettings.DATA_FORMAT_ES) + .setPacketSize(PACKET_SIZE) + .build()); + if (DEBUG) { + Log.d(TAG, "config res=" + res); + } + File file = new File(ES_PATH); + if (file.exists()) { + try { + dvr.setFileDescriptor( + ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)); + } catch (FileNotFoundException e) { + Log.e(TAG, "Failed to create FD"); + } + } else { + Log.w(TAG, "File not existing"); + } + return dvr; + } + + private void tune() { + DvbtFrontendSettings feSettings = DvbtFrontendSettings.builder() + .setFrequency(FREQUENCY) + .setTransmissionMode(DvbtFrontendSettings.TRANSMISSION_MODE_AUTO) + .setBandwidth(DvbtFrontendSettings.BANDWIDTH_8MHZ) + .setConstellation(DvbtFrontendSettings.CONSTELLATION_AUTO) + .setHierarchy(DvbtFrontendSettings.HIERARCHY_AUTO) + .setHighPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO) + .setLowPriorityCodeRate(DvbtFrontendSettings.CODERATE_AUTO) + .setGuardInterval(DvbtFrontendSettings.GUARD_INTERVAL_AUTO) + .setHighPriority(true) + .setStandard(DvbtFrontendSettings.STANDARD_T) + .build(); + mTuner.setOnTuneEventListener(new HandlerExecutor(mHandler), new OnTuneEventListener() { + @Override + public void onTuneEvent(int tuneEvent) { + if (DEBUG) { + Log.d(TAG, "onTuneEvent " + tuneEvent); + } + long read = mDvr.read(INPUT_FILE_MAX_SIZE); + if (DEBUG) { + Log.d(TAG, "read=" + read); + } + } + }); + mTuner.tune(feSettings); + } + + private boolean initCodec() { + if (mMediaCodec != null) { + mMediaCodec.release(); + mMediaCodec = null; + } + try { + mMediaCodec = MediaCodec.createDecoderByType("video/avc"); + mMediaCodec.configure(VIDEO_FORMAT, mSurface, null, 0); + } catch (IOException e) { + Log.e(TAG, "Error: " + e.getMessage()); + } + + if (mMediaCodec == null) { + Log.e(TAG, "null codec!"); + notifyVideoUnavailable(VIDEO_UNAVAILABLE_REASON_UNKNOWN); + return false; + } + return true; + } + + private void decodeInternal() { + mDataQueue = new ArrayDeque<>(); + mSavedData = new ArrayList<>(); + mTuner = new Tuner(mContext, mSessionId, + TvInputService.PRIORITY_HINT_USE_CASE_TYPE_LIVE); + + mAudioFilter = audioFilter(); + mVideoFilter = videoFilter(); + mAudioFilter.start(); + mVideoFilter.start(); + // use dvr playback to feed the data on platform without physical tuner + mDvr = dvrPlayback(); + tune(); + mDvr.start(); + mMediaCodec.start(); + + try { + while (!Thread.interrupted()) { + if (!mDataReady) { + Thread.sleep(100); + } + if (!mDataQueue.isEmpty()) { + if (queueCodecInputBuffer(mDataQueue.getFirst())) { + // data consumed, remove. + mDataQueue.pollFirst(); + } + } else if (SAVE_DATA) { + mDataQueue.addAll(mSavedData); + } + releaseCodecOutputBuffer(); + } + } catch (Exception e) { + Log.e(TAG, "Error: " + e.getMessage()); + } + } + + private boolean queueCodecInputBuffer(MediaEvent mediaEvent) { + int res = mMediaCodec.dequeueInputBuffer(TIMEOUT_US); + if (res >= 0) { + ByteBuffer buffer = mMediaCodec.getInputBuffer(res); + if (buffer == null) { + throw new RuntimeException("Null decoder input buffer"); + } + + ByteBuffer data = mediaEvent.getLinearBlock().map(); + int sampleSize = (int) mediaEvent.getDataLength(); + int offset = (int) mediaEvent.getOffset(); + long pts = mediaEvent.getPts(); + + if (offset > 0 && offset < data.limit()) { + data.position(offset); + } else { + data.position(0); + } + + if (DEBUG) { + Log.d( + TAG, + "Decoder: Send data to decoder." + + " Sample size=" + + sampleSize + + " pts=" + + pts + + " limit=" + + data.limit() + + " pos=" + + data.position() + + " size=" + + (data.limit() - data.position())); + } + while (data.position() < data.limit()) { + // fill codec input buffer + buffer.put(data.get()); + } + + mMediaCodec.queueInputBuffer(res, 0, sampleSize, pts, 0); + } else { + Log.d(TAG, "queueCodecInputBuffer res=" + res); + return false; + } + return true; + } + + private void releaseCodecOutputBuffer() { + // play frames + BufferInfo bufferInfo = new BufferInfo(); + int res = mMediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); + if (res >= 0) { + mMediaCodec.releaseOutputBuffer(res, true); + notifyVideoAvailable(); + if (DEBUG) { + Log.d(TAG, "notifyVideoAvailable"); + } + } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + MediaFormat format = mMediaCodec.getOutputFormat(); + if (DEBUG) { + Log.d(TAG, "releaseCodecOutputBuffer: Output format changed:" + format); + } + } else if (res == MediaCodec.INFO_TRY_AGAIN_LATER) { + if (DEBUG) { + Log.d(TAG, "releaseCodecOutputBuffer: timeout"); + } + } else { + if (DEBUG) { + Log.d(TAG, "Return value of releaseCodecOutputBuffer:" + res); + } + } + } + } }
\ No newline at end of file |