02. Overall structure of video player
catalogue
- 01. Common layout view of video
- 02. Views that may be involved later
- 03. Purpose and effect to be achieved
- 04. Video view hierarchy diagram
- 05. Overall framework analysis process
- 06. How to create different players
- 07. How to deal with player UI in a friendly way
- 08. Leave interaction to external developers
- 09. About priority view display
- 10. Code project Lib code introduction
00. General framework for video player
- Basic package video player, can be in ExoPlayer, MediaPlayer, audio network RTC video player kernel, native MediaPlayer can be freely toggle
- For view state switching and later maintenance expansion, avoid coupling between functions and services. For example, you need to support player UI customization rather than the UI code in the lib library
- For video playback, audio playback, playback, and live video functions. Simple to use, strong code expansion, good encapsulation, mainly and business completely decoupled, exposed interface monitoring for developers to deal with specific business logic
- The overall architecture of the player: player kernel (free switching) + video player + play while caching + highly customized PLAYER UI view layer
- Project address: github.com/yangchong21…
- About the overall function of the video player: juejin.cn/post/688345…
01. Common layout view of video
- Video base diagram (used to display the cover diagram when initializing the video), video state view [Loading loading, abnormal playing, video loading failure, video playing completion, etc.]
- Change brightness and Sound [Change sound view, change brightness view], change video fast forward and fast back, swipe left and right fast forward and fast back view (gesture swipe prompt box for fast forward and fast back)
- Top control area view (including return key, title, etc.), bottom control area view (including progress bar, play pause, time, switch to full screen, etc.)
- Lock screen layout view (full screen display, other hidden), bottom play progress bar view (many players have this), Clarity list view (Switch clarity popover)
- The progress bar view plays at the bottom (many players have this), hidden when the Bottom view is displayed and hidden when the bottom view is displayed
02. Views that may be involved later
- Gesture guide page (some players have novice guide function), offline download interface (this interface contains the download list, the list of items to edit (select all, delete))
- The user switches from wifi to 4G network, prompting the network switch popup interface (it will be displayed when the network changes from wifi to 4G)
- Image AD view (with countdown gone), start video AD view, non-member trial view
- Bullet screen view (this is important), watermark display view, double speed playback screen (for controlling double speed), bottom video list thumbnail view
- Screen projection video view interface, gift brushing interface in video studio, teacher start class interface, display more views (download, share, switch audio, etc.)
03. Purpose and effect to be achieved
- Basic package video player, can be in ExoPlayer, MediaPlayer, audio network RTC video player kernel, native MediaPlayer can be freely toggle
- For view state switching and later maintenance expansion, avoid coupling between functions and services. For example, you need to support player UI customization rather than the UI code in the lib library
- For video playback, audio playback, playback, and live video functions. Simple to use, strong code expansion, good encapsulation, mainly and business completely decoupled, exposed interface monitoring for developers to deal with specific business logic
04. Video view hierarchy diagram
05. Overall framework analysis process
- Player kernel
- AbstractVideoPlayer, MediaPlayer, IjkPlayer, AbstractVideoPlayer, etc
- Define abstract player, mainly including video initialization, Settings, state Settings, and play listener. Since each kernel player API may be different, we need a player class that implements AbstractVideoPlayer abstract class for the convenience of unified invocation later
- To make it easier to create different kernel players, you need to create a PlayerFactory, define an abstract method of creating a player for createPlayer, and then each kernel implements it to create its own player
- VideoPlayer player
- You can switch between video cores, Player+Controller. The player is responsible for the playback logic, the Controller is responsible for the view-specific logic, and they communicate with each other through an interface
- For Controller, an interface needs to be defined, which is mainly responsible for View UI processing logic and supports the addition of various customized View views [unified implementation of customized interface Control]. Each View should be as simple as possible, and finally added in the form of addView
- For Player, an interface needs to be defined, which is mainly responsible for the video playing processing logic, such as video playing, pause, setting the playing progress, setting the video link, switching the playing mode and other operations. Note that the Controller is set inside the Player, and the two interact via the interface
- UI Controller view
- Define a BaseVideoController class, this is mainly integrated with various events processing logic, such as player state changes, control view hide and display, play progress changes, lock state changes, device direction listening and so on
- Define a view interface InterControlView, in this class to define the binding view, view hide and display, play state, play mode, play progress, lock screen and other operations. Every implementation class can get those properties
- Use LinkedHashMap to save each custom view in BaseVideoController, put it in, and then add the view to the controller through addView, which makes it very easy to add custom views
- If the Player state needs to change the Controller view, for example, if the video exception needs to display the exception view, they interact with each other through ControlWrapper(which implements both the Controller and Player interfaces)
06. How to create different players
- Goals and objectives
- The basic player package includes ExoPlayer, MediaPlayer, ijkPlayer, soundnet video player, etc
- You can freely toggle to initialize either video player, such as creating a different video player by constructing an incoming type parameter
PlayerFactory playerFactory = IjkPlayerFactory.create(); IjkVideoPlayer ijkVideoPlayer = (IjkVideoPlayer) playerFactory.createPlayer(this); PlayerFactory playerFactory = ExoPlayerFactory.create(); ExoMediaPlayer exoMediaPlayer = (ExoMediaPlayer) playerFactory.createPlayer(this); PlayerFactory playerFactory = MediaPlayerFactory.create(); AndroidMediaPlayer androidMediaPlayer = (AndroidMediaPlayer) playerFactory.createPlayer(this); Copy the code
- Use that form to create a player
- The factory pattern
- Hidden kernel player creation details, developers only need to care about the factory of the desired product, do not need to care about the creation details to create the player. In line with the open and close principle
- Adapter mode
- This is also an ex post facto pattern, but it is not attempted in this library. www.runoob.com/design-patt…
- How to achieve seamless kernel switching?
- Specific code examples, and how to do them, will be covered in the next blog post. Or just look at the code: video player
- The factory pattern
- The architecture diagram of the player kernel is shown below
07. How to deal with player UI in a friendly way
- Problems encountered in development
- The player can be played in multiple scenarios. Multiple products may use the same player, which causes a problem. When the player status of one player service changes, other player services must update the player status synchronously. Development and maintenance costs can increase dramatically, making subsequent development unsustainable.
- The player kernel is coupled to the UI layer
- In other words, the video player and UI are softer together, especially the interaction between the two. For example, the UI progress bar needs to be updated during playback, and the abnormal UI needs to be displayed during playback. It is difficult to handle the UI update operation when the player status changes
- The UI is difficult to customize or modify
- Common video players, for example, write the various views of the video to XML, which can be very large in the later code, and changing a small layout can have a big impact. In this way, in the later stage, the code is often only added, but not removed…
- Sometimes it is difficult to adapt to a new scene, such as adding a play AD, a teacher to start a class, or a video to guide the business needs, and you need to write a lot of business code into the player. In the late iteration, the open and closed principle is violated, and the video player needs to be separated from the business
- The video player structure needs to be clear
- This refers to whether the video player can quickly get started after reading the document and know the general process of packaging. It is convenient for others to modify and maintain later, so the video player function needs to be separated. For example, switch kernel + video player (Player + Controller + View)
- Be sure to decouple
- Decoupling of player player and video UI: Supports adding custom video views, such as adding custom ads, novice guidance, or video playback exception views, which requires strong scalability
- Suitable for various service scenarios
- For example, it is suitable for playing single videos, multiple videos, and list videos, or one video per page like Douyin, and there are small Windows for playing videos. That is, for most business scenarios
- Specific operation
- The change of playback status causes cross-synchronization between different playback service scenarios. The direct control of playback services on the player is removed and interface listening is used to decouple the player. For example: player + controller + interface
- Specific code examples, and how to do them, will be covered in the next blog post. Or just look at the code: video player
08. Leave interaction to external developers
- In the player, it is very important to expose the player player’s play mode (small screen, normal, full-screen mode) and play state (play, pause, abnormal, complete, load, buffer and other states) to the control layer view for the convenience of UI update.
- For example, if an external developer wants to add an AD view, it must give it the player status
- Added custom player view, such as adding video ads, can choose to skip, choose to pause play. So this view, this view, definitely needs to manipulate the player or get the state of the player. In this case, it is necessary to expose the status interface listening for the video playback
- First define an InterControlView interface, that is to say, all custom video view view needs to realize this interface, the core method of the interface is: binding view to the player, view display hidden change monitoring, play state monitoring, play mode monitoring, progress monitoring, lock screen monitoring and so on
- In the BaseVideoController state listener, the player state can be passed to subclasses through the InterControlView interface object
- Take a code example
- For example, there is a business requirement to add an AD view at the beginning of the video player, wait 120 seconds for the AD countdown, and then go straight to the video logic. I believe this business scenario is very common, you have encountered, using the player is particularly simple, the code is as follows:
- I’m going to create a custom view, and I’m going to implement the InterControlView interface, and I’m going to rewrite all the abstract methods in that interface, and I’m going to omit a lot of code here, see the demo.
public class AdControlView extends FrameLayout implements InterControlView.View.OnClickListener { private ControlWrapper mControlWrapper; public AdControlView(@NonNull Context context) { super(context); init(context); } private void init(Context context){ LayoutInflater.from(getContext()).inflate(R.layout.layout_ad_control_view, this.true); } /** ** Play status * -1 play error * 0 Play not started * 1 Play ready * 2 Play ready * 3 Playing * 4 Pause playing * 5 Buffering (when the player is playing, the buffer is not enough, buffer, Buffer data enough to resume playing) * 6 pause buffering (when the player is playing, the buffer data is insufficient, buffering, pause the player, continue buffering, buffer data enough to resume pause * 7 play finished * 8 start play stop *@paramPlayState refers to the state of the player */ @Override public void onPlayStateChanged(int playState) { switch (playState) { case ConstantKeys.CurrentState.STATE_PLAYING: mControlWrapper.startProgress(); mPlayButton.setSelected(true); break; case ConstantKeys.CurrentState.STATE_PAUSED: mPlayButton.setSelected(false); break; }}/** * Play mode * Normal mode, small window mode, or normal mode * MODE_NORMAL Normal mode * MODE_FULL_SCREEN full screen mode * MODE_TINY_WINDOW Small screen mode *@paramPlayerState Play mode */ @Override public void onPlayerStateChanged(int playerState) { switch (playerState) { case ConstantKeys.PlayMode.MODE_NORMAL: mBack.setVisibility(GONE); mFullScreen.setSelected(false); break; case ConstantKeys.PlayMode.MODE_FULL_SCREEN: mBack.setVisibility(VISIBLE); mFullScreen.setSelected(true); break; } // The full screen adaptation logic is not implemented yet, you need to complete}}Copy the code
- And then how do I use this custom view? Very simple, in the previous basis, through the controller object add, code as shown below
controller = new BasisVideoController(this); AdControlView adControlView = new AdControlView(this); adControlView.setListener(new AdControlView.AdControlListener() { @Override public void onAdClick(a) { BaseToast.showRoundRectToast( "AD click jump"); } @Override public void onSkipAd(a) { playVideo(); }}); controller.addControlComponent(adControlView);// Set the controller mVideoPlayer.setController(controller); mVideoPlayer.setUrl(proxyUrl); mVideoPlayer.start(); Copy the code
09. About priority view display
- In order to expand the video player, it is necessary to expose the View interface for external developers to customize the video player view and add it to the controller of the player in the form of addView.
- This involves the hierarchy of views. It is especially important to control the display and hiding of views, and to get the player state in the custom view
- Take a simple example, basic video player
- Added several playback views with basic playback capabilities. There are play completion, play exception, play load, top title bar, bottom control bar, lock screen, and gesture slider. How do you control their show and hide toggle?
- In addView, most views are hidden by default GONE. For example, when the video is initialized, buffering first shows the buffered view and hides other views, and then playing shows the top/bottom view and hides other views
- For example, what if I need to display two different custom views
- For example, if you click on the video while it’s playing, it will show the top title view and the bottom control bar view, so it will show both views.
- Click the back button of the top title view to close the player, and click the play pause button of the bottom control bar view to control the playing conditions. At this point, the ChildView of the bottom control bar view FrameLayout is at the bottom of the whole video, and the ChildView of the top title view FrameLayout is at the top of the whole video, so that the upper and lower levels can be corresponding events.
- So FrameLayout layer upon layer, how do you make the lower layer not respond to events
- Add: Android :clickable=”true” to the top layer to avoid clicking on the top layer to trigger the bottom layer. Alternatively, you can set the control to a background color.
10. Code project Lib code introduction