Android音視頻播放庫:wlmedia

 

GitHubhttps://github.com/wanliyang1990/wlmedia

功能

**支持:http、https、rtsp、rtp、rtmp、byte[]、加密視頻和各種文件格式視頻;

**截圖、音軌選擇、自定義視頻濾鏡、變速變調、聲道切換、無縫切換surface(surfaceview和textureview)、視頻比例設置等;

視頻演示

1、Usage

Gradle: 

implementation 'ywl.ywl5320:wlmedia:1.0.5'

2、實例圖片


3、調用方式

配置NDK編譯平臺:

defaultConfig {
    ...
	ndk {
	    abiFilter("arm64-v8a")
	    abiFilter("armeabi-v7a")
	    abiFilter("x86")
	    abiFilter("x86_64")
	}
	...
}

基本權限

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

4、接入代碼

4.1、視頻surface選擇

// WlSurfaceView 一般播放使用
<com.ywl5320.wlmedia.surface.WlSurfaceView
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

// WlTextureView 需要做透明、移動、旋轉等使用
<com.ywl5320.wlmedia.surface.WlTextureView
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

4.2、創建播放器

4.2.1 播放視頻

WlMedia wlMedia = WlMedia.getInstance();//單例模式主要用於視頻,音頻可以new對象
wlMedia.setPlayModel(WlPlayModel.PLAYMODEL_AUDIO_VIDEO);//同時播放音頻視頻
wlSurfaceView.setWlMedia(wlMedia);//給視頻surface設置播放器

//異步準備完成後開始播放
wlMedia.setOnPreparedListener(new WlOnPreparedListener() {
        @Override
        public void onPrepared() {
            wlMedia.start();//開始播放
        }
    });
//surface初始化好了後 開始播放視頻(用於一打開activity就播放場景)
wlSurfaceView.setOnVideoViewListener(new WlOnVideoViewListener() {
        @Override
        public void initSuccess() {
            wlMedia.setSource("/storage/sdcard1/fcrs.1080p.mp4");//設置數據源
            wlMedia.prepared();//異步開始準備播放
        }

        @Override
        public void moveSlide(double value) {

        }

        @Override
        public void movdFinish(double value) {
            wlMedia.seek(value);//seek
        }
    });
//自定義濾鏡方式
String fs = "precision mediump float;" +
            "varying vec2 ft_Position;" +
            "uniform sampler2D sTexture; " +
            "void main() " +
            "{ " +
            "vec4 v=texture2D(sTexture, ft_Position); " +
            "float average = (v.r + v.g + v.b) / 3.0;" +
            "gl_FragColor = vec4(average, average, average, v.a);" +
            "}";
wlMedia.setfShader(fs);
wlMedia.changeFilter();

4.2.2 播放音頻

WlMedia wlMedia = new WlMedia();
wlMedia.setPlayModel(WlPlayModel.PLAYMODEL_ONLY_AUDIO);//設置只播放音頻(必須)
wlMedia.setSource(WlAssetsUtil.getAssetsFilePath(this, "mydream.m4a"));//設置數據源
wlMedia.setOnPreparedListener(new WlOnPreparedListener() {
    @Override
    public void onPrepared() {
        wlMedia.start();
    }
});
wlMedia.prepared();

4.2.3 播放加密視頻文件

WlMedia wlMedia = WlMedia.getInstance();//單例模式主要用於視頻,音頻可以new對象
wlMedia.setPlayModel(WlPlayModel.PLAYMODEL_AUDIO_VIDEO);
wlSurfaceView.setWlMedia(wlMedia);
wlMedia.setBufferSource(true, true);//必須都爲true
wlMedia.setOnDecryptListener(new WlOnDecryptListener() {

    //解密算法
    @Override
    public byte[] decrypt(byte[] encrypt_data) {
        int length = encrypt_data.length;
        for(int i = 0; i < length; i++)
        {
            encrypt_data[i] = (byte) ((int)encrypt_data[i] ^ 666);
        }
        WlLog.d("decrypt");
        return encrypt_data;
    }
});
wlSurfaceView.setOnVideoViewListener(new WlOnVideoViewListener() {
        @Override
        public void initSuccess() {
            wlMedia.setSource(WlAssetsUtil.getAssetsFilePath(WlEncryptActivity.this, "fhcq-ylgzy-dj-encrypt.mkv"));//加密視頻源
            wlMedia.prepared();
        }

        @Override
        public void moveSlide(double value) {

        }

        @Override
        public void movdFinish(double value) {
            wlMedia.seek(value);
        }
    });

4.2.4 播放byte[]音視頻數據

WlMedia wlMedia = WlMedia.getInstance();//單例模式主要用於視頻,音頻可以new對象
wlMedia.setBufferSource(true, false);//必須第一個爲true,第二個爲false
wlMedia.setPlayModel(WlPlayModel.PLAYMODEL_ONLY_VIDEO);//根據byte類型來設置(可以音頻、視頻、音視頻)
wlTextureView.setWlMedia(wlMedia);
wlMedia.setOnPreparedListener(new WlOnPreparedListener() {
        @Override
        public void onPrepared() {
            wlMedia.start();
        }
    });
wlTextureView.setOnVideoViewListener(new WlOnVideoViewListener() {
    @Override
    public void initSuccess() {
        new Thread(new Runnable() {
            long length = 0;
            @Override
            public void run() {
                try {
                    //從文件模擬byte[]數據
                    File file = new File(WlAssetsUtil.getAssetsFilePath(WlBufferActivity.this, "mytest.h264"));
                    FileInputStream fi = new FileInputStream(file);
                    byte[] buffer = new byte[1024 * 64];
                    int buffersize = 0;
                    int bufferQueueSize = 0;
                    exit = false;
                    while(true)
                    {
                        if(exit)
                        {
                            break;
                        }
                        if(wlMedia.isPlay())
                        {
                            WlLog.d("read thread " + bufferQueueSize);
                            if(bufferQueueSize < 20)//控制隊列大小,不然內存可能會增大溢出
                            {
                                buffersize = fi.read(buffer);
                                if(buffersize <= 0)
                                {
                                    WlLog.d("read thread ==============================  read buffer exit ...");
                                    wlMedia.putBufferSource(null, -1);
                                    break;
                                }
                                bufferQueueSize = wlMedia.putBufferSource(buffer, buffersize);//返回值爲底層buffer隊列個數
                                while(bufferQueueSize < 0)
                                {
                                    bufferQueueSize = wlMedia.putBufferSource(buffer, buffersize);
                                }
                            }
                            else
                            {
                                bufferQueueSize = wlMedia.putBufferSource(null, 0);
                            }
                            sleep(10);
                        }
                        else
                        {
                            WlLog.d("buffer exit");
                            break;
                        }

                    }
                    wlMedia.stop();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        wlMedia.prepared();
    }

    @Override
    public void moveSlide(double value) {

    }

    @Override
    public void movdFinish(double value) {

    }
});

5、API

5.1 播放器API

public WlMedia();//構造函數,不依賴上下文

public void setSource(String source);//設置數據源(可以是file、url)

public void setPlayModel(WlPlayModel playModel);//設置音視頻播放模式(可以獨立播放音頻和視頻或者同時播放音視頻,默認同時播放音視頻)

public void setCodecType(WlCodecType codecType);//設置解碼器類型(默認優先使用硬解碼)

public void prepared();異步開始準備播放,成功後會回調WlOnPreparedListener接口

public void next();//切歌(數據源設置方法爲:setSource)

public void start();//開始播放(當異步準備完成後,在WlOnPreparedListener回調裏面開始播放)

public void pause();//播放暫停

public void resume();//暫停後恢復播放

public boolean isPlay();//判斷是否在播放中

public void setMute(WlMute mute);//設置聲道(立體聲、左聲道、右聲道)

public void setVolume(int percent);//設置聲音(0~100)

public void setSpeed(float speed);//設置播放速度(0.5f~2f)

public void setPitch(float pitch);//設置播放音調(0.5f~2f)

public void seek(double secds);//seek 目前是到關鍵幀(實測精確到時間幀,體驗不好,平均等待時間過長,故棄之)

public void setSampleRate(WlSampleRate wlSampleRate);//設置音頻採樣率(用於回調pcm時需要制定採樣率情況)

public void setShowPcm(boolean showPcm);//設置是否回調pcm音頻數據,回調方法:WlOnPcmDataListener

public double getNowClock();//獲取當前播放時間

public double getDuration();//獲取時長(如果有在異步準備好後可獲取)

public void takePicture();//視頻截圖(是截取surface屏幕圖片,自己可根據surface大小和視頻寬高對視頻圖片進行裁剪),回調方法:WlOnTakePictureListener

public int putBufferSource(byte[] buffer, int buffer_len);//byte[]方式播放數據入口,返回值爲底層隊列大小,當buffer_len == 0時,也返回底層隊列大小。

public void seekNoCallTime();//設置不回調時間,可用於seek過程中UI展示(具體看自己的需求)

public void onSurfaceCreate(Surface surface);//設置surface(用於自定義surfaceview)

public void onSurfaceChange(int width, int height, Surface surface);//surface大小改變

public void onSurfaceDestroy();//surface銷燬

public void release();//回收surface底層opengl資源

public void setTransportModel(WlTransportModel transportModel);//設置播放rtsp的方式(UDP/TCP)

public void setClearLastPicture(boolean clearLastPicture);//視頻播放完最後一幀是否清屏,true:停留在最後一幀;false:清屏爲黑色

/**
 * 設置數據源模式
 * 1、bufferSource爲true,encryptFileSource爲false時,是byte[]模式
 * 2、bufferSource爲true,encryptFileSource爲true時,是file模式,可用於加密視頻播放,(回WlOnDecryptListener調裏面自己解密)
 * @param bufferSource byte[]模式
 * @param encryptFileSource 是否加密
 */
public void setBufferSource(boolean bufferSource, boolean encryptFileSource);

public void setvShader(String vShader);//設置頂點着色器

public void setfShader(String fShader);//設置紋理着色器

public void changeFilter();//設置着色器後用於使之生效(切換濾鏡)

public void scaleVideo(int w, int h);//設置視頻寬高比

public int getVideoWidth();//獲取視頻寬

public int getVideoHeight();//獲取視頻高

public String[] getAudioChannels();//獲取所有音軌,返回音軌名字(默認爲Audio)

public void setAudioChannel(int index);//設置播放音軌,index爲getAudioChannels中得到音軌的索引

public void setDelayOffsetTime(double offsetTime);//用於單獨播放視頻(buffer)時動態調整視頻渲染速率,單位秒。

5.2 WlSurfaceView和WlTextureView

SDK自帶這2個自定義surfaceview,通過setWlMedia方法與播放器關聯,updateMedia方法用於播放中切換顯示surface;WlOnVideoViewListener爲surface初始化完成回調。
開發者可以根據自己情況自定義自己的surfaceview,實現自己的特殊需求。


***注:***
自定義surface必須調用方法:

public void onSurfaceCreate(Surface surface);

public void onSurfaceChange(int width, int height, Surface surface);

public void onSurfaceDestroy();

5.3 回調函數

public interface WlOnPreparedListener; //異步準備完成回調

public interface WlOnTimeInfoListener; //播放時間回調

public interface WlOnLoadListener; //加載狀態回調

public interface WlOnErrorListener; //錯誤回調

public interface WlOnCompleteListener; //播放(資源回收)完成回調

public interface WlOnDecryptListener; //解密算法回調

public interface WlOnPcmDataListener; //音頻PCM數據回調

public interface WlOnTakePictureListener; //截圖回調

public interface WlOnVideoViewListener; //surface 初始化完成回調

6、混淆

-keep class com.ywl5320.wlmedia.* {*;} 

7、注意事項

7.1播放器activity配置:

android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTask"//(建議)

7.2播放器生命週期邏輯

播放器結束有2個回調:error和complete,二者只可能回調其中一個,所以回收回收資源可以在這個2個回調裏面進行。
如:activity:back->stop->error/complete->release->finish

8、參考資料

我的視頻課程(基礎):《(NDK)FFmpeg打造Android萬能音頻播放器》

我的視頻課程(進階):《(NDK)FFmpeg打造Android視頻播放器》

我的視頻課程(編碼直播推流):《Android視頻編碼和直播推流》

我的視頻課程(C++ OpenGL):《Android C++ OpenGL》

Create By:ywl5320 2019-12-16

發佈了64 篇原創文章 · 獲贊 154 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章