安卓视频播放(阿里云视频点播播放器SDK+SurfaceView)

本篇文章讲述使用阿里云视频视频播放sdk中的高级播放器加上SurfaceView实现,采用id+STS方法进行视频播放。

目录

  • 概念介绍
  • STS的播放流程
  • 如何导入
  • 自定义SurfaceView
  • 横竖屏切换
  • 滑动快进,后退
  • 滑动调节音量
  • 滑动调节亮度
  • 按Home键后重新点开黑屏问题
  • Token过期问题

一 首先是概念介绍(只介绍用到的)

  1. 高级播放器:除了具备基础播放器的所有功能外,还提供播放视频的高级能力,如视频加密、安全下载、边播边下缓存、清晰度切换等功能,建议使用阿里视频云点播和直播业务的用户使用。
  2. RAM和STS:RAM和STS是阿里云提供的权限管理系统。RAM主要的作用是控制账号系统的权限。通过使用RAM可以将在主账号的权限范围内创建子用户,给不同的子用户分配不同的权限从而达到授权管理的目的。STS是一个安全凭证(Token)的管理系统,用来授予临时的访问权限,这样就可以通过STS来完成对于临时用户的访问授权。

二 STS的播放流程

流程:用户App获取STS凭证 -> 服务端下发STS凭证 -> 用户上传视频并获取vid -> 服务端获取STS凭证 -> 将STS凭证下发给客户端 -> 完成视频播放。


TIM图片20180327175524.png

三 如何导入

请看阿里云文档=========》》》》》》阿里云-高级播放器Android使用说明

四 自定义SurfaceView

  • 为什么不直接用SurfaceView而是自定义?
    如果想实现触摸滑动事件,就必须自定义。
    自定义SurfaceView需要继承SurfaceView,我们现在的需求是滑动触控。
罗列出这几种情况:
  1. 亮度手势,手指在SurfaceView左半部上下滑动时候调用
    2.音量手势,手指在SurfaceView右半部上下滑动时候调用
    3.快进快退手势,手指在SurfaceView左右滑动的时候调用
    4.按下手势,第一根手指按下时候调用
    5.快进快退执行后的松开时候调用
    我就这几种情况创建了一个接口 :VideoGesureListener
public interface VideoGesureListener{
        //亮度手势,手指在SurfaceView左半部上下滑动时候调用
        public void onBrightnessGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //音量手势,手指在SurfaceView右半部上下滑动时候调用
        public void onVolumeGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //快进快退手势,手指在SurfaceView左右滑动的时候调用
        public void onFF_REWGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);

        //按下手势,第一根手指按下时候调用
        public void onDown(MotionEvent e);

        //快进快退执行后的松开时候调用
        public void onEndFF_REW(MotionEvent e);
    }

接下来我们来看一下安卓给我们提供的手势控制类

GestureDetector

接口

  • OnGestureListener:监听一些手势,如单击、滑动、长按等操作
    1.onDown(MotionEvent e):按下屏幕的时候回调
    2.onShowPress(MotionEvent e):按下后100ms内未松开的时候回调(目前不知有何用)
    3.onLongPress(MotionEvent e):用户长按后回调
    4.onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):手指滑动的时候回调,e1,e2分别是之前DOWN事件和当前的MOVE事件,distanceX和distanceY就是当前MOVE事件和上一个MOVE事件的位移量
    5.onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY):滑动后惯性操作回调
    6.onSingleTapUp(MotionEvent e):点击抬起时间,没有长按和滑动时回调
  • OnDoubleTapListener:监听双击和单击事件。
    1.onSingleTapConfirmed(MotionEvent e):单击事件回调
    2.onDoubleTap(MotionEvent e):双击事件回调
    3.onDoubleTapEvent(MotionEvent e):onDoubleTap双击后的回调
  • OnContextClickListener:监听鼠标右键
    1.onContextClick(MotionEvent e):当鼠标/触摸板,右键点击时候的回调。
    内部类
  • SimpleOnGestureListener:实现上面三个接口的内部类,拥有上面三个的所有回调
    因为SimpleOnGestureListener不是抽象类,所以我们一般应它进行操纵,这样我们就可以只写我们用到的方法,就不用全部重写所有的方法。
GestureDetector的使用与需要结合触摸事件。只有感受到触摸事件才能去进行手指触控。

setOnTouchListener(this);

 @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction()==MotionEvent.ACTION_UP){
            if (hasFF_REW){
               if (mVideoGestureListener!=null){
                    mVideoGestureListener.onEndFF_REW(event);
               }
                hasFF_REW=false;
            }
        }
        return mGestureDetector.onTouchEvent(event);
    }

其中定义了四种状态 NONE = 0, VOLUME = 1, BRIGHTNESS = 2, FF_REW = 3;
接下来我们来看一下我们自定义的SurfaceViewOnGestureListener继承 GestureDetector.SimpleOnGestureListener主要用到了
onDown(MotionEvent e)
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)两个方法

 public class SurfaceViewOnGestureListener extends GestureDetector.SimpleOnGestureListener{
       private  MySurfaceView mySurfaceView;
       public SurfaceViewOnGestureListener(MySurfaceView mySurfaceView) {
           this.mySurfaceView =mySurfaceView;
       }

   A:    @Override
       public boolean onDown(MotionEvent e) {
           hasFF_REW=false;
           mScrollMode=NONE;
           if (mVideoGestureListener!=null){
               mVideoGestureListener.onDown(e);
           }
           return true;
       }

       @Override
       public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
           switch (mScrollMode){
B:
               case NONE:
                   if (Math.abs(distanceX)-Math.abs(distanceY)>offsetX){
                       mScrollMode=FF_REW;
                   }else {
C:
                       if (e1.getX()<getWidth()/2){
                           mScrollMode=BRIGHTNESS;
                       }else {
                           mScrollMode=VOLUME;
                       }
                   }
                   break;
               case VOLUME:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onVolumeGesture(e1,e2,distanceX,distanceY);
                   }
                   break;
               case BRIGHTNESS:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onBrightnessGesture(e1,e2,distanceX,distanceY);
                   }
                   break;
               case FF_REW:
                   if (mVideoGestureListener!=null){
                       mVideoGestureListener.onFF_REWGesture(e1,e2,distanceX,distanceY);
                   }
                   hasFF_REW = true;

                   break;
           }
           return true;
       }

a.在onDown的时候把状态设置NONE
b.判断横向滑动的距离大于纵向滑动的距离,就把模式赋值为快进和后退
c.在onScroll中进行状态赋值,根据滑动的距离,如果按下的点在屏幕的左半部分就吧状态设置为调节亮度BRIGHTNESS,如果在右半部分就是调节音量
d.各种情况调用各自的接口方法

五 横竖屏切换

  1. 不设置Activity的android:configChanges,或着设置Activity的android:configChanges="orientation",再或着设置Activity的android:configChanges="orientation|keyboardHidden",切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行一次。
  2. 配置 android:configChanges="orientation|keyboardHidden|screenSize",不销毁 activity,只调用 onConfigurationChanged方法。
  3. 首先我们要先判断当前的屏幕是横屏还是竖屏getResources().getConfiguration().orientation (获取状态)进行比较
    Configuration.ORIENTATION_LANDSCAPE(横屏)
    Configuration.ORIENTATION_PORTRAIT(竖屏)
    然后用 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);(setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);)进行横竖屏设置
    接下来我们需要在onConfigurationChanged(Configuration newConfig)方法里面进行设置屏幕切换后大小即可
  //获得屏幕宽高
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        width = displayMetrics.widthPixels;
        height = displayMetrics.heightPixels;

        //设置宽高,首先要找到外部包裹视频界面的布局
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mySurfaceView.getLayoutParams();
        params.width = width;
        if (newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
            params.height = height;
        }
        if (newConfig.orientation==Configuration.ORIENTATION_PORTRAIT){
            params.height = (width /16)*9;(正常比例)
        }
        mySurfaceView.setLayoutParams(params);

六 滑动快进,后退

快进和后退,我们需要知道的就是我们滑动的距离如何与视频的长度关联起来。
那么咱们就可以把视频的总长度与屏幕的总长度相比,这样就能知道你手指滑动的距离占视频的多少了。
我们可以通过 l = duration / mySurfaceView.getWidth();来获得这个比例,然后用当前的进度加上指滑动的距离占视频的长度就是要播放的视频位置

 float offset = e2.getX() - e1.getX();
        if (offset>0){
            float v = currentPosition + (offset * l) / 4;(除以4是为了让快进的量不是特别大)
            aliyunVodPlayer.seekTo((int) v);
        }else {
            float v = currentPosition +(offset * l) / 4;
            aliyunVodPlayer.seekTo((int) v);
        }
    }

抬起点的x坐标与按下点的X坐标所得的距离,大于0是快进,小于零是后退。

七 滑动调节音量

系统的音量有很多,包括通话音量值,系统铃声值,音乐音量值,闹铃音量值,等等吧。
做一下笔记以备以后用到

 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
  max = am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);// 0
  current= am.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
  • AudioManager.STREAM_VOICE_CALL :通话音量值
  • AudioManager.STREAM_SYSTEM:系统音量值
  • AudioManager.STREAM_RING:系统铃声值
  • AudioManager.STREAM_MUSIC:音乐音量值
  • AudioManager.STREAM_ALARM:闹铃音量值
  • AudioManager.STREAM_NOTIFICATION:提示声音音量值

视频播放我们用的是音乐音量值,同样的道理,我们需要把音量和高度进行关联,我们可以控件的高度闭上最大音量得出比例后就可以知道你滑动的距离占音量的多少了。

  int value =  mySurfaceView.getHeight()/maxVolume ;
        int newVolume = (int) ((e1.getY()-e2.getY())/value+oldVolume);
        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,newVolume,AudioManager.FLAG_PLAY_SOUND);

八 滑动调节亮度

1. 屏幕亮度的调节模式有两种
  • Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC:值为1,自动调节亮度。
  • Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL:值为0,手动模式。
2. 屏幕最大亮度为255, 屏幕最低亮度为0, 屏幕亮度值范围必须位于:0~255。
3. 在设置系统屏幕亮度前,需要保证AndroidManifest.xml中声明如下权限(6.0需要动态设置):<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
设置屏幕亮度有两种方式
  • 一种是通过WindowManager去设置当前界面的亮度
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.screenBrightness = Float.valueOf(brightness) ;
activity.getWindow().setAttributes(lp);

这里有个注意点就是activity是当前的这个界面,设置的是当前的界面,离开这个界面后就不管用了。

  • 第二种方式时通过修改系统数据库来设置亮度
ContentResolver contentResolver = context.getContentResolver();
try {
            if(Settings.System.getInt(contentResolver,Settings.System.SCREEN_BRIGHTNESS_MODE)== Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC){
                Settings.System.putInt(contentResolver,Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
            }
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
        }
        Settings.System.putInt(contentResolver, Settings.System.SCREEN_BRIGHTNESS,newBrightness);

相同的道理和滑动调节音量一样也是获得屏幕的高度比上最大的亮度,然后计算滑动的距离转换成亮度是多少。(这里不多讲了)

九 按Home键后重新点开黑屏问题

为什么会出现黑屏,就是按Home键再点App回来后,只有声音没有图片的问题,因为我们用的是SurfaceView,每次点击Home键时会销毁这个SurfaceView,再回来时又会重新创建,这样我们的阿里云播放器与SurfaceView就没有绑定了,画面就没有了。
这样我们需要 给surfaceView添加mySurfaceView.getHolder().addCallback(this);

 @Override
    public void surfaceCreated(SurfaceHolder holder) {

        if (des==true){
            aliyunVodPlayer.setSurface(mySurfaceView.getHolder().getSurface());
            aliyunVodPlayer.resume();

        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        aliyunVodPlayer.pause();

        des = true;
    }

我们在按home键的时候会走surfaceDestroyed。这样,我们就可以在这里做一个标识,让他暂停,然后再回来的时候就会走surfaceCreated,判断标识,然后进行处理就可以了。切记一定要重新让aliyunVodPlayer与SurfaceView进行关联,这样才能有画面也有声音。

十 Token过期问题

由于我们的视频在阿里云的服务器上存着,访问阿里云的服务器需要临时凭证,我们通过STS来获取Token,但是这个Token是有时间限制,正好阿里云的播放器给我们提供了播放视频出错时候的回调接口,我们只需要在这里面进行重新请求Token就可以了

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

推荐阅读更多精彩内容