Video player kernel switching package

Video player kernel switching package

Directory introduction

  • 01. Multi video kernel adaptation background
    • 1.1 the player kernel is difficult to switch
    • 1.2 player kernel and business linkage
  • 02. Multi video kernel design objectives
    • 2.1 establish a set of video API
    • 2.2 key points of defining unified API
    • 2.3 seamless kernel modification
  • 03. Overall architecture of player kernel
    • 3.1 overall kernel architecture
    • 3.2 kernel UML class diagram architecture
    • 3.3 about dependencies
  • 04. Unified video interface design
    • 4.1 define video status interface
    • 4.2 definition of abstract video player
    • 4.3 specific ijk implementation cases
  • 05. Easily create different kernel players
    • 5.1 how to create different kernels
    • 5.2 use one point design mode
    • 5.3 design pattern code display
  • 06. Other design specifications
    • 6.1 stability design description
    • 6.2 test case design
    • 6.3 expansibility related design

00. General framework of video player

  • The basic package of video player player can be in ExoPlayer, MediaPlayer, sound network RTC video player kernel, and the native MediaPlayer can be switched freely
  • For view state switching and later maintenance expansion, avoid coupling between functions and services. For example, you need to support high customization of the Player UI, rather than the UI code in the lib library
  • For video playback, audio playback, playback, and live video broadcast. It is easy to use, has strong code expansibility and good encapsulation. It is mainly completely decoupled from the business, and exposes interface monitoring to developers to deal with the specific logic of the business
  • The overall architecture of the player: player kernel (free switching) + video player + caching while playing + highly customized Player UI view layer
  • Project address: https://github.com/yangchong211/YCVideoPlayer
  • Introduction document on the overall function of video player: https://juejin.im/post/6883457444752654343

01. Multi adaptation kernel adaptation background

1.1 the player kernel is difficult to switch

  • Due to historical reasons, the company uses different video players for many app s. Some use dumpling players, some use native players, and some use Google players. In order to build a player suitable for multiple business lines, you need to package your own video player interface.

1.2 player kernel and business linkage

  • And business linkage is difficult to separate
    • Player is a core function. In previous projects, you can directly create a player on the Activity page and then play the video. There are also many video businesses... Then the product says, add a list video, make a painful change, and return to the playback function after the change.
  • Add new business change
    • The player kernel is decoupled from the player, but also from specific services: it supports more playback scenarios and fast access to new playback services, and does not affect other playback services, such as adding Alibaba cloud player kernel or Tencent player kernel later.

02. Multi video kernel design objectives

2.1 establish a set of video API

  • Different video player cores have different APIs, so it is difficult to switch operations. If you want to be compatible with kernel switching, you must develop a video interface + implementation class player

2.2 key points of defining unified API

  • Question: for different kernel players, such as Google's ExoPlayer, IjkPlayer in station B, and native MediaPlayer, some APIs are different. How to unify the APIs when using them?
    • For example, ijk's and exo's video player listener listening APIs are completely different. Compatibility processing is required at this time
    • Define the interface, and then implement the interface for different kernel players to rewrite the abstract method. When calling, obtain the interface object and call the API, so that the API can be unified
  • Define an interface. What is the purpose of this interface? This interface defines general video player methods, such as video initialization, setting url, loading, and playback status. It can be divided into three parts.
    • Part I: Video initialization instance object method, mainly including: initPlayer initializes the video, setDataSource sets the video player address, setSurface sets the video player rendering view, and prepareAsync starts preparing for playback
    • Part II: video player status method, mainly including: play, pause, resume, replay, set progress, release resources, obtain progress, set speed and set volume
    • Part 3: after the player binds the view, it needs to monitor the playback status, such as playback exceptions, playback completion, playback preparation, playback size changes, and playback preparation

2.2 seamless kernel modification

  • It is convenient to create different kernels by passing in different types
    • Hide the specific details of kernel player creation. Developers only need to care about the factory corresponding to the required product, do not need to care about the creation details, and even do not need to know the class name of the specific player class. It needs to comply with the opening and closing principle

03. Overall architecture of player kernel

3.1 overall kernel architecture

  • Player kernel architecture

3.2 kernel UML class diagram architecture

  • The kernel UML class diagram architecture is shown below

3.3 about dependencies

  • Google player needs to rely on some Google services. Ijk video player needs to rely on ijk related libraries. If you want to expand other video players, you need to add dependencies. Note that the added library uses compileOnly.

04. Unified video interface design

4.1 define video status interface

  • It mainly monitors the playback status of the video during video playback, such as exceptions, playback completion, preparation stage, video size changes, etc.
    public interface VideoPlayerListener {
        /**
         * abnormal
         * 1          Link representing error
         * 2          Represents a parsing exception
         * 3          Indicates other exceptions
         * @param type                          Error type
         */
        void onError(@PlayerConstant.ErrorType int type , String error);
    
        /**
         * complete
         */
        void onCompletion();
    
        /**
         * video information 
         * @param what                          what
         * @param extra                         extra
         */
        void onInfo(int what, int extra);
    
        /**
         * prepare
         */
        void onPrepared();
    
        /**
         * Video change monitoring
         * @param width                         wide
         * @param height                        high
         */
        void onVideoSizeChanged(int width, int height);
    }
    

4.2 definition of abstract video player

  • This is a unified video player interface, which is mainly divided into three parts. The first part: Video initialization instance object method; Part II: video player state method; Part 3: after the player binds the view, it needs to monitor the playback status
  • Part I: Video initialization instance object method
    //Video player step 1: create a video player
    public abstract void initPlayer();
    //Set playback address
    public abstract void setDataSource(String path, Map<String, String> headers);
    //Used to play video files in raw and asset
    public abstract void setDataSource(AssetFileDescriptor fd);
    //Set the View of rendered video, mainly used for TextureView
    public abstract void setSurface(Surface surface);
    //Ready to play (asynchronous)
    public abstract void prepareAsync();
    
  • Part II: video player state method
    //play
    public abstract void start();
    //suspend
    public abstract void pause();
    //stop it
    public abstract void stop();
    //Reset player
    public abstract void reset();
    //Is it playing
    public abstract boolean isPlaying();
    //Adjust progress
    public abstract void seekTo(long time);
    //Release player
    public abstract void release();
    //Other omissions
    
  • Part 3: after the player binds the view, it needs to monitor the playback status
    /**
     * Bind VideoView, listen for playback exceptions, complete, start preparation, video size change, video information, etc
     */
    public void setPlayerEventListener(VideoPlayerListener playerEventListener) {
        this.mPlayerEventListener = playerEventListener;
    }
    

4.3 specific ijk implementation cases

  • The kernel implementation class code of ijk is as follows
    public class IjkVideoPlayer extends AbstractVideoPlayer {
    
        protected IjkMediaPlayer mMediaPlayer;
        private int mBufferedPercent;
        private Context mAppContext;
    
        public IjkVideoPlayer(Context context) {
            if (context instanceof Application){
                mAppContext = context;
            } else {
                mAppContext = context.getApplicationContext();
            }
        }
    
        @Override
        public void initPlayer() {
            mMediaPlayer = new IjkMediaPlayer();
            //native log
            IjkMediaPlayer.native_setLogLevel(VideoLogUtils.isIsLog()
                    ? IjkMediaPlayer.IJK_LOG_INFO : IjkMediaPlayer.IJK_LOG_SILENT);
            setOptions();
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            initListener();
        }
    
        @Override
        public void setOptions() {
        }
    
        /**
         * ijk Video player monitor listener
         */
        private void initListener() {
            // Set listening. You can view imedia player source code listening events in ijk
            // Set video error listener
            mMediaPlayer.setOnErrorListener(onErrorListener);
            // Set video playback completion listening event
            mMediaPlayer.setOnCompletionListener(onCompletionListener);
            // Set up video message listener
            mMediaPlayer.setOnInfoListener(onInfoListener);
            // Set video buffer update listening event
            mMediaPlayer.setOnBufferingUpdateListener(onBufferingUpdateListener);
            // Set listening events for preparing video playback
            mMediaPlayer.setOnPreparedListener(onPreparedListener);
            // Set video size change listener
            mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
            // Set video seek to complete listening event
            mMediaPlayer.setOnSeekCompleteListener(onSeekCompleteListener);
            // Set time text listener
            mMediaPlayer.setOnTimedTextListener(onTimedTextListener);
            mMediaPlayer.setOnNativeInvokeListener(new IjkMediaPlayer.OnNativeInvokeListener() {
                @Override
                public boolean onNativeInvoke(int i, Bundle bundle) {
                    return true;
                }
            });
        }
    
        /**
         * Set playback address
         *
         * @param path    Playback address
         * @param headers Playback address request header
         */
        @Override
        public void setDataSource(String path, Map<String, String> headers) {
            // Set dataSource
            if(path==null || path.length()==0){
                if (mPlayerEventListener!=null){
                    mPlayerEventListener.onInfo(PlayerConstant.MEDIA_INFO_URL_NULL, 0);
                }
                return;
            }
            try {
                //Parse path
                Uri uri = Uri.parse(path);
                if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
                    RawDataSourceProvider rawDataSourceProvider = RawDataSourceProvider.create(mAppContext, uri);
                    mMediaPlayer.setDataSource(rawDataSourceProvider);
                } else {
                    //Handling UA issues
                    if (headers != null) {
                        String userAgent = headers.get("User-Agent");
                        if (!TextUtils.isEmpty(userAgent)) {
                            mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", userAgent);
                        }
                    }
                    mMediaPlayer.setDataSource(mAppContext, uri, headers);
                }
            } catch (Exception e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * Used to play video files in raw and asset
         */
        @Override
        public void setDataSource(AssetFileDescriptor fd) {
            try {
                mMediaPlayer.setDataSource(new RawDataSourceProvider(fd));
            } catch (Exception e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * Set the View of rendered video, mainly used for TextureView
         * @param surface                           surface
         */
        @Override
        public void setSurface(Surface surface) {
            mMediaPlayer.setSurface(surface);
        }
    
        /**
         * Ready to play (asynchronous)
         */
        @Override
        public void prepareAsync() {
            try {
                mMediaPlayer.prepareAsync();
            } catch (IllegalStateException e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * suspend
         */
        @Override
        public void pause() {
            try {
                mMediaPlayer.pause();
            } catch (IllegalStateException e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * play
         */
        @Override
        public void start() {
            try {
                mMediaPlayer.start();
            } catch (IllegalStateException e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * stop it
         */
        @Override
        public void stop() {
            try {
                mMediaPlayer.stop();
            } catch (IllegalStateException e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * Reset player
         */
        @Override
        public void reset() {
            mMediaPlayer.reset();
            mMediaPlayer.setOnVideoSizeChangedListener(onVideoSizeChangedListener);
            setOptions();
        }
    
        /**
         * Is it playing
         */
        @Override
        public boolean isPlaying() {
            return mMediaPlayer.isPlaying();
        }
    
    
        /**
         * Adjust progress
         */
        @Override
        public void seekTo(long time) {
            try {
                mMediaPlayer.seekTo((int) time);
            } catch (IllegalStateException e) {
                mPlayerEventListener.onError();
            }
        }
    
        /**
         * Release player
         */
        @Override
        public void release() {
            mMediaPlayer.setOnErrorListener(null);
            mMediaPlayer.setOnCompletionListener(null);
            mMediaPlayer.setOnInfoListener(null);
            mMediaPlayer.setOnBufferingUpdateListener(null);
            mMediaPlayer.setOnPreparedListener(null);
            mMediaPlayer.setOnVideoSizeChangedListener(null);
            new Thread() {
                @Override
                public void run() {
                    try {
                        mMediaPlayer.release();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    
        /**
         * Get the current playback position
         */
        @Override
        public long getCurrentPosition() {
            return mMediaPlayer.getCurrentPosition();
        }
    
        /**
         * Total duration of video acquisition
         */
        @Override
        public long getDuration() {
            return mMediaPlayer.getDuration();
        }
    
        /**
         * Get buffer percentage
         */
        @Override
        public int getBufferedPercentage() {
            return mBufferedPercent;
        }
    
        /**
         * Set the View of rendered video, mainly used for SurfaceView
         */
        @Override
        public void setDisplay(SurfaceHolder holder) {
            mMediaPlayer.setDisplay(holder);
        }
    
        /**
         * set volume 
         */
        @Override
        public void setVolume(float v1, float v2) {
            mMediaPlayer.setVolume(v1, v2);
        }
    
        /**
         * Set whether to cycle
         */
        @Override
        public void setLooping(boolean isLooping) {
            mMediaPlayer.setLooping(isLooping);
        }
    
        /**
         * Set playback speed
         */
        @Override
        public void setSpeed(float speed) {
            mMediaPlayer.setSpeed(speed);
        }
    
        /**
         * Get playback speed
         */
        @Override
        public float getSpeed() {
            return mMediaPlayer.getSpeed(0);
        }
    
        /**
         * Gets the current buffered network speed
         */
        @Override
        public long getTcpSpeed() {
            return mMediaPlayer.getTcpSpeed();
        }
    
        /**
         * Set video error listener
         * int MEDIA_INFO_VIDEO_RENDERING_START = 3;//Video ready to render
         * int MEDIA_INFO_BUFFERING_START = 701;//Start buffering
         * int MEDIA_INFO_BUFFERING_END = 702;//End of buffer
         * int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//Video selection information
         * int MEDIA_ERROR_SERVER_DIED = 100;//Video interruption is generally an exception of the video source or an unsupported video type.
         * int MEDIA_ERROR_IJK_PLAYER = -10000,//Generally, there is a problem with the video source or the data format is not supported. For example, the audio is not AAC
         * int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//Data error no valid recycle
         */
        private IMediaPlayer.OnErrorListener onErrorListener = new IMediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(IMediaPlayer iMediaPlayer, int framework_err, int impl_err) {
                mPlayerEventListener.onError();
                VideoLogUtils.d("IjkVideoPlayer----listener---------onError -> STATE_ERROR -- what: " + framework_err + ", extra: " + impl_err);
                return true;
            }
        };
    
        /**
         * Set video playback completion listening event
         */
        private IMediaPlayer.OnCompletionListener onCompletionListener = new IMediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(IMediaPlayer iMediaPlayer) {
                mPlayerEventListener.onCompletion();
                VideoLogUtils.d("IjkVideoPlayer----listener---------onCompletion -> STATE_COMPLETED");
            }
        };
    
    
        /**
         * Set up video message listener
         */
        private IMediaPlayer.OnInfoListener onInfoListener = new IMediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(IMediaPlayer iMediaPlayer, int what, int extra) {
                mPlayerEventListener.onInfo(what, extra);
                VideoLogUtils.d("IjkVideoPlayer----listener---------onInfo -> -- what: " + what + ", extra: " + extra);
                return true;
            }
        };
    
        /**
         * Set video buffer update listening event
         */
        private IMediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new IMediaPlayer.OnBufferingUpdateListener() {
            @Override
            public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int percent) {
                mBufferedPercent = percent;
            }
        };
    
    
        /**
         * Set listening events for preparing video playback
         */
        private IMediaPlayer.OnPreparedListener onPreparedListener = new IMediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(IMediaPlayer iMediaPlayer) {
                mPlayerEventListener.onPrepared();
                VideoLogUtils.d("IjkVideoPlayer----listener---------onPrepared -> STATE_PREPARED");
            }
        };
    
        /**
         * Set video size change listener
         */
        private IMediaPlayer.OnVideoSizeChangedListener onVideoSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int width, int height,
                                           int sar_num, int sar_den) {
                int videoWidth = iMediaPlayer.getVideoWidth();
                int videoHeight = iMediaPlayer.getVideoHeight();
                if (videoWidth != 0 && videoHeight != 0) {
                    mPlayerEventListener.onVideoSizeChanged(videoWidth, videoHeight);
                }
                VideoLogUtils.d("IjkVideoPlayer----listener---------onVideoSizeChanged -> WIDTH: " + width + ", HEIGHT: " + height);
            }
        };
    
        /**
         * Set time text listener
         */
        private IMediaPlayer.OnTimedTextListener onTimedTextListener = new IMediaPlayer.OnTimedTextListener() {
            @Override
            public void onTimedText(IMediaPlayer iMediaPlayer, IjkTimedText ijkTimedText) {
    
            }
        };
    
        /**
         * Set video seek to complete listening event
         */
        private IMediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new IMediaPlayer.OnSeekCompleteListener() {
            @Override
            public void onSeekComplete(IMediaPlayer iMediaPlayer) {
    
            }
        };
    }
    

05. Easily create different kernel players

5.1 how to create different kernels

  • Let's take a look at the code for creating different kernel players. The developer only needs to pass in a type parameter to create instance objects of different classes. The code is shown below
    /**
     * Obtain the specific implementation class of PlayerFactory and obtain the kernel
     * When creating an object, you only need to pass the type instead of the corresponding factory to create a specific product object
     * TYPE_IJK                 IjkPlayer,Packaging player based on IjkPlayer
     * TYPE_NATIVE              MediaPlayer,Based on native player controls
     * TYPE_EXO                 Based on Google Video Player
     * TYPE_RTC                 RTC based video player
     * @param type                              type
     * @return
     */
    public static AbstractVideoPlayer getVideoPlayer(Context context,@PlayerConstant.PlayerType int type){
        if (type == PlayerConstant.PlayerType.TYPE_EXO){
            return ExoPlayerFactory.create().createPlayer(context);
        } else if (type == PlayerConstant.PlayerType.TYPE_IJK){
            return IjkPlayerFactory.create().createPlayer(context);
        } else if (type == PlayerConstant.PlayerType.TYPE_NATIVE){
            return MediaPlayerFactory.create().createPlayer(context);
        } else if (type == PlayerConstant.PlayerType.TYPE_RTC){
            return IjkPlayerFactory.create().createPlayer(context);
        } else {
            return IjkPlayerFactory.create().createPlayer(context);
        }
    }
    

5.2 use one point design mode

  • What is the motivation to create different objects using the factory pattern and why?
    • A video player can provide multiple kernel players (such as ijk, exo, media, rtc, etc.). These players all come from the same base class, but after inheriting the base class, different subclasses modify some properties so that they can present different appearances.
    • If you want to use these kernel player s, you don't need to know the names of these specific kernels. You only need to know a parameter representing the kernel class, and provide a convenient method to call. You can return a corresponding kernel object by passing the parameter into the method. At this time, you can use the factory mode.
  • Firstly, a factory abstract class is defined, and then different kernel players create their specific factory implementation classes respectively
    • PlayerFactory: abstract factory. It is the core of the factory method pattern. Any factory class that creates objects in the pattern must implement this interface
    • ExoPlayerFactory: specific factory. Specific factory roles contain logic closely related to business and are called by users to create specific product objects.
  • How to use is divided into three steps. The specific operations are as follows
    • 1. First call the createPlayer method in the specific factory object; 2. Obtain specific product objects according to the passed in product type parameters; 3. Return the product object and use it.
    • In short, when creating an object, you only need to pass the type instead of the corresponding factory to create a specific product object

5.3 design pattern code display

  • Abstract factory class. The code is as follows
    public abstract class PlayerFactory<T extends AbstractVideoPlayer> {
        public abstract T createPlayer(Context context);
    }
    
  • The specific implementation of the factory class is shown below
    public class ExoPlayerFactory extends PlayerFactory<ExoMediaPlayer> {
    
        public static ExoPlayerFactory create() {
            return new ExoPlayerFactory();
        }
    
        @Override
        public ExoMediaPlayer createPlayer(Context context) {
            return new ExoMediaPlayer(context);
        }
    }
    
  • The biggest advantage of this method of creating objects
    • The factory method is used to create the required product and hide the details of which specific product class will be instantiated. Users only need to care about the factory corresponding to the required product, do not need to care about the creation details, or even need not know the class name of the specific product class.
    • When adding new products, such as adding an Ali player kernel later, you only need to add a specific factory and specific products. The scalability of the system becomes very good, which fully conforms to the "opening and closing principle"

06. Other design specifications

6.1 stability design description

  • No, there's nothing to say here.

6.2 test case design

  • At present, when setting the video link for the video kernel player, it may play abnormally. There may be many types of exceptions. Here I simply divide them into three categories. It is roughly as follows:
    @Retention(RetentionPolicy.SOURCE)
    public @interface ErrorType {
        //Wrong link
        int TYPE_SOURCE = 1;
        //Parsing exception
        int TYPE_PARSE = 2;
        //Other exceptions
        int TYPE_UNEXPECTED = 3;
    }
    
  • When is an error a link exception given by Google player
    @Override
    public void onPlayerError(ExoPlaybackException error) {
        if (mPlayerEventListener != null) {
            int type = error.type;
            if (type == TYPE_SOURCE){
                //Wrong link
                mPlayerEventListener.onError(PlayerConstant.ErrorType.TYPE_SOURCE,error.getMessage());
            } 
        }
    }
    
  • When an error occurs is a parsing exception. The ijk player gives an example, and the code is as follows
    try {
        //Parse path
        Uri uri = Uri.parse(path);
        if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
            RawDataSourceProvider rawDataSourceProvider = RawDataSourceProvider.create(mAppContext, uri);
            mMediaPlayer.setDataSource(rawDataSourceProvider);
        } else {
            //Handling UA issues
            if (headers != null) {
                String userAgent = headers.get("User-Agent");
                if (!TextUtils.isEmpty(userAgent)) {
                    mMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "user_agent", userAgent);
                }
            }
            mMediaPlayer.setDataSource(mAppContext, uri, headers);
        }
    } catch (Exception e) {
        mPlayerEventListener.onError(PlayerConstant.ErrorType.TYPE_PARSE,e.getMessage());
    }
    
  • When an error occurs is another exception. The ijk player gives an example. The code is as follows. It is mainly the error monitoring given by the player. For details, see the source code of the video library.
    /**
     * Set video error listener
     * int MEDIA_INFO_VIDEO_RENDERING_START = 3;//Video ready to render
     * int MEDIA_INFO_BUFFERING_START = 701;//Start buffering
     * int MEDIA_INFO_BUFFERING_END = 702;//End of buffer
     * int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//Video selection information
     * int MEDIA_ERROR_SERVER_DIED = 100;//Video interruption is generally an exception of the video source or an unsupported video type.
     * int MEDIA_ERROR_IJK_PLAYER = -10000,//Generally, there is a problem with the video source or the data format is not supported. For example, the audio is not AAC
     * int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//Data error no valid recycle
     */
    private IMediaPlayer.OnErrorListener onErrorListener = new IMediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(IMediaPlayer iMediaPlayer, int framework_err, int impl_err) {
            mPlayerEventListener.onError(PlayerConstant.ErrorType.TYPE_UNEXPECTED,"Listening exception"+ framework_err + ", extra: " + impl_err);
            VideoLogUtils.d("IjkVideoPlayer----listener---------onError -> STATE_ERROR -- what: " + framework_err + ", extra: " + impl_err);
            return true;
        }
    };
    

6.3 expansibility related design

  • For example, you want to add a player with Tencent Video Kernel in the later stage. The code is as follows, which is simplified
    public class TxPlayerFactory extends PlayerFactory<TxMediaPlayer> {
    
        public static TxPlayerFactory create() {
            return new TxPlayerFactory();
        }
    
        @Override
        public TxMediaPlayer createPlayer(Context context) {
            return new TxMediaPlayer(context);
        }
    }
    
    public class TxMediaPlayer extends AbstractVideoPlayer {
        //Omit the implementation method code of the interface
    }
    

Project address: https://github.com/yangchong211/YCVideoPlayer

Added by martinacevedo on Fri, 17 Dec 2021 00:35:42 +0200