1. Project introduction
1. Background and meaning
With the development of mobile Internet, video has become one of the media forms with the largest traffic. Whether it is social short videos, online video playback, or live streaming, Android applications have ubiquitous demand for video playback. To achieve a stable, smooth and functional richVideo playbackModules require mastering multiple underlying APIs and third-party frameworks to cope with different networks, formats, coding and business scenarios.
This tutorial will give a comprehensive introduction to the implementation of video playback on AndroidMultiple solutions,include:
system
VideoView
: The easiest API, fast integrationNative
MediaPlayer
+SurfaceView
: More flexible underlying implementationNative
MediaPlayer
+TextureView
: Supports transformations such as rotation and scalingExoPlayer: Google recommends, supports DASH/HLS, cache, DRM
Media3(Jetpack)**: Inheriting ExoPlayer, future trends
Third-party player: Such as IJKPlayer (FFmpeg), Vitamio, etc.
Lower level
MediaCodec
: Custom decoding pipelines, suitable for special needsCompose +
AndroidView
: Integrate videos in Jetpack Compose
By comparing the usage, advantages and disadvantages of each solution, applicable scenarios, and complete sample code, you will be able to quickly make decisions and integrate video playback functions according to project needs.
2. Related knowledge
Before diving into the code, please understand the following core concepts:
-
Container Type
SurfaceView
: Independent rendering buffer, high performance but does not support ordinary View hierarchical transformation.TextureView
: Rendering in a normal View layer, supports translation, rotation, and scaling, but has a slightly lower performance.PlayerView
/StyledPlayerView
: Encapsulated view provided by ExoPlayer.
-
Player API Layer
VideoView
: EncapsulatedMediaPlayer
+SurfaceView
, fast integration but poor customization.MediaPlayer
: Android native media playback engine, supports local and network streaming media.ExoPlayer
: Open source of Google, supports DASH, HLS, SmoothStreaming, and custom data sources.Media3
: A higher-level Jetpack media library, future recommendations.
-
Streaming protocol
HTTP Progressive: Download MP4, MKV and other files directly.
HLS (M3U8):pass
#EXTM3U
Player downloads and plays.DASH (MPD): Dynamic adaptive bit rate.
-
DRM and clarity switch
ExoPlayer and Media3 have built-in support for DRM such as Widevine, PlayReady, etc.
Dynamically switch resolution and code rate, it needs to be implemented
TrackSelector
orDefaultTrackSelector
。
-
Lifecycle and recycling
Activity/Fragment's
onStart
/onStop
oronResume
/onPause
The player is controlledplay()
/pause()
and when destroyedrelease()
。
Ideas for realization
We will implement and compare each solution in the following order:
Plan 1:
VideoView
Plan 2:
MediaPlayer
+SurfaceView
Plan 3:
MediaPlayer
+TextureView
Solution 4: ExoPlayer
Plan 5: Media3
Plan 6: IJKPlayer (FFmpeg)
Plan 7:
MediaCodec
Self-decodingSolution 8: Jetpack Compose Integration Solution
Each program will provide:
Layout example
Activity/Fragment code
Lifecycle Management
Error handling and callbacks
Finally, we will summarize the advantages and disadvantages of each plan and give best practice suggestions for different scenarios.
4. Environment and dependence
// app/ plugins { id '' id 'kotlin-android' } android { compileSdkVersion 34 defaultConfig { applicationId "" minSdkVersion 21 targetSdkVersion 34 } buildFeatures { viewBinding true } kotlinOptions { jvmTarget = "1.8" } } dependencies { // ExoPlayer implementation ':exoplayer:2.18.2' // Media3 implementation "androidx.media3:media3-exoplayer:1.0.0" implementation "androidx.media3:media3-ui:1.0.0" // IJKPlayer implementation ':ijkplayer-java:0.8.8' implementation ':ijkplayer-arm64:0.8.8' // Compose (for Compose plan) implementation ":ui:1.4.0" implementation ":material:1.4.0" implementation ":activity-compose:1.7.0" }
5. Integrate code
// ======================================================= // File: res/layout/activity_main.xml// Description: Simple navigation, select different playback schemes// ======================================================= <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro android:orientation="vertical" android:padding="16dp" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android: android:text="VideoView Solution"/> <Button android: android:text="MediaPlayer+SurfaceView"/> <Button android: android:text="MediaPlayer+TextureView"/> <Button android: android:text="ExoPlayer Solution"/> <Button android: android:text="Media3 Solution"/> <Button android: android:text="IJKPlayer Solution"/> <Button android: android:text="MediaCodec Self-decoding"/> <Button android: android:text="Compose Integration Solution"/> </LinearLayout> // ======================================================= // document:// Description: Jump to each example Activity// ======================================================= package import import import import class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(s: Bundle?) { (s) binding = (layoutInflater) setContentView() .setOnClickListener { startActivity(Intent(this, VideoViewActivity::)) } .setOnClickListener { startActivity(Intent(this, SurfaceActivity::)) } .setOnClickListener { startActivity(Intent(this, TextureActivity::)) } .setOnClickListener { startActivity(Intent(this, ExoActivity::)) } binding.btnMedia3 .setOnClickListener { startActivity(Intent(this, Media3Activity::)) } .setOnClickListener { startActivity(Intent(this, IjkActivity::)) } .setOnClickListener { startActivity(Intent(this, CodecActivity::)) } .setOnClickListener { startActivity(Intent(this, ComposeActivity::)) } } } // ======================================================= // Plan 1:// Layout: res/layout/activity_video_view.xml // ======================================================= // activity_video_view.xml /* <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:andro android:layout_width="match_parent" android:layout_height="match_parent"> <VideoView android: android:layout_width="match_parent" android:layout_height="match_parent"/> <ProgressBar android: style="?android:attr/progressBarStyleLarge" android:layout_gravity="center"/> </FrameLayout> */ // package import import import import import class VideoViewActivity : AppCompatActivity() { private lateinit var binding: ActivityVideoViewBinding override fun onCreate(s: Bundle?) { (s) binding = (layoutInflater) setContentView() val uri = ("/video.mp4") () (uri) (MediaController(this)) { () = true () } } override fun onPause(){ (); () } override fun onResume(){ (); () } override fun onDestroy(){ (); () } } // ======================================================= // Scheme 2: SurfaceView + MediaPlayer// File: res/layout/activity_surface.xml // ======================================================= /* <?xml version="1.0" encoding="utf-8"?> <FrameLayout ...> <SurfaceView android: .../> <ProgressBar android: .../> </FrameLayout> */ // package import import import import import class SurfaceActivity: AppCompatActivity(), { private lateinit var binding: ActivitySurfaceBinding private var player: MediaPlayer? = null override fun onCreate(s: Bundle?){ (s) binding = (layoutInflater) setContentView() (this) } override fun surfaceCreated(holder: SurfaceHolder) { player = MediaPlayer().apply { setDataSource("https://.../video.mp4") setDisplay(holder) setOnPreparedListener { () isLooping = true; start() } prepareAsync() } } override fun surfaceDestroyed(holder: SurfaceHolder) { player?.release(); player = null } override fun surfaceChanged(h: SurfaceHolder, f:Int, w:Int, h2:Int){} } // ======================================================= // Scheme 3: TextureView + MediaPlayer// File: res/layout/activity_texture.xml // ======================================================= /* <?xml version="1.0" encoding="utf-8"?> <FrameLayout ...> <TextureView android: .../> <ProgressBar android: .../> </FrameLayout> */ // package import import import import import import class TextureActivity: AppCompatActivity(), { private lateinit var binding: ActivityTextureBinding private var player: MediaPlayer? = null override fun onCreate(s: Bundle?){ (s) binding = (layoutInflater) setContentView() = this } override fun onSurfaceTextureAvailable(st: SurfaceTexture, w:Int, h:Int){ player = MediaPlayer().apply { setSurface((st)) setDataSource("https://.../video.mp4") setOnPreparedListener { () isLooping=true; start() } prepareAsync() } } override fun onSurfaceTextureSizeChanged(st:SurfaceTexture,w:Int,h:Int){} override fun onSurfaceTextureDestroyed(st:SurfaceTexture):Boolean{ player?.release(); player=null; return true } override fun onSurfaceTextureUpdated(st:SurfaceTexture){} } // ======================================================= // Solution 4: ExoPlayer// File: res/layout/activity_exo.xml // ======================================================= /* <?xml version="1.0" encoding="utf-8"?> <. xmlns:andro android: .../> */ // package import import import import import . import . class ExoActivity: AppCompatActivity() { private lateinit var binding: ActivityExoBinding private var player: ExoPlayer? = null override fun onCreate(s: Bundle?){ (s) binding = (layoutInflater) setContentView() player = (this).build().also { = it val mediaItem = (("https://.../video.mp4")) (mediaItem); = ExoPlayer.REPEAT_MODE_ALL (); () } } override fun onPause(){ (); player?.pause() } override fun onResume(){ (); player?.play() } override fun onDestroy(){ (); player?.release(); player=null } } // ======================================================= // Plan 5: Media3 (Jetpack)// File: res/layout/activity_media3.xml // ======================================================= /* <?xml version="1.0" encoding="utf-8"?> <androidx. ... android:/> */ // package import import import import androidx. import androidx. import .ActivityMedia3Binding class Media3Activity: AppCompatActivity() { private lateinit var binding: ActivityMedia3Binding private var player: ExoPlayer? = null override fun onCreate(s: Bundle?){ (s) binding = (layoutInflater) setContentView() player = (this).build().apply { setMediaItem((("https://.../video.mp4"))) repeatMode = ExoPlayer.REPEAT_MODE_ALL; prepare(); play() } = player } override fun onPause(){ (); player?.pause() } override fun onResume(){ (); player?.play() } override fun onDestroy(){ (); player?.release(); player=null } } // ======================================================= // Plan 6: IJKPlayer// File: res/layout/activity_ijk.xml // ======================================================= /* <?xml version="1.0" encoding="utf-8"?> < ... android:/> */ // package import import import import class IjkActivity: AppCompatActivity() { private lateinit var binding: ActivityIjkBinding override fun onCreate(s: Bundle?){ (s) binding = (layoutInflater) setContentView() (null); IjkMediaPlayer.native_profileBegin("") ("https://.../video.mp4") () } override fun onDestroy(){ () () IjkMediaPlayer.native_profileEnd() } } // ======================================================= // Scheme 7: MediaCodec self-decoding (short description)// File: // ======================================================= // Hundreds of lines of self-decoded code are omitted here, and only brief descriptions are given:// - Use MediaExtractor to separate tracks// - Decode to Surface with MediaCodec// - Render with SurfaceView / TextureView// It is recommended to consult the official documents and Codelab to implement it in depth. // ======================================================= // Solution 8: Compose integration// File: // ======================================================= package import import import import import import import import import androidx. import androidx. class ComposeActivity: AppCompatActivity() { override fun onCreate(s: Bundle?){ (s) val player = (this).build().apply { setMediaItem((("https://.../video.mp4"))) prepare(); play() } setContent { Box(()) { AndroidView(factory = { ctx -> PlayerView(ctx).apply { = player; useController=true } }, modifier=()) } } } override fun onDestroy(){ () () } }
6. Code interpretation
-
VideoView
Simple and easy to use, high packaging;
Unable to control underlying buffering or custom rendering;
-
MediaPlayer
+SurfaceView
Suitable for large-scale video or live broadcast;
High performance, but does not support View transformation;
-
MediaPlayer
+TextureView
Supports any 2D transformation (rotate, zoom);
Performance is second to SurfaceView;
-
ExoPlayer
Supports DASH, HLS, and custom loading;
Have rich extensions (cache, DRM, subtitles);
-
Media3
Jetpack new recommendations, compatible with future updates;
The API is basically the same as ExoPlayer;
-
IJKPlayer
Based on FFmpeg, support more formats;
Need to deploy native libraries, with a large package;
-
MediaCodec
Lowest-level control, suitable for custom rendering or special decoding needs;
High development costs;
-
Compose integration
Can be used in Compose
AndroidView
Embed any View;In the future, you can expect native Compose video components;
7. Performance and Optimization
-
Hardware acceleration
SurfaceView
with ExoPlayer default hardware acceleration;
-
Network buffering
ExoPlayer can be customized
LoadControl
;
-
Concurrency and Switching
Avoid frequent
prepare()
/release()
;
-
Memory management
Timely
release()
Resources to avoid leakage;
-
UI and Rendering
Avoid heavy UI operations on the main thread;
8. Project Summary and Expansion
This article introduces almost all mainstream video playback implementation methods on Android from multiple angles and in full schemes, and compares sample code with advantages and disadvantages to facilitate making choices in different business scenarios. Future scalable:
Adaptive code rate:HLS/DASH dynamic switching
DRM:Protected clearplay
Save traffic: Integrated cache, pre-download
UI special effects: Filters, barrage, picture in picture
9. FAQ
Q1: Which solution is the easiest?
A:VideoView
, but the customizability is minimal.
Q2: Which one is recommended?
A: ExoPlayer/Media3, the most complete functions and active community.
Q3: How to play live HLS?
A: ExoPlayer Direct("https://.../live.m3u8")
Just do it.
Q4: What should I do if IJKPlayer has a large body?
A: The native library can be customized and only package the required ABI.
Q5: Will there be native video components in Compose in the future?
A: It is under development, but it is still necessaryAndroidView
Embed.
The above is the detailed content of various solutions for realizing video playback on Android. For more information about Android video playback, please follow my other related articles!