directory
- Overview of ExoPlayer
- Basic use of ExoPlayer
- Problems encountered
- data
- harvest
From this post we move on to Phase FIVE – learning usage analysis of some audio and video open source projects. Today we move on to learning practices in the ExoPlayer section
1. Introduction to ExoPlayer
ExoPlayer is a Google open source, app-level audio and video player. ExoPlayer supports DYNAMIC Adaptive streaming (DASH) over HTTP, SmoothStreaming, and universal encryption, as well as excellent support for playback queues and seamless switching between playback sources. It has a design that is easy to customize and extend. The internal implementation also calls low-level apis such as MediaCodec, AudioTrack, and so on
Let me draw a table comparing ExoPlayer and MediaPlayer to give you an intuition
ExoPlayer’s repository is located at * github.com/google/ExoP…
The red box, the core and UI library is also the focus of this series.
The core of ExoPlayer is the Interface of ExoPlayer, which defines the functions of traditional players (buffer audio and video, play, pause, seek, etc.). ExoPlayer does not specify the media type that can be played, how to store it, how to render it, nor does it directly load and play it. This is done by proxying the work to a registered component when the player is created or ready to play. Here are some common ExoPlayer component implementations:
- MediaSource to load media, register with exoplayer.prepare
- TrackSelector: Audio/visual track extractor that extracts track data from MediaSource
- Render: Render the data extracted by TrackSelector, AudioTrack plays the audio and Surface renders the video
- LoadControl: Controls MediaSource (when to start buffering, how much to buffer, etc.) ExoPlayer provides default implementations for these components and can extend the implementation by customizing components if needed.
Through ExoPlayer’s architecture diagram, we can also see the modular design of its components, which is worth learning and an important requirement of a good component /SDK. In our daily project development, we develop a component from the perspective of ease of use and extensibility. We need to ensure that it is easy for users to use (provide a set of default implementations), but also have the ability for users to easily expand according to their own scenarios.
Before looking at the State machine in ExoPlayer, let’s take a look at the MeidaPlayer state machine
You can see that MediaPlayer has a lot of state, and if you trigger mismatched operations in the wrong place when you use MediaPlayer, you will crash. Unlike MediaPlayer, which cannot play until prepared, ExoPlayer has many listeners and isplaying apis that listen for state changes. The four states of an ExoPlayer are as follows
/** * Playback state. One of {@link #STATE_IDLE}, {@link #STATE_BUFFERING}, {@link #STATE_READY} or * {@link #STATE_ENDED}. */ @Retention(RetentionPolicy.SOURCE) @IntDef({STATE_IDLE, STATE_BUFFERING, STATE_READY, STATE_ENDED}) @interface State {} /** The player does not have any media to play. */ int STATE_IDLE = 1; /** * The player is not able to immediately play from its current position. This state typically * occurs when more data needs to be loaded. */ int STATE_BUFFERING = 2; /** * The player is able to immediately play from its current position. The player will be playing if * {@link #getPlayWhenReady()} is true, and paused otherwise. */ int STATE_READY = 3; /** The player has finished playing the media. */ int STATE_ENDED = 4;Copy the code
STATE_IDLE: Indicates the initial state. The player does not have resources to play. The player is in this state even when the player stops playing or fails to play. The player can play immediately, depending on the value of playWhenReady, which indicates the user’s intention. If true, the player will play, otherwise it will not play. STATE_ENDED: the state is changed after all resources are played
2. Easy to use ExoPlayer
In this section we learn to practice using ExoPlayer
2.1 The introduction of Library ExoPlayer in AS has good scalability and customisability. You can select the corresponding modules according to the needs of the project, or you can include all of them.
exoplayer-core: Core functionality (required).
exoplayer-dash: Support for DASH content.
exoplayer-hls: Support for HLS content.
exoplayer-smoothstreaming: Support for SmoothStreaming content.
exoplayer-ui: UI components and resources for use with ExoPlayer.
We add libraries as needed
Implementation 'com. Google. Android. Exoplayer: exoplayer - core: 2.13.3' implementation 'com. Google. Android. Exoplayer: exoplayer - UI: 2.13.3'Copy the code
The next step is to create a container PlayerView and an ExoPlayerView to play
2.2 Create a player, bind the player container, set data source, and prepare
Player = simpleExoplayer.builder (this).build() printCurPlaybackState("init"); //2. Player and player container binding playerView.player = player //3. Set up the data source / / audio val mediaItem = mediaItem. FromUri (" https://storage.googleapis.com/exoplayer-test-media-0/play.mp3 ") player.setMediaItem(mediaItem) //4. Player. playWhenReady = true //5. Player.prepare () printCurPlaybackState("prepare") // At STATE_BUFFERING = 2;Copy the code
2.3 Playing Listening Whether listening is playing
public final boolean isPlaying() {
return getPlaybackState() == Player.STATE_READY
&& getPlayWhenReady()
&& getPlaybackSuppressionReason() == PLAYBACK_SUPPRESSION_REASON_NONE;
}
Copy the code
Play the status change listener, audio related listener, and video related listener
playbackListener = PlaybackListener() player.addListener(playbackListener) player.addAudioListener(playbackListener) player.addVideoListener(playbackListener) class PlaybackListener : Player.EventListener, AudioListener, VideoListener { override fun onPlaybackStateChanged(playbackState: Int) { val stateString: String stateString = when (playbackState) { ExoPlayer.STATE_IDLE -> "ExoPlayer.STATE_IDLE -" ExoPlayer.STATE_BUFFERING -> "ExoPlayer.STATE_BUFFERING -" ExoPlayer.STATE_READY -> "ExoPlayer.STATE_READY -" ExoPlayer.STATE_ENDED -> "Exoplayer.state_ended -" else -> "UNKNOWN_STATE -"} log.d ("ExoBaseUserActivity"); "changed state to $stateString") } override fun onAudioSessionIdChanged(audioSessionId: Int) { Log.d("ExoBaseUserActivity", "onAudioSessionIdChanged--sessionId=" + audioSessionId) } override fun onAudioAttributesChanged(audioAttributes: AudioAttributes) { Log.d("ExoBaseUserActivity", "onAudioAttributesChanged--audioAttributes=" + audioAttributes.toString()) } override fun onVolumeChanged(volume: Float) { Log.d("ExoBaseUserActivity", "onVolumeChanged--volume=" + volume) } override fun onSkipSilenceEnabledChanged(skipSilenceEnabled: Boolean) { Log.d("ExoBaseUserActivity", "onSkipSilenceEnabledChanged--skipSilenceEnabled=" + skipSilenceEnabled) } override fun onVideoSizeChanged(width: Int, height: Int, unappliedRotationDegrees: Int, pixelWidthHeightRatio: Float) { Log.d("ExoBaseUserActivity", "onVideoSizeChanged--width=" + width + " height=" + height + " unappliedRotationDegrees=" + unappliedRotationDegrees + " pixelWidthHeightRatio=" + pixelWidthHeightRatio) } override fun onSurfaceSizeChanged(width: Int, height: Int) { Log.d("ExoBaseUserActivity", "onSurfaceSizeChanged--width=" + width + " height=" + height) } override fun onRenderedFirstFrame() { Log.d("ExoBaseUserActivity", "onRenderedFirstFrame") } }Copy the code
A listener for analysis (which outputs more detailed information)
AnalyticsListener = EventLogger(DefaultTrackSelector()) player.addAnalyticsListener(analyticsListener)Copy the code
2.4 Releasing Resources Resources should be released when the page is not visible or destroyed (to see whether background playback is required)
override fun onDestroy() {
super.onDestroy()
player.removeAnalyticsListener(analyticsListener)
player.removeListener(playbackListener)
player.removeAudioListener(playbackListener)
player.removeVideoListener(playbackListener)
player.release()
}
Copy the code
The full code has been uploaded to github github.com/ayyb1988/me…
Three, encountered problems
Question 1
Failed to resolve: com. Google. Android. Exoplayer: exoplayer: 2.13.3)Copy the code
2.13.3 There is an extra space in the beginning, which is too… Details sometimes you can waste a lot of time without paying attention.
Question 2
java.lang.SecurityException: Permission denied (missing INTERNET permission?) at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:150) at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103) at java.net.InetAddress.getAllByName(InetAddress.java:1152) at com.android.okhttp.Dns$1.lookup(Dns.java:41) at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:178) at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:144) at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:86) at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:176) at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:128) at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:97) at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:289) at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:232) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:465) at com.android.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:131) at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.connect(DelegatingHttpsURLConnection.java:90) at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:30) at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:641) at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:528) at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:349) at com.google.android.exoplayer2.upstream.DefaultDataSource.open(DefaultDataSource.java:201) at com.google.android.exoplayer2.upstream.StatsDataSource.open(StatsDataSource.java:84) at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1015) at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:415) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) at java.lang.Thread.run(Thread.java:919)Copy the code
There is no reason to add network permission, after static registration in Mainfest, under dynamic request in requesetPermission. From this crash stack, we can see that ExoPlayer loads web videos using Okhttp
Question 3
The 2021-05-15 18:41:17. 414, 11144-11144 /? I/ av.mediaJourne: Not late enabling Xcheck: JNI (already on) 2021-05-15 18:41:17.487 11144-11144/? E/av. mediaJourne: Unknown bits set in runtime_flags: 0x8000 2021-05-15 18:41:17.489 11144-11144/? W/av.mediajourne: Unexpected CPU variant for X86 using defaults: x86Copy the code
The X86 emulator will occasionally flash out, but the real machine is normal. Model equipment adaptation is always a big problem
Four, data
- Media streaming with ExoPlayer
- ExoPlayer blog
- ExoPlayer developer guide
- How to use ExoPlayer to play audio and video
Five, the harvest
The results of this study and practice are as follows:
- Understand the background of ExoPlayer and its advantages and disadvantages compared to MediaPlayer
- Understand the basic functions of ExoPlayer
- Simple and practical
Thank you for reading
In the next article, we will continue to learn how to implement ExoPlayer, a simple audio player. Please follow the official account “Audio and Video Development Journey” to learn and grow together.
Welcome to communicate