blob: d85ab776964cd21cca4f8414c12fb1dac960033f [file] [log] [blame]
/*
* Copyright (C) 2022 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.samples.sampletvinteractiveappservice;
import android.annotation.TargetApi;
import android.app.Presentation;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaPlayer;
import android.media.tv.AdRequest;
import android.media.tv.AdResponse;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.SectionRequest;
import android.media.tv.SectionResponse;
import android.media.tv.StreamEventRequest;
import android.media.tv.StreamEventResponse;
import android.media.tv.TableRequest;
import android.media.tv.TableResponse;
import android.media.tv.TvTrackInfo;
import android.media.tv.interactive.AppLinkInfo;
import android.media.tv.interactive.TvInteractiveAppManager;
import android.media.tv.interactive.TvInteractiveAppService;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TiasSessionImpl extends TvInteractiveAppService.Session {
private static final String TAG = "SampleTvInteractiveAppService";
private static final boolean DEBUG = true;
private static final String VIRTUAL_DISPLAY_NAME = "sample_tias_display";
// For testing purposes, limit the number of response for a single request
private static final int MAX_HANDLED_RESPONSE = 3;
private final Context mContext;
private TvInteractiveAppManager mTvIAppManager;
private final Handler mHandler;
private final String mAppServiceId;
private final int mType;
private final ViewGroup mViewContainer;
private Surface mSurface;
private VirtualDisplay mVirtualDisplay;
private List<TvTrackInfo> mTracks;
private TextView mTvInputIdView;
private TextView mChannelUriView;
private TextView mVideoTrackView;
private TextView mAudioTrackView;
private TextView mSubtitleTrackView;
private TextView mLogView;
private VideoView mVideoView;
private SurfaceView mAdSurfaceView;
private Surface mAdSurface;
private ParcelFileDescriptor mAdFd;
private FrameLayout mMediaContainer;
private int mAdState;
private int mWidth;
private int mHeight;
private int mScreenWidth;
private int mScreenHeight;
private String mCurrentTvInputId;
private Uri mCurrentChannelUri;
private String mSelectingAudioTrackId;
private String mFirstAudioTrackId;
private int mGeneratedRequestId = 0;
private boolean mRequestStreamEventFinished = false;
private int mSectionReceived = 0;
private List<String> mStreamDataList = new ArrayList<>();
private boolean mIsFullScreen = true;
public TiasSessionImpl(Context context, String iAppServiceId, int type) {
super(context);
if (DEBUG) {
Log.d(TAG, "Constructing service with iAppServiceId=" + iAppServiceId
+ " type=" + type);
}
mContext = context;
mAppServiceId = iAppServiceId;
mType = type;
mHandler = new Handler(context.getMainLooper());
mTvIAppManager = (TvInteractiveAppManager) mContext.getSystemService(
Context.TV_INTERACTIVE_APP_SERVICE);
mViewContainer = new LinearLayout(context);
mViewContainer.setBackground(new ColorDrawable(0));
}
@Override
public View onCreateMediaView() {
mAdSurfaceView = new SurfaceView(mContext);
if (DEBUG) {
Log.d(TAG, "create surfaceView");
}
mAdSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
mAdSurfaceView
.getHolder()
.addCallback(
new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
mAdSurface = holder.getSurface();
}
@Override
public void surfaceChanged(
SurfaceHolder holder, int format, int width, int height) {
mAdSurface = holder.getSurface();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
});
mAdSurfaceView.setVisibility(View.INVISIBLE);
ViewGroup.LayoutParams layoutParams =
new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mAdSurfaceView.setLayoutParams(layoutParams);
mMediaContainer.addView(mVideoView);
mMediaContainer.addView(mAdSurfaceView);
return mMediaContainer;
}
@Override
public void onAdResponse(AdResponse adResponse) {
mAdState = adResponse.getResponseType();
switch (mAdState) {
case AdResponse.RESPONSE_TYPE_PLAYING:
long time = adResponse.getElapsedTimeMillis();
updateLogText("AD is playing. " + time);
break;
case AdResponse.RESPONSE_TYPE_STOPPED:
updateLogText("AD is stopped.");
mAdSurfaceView.setVisibility(View.INVISIBLE);
break;
case AdResponse.RESPONSE_TYPE_FINISHED:
updateLogText("AD is play finished.");
mAdSurfaceView.setVisibility(View.INVISIBLE);
break;
}
}
@Override
public void onRelease() {
if (DEBUG) {
Log.d(TAG, "onRelease");
}
if (mSurface != null) {
mSurface.release();
mSurface = null;
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
}
@Override
public boolean onSetSurface(Surface surface) {
if (DEBUG) {
Log.d(TAG, "onSetSurface");
}
if (mSurface != null) {
mSurface.release();
}
updateSurface(surface, mWidth, mHeight);
mSurface = surface;
return true;
}
@Override
public void onSurfaceChanged(int format, int width, int height) {
if (DEBUG) {
Log.d(TAG, "onSurfaceChanged format=" + format + " width=" + width +
" height=" + height);
}
if (mSurface != null) {
updateSurface(mSurface, width, height);
mWidth = width;
mHeight = height;
}
}
@Override
public void onStartInteractiveApp() {
if (DEBUG) {
Log.d(TAG, "onStartInteractiveApp");
}
mHandler.post(
() -> {
initSampleView();
setMediaViewEnabled(true);
requestCurrentTvInputId();
requestCurrentChannelUri();
requestTrackInfoList();
}
);
}
@Override
public void onStopInteractiveApp() {
if (DEBUG) {
Log.d(TAG, "onStopInteractiveApp");
}
}
public void prepare(TvInteractiveAppService serviceCaller) {
// Slightly delay our post to ensure the Manager has had time to register our Session
mHandler.postDelayed(
() -> {
if (serviceCaller != null) {
serviceCaller.notifyStateChanged(mType,
TvInteractiveAppManager.SERVICE_STATE_READY,
TvInteractiveAppManager.ERROR_NONE);
}
},
100);
}
@Override
public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) {
// TODO: use a menu view instead of key events for the following tests
switch (keyCode) {
case KeyEvent.KEYCODE_PROG_RED:
tuneToNextChannel();
return true;
case KeyEvent.KEYCODE_A:
updateLogText("stop video broadcast begin");
tuneChannelByType(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_STOP,
mCurrentTvInputId,
null);
updateLogText("stop video broadcast end");
return true;
case KeyEvent.KEYCODE_B:
updateLogText("resume video broadcast begin");
tuneChannelByType(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_TUNE,
mCurrentTvInputId,
mCurrentChannelUri);
updateLogText("resume video broadcast end");
return true;
case KeyEvent.KEYCODE_C:
updateLogText("unselect audio track");
mSelectingAudioTrackId = null;
selectTrack(TvTrackInfo.TYPE_AUDIO, null);
return true;
case KeyEvent.KEYCODE_D:
updateLogText("select audio track " + mFirstAudioTrackId);
mSelectingAudioTrackId = mFirstAudioTrackId;
selectTrack(TvTrackInfo.TYPE_AUDIO, mFirstAudioTrackId);
return true;
case KeyEvent.KEYCODE_E:
if (mVideoView != null) {
if (mVideoView.isPlaying()) {
updateLogText("stop media");
mVideoView.stopPlayback();
mVideoView.setVisibility(View.GONE);
tuneChannelByType(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_TUNE,
mCurrentTvInputId,
mCurrentChannelUri);
} else {
updateLogText("play media");
tuneChannelByType(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_STOP,
mCurrentTvInputId,
null);
mVideoView.setVisibility(View.VISIBLE);
// TODO: put a file sample.mp4 in res/raw/ and use R.raw.sample for the URI
Uri uri = Uri.parse(
"android.resource://" + mContext.getPackageName() + "/");
mVideoView.setVideoURI(uri);
mVideoView.start();
updateLogText("media is playing");
}
}
return true;
case KeyEvent.KEYCODE_F:
updateLogText("request StreamEvent");
mRequestStreamEventFinished = false;
mStreamDataList.clear();
// TODO: build target URI instead of using channel URI
requestStreamEvent(
mCurrentChannelUri == null ? null : mCurrentChannelUri.toString(),
"event1");
return true;
case KeyEvent.KEYCODE_G:
updateLogText("change video bounds");
if (mIsFullScreen) {
setVideoBounds(new Rect(100, 150, 960, 540));
updateLogText("Change video broadcast size(100, 150, 960, 540)");
mIsFullScreen = false;
} else {
setVideoBounds(new Rect(0, 0, mScreenWidth, mScreenHeight));
updateLogText("Change video broadcast full screen");
mIsFullScreen = true;
}
return true;
case KeyEvent.KEYCODE_H:
updateLogText("request section");
mSectionReceived = 0;
requestSection(false, 0, 0x0, -1);
return true;
case KeyEvent.KEYCODE_I:
if (mTvIAppManager == null) {
updateLogText("TvIAppManager null");
return false;
}
List<AppLinkInfo> appLinks = getAppLinkInfoList();
if (appLinks.isEmpty()) {
updateLogText("Not found AppLink");
} else {
AppLinkInfo appLink = appLinks.get(0);
Intent intent = new Intent();
intent.setComponent(appLink.getComponentName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.getApplicationContext().startActivity(intent);
updateLogText("Launch " + appLink.getComponentName());
}
return true;
case KeyEvent.KEYCODE_J:
updateLogText("Request SI Tables ");
// Network Information Table (NIT)
requestTable(false, 0x40, /* TableRequest.TABLE_NAME_NIT */ 3, -1);
// Service Description Table (SDT)
requestTable(false, 0x42, /* TableRequest.TABLE_NAME_SDT */ 5, -1);
// Event Information Table (EIT)
requestTable(false, 0x4e, /* TableRequest.TABLE_NAME_EIT */ 6, -1);
return true;
case KeyEvent.KEYCODE_K:
updateLogText("Request Video Bounds");
requestCurrentVideoBoundsWrapper();
return true;
case KeyEvent.KEYCODE_L: {
updateLogText("stop video broadcast with blank mode");
Bundle params = new Bundle();
params.putInt(
/* TvInteractiveAppService.COMMAND_PARAMETER_KEY_STOP_MODE */
"command_stop_mode",
/* TvInteractiveAppService.COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK */
1);
tuneChannelByType(TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_STOP,
mCurrentTvInputId, null, params);
return true;
}
case KeyEvent.KEYCODE_M: {
updateLogText("stop video broadcast with freeze mode");
Bundle params = new Bundle();
params.putInt(
/* TvInteractiveAppService.COMMAND_PARAMETER_KEY_STOP_MODE */
"command_stop_mode",
/* TvInteractiveAppService.COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE */
2);
tuneChannelByType(TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_STOP,
mCurrentTvInputId, null, params);
return true;
}
case KeyEvent.KEYCODE_N: {
updateLogText("request AD");
requestAd();
return true;
}
default:
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_PROG_RED:
case KeyEvent.KEYCODE_A:
case KeyEvent.KEYCODE_B:
case KeyEvent.KEYCODE_C:
case KeyEvent.KEYCODE_D:
case KeyEvent.KEYCODE_E:
case KeyEvent.KEYCODE_F:
case KeyEvent.KEYCODE_G:
case KeyEvent.KEYCODE_H:
case KeyEvent.KEYCODE_I:
case KeyEvent.KEYCODE_J:
case KeyEvent.KEYCODE_K:
case KeyEvent.KEYCODE_L:
case KeyEvent.KEYCODE_M:
case KeyEvent.KEYCODE_N:
return true;
default:
return super.onKeyUp(keyCode, event);
}
}
public void updateLogText(String log) {
if (DEBUG) {
Log.d(TAG, log);
}
mLogView.setText(log);
}
private void updateSurface(Surface surface, int width, int height) {
mHandler.post(
() -> {
// Update our virtualDisplay if it already exists, create a new one otherwise
if (mVirtualDisplay != null) {
mVirtualDisplay.setSurface(surface);
mVirtualDisplay.resize(width, height, DisplayMetrics.DENSITY_DEFAULT);
} else {
DisplayManager displayManager =
mContext.getSystemService(DisplayManager.class);
if (displayManager == null) {
Log.e(TAG, "Failed to get DisplayManager");
return;
}
mVirtualDisplay = displayManager.createVirtualDisplay(VIRTUAL_DISPLAY_NAME,
width,
height,
DisplayMetrics.DENSITY_DEFAULT,
surface,
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
Presentation presentation =
new Presentation(mContext, mVirtualDisplay.getDisplay());
presentation.setContentView(mViewContainer);
presentation.getWindow().setBackgroundDrawable(new ColorDrawable(0));
presentation.show();
}
});
}
private void initSampleView() {
View sampleView = LayoutInflater.from(mContext).inflate(R.layout.sample_layout, null);
TextView appServiceIdText = sampleView.findViewById(R.id.app_service_id);
appServiceIdText.setText("App Service ID: " + mAppServiceId);
mTvInputIdView = sampleView.findViewById(R.id.tv_input_id);
mChannelUriView = sampleView.findViewById(R.id.channel_uri);
mVideoTrackView = sampleView.findViewById(R.id.video_track_selected);
mAudioTrackView = sampleView.findViewById(R.id.audio_track_selected);
mSubtitleTrackView = sampleView.findViewById(R.id.subtitle_track_selected);
mLogView = sampleView.findViewById(R.id.log_text);
// Set default values for the selected tracks, since we cannot request data on them directly
mVideoTrackView.setText("No video track selected");
mAudioTrackView.setText("No audio track selected");
mSubtitleTrackView.setText("No subtitle track selected");
mVideoView = new VideoView(mContext);
mVideoView.setVisibility(View.GONE);
mVideoView.setOnCompletionListener(
new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
mVideoView.setVisibility(View.GONE);
mLogView.setText("MediaPlayer onCompletion");
tuneChannelByType(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_TUNE,
mCurrentTvInputId,
mCurrentChannelUri);
}
});
mWidth = 0;
mHeight = 0;
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mScreenWidth = wm.getDefaultDisplay().getWidth();
mScreenHeight = wm.getDefaultDisplay().getHeight();
mViewContainer.addView(sampleView);
}
private void updateTrackSelectedView(int type, String trackId) {
mHandler.post(
() -> {
if (mTracks == null) {
return;
}
TvTrackInfo newSelectedTrack = null;
for (TvTrackInfo track : mTracks) {
if (track.getType() == type && track.getId().equals(trackId)) {
newSelectedTrack = track;
break;
}
}
if (newSelectedTrack == null) {
if (DEBUG) {
Log.d(TAG, "Did not find selected track within track list");
}
return;
}
switch (newSelectedTrack.getType()) {
case TvTrackInfo.TYPE_VIDEO:
mVideoTrackView.setText(
"Video Track: id= " + newSelectedTrack.getId()
+ ", height=" + newSelectedTrack.getVideoHeight()
+ ", width=" + newSelectedTrack.getVideoWidth()
+ ", frame_rate=" + newSelectedTrack.getVideoFrameRate()
+ ", pixel_ratio=" + newSelectedTrack.getVideoPixelAspectRatio()
);
break;
case TvTrackInfo.TYPE_AUDIO:
mAudioTrackView.setText(
"Audio Track: id=" + newSelectedTrack.getId()
+ ", lang=" + newSelectedTrack.getLanguage()
+ ", sample_rate=" + newSelectedTrack.getAudioSampleRate()
+ ", channel_count=" + newSelectedTrack.getAudioChannelCount()
);
break;
case TvTrackInfo.TYPE_SUBTITLE:
mSubtitleTrackView.setText(
"Subtitle Track: id=" + newSelectedTrack.getId()
+ ", lang=" + newSelectedTrack.getLanguage()
);
break;
}
}
);
}
private void tuneChannelByType(String type, String inputId, Uri channelUri, Bundle bundle) {
Bundle parameters = bundle == null ? new Bundle() : bundle;
if (TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_TUNE.equals(type)) {
parameters.putString(
TvInteractiveAppService.COMMAND_PARAMETER_KEY_CHANNEL_URI,
channelUri == null ? null : channelUri.toString());
parameters.putString(TvInteractiveAppService.COMMAND_PARAMETER_KEY_INPUT_ID, inputId);
}
mHandler.post(() -> sendPlaybackCommandRequest(type, parameters));
// Delay request for new information to give time to tune
mHandler.postDelayed(
() -> {
requestCurrentTvInputId();
requestCurrentChannelUri();
requestTrackInfoList();
},
1000
);
}
private void tuneChannelByType(String type, String inputId, Uri channelUri) {
tuneChannelByType(type, inputId, channelUri, new Bundle());
}
private void tuneToNextChannel() {
tuneChannelByType(TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_TUNE_NEXT, null, null);
}
@Override
public void onCurrentChannelUri(Uri channelUri) {
if (DEBUG) {
Log.d(TAG, "onCurrentChannelUri uri=" + channelUri);
}
mCurrentChannelUri = channelUri;
mChannelUriView.setText("Channel URI: " + channelUri);
}
@Override
public void onTrackInfoList(List<TvTrackInfo> tracks) {
if (DEBUG) {
Log.d(TAG, "onTrackInfoList size=" + tracks.size());
for (int i = 0; i < tracks.size(); i++) {
TvTrackInfo trackInfo = tracks.get(i);
if (trackInfo != null) {
Log.d(TAG, "track " + i + ": type=" + trackInfo.getType() +
" id=" + trackInfo.getId());
}
}
}
for (TvTrackInfo info : tracks) {
if (info.getType() == TvTrackInfo.TYPE_AUDIO) {
mFirstAudioTrackId = info.getId();
break;
}
}
mTracks = tracks;
}
@Override
public void onTracksChanged(List<TvTrackInfo> tracks) {
if (DEBUG) {
Log.d(TAG, "onTracksChanged");
}
onTrackInfoList(tracks);
}
@Override
public void onTrackSelected(int type, String trackId) {
if (DEBUG) {
Log.d(TAG, "onTrackSelected type=" + type + " trackId=" + trackId);
}
updateTrackSelectedView(type, trackId);
if (TextUtils.equals(mSelectingAudioTrackId, trackId)) {
if (mSelectingAudioTrackId == null) {
updateLogText("unselect audio succeed");
} else {
updateLogText("select audio succeed");
}
}
}
@Override
public void onCurrentTvInputId(String inputId) {
if (DEBUG) {
Log.d(TAG, "onCurrentTvInputId id=" + inputId);
}
mCurrentTvInputId = inputId;
mTvInputIdView.setText("TV Input ID: " + inputId);
}
@Override
public void onTuned(Uri channelUri) {
mCurrentChannelUri = channelUri;
}
@Override
public void onCurrentVideoBounds(@NonNull Rect bounds) {
updateLogText("Received video Bounds " + bounds.toShortString());
}
@Override
public void onBroadcastInfoResponse(BroadcastInfoResponse response) {
if (mGeneratedRequestId == response.getRequestId()) {
if (!mRequestStreamEventFinished && response instanceof StreamEventResponse) {
handleStreamEventResponse((StreamEventResponse) response);
} else if (mSectionReceived < MAX_HANDLED_RESPONSE
&& response instanceof SectionResponse) {
handleSectionResponse((SectionResponse) response);
} else if (response instanceof TableResponse) {
handleTableResponse((TableResponse) response);
}
}
}
private void handleSectionResponse(SectionResponse response) {
mSectionReceived++;
byte[] data = null;
Bundle params = response.getSessionData();
if (params != null) {
// TODO: define the key
data = params.getByteArray("key_raw_data");
}
int version = response.getVersion();
updateLogText(
"Received section data version = "
+ version
+ ", data = "
+ Arrays.toString(data));
}
private void handleStreamEventResponse(StreamEventResponse response) {
updateLogText("Received stream event response");
byte[] rData = response.getData();
if (rData == null) {
mRequestStreamEventFinished = true;
updateLogText("Received stream event data is null");
return;
}
// TODO: convert to Hex instead
String data = Arrays.toString(rData);
if (mStreamDataList.contains(data)) {
return;
}
mStreamDataList.add(data);
updateLogText(
"Received stream event data("
+ (mStreamDataList.size() - 1)
+ "): "
+ data);
if (mStreamDataList.size() >= MAX_HANDLED_RESPONSE) {
mRequestStreamEventFinished = true;
updateLogText("Received stream event data finished");
}
}
private void handleTableResponse(TableResponse response) {
updateLogText(
"Received table data version = "
+ response.getVersion()
+ ", size="
+ response.getSize()
+ ", requestId="
+ response.getRequestId()
+ ", data = "
+ Arrays.toString(getTableByteArray(response)));
}
private void selectTrack(int type, String trackId) {
Bundle params = new Bundle();
params.putInt(TvInteractiveAppService.COMMAND_PARAMETER_KEY_TRACK_TYPE, type);
params.putString(TvInteractiveAppService.COMMAND_PARAMETER_KEY_TRACK_ID, trackId);
mHandler.post(
() ->
sendPlaybackCommandRequest(
TvInteractiveAppService.PLAYBACK_COMMAND_TYPE_SELECT_TRACK,
params));
}
private int generateRequestId() {
return ++mGeneratedRequestId;
}
private void requestStreamEvent(String targetUri, String eventName) {
if (targetUri == null) {
return;
}
int requestId = generateRequestId();
BroadcastInfoRequest request =
new StreamEventRequest(
requestId,
BroadcastInfoRequest.REQUEST_OPTION_AUTO_UPDATE,
Uri.parse(targetUri),
eventName);
requestBroadcastInfo(request);
}
private void requestSection(boolean repeat, int tsPid, int tableId, int version) {
int requestId = generateRequestId();
BroadcastInfoRequest request =
new SectionRequest(
requestId,
repeat ?
BroadcastInfoRequest.REQUEST_OPTION_REPEAT :
BroadcastInfoRequest.REQUEST_OPTION_AUTO_UPDATE,
tsPid,
tableId,
version);
requestBroadcastInfo(request);
}
private void requestTable(boolean repeat, int tableId, int tableName, int version) {
int requestId = generateRequestId();
BroadcastInfoRequest request =
new TableRequest(
requestId,
repeat
? BroadcastInfoRequest.REQUEST_OPTION_REPEAT
: BroadcastInfoRequest.REQUEST_OPTION_AUTO_UPDATE,
tableId,
tableName,
version);
requestBroadcastInfo(request);
}
public void requestAd() {
try {
// TODO: add the AD file to this project
RandomAccessFile adiFile =
new RandomAccessFile(
mContext.getApplicationContext().getFilesDir() + "/ad.mp4", "r");
mAdFd = ParcelFileDescriptor.dup(adiFile.getFD());
} catch (Exception e) {
updateLogText("open advertisement file failed. " + e.getMessage());
return;
}
long startTime = 20000;
long stopTime = startTime + 25000;
long echoInterval = 1000;
String mediaFileType = "MP4";
mHandler.post(
() -> {
AdRequest adRequest;
if (mAdState == AdResponse.RESPONSE_TYPE_PLAYING) {
updateLogText("RequestAd stop");
adRequest =
new AdRequest(
mGeneratedRequestId,
AdRequest.REQUEST_TYPE_STOP,
null,
0,
0,
0,
null,
null);
} else {
updateLogText("RequestAd start");
int requestId = generateRequestId();
mAdSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
mAdSurfaceView.setVisibility(View.VISIBLE);
Bundle bundle = new Bundle();
bundle.putParcelable("dai_surface", mAdSurface);
adRequest =
new AdRequest(
requestId,
AdRequest.REQUEST_TYPE_START,
mAdFd,
startTime,
stopTime,
echoInterval,
mediaFileType,
bundle);
}
requestAd(adRequest);
});
}
@TargetApi(34)
private List<AppLinkInfo> getAppLinkInfoList() {
if (Build.VERSION.SDK_INT < 34 || mTvIAppManager == null) {
return new ArrayList<>();
}
return mTvIAppManager.getAppLinkInfoList();
}
@TargetApi(34)
private void requestCurrentVideoBoundsWrapper() {
if (Build.VERSION.SDK_INT < 34) {
return;
}
requestCurrentVideoBounds();
}
@TargetApi(34)
private byte[] getTableByteArray(TableResponse response) {
if (Build.VERSION.SDK_INT < 34) {
return null;
}
return response.getTableByteArray();
}
}