Android 集成腾讯视频播放器实现视频播放功能

最近项目中新增了视频播放的功能,经查找了解及朋友推荐我选择了集成腾讯云的视频播放实现此功能,相对来说腾讯播放支持的功能比较全面,支持推流直播点播短视频连麦功能,这里总结一下实现过程及遇到的问题解决。
首先进入腾讯云官网的文档中心,向下滑找到视频服务模块 ,点击点播进入点播功能的文档首页,在左侧导航栏中找到播放器SDK手册展开选择 超级播放器Android ,现在就可以查看点播的功能介绍,SDK+Demo的下载地址及相关主要的方法使用介绍,eg:创建播放器视频信息获取切换视频移除播放器 等,看到这里就应该能明白这个Demo中实现的功能了,进入SDK下载页面选择要下载的SDK。

文档位置1.jpg

文档位置2.jpg
sdk下载.jpg

下载好SDK+Demo接下来就开始集成。这里以 全功能专业版为例:

一:配置工程,有aar和jar两种方式,以aar集成为例

1、解压下载的SDK,看到如下文件:

sdk文件.jpg

2、在LiteAVSDK_UGC_Android_4.4.3774\LiteAVSDK_UGC_Android\Demo\app\libs文件夹下找到LiteAVSDK_UGC.aar文件,copy到自己的项目libs文件夹中。
3、在工程app目录下的build.gradle文件中,添加引入aar包的代码:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    // 导入腾讯云 SDK aar
    compile(name: 'LiteAVSDK_UGC', ext: 'aar')
    ……
}

4、在工程目录下的build.gradle文件中,添加flatDirr,指定本地仓库:

allprojects {
    repositories {
        flatDir {
            dirs 'libs'
        }
        jcenter()
    }
}

5、在工程目录下的build.gradle的defaultConfig里面,指定ndk兼容架构:

 defaultConfig {
        ……
        ndk {
            abiFilters "armeabi", "armeabi-v7a"
        }
}

注意:配置完第五条,记得编译工程Rebuild Project
6、添加App权限:
在AndroidManifest.xml中添加音视频所需权限,如下。

    <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_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-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"/>
    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.BLUETOOTH"/>

7、配置完成后验证下工程设备是否正确:
在你的MainActivityonCreate中调用TXLiveBase.getSDKVersion

String sdkver = TXLiveBase.getSDKVersionStr();
Log.d("liteavsdk", "liteav sdk version is : " + sdkver);

若前面几个步骤中的配置都正确,工程将顺利编译通过,运行后会在LogCat看到相关的log信息。

二:写视频播放的代码

在确定工程都已配置好以后,就可以进行视频播放功能编写了。视频云中的超级播放器SuperVideoPlayer是基于TXVodPlayer实现的集视频信息拉取、横竖屏切换、清晰度选择、弹幕等功能于一体的解决方案,且完全开源。我们将demo工程中的SuperVideoPlayer文件及其中引用到的相关类和资源都copy到自己的工程中,导入相关的包。可以参考点播的完整文档SuperVideoPlayer文件中修改相关的属性。

  • 播放方式(url方式和fileId方式):
    1、url方式:
    TXVodPlayer 内部会自动识别播放协议,您只需要将您的播放 URL 传给 startPlay 函数即可。
String url = "http://www.jmzsjy.com/UploadFile/微课/地方风味小吃——宫廷香酥牛肉饼.mp4";
mVodPlayer.startPlay(url);

2、field方式:

TXPlayerAuthBuilder authBuilder = new TXPlayerAuthBuilder();
authBuilder.setAppId(1252463788);
authBuilder.setFileId("4564972819220421305");
mVodPlayer.startPlay(authBuilder);
  • 画面调整(和);
    1、setRenderMode:铺满or适应
    RENDER_MODE_FILL_SCREEN将图像等比例铺满整个屏幕,多余部分裁剪掉,此模式下画面不会留黑边,但可能因为部分区域被裁剪而显示不全。
    RENDER_MODE_ADJUST_RESOLUTION:将图像等比例缩放,适配最长边,缩放后的宽和高都不会超过显示区域,居中显示,画面可能会留有黑边。

    2、setRenderRotation:画面旋转
    RENDER_ROTATION_PORTRAIT:正常播放(Home键在画面正下方)
    RENDER_ROTATION_LANDSCAPE:画面顺时针旋转270度(Home键在画面正左方)

  • 变速播放
    通过接口setRate设置点播播放速率来完成,支持快速与慢速播放,如0.5X、1.0X、1.2X、2X等。

//如下代码用于展示点播倍速播放
//设置1.2倍速播放
mVodPlayer.setRate(1.2); 
// ...
//开始播放
mVodPlayer.startPlay(playUrl,_playType);
  • 本地缓存
//指定一个本地mp4缓存目录
TXVodPlayConfig mConfig = new TXVodPlayConfig();
mConfig.setCacheFolderPath(
         Environment.getExternalStorageDirectory().getPath(); +"/txcache");

//指定本地最多缓存多少文件,避免缓存太多数据
mConfig.setMaxCacheItems(10);
mVodPlayer.setConfig(mConfig); 
// ...
//开始播放
mVodPlayer.startPlay(playUrl);

以下是视频播放的布局文件及主要代码及,仅供参考。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
  >
    <FrameLayout
        android:id="@+id/layout_player"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/color_black"
        android:minHeight="200dp"
        >
        <com.doctor.videoplay.view.SuperVideoPlayer
            android:id="@+id/video_player_item"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="visible"></com.doctor.videoplay.view.SuperVideoPlayer>

        <ImageView
            android:id="@+id/play_btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:src="@drawable/biz_video_list_play_icon_big" />
    </FrameLayout>
</LinearLayout>

视频播放activity的代码

import android.view.WindowManager;
import android.widget.ImageView;
import com.doctor.videoplay.model.Video;
import com.doctor.videoplay.model.VideoUrl;
import com.doctor.videoplay.view.MediaController;
import com.doctor.videoplay.view.SuperVideoPlayer;
import com.doctor.videoplay.view.VodRspData;
import com.tencent.rtmp.TXLivePlayer;

public class PlayVideoActivity extends Activity implements View.OnClickListener {
    private SuperVideoPlayer mSuperVideoPlayer;
    private ImageView mPlayBtnView;
    private String videoUrl;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play_video);
        hideBottomUIMenu();//隐藏虚拟按键,否则全屏时虚拟按键会影响视频播放进度条的调整。
        checkPermission();//检查权限
        getWindow().addFlags(WindowManager.LayoutParams.
                FLAG_KEEP_SCREEN_ON);
        initView();
    }

    /**
     * 初始化控件
     */
    private void initView() {
        videoUrl = "http://www.jmzsjy.com/UploadFile/微课/地方风味小吃——宫廷香酥牛肉饼.mp4";
        mSuperVideoPlayer = (SuperVideoPlayer) findViewById(R.id.video_player_item);
        //创建主播放器
        mSuperVideoPlayer.setVideoPlayCallback(mVideoPlayCallback);
        //播放按钮
        mPlayBtnView = (ImageView) findViewById(R.id.play_btn);
        mPlayBtnView.setOnClickListener(this);
        playVideo();
    }

    /**
     * 检查权限
     */
    private void checkPermission() {
        if (Build.VERSION.SDK_INT >= 23) {
            List<String> permissions = new ArrayList<>();
            if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)) {
                permissions.add(Manifest.permission.CAMERA);
            }
            if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
            }
            if (permissions.size() != 0) {
                ActivityCompat.requestPermissions(this, permissions.toArray(new String[0]), 100);
            }
        }
    }


    /**
     * 播放视频
     */
    private void playVideo() {
        mPlayBtnView.setVisibility(View.GONE);
        mSuperVideoPlayer.setVisibility(View.VISIBLE);
        mSuperVideoPlayer.setAutoHideController(false);
        Video video = new Video();
        VideoUrl videoUrl1 = new VideoUrl();
        videoUrl1.setFormatName("1080P");//视频格式名称,例如高清,标清,720P等等
        videoUrl1.setFormatUrl(videoUrl);//视频Url
        videoUrl1.setIsOnlineVideo(false);//设置是否在线播放
        ArrayList<VideoUrl> arrayList1 = new ArrayList<>();
        arrayList1.add(videoUrl1);
        video.setVideoName("测试视频一");
        video.setVideoUrl(arrayList1);
        ArrayList<Video> videoArrayList = new ArrayList<>();
        videoArrayList.add(video);
        mSuperVideoPlayer.loadMultipleVideo(videoArrayList, 0, 0, 0);//按照指定的格式,指定的进度播放视频列表中指定的视频。
    }

//视频播放中的回调处理
    private SuperVideoPlayer.VideoPlayCallbackImpl mVideoPlayCallback = new SuperVideoPlayer.VideoPlayCallbackImpl() {
        @Override
        public void onCloseVideo() {
            mSuperVideoPlayer.onDestroy();
            mPlayBtnView.setVisibility(View.VISIBLE);
            mSuperVideoPlayer.setVisibility(View.GONE);
            resetPageToPortrait();
        }

        @Override
        public void onSwitchPageType() {
            if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                mSuperVideoPlayer.setPageType(MediaController.PageType.SHRINK);
            } else {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                mSuperVideoPlayer.setPageType(MediaController.PageType.EXPAND);
            }
        }

        @Override
        public void onPlayFinish() {
            mPlayBtnView.setVisibility(View.VISIBLE);
        }

        @Override
        public void onBack() {
            if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                mSuperVideoPlayer.setPageType(MediaController.PageType.SHRINK);
            } else {
                finish();
            }
        }

        @Override
        public void onLoadVideoInfo(VodRspData data) {
        }
    };

    /***
     * 恢复屏幕至竖屏
     */
    private void resetPageToPortrait() {
        if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
            mSuperVideoPlayer.setPageType(MediaController.PageType.SHRINK);
        }
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.play_btn) {
            playVideo();

        }
    }

    /**
     * 隐藏虚拟按键,并且全屏
     */
    protected void hideBottomUIMenu() {
        //隐藏虚拟按键,并且全屏
        if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api
            View v = this.getWindow().getDecorView();
            v.setSystemUiVisibility(View.GONE);
        } else if (Build.VERSION.SDK_INT >= 19) {
            //for new api versions.
            View decorView = getWindow().getDecorView();
            int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                    | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                    | View.SYSTEM_UI_FLAG_IMMERSIVE;
            decorView.setSystemUiVisibility(uiOptions);
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        }
    }

    @Override
    protected void onDestroy() {
        if (mSuperVideoPlayer != null) {
            mSuperVideoPlayer.onDestroy();
        }
        super.onDestroy();
    }
}
竖屏自适应播放图.png
横屏满屏播放图.png

三、注意事项:

1、官方文档标明:SDK支持在Android4.0.3 (API 15)及以上系统上运行,但只有Android4.3(API18)以上的系统才能开启硬件编码。
2、SDK的开发环境为:

Android NDK: android-ndk-r12b
Android SDK Tools: android-sdk_25.0.2
minSdkVersion: 15
targetSdkVersion: 21

app的开发环境不需要跟SDK的一致,保持兼容就好。
3、视频云 SDK 中的播放器只支持 FLV 、RTMP 和 HLS(m3u8)三种格式的直播地址,以及 MP4、 HLS(m3u8)和 FLV 三种格式的点播地址。

四、遇到的问题及解决方法:

1、如图:


问题1.jpg

解决方法:
因为ndk中多了,"arm64-v8a",删除这段代码并Rebuild Project,再运行就ok啦。官方的文档中说明了完整版的连麦功能所使用的声学组件暂时不支持X64架构的手机。

  ndk {
            abiFilters "armeabi", "armeabi-v7a","arm64-v8a"
        }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容