Do TV development for a period of time, domestic information about this aspect is not much, here I would like to share my experience on TIF use. Android TIF(Android TV Input Framework) is a standard API that Google provides to TV manufacturers to create input modules to control Android TV. The underlying implementation of this API is based on aiDL and Provider, which enables cross-process communication. The system or third-party applications can obtain all input sources (including search module, MDMI module, network module, etc.) through TIF, and output them to the screen through AIDL cutting table. Before introducing this framework, let’s talk about TV: HDMI: High definition multimedia interface High Definition Multimedia Interface (HDMI) is a kind of digital video/audio Interface technology. It is a special digital Interface suitable for image transmission. Corresponding IPTV: network TV, also called VOD TV, the video resources provided by such and such a video company are broadcast on TV. DTV: digital TV ATV: analog TV 2. TIF components: 1) the TV Provider (. Com. Android will. TV. TvProvider) : a databases in the channel, program and related rights. 2) TV App (com. Android. TV. TvActivity) : a system application and user interaction. 3) TV Input Manager (android. Media. TV. TvInputManager) : an intermediate interface layer, make TV Inputs and TV App to communicate. 4)TV Input: Can be seen as an application representing a physical or virtual TV receiver or Input port. Input is thought of as an Input source in TIF. 5)TV Input HAL (TV_input module): this hardware abstraction layer enables TV inputs to access specific HARDWARE. Monica: Hey, you know what? You know, you can get Parental Control. 7)HDMI-CEC: A technology that can be remotely controlled on a variety of devices through HDMI. CEC(Consumer Electronics Control) TIF tidy up and use process.

3) TvinputService. Sssion: the session class TvView will specify the corresponding inputId through the Tune method (usually the corresponding “package name/” of the service). Class name “) and URI, uri contains the corresponding program ID, the tune method will call Session Onturn method, in this method to parse the id, according to the ID to use TvProvider to query the database data, set to player, Here we use the onSetSurface() method to set the surface created by TvView to the Player, which then displays the content on the surface. 4)TvContract: a layer of encapsulation between TvProvider and TvApp that encapsulates some URIs. There are two inner classes that are javabeans. They are TVContract.Channels, TVContract.programs, Programs, and so on. 5) TvInputManager: this is the core of the TIF class, it is the class system, can be detected in the system of service registration “android. Media. TV. Action. QUERY_CONTENT_RATING_SYSTEMS” action class, And set it as a one-way source. It manages callbacks such as whether the video is available and whether the size of the video changes. To obtain a TvInputManager, TvInputManager TvInputManager =(TvInputManager) getSystemService(context.tv_input_service); After getting the TvInputManager, we can iterate over how many services are currently Tv sources in the system. The code is as follows:

List<TvInputInfo> list = tvInputManager.getTvInputList(); &emsp; &emsp;for(TvInputInfo info:list){ &emsp; &emsp; Log.i(TAG,"id:"+ info.getId()); } ' 'I typed it herelogAs follows:Copy the code

01-03 06:58:11. 893, 29023-29023 / com. Lenovo. Tvviewsimple I/SWJ: Id:com.mediatek.tvinput/.component.Com ponentInputService/HW1 01-03 06:58:11. 893, 29023-29023 / com. Lenovo. Tvviewsimple I/SWJ: id: com. Mediatek. Tvinput /. DTV. TunerInputService/HW0 01-03 06:58:11. 893, 29023-29023 / com. Lenovo. Tvviewsimple I/SWJ: Id:com.mediatek.tvinput/.composite.Com positeInputService/HW2 01-03 06:58:11. 893, 29023-29023 / com. Lenovo. Tvviewsimple I/swj: id:lenovo.com.ismartvlive/.Ismartvliveservice

You can see that there are so many sources available. We can take the inputId and set it in the TUNE method of TvView. The source is registered with the service and not enabled. The service is started when the TUNE method of the TvView is called. &emsp; &emsp; 6) TvInputInfo:TvInput information. Including channel type, icon, name and other information. &emsp; &emsp; 7) TvInputCallback. This is an internal class of TvView, and TvInputCallBack can give TvView some information such as whether the connection to the service is successful, whether the Video is available, etc. Part of the code is as follows:Copy the code

@override public void onConnectionFailed(String inputId) {tvView.setCallback(new tvView.tvinputCallback () {@override public void onConnectionFailed(String inputId) { super.onConnectionFailed(inputId); LogUtil.i(this,”MainActivity.onConnectionFailed:”+inputId); } @Override public void onDisconnected(String inputId) { super.onDisconnected(inputId); LogUtil.i(this,”MainActivity.onDisconnected.”); } @Override public void onVideoSizeChanged(String inputId, int width, int height) { super.onVideoSizeChanged(inputId, width, height); LogUtil.i(this,”MainActivity.onVideoSizeChanged.”); } @Override public void onVideoAvailable(String inputId) { super.onVideoAvailable(inputId); LogUtil.i(this,”MainActivity.onVideoAvailable.inputId:”+inputId); } @Override public void onVideoUnavailable(String inputId, int reason) { super.onVideoUnavailable(inputId, reason); LogUtil.i(this,”MainActivity.onVideoUnavailable.”); }… }); Five. Simple example renderings:

Github.com/songwenju/T…

The following is the general process of the project: 1. There are three functions in tifService Module: one is responsible for requesting network data, retrofit+ RxJava is used here, and network data is written into tv.db using TvProvider; the other is used to load and provide TvInputService class. This class is the controler of the Tif. The third is to provide the player responsible for playing. It should be explained here that the player is in service, but it is displayed on the interface of TvView. The reason is that TvView passes a surface when it is in Tune, and the content played here is displayed on the surface. The core code of these three steps is: 1) Request data:

private void addData() {
    LogUtil.i(this,"MainActivity.addData."); MChannelService. GetResult (). SubscribeOn (Schedulers. NewThread ()) / / request data in the child thread. The map (new Func1 < ChannelResult, ChannelResult>() { @Override public ChannelResult call(ChannelResult channelResult) { List<GooglevideosBean> googlevideos = channelResult.getGooglevideos();for (GooglevideosBean googlevideosBean : googlevideos) {
                        for(VideosBean videoBean : googlevideosBean.getVideos()) { insertChannelsData(mContext, videoBean); }}returnchannelResult; } }).subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Action1<ChannelResult>() {// equal to onNext() @override public void call(ChannelResult s) {logutil. I (this,"MainActivity.endCall.");
                    Toast.makeText(mContext,"Data written complete",Toast.LENGTH_SHORT).show(); }}, New Action1<Throwable>() {// equivalent to onError() @override public void call(Throwable) { throwable.printStackTrace(); }}); } /** * write channel to database ** @param context *@param videoBean videoBean */ public static void insertChannelsData(Context context, VideosBean videoBean) { ContentValues value = new ContentValues(); value.put(TvContract.Channels.COLUMN_INPUT_ID,"com.songwenju.tifservice/.TvService");
    value.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, videoBean.getSources().get(0));    //url
    value.put(TvContract.Channels.COLUMN_DISPLAY_NAME, videoBean.getTitle());               //name
    value.put(TvContract.Channels.COLUMN_DESCRIPTION, videoBean.getDescription());
           //description    context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, value);
}
Copy the code

2) TvInputService

public class TvService extends TvInputService {
    private SimpleSessionImpl mSimpleSession;
    private Context mContext;
    @Nullable
    @Override
    public Session onCreateSession(String inputId) {
        LogUtil.i(this, "TvService.onCreateSession.inputId:" + inputId);
        mContext = this;
        mSimpleSession = new SimpleSessionImpl(this);
        return mSimpleSession;
    }
    public class SimpleSessionImpl extends Session {
        private MediaPlayer mMediaPlayer;
        private Surface mSurface;
        /**         
          * Creates a new Session. 
         *         
         * @param context The context of the application
         */
        public SimpleSessionImpl(Context context) {
            super(context);
            LogUtil.i(this, "SimpleSessionImpl.SimpleSessionImpl.");
        }
        @Override
        public void onRelease() {
            LogUtil.i(this, "SimpleSessionImpl.onRelease.");
        }
        @Override
        public boolean onSetSurface(Surface surface) {
            //
            LogUtil.i(this, "SimpleSessionImpl.onSetSurface." + surface);
            mSurface = surface;
            return true;
       }
        @Override
        public void onSetStreamVolume(float volume) {
            LogUtil.i(this, "SimpleSessionImpl.onSetStreamVolume.");
        }
        @Override
        public boolean onTune(Uri channelUri) { 
           LogUtil.i(this, "SimpleSessionImpl.onTune.");
            Long channelId = ContentUris.parseId(channelUri);
            LogUtil.d(this, "channelId:" + channelId);
            return setChannelIdAndPlay(channelId); }}Copy the code

3) The logic of playback

/** * set ChannelId and play ** @param ChannelId * @return
 */
private boolean setChannelIdAndPlay(Long channelId) {
    VideosBean dbChannel = getDbChannel(mContext, channelId);
    LogUtil.i(this, "SimpleSessionImpl.setChannelIdAndPlay."+ dbChannel.toString()); mMediaPlayer = new MediaPlayer(); String playUrl; try { playUrl = dbChannel.getSources().get(0); // Google json sometimes doesn't workif (TextUtils.isEmpty(playUrl)) {
            if(channelId == 1) {// If Google cannot connect to the network, the default address is playUrl ="http://cord.tvxio.com/v1_0/I2/frk/api/live/m3u8/9/5f754b84-ec33-4d62-bb81-3e4de21c8460/medium/";
            }else {
                playUrl = " http://cord.tvxio.com/v1_0/I2/frk/api/live/m3u8/9/577da15a-9007-4fdd-a9cf-6e19d7a04528/medium/";
            }
        }
        LogUtil.i(this, "SimpleSessionImpl.setChannelIdAndPlay.playUrl=" + playUrl);
        mMediaPlayer.reset();
        mMediaPlayer.setDataSource(playUrl);
        mMediaPlayer.setSurface(mSurface);
        mMediaPlayer.setOnErrorListener(new OnErrorListener());
        mMediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener());
        mMediaPlayer.setOnInfoListener(new OnInfoListener());
        mMediaPlayer.setOnPreparedListener(new OnPreparedListener());
        mMediaPlayer.prepareAsync();
    } catch (IOException e) {
        e.printStackTrace();
    } 
   return false;
}
Copy the code

Parse (“content://main/250”). The last 250 is the id to parse. Use this ID to get the channel’s playlist. Set it to MediaPlayer to play.

ChannelId = contenturis.parseId (channelUri); 2. In TvView, if we want to obtain some player states, such as buffer state, there is a loading state before the playback, obtain the size transformation of the program, and some customized states. 1) Return of lodding state: At the time of most cerebral sci-film method using mSimpleSession. NotifyVideoUnavailable (TvInputManager. VIDEO_UNAVAILABLE_REASON_TUNING); Notice Video is unavailable because of tuning. Other corresponding states include:

Tvinputmanager. VIDEO_UNAVAILABLE_REASON_UNKNOWN: Unknown cause tvinputManager. VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL: the signal is weak&emSP; Tvinputmanager.video_unavailable_reason_buffering: buffer &emsp; Tvinputmanager.video_unavailable_reason_audio_only: only audio ' '&emsp; &emsp; At the time of video playback is call when onprepared ` ` ` mSimpleSession. NotifyVideoAvailable (); 2) Return of buffer states: In MediaPlayer, buffer states 701 and 702 are used to start and end buffers. In the MediaPlayer onInfo method received 701 calls ` ` ` mSimpleSession. NotifyVideoUnavailable (TvInputManager VIDEO_UNAVAILABLE_REASON_BUFFERI NG); 3) Custom state, which can only be referenced when using make because the method is annotated with @system API. You can pass a bundle object. notifySessionEvent(@NonNull final String eventType, final Bundle eventArgs) 3. When I used the Program table provided by TvProvider, I encountered a problem here. I found that the data in the table would be cleared irregularly. The test gave me an occasional one, too. Through disconnecting the network, cutting the table, restarting the system found that the programs table is always cleared. There's nothing better for development than finding the replay steps for a bug. By reading the TvProvider source code, you can see that there is a class that is responsible for clearing the data of Programs. The code is as follows: In EpgDataCleanupService. In Java will show information before you go to remove the current time, corresponding COLUMN_END_TIME_UTC_MILLIS time information in this field, and the time in milliseconds, our server for data in seconds, So it's going to be emptied. I just need to fix it.Copy the code

/** 77 * Clear program info that ended before {@code maxEndTimeMillis}. 78 */ 79 @VisibleForTesting 80 void clearOldPrograms(long maxEndTimeMillis) { 81 int deleteCount = getContentResolver().delete( 82 Programs.CONTENT_URI, 83 Programs.COLUMN_END_TIME_UTC_MILLIS + “<?”, 84 new String[] { String.valueOf(maxEndTimeMillis) }); 85 if (DEBUG && deleteCount > 0) { 86 Log.d(TAG, “Deleted ” + deleteCount + ” programs” 87 + ” (reason: ended before ” 88 + DateUtils.getRelativeTimeSpanString(this, maxEndTimeMillis) + “)”); 90 89}}

Examples can be found in the [https://github.com/songwenju/TIFSample] (https://github.com/songwenju/TIFSample), if you have any help to you, welcome to star and fork. This is the end of the Android TIF introduction and framework use section, if you have a new understanding to add in the future. ** Copyright notice: This article is the blogger's original article, please indicate the source. **Copy the code