[Android多媒體技術] 播放Raw/Assets音視訊方法總結
前言
本文介紹如何通過 系統MediaPlayer
, IjkPlayer
, ExoPlayer
分別播放安卓專案下的Raw或Assets資料夾中的音視訊檔案。
在某些情況下,我們會把一些音視訊檔案,如Mp3,Mp4等,直接放在安裝包中的Raw或者Assets資料夾裡,這些音視訊檔案可能作為特定場景的提示音,或者視訊片頭等等。關於Raw和Assets資原始檔,這裡不作過多討論,總的來講,他們都是被打包進APK中的檔案,不會被編譯成二進位制,程式可以直接訪問,無需額外的許可權。
先說明一下本文程式碼的構建環境和使用的播放核心版本:
效果演示

Raw/Assets資原始檔訪問方式
在專案資料夾中的位置:

Raw檔案訪問方式
-
Raw
檔案位於res/raw目錄下,Raw檔案會被對映到R.java檔案中,所以訪問的時候直接使用資源ID即可,如
R.raw.raw_video
或者獲得該檔案的AssetFileDescriptor:
AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video);
Assets檔案訪問方式
-
Assets
資料夾下的檔案不會被對映到R.java中,訪問的時候需要AssetManager類。
AssetManager am = getAssets(); try { AssetFileDescriptor afd= am.openFd(fileName); } catch (IOException e) { e.printStackTrace(); }
AssetFileDescriptor可以理解成訪問Raw/Assets檔案的一個入口,或者說是一把鑰匙。
Raw/Assets檔案還有其他的訪問方式,比如通過 ContentResolver
,又或者直接開啟一個InputStream去讀取檔案,這應該是播放器核心需要做的事情,我們只需要給播放器提供以上的資訊即可。
·········································································································
下面直接上程式碼

通過系統MediaPlayer播放音視訊
- Raw檔案
//例項化播放核心 android.media.MediaPlayer mediaPlayer = new android.media.MediaPlayer(); //獲得播放源訪問入口 AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video); // 注意這裡的區別 //給MediaPlayer設定播放源 mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); //設定準備就緒狀態監聽 mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 開始播放 mediaPlayer.start(); } }); //準備播放 mediaPlayer.prepareAsync();
- Assets 檔案
//例項化播放核心 android.media.MediaPlayer mediaPlayer = new android.media.MediaPlayer(); //獲得播放源訪問入口 AssetManager am = getAssets(); try { AssetFileDescriptor afd = am.openFd("assets_video.mp4");// 注意這裡的區別 //給MediaPlayer設定播放源 mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); } catch (IOException e) { e.printStackTrace(); } //設定準備就緒狀態監聽 mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 開始播放 mediaPlayer.start(); } }); //準備播放 mediaPlayer.prepareAsync();
程式碼中每一步都有註釋,可以說非常詳細了。簡單總結一下,無論播放Raw檔案還是Assets檔案,我們首先獲得AssetFileDescriptor,然後設定給MediaPlayer。
·········································································································
通過IjkPlayer播放音視訊
- Raw 檔案
//例項化播放核心 tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer(); //獲得播放源訪問入口 AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.raw_video); // 注意這裡的區別 //構建IjkPlayer能識別的IMediaDataSource,下面的RawDataSourceProvider實現了IMediaDataSource介面 RawDataSourceProvider sourceProvider = new RawDataSourceProvider(fd); //給IjkPlayer設定播放源 ijkPlayer.setDataSource(sourceProvider); //設定準備就緒狀態監聽 ijkPlayer .setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 開始播放 ijkPlayer.start(); } }); //準備播放 ijkPlayer.prepareAsync();
- Assets 檔案
//例項化播放核心 tv.danmaku.ijk.media.player.IjkMediaPlayer ijkPlayer = new tv.danmaku.ijk.media.player.IjkMediaPlayer(); //獲得播放源訪問入口 AssetManager am = getAssets(); try { AssetFileDescriptor afd = am.openFd("assets_video.mp4");// 注意這裡的區別 //構建IjkPlayer能識別的IMediaDataSource,下面的RawDataSourceProvider實現了IMediaDataSource介面 RawDataSourceProvider sourceProvider = new RawDataSourceProvider(fd); //給IjkPlayer設定播放源 ijkPlayer.setDataSource(sourceProvider); } catch (IOException e) { e.printStackTrace(); } //設定準備就緒狀態監聽 ijkPlayer .setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { // 開始播放 ijkPlayer.start(); } }); //準備播放 ijkPlayer.prepareAsync();
補充下,其中的 RawDataSourceProvider
實現了 IMediaDataSource
介面, IMediaDataSource
是 IjkPlayer
包中的介面,實現了 IMediaDataSource
介面的類可以設定給IjkPlayer作為播放源。就像下面這樣:
import tv.danmaku.ijk.media.player.misc.IMediaDataSource; public class RawDataSourceProvider implements IMediaDataSource { private AssetFileDescriptor mDescriptor; private byte[] mMediaBytes; public RawDataSourceProvider(AssetFileDescriptor descriptor) { this.mDescriptor = descriptor; } @Override public int readAt(long position, byte[] buffer, int offset, int size) { if (position + 1 >= mMediaBytes.length) { return -1; } int length; if (position + size < mMediaBytes.length) { length = size; } else { length = (int) (mMediaBytes.length - position); if (length > buffer.length) length = buffer.length; length--; } // 把檔案內容copy到buffer中; System.arraycopy(mMediaBytes, (int) position, buffer, offset, length); return length; } @Override public long getSize() throws IOException { long length = mDescriptor.getLength(); if (mMediaBytes == null) { InputStream inputStream = mDescriptor.createInputStream(); mMediaBytes = readBytes(inputStream); } return length; } @Override public void close() throws IOException { if (mDescriptor != null) mDescriptor.close(); mDescriptor = null; mMediaBytes = null; } //讀取檔案內容 private byte[] readBytes(InputStream inputStream) throws IOException { ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream(); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int len; while ((len = inputStream.read(buffer)) != -1) { byteBuffer.write(buffer, 0, len); } return byteBuffer.toByteArray(); } }
小結一下,我們首先獲取到Raw/Assets檔案的 AssetFileDescriptor
,然後用它去構建一個 IMediaDataSource
,最後設定給 IjkPlayer
。
·········································································································
通過ExoPlayer播放音視訊
- ExoPlayer播放器核心的例項化有點複雜,這裡單獨寫
//例項化播放核心 TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter()); DefaultTrackSelector mTrackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); boolean preferExtensionDecoders = true; boolean useExtensionRenderers = true;//是否開啟擴充套件 @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = useExtensionRenderers ? (preferExtensionDecoders ? DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON) : DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF; DefaultRenderersFactory rendererFactory = new DefaultRenderersFactory(mAppContext, extensionRendererMode); DefaultLoadControl loadControl = new DefaultLoadControl(); //工廠方法獲得播放器例項 om.google.android.exoplayer2.SimpleExoPlayer exoPlayer = ExoPlayerFactory.newSimpleInstance(rendererFactory, mTrackSelector, loadControl, null);
- Raw 檔案
//構建Raw檔案播放源--RawResourceDataSource DataSpec dataSpec = new DataSpec(RawResourceDataSource.buildRawResourceUri(R.raw.raw_video)); RawResourceDataSource rawResourceDataSource = new RawResourceDataSource(this); try { rawResourceDataSource.open(dataSpec); } catch (RawResourceDataSource.RawResourceDataSourceException e) { e.printStackTrace(); } //構建ExoPlayer能識別的播放源--MediaSource String url = rawDataSource.getUri().toString(); MediaSource mediaSource = ExoSourceManager.newInstance(mAppContext, getHeaders()).getMediaSource( url, false, false, MediaPlayerManager.instance().isLooping(), null ); //給ExoPlayer設定播放源,並準備播放 exoPlayer.prepare(mediaSource); //讓ExoPlayer準備好後就開始播放 exoPlayer.setPlayWhenReady(true);
- Assets 檔案
//構建ExoPlayer能識別的播放源--MediaSource String url = "file:///android_asset/" + "assets_video.mp4"; MediaSource mediaSource = ExoSourceManager.newInstance(mAppContext, getHeaders()).getMediaSource( url, false, false, MediaPlayerManager.instance().isLooping(), null ); //給ExoPlayer設定播放源,並準備播放 exoPlayer.prepare(mediaSource); //讓ExoPlayer準備好後就開始播放 exoPlayer.setPlayWhenReady(true);
小結:ExoPlayer例項化的時候複雜一點,需要按照官方文件一步步來。播放Raw檔案的時候,需要先構建出 RawResourceDataSource
,這是ExoPlayer為了播放Raw音視訊檔案提供的類,然後獲取其中的Uri構建 MediaSource
,最後設定給ExoPlayer。播放Assets檔案就更簡單了,形如 String url = "file:///android_asset/" + "filename"
的Assets檔案播放地址可以直接用來構建 MediaSource
。
專案地址: ofollow,noindex">https://github.com/maiwenchang/ArtPlayer 歡迎各位Star~~