1. 程式人生 > >騰訊雲直播初體驗

騰訊雲直播初體驗

移動直播安卓的整合其實相對簡單,主要分為推流端功能和播放端功能,主要實現條件大致分為以下三點:
①RTMP SDK整合與使用
②拼接出推流地址(可由後臺伺服器提供)
③拼接出播放地址(可由後臺伺服器提供)

***本文將主要介紹SDK的整合和功能實現方案。***
一、RTMP SDK整合

① [SDK下載地址](https://www.qcloud.com/product/mlvb#wiki)

解壓後可看到以下檔案


將jar包複製到專案下的libs檔案。在專案的main資料夾下新建jniLibs資料夾將so檔案拷貝進去,同步下專案生成如下專案結構。


②配置相關的許可權

[java] view plain
copy print?
  1. <uses-permission android:name=“android.permission.INTERNET” />  
  2. <uses-permission android:name=”android.permission.ACCESS_NETWORK_STATE” />  
  3. <uses-permission android:name=”android.permission.ACCESS_WIFI_STATE” />  
  4. <uses-permission android:name=”android.permission.READ_PHONE_STATE”
     />  
  5. <uses-permission android:name=”android.permission.CALL_PHONE”/>  
  6. <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>  
  7. <uses-permission android:name=”android.permission.READ_LOGS” />  
  8. <uses-permission android:name=”android.permission.RECORD_AUDIO” />  
  9. <uses-permission android:name=”android.permission.CAMERA” />  
  10. <uses-feature android:name=”android.hardware.Camera”/>  
  11. <uses-feature android:name=”android.hardware.camera.autofocus” />  
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.Camera"/>
<uses-feature android:name="android.hardware.camera.autofocus" />


注意:安卓6.0以後的系統需要在程式碼中動態獲取相機、麥克風、sd卡許可權,相關方法百度很多就不在過多贅述,謹在此提供一下小編的一種方案

[java] view plain copy print?
  1. privatestaticfinalint REQUEST_EXTERNAL_STORAGE = 1;  
  2. privatestatic String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};  
  3. privateboolean checkedPermission;  
  4. privatevoid checkPermission() {  
  5.     if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)  
  6.             != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)  
  7.             != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)  
  8.             != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)  
  9.             != PackageManager.PERMISSION_GRANTED) {  
  10.         //進入到這裡代表沒有許可權.
  11.         if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {  
  12.             //已經禁止提示了
  13.             Toast.makeText(MainActivity.this, TCConstants.ERROR_MSG_PERMISSION_FORBID, Toast.LENGTH_SHORT).show();  
  14.         } else {  
  15.             ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);  
  16.         }  
  17.     } else {  
  18.         checkedPermission = true;  
  19.     }  
  20. }  
  21. @Override
  22. publicvoid onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNullint[] grantResults) {  
  23.     switch (requestCode) {  
  24.         case REQUEST_EXTERNAL_STORAGE:  
  25.             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
  26.                 //使用者同意授權
  27.                 checkedPermission = true;  
  28.             }  
  29.             break;  
  30.     }  
  31. }  
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
private boolean checkedPermission;

private void checkPermission() {
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)
            != PackageManager.PERMISSION_GRANTED) {
        //進入到這裡代表沒有許可權.
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
            //已經禁止提示了
            Toast.makeText(MainActivity.this, TCConstants.ERROR_MSG_PERMISSION_FORBID, Toast.LENGTH_SHORT).show();
        } else {
            ActivityCompat.requestPermissions(this, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
        }
    } else {
        checkedPermission = true;
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_EXTERNAL_STORAGE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //使用者同意授權
                checkedPermission = true;
            }
            break;
    }
}

③驗證SDK配置情況 [java] view plain copy print?
  1. //在onCreate中呼叫getSDKVersion介面獲取版本號:
  2. int[] sdkver = TXLivePusher.getSDKVersion();  
  3. if (sdkver != null && sdkver.length >= 3) {  
  4.     Log.d(”rtmpsdk”,“rtmp sdk version is:” + sdkver[0] + “.” + sdkver[1] + “.” + sdkver[2]);  
  5. }  
//在onCreate中呼叫getSDKVersion介面獲取版本號:

int[] sdkver = TXLivePusher.getSDKVersion();
if (sdkver != null && sdkver.length >= 3) {
    Log.d("rtmpsdk","rtmp sdk version is:" + sdkver[0] + "." + sdkver[1] + "." + sdkver[2]);
}

執行之後將在logcat中看到如下log資訊:07-1320:25:05.09926119-26119/? D/rtmpsdk: rtmp sdk version is:1.5.188


二、主播端推流功能實現

①視訊播放器,用於給主播展示推流的視訊

[java] view plain copy print?
  1. <span style=“font-size:18px;”><com.tencent.rtmp.ui.TXCloudVideoView  
  2.     android:id=”@+id/video_view”
  3.     android:layout_width=”match_parent”
  4.     android:layout_height=”match_parent” /></span>  
<com.tencent.rtmp.ui.TXCloudVideoView
    android:id="@+id/video_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

②開啟推流

[java] view plain copy print?
  1. //推流相關
  2. private TXCloudVideoView mTXCloudVideoView;  
  3. private TXLivePusher mTXLivePusher;  
  4. private TXLivePushConfig mTXPushConfig = new TXLivePushConfig();  
  5. //開啟推流
  6. protectedvoid startPublish() {  
  7.     if (mTXLivePusher == null) {  
  8.         mTXLivePusher = new TXLivePusher(this);  
  9.         mTXLivePusher.setConfig(mTXPushConfig);  
  10.     }  
  11.     //可由伺服器拼接此推流地址
  12.     mTXLivePusher.startPusher(TCConstants.PUBLISH_URL);  
  13.     mTXLivePusher.startCameraPreview(mTXCloudVideoView);  
  14. }  
//推流相關
private TXCloudVideoView mTXCloudVideoView;
private TXLivePusher mTXLivePusher;
private TXLivePushConfig mTXPushConfig = new TXLivePushConfig();

//開啟推流
protected void startPublish() {
    if (mTXLivePusher == null) {
        mTXLivePusher = new TXLivePusher(this);
        mTXLivePusher.setConfig(mTXPushConfig);
    }
    //可由伺服器拼接此推流地址
    mTXLivePusher.startPusher(TCConstants.PUBLISH_URL);
    mTXLivePusher.startCameraPreview(mTXCloudVideoView);
}
③停止推流,注意結束推流很簡單,不過要做好清理工作,因為用於推流的 TXLivePusher 和用於顯示影像的 TXCloudVideoView 都是不能多例項並行運轉的,所以清理工作不當會導致下次直播遭受不良的影響。
[java] view plain copy print?
  1. //停止推流
  2. protectedvoid stopPublish() {  
  3.     if (mTXLivePusher != null) {  
  4.         mTXLivePusher.stopCameraPreview(true);//停止攝像頭預覽
  5.         mTXLivePusher.stopPusher();//停止推流
  6.         mTXLivePusher.setPushListener(null);//解綁 listener
  7.     }  
  8.     this.finish();  
  9. }  
//停止推流
protected void stopPublish() {
    if (mTXLivePusher != null) {
        mTXLivePusher.stopCameraPreview(true);//停止攝像頭預覽
        mTXLivePusher.stopPusher();//停止推流
        mTXLivePusher.setPushListener(null);//解綁 listener
    }
    this.finish();
}

三、客戶端視訊播放

①準備視訊播放器

[java] view plain copy print?
  1. <com.tencent.rtmp.ui.TXCloudVideoView  
  2.     android:id=”@+id/play_view”
  3.     android:layout_width=”match_parent”
  4.     android:layout_height=”match_parent” />  
<com.tencent.rtmp.ui.TXCloudVideoView
    android:id="@+id/play_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

②開始播放

[java] view plain copy print?
  1. private TXCloudVideoView playerView;  
  2. private TXLivePlayer livePlayer;  
  3. protectedvoid starPlay() {  
  4.     //建立player物件
  5.     livePlayer = new TXLivePlayer(this);  
  6.     //關聯player物件與介面view
  7.     livePlayer.setPlayerView(playerView);  
  8.     livePlayer.setConfig(new TXLivePlayConfig());  
  9.     livePlayer.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);  
  10.     //推薦FLV播放格式
  11.     livePlayer.startPlay(TCConstants.PLAY_URL, TXLivePlayer.PLAY_TYPE_LIVE_FLV);  
  12. }  
private TXCloudVideoView playerView;
private TXLivePlayer livePlayer;
protected void starPlay() {
    //建立player物件
    livePlayer = new TXLivePlayer(this);
    //關聯player物件與介面view
    livePlayer.setPlayerView(playerView);
    livePlayer.setConfig(new TXLivePlayConfig());
    livePlayer.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN);
    //推薦FLV播放格式
    livePlayer.startPlay(TCConstants.PLAY_URL, TXLivePlayer.PLAY_TYPE_LIVE_FLV);
}

③停止播放

[java] view plain copy print?
  1. protectedvoid stopPlay() {  
  2.     if (playerView != null) {  
  3.         livePlayer.setPlayListener(null);  
  4.         livePlayer.stopPlay(true);  
  5.         playerView.onDestroy();  
  6.     }  
  7.     this.finish();  
  8. }  
protected void stopPlay() {
    if (playerView != null) {
        livePlayer.setPlayListener(null);
        livePlayer.stopPlay(true);
        playerView.onDestroy();
    }
    this.finish();
}

到此直播的基本功能已經實現了,後續會介紹一些精細的用法和注意的地方,敬請期待。有什麼問題可以在評論區留言,相互交流,我會不定期回覆解答