本篇文章講述使用阿里云視頻視頻播放sdk中的高級(jí)播放器加上SurfaceView實(shí)現(xiàn)四敞,采用id+STS方法進(jìn)行視頻播放。
目錄
- 概念介紹
- STS的播放流程
- 如何導(dǎo)入
- 自定義SurfaceView
- 橫豎屏切換
- 滑動(dòng)快進(jìn)拔妥,后退
- 滑動(dòng)調(diào)節(jié)音量
- 滑動(dòng)調(diào)節(jié)亮度
- 按Home鍵后重新點(diǎn)開黑屏問(wèn)題
- Token過(guò)期問(wèn)題
一 首先是概念介紹(只介紹用到的)
- 高級(jí)播放器:除了具備基礎(chǔ)播放器的所有功能外忿危,還提供播放視頻的高級(jí)能力,如視頻加密没龙、安全下載铺厨、邊播邊下緩存、清晰度切換等功能硬纤,建議使用阿里視頻云點(diǎn)播和直播業(yè)務(wù)的用戶使用解滓。
- RAM和STS:RAM和STS是阿里云提供的權(quán)限管理系統(tǒng)。RAM主要的作用是控制賬號(hào)系統(tǒng)的權(quán)限筝家。通過(guò)使用RAM可以將在主賬號(hào)的權(quán)限范圍內(nèi)創(chuàng)建子用戶洼裤,給不同的子用戶分配不同的權(quán)限從而達(dá)到授權(quán)管理的目的。STS是一個(gè)安全憑證(Token)的管理系統(tǒng)肛鹏,用來(lái)授予臨時(shí)的訪問(wèn)權(quán)限逸邦,這樣就可以通過(guò)STS來(lái)完成對(duì)于臨時(shí)用戶的訪問(wèn)授權(quán)。
二 STS的播放流程
流程:用戶App獲取STS憑證 -> 服務(wù)端下發(fā)STS憑證 -> 用戶上傳視頻并獲取vid -> 服務(wù)端獲取STS憑證 -> 將STS憑證下發(fā)給客戶端 -> 完成視頻播放在扰。
三 如何導(dǎo)入
請(qǐng)看阿里云文檔=========》》》》》》阿里云-高級(jí)播放器Android使用說(shuō)明
四 自定義SurfaceView
- 為什么不直接用SurfaceView而是自定義缕减?
如果想實(shí)現(xiàn)觸摸滑動(dòng)事件,就必須自定義芒珠。
自定義SurfaceView需要繼承SurfaceView桥狡,我們現(xiàn)在的需求是滑動(dòng)觸控。
羅列出這幾種情況:
- 亮度手勢(shì)皱卓,手指在SurfaceView左半部上下滑動(dòng)時(shí)候調(diào)用
2.音量手勢(shì)裹芝,手指在SurfaceView右半部上下滑動(dòng)時(shí)候調(diào)用
3.快進(jìn)快退手勢(shì),手指在SurfaceView左右滑動(dòng)的時(shí)候調(diào)用
4.按下手勢(shì)娜汁,第一根手指按下時(shí)候調(diào)用
5.快進(jìn)快退執(zhí)行后的松開時(shí)候調(diào)用
我就這幾種情況創(chuàng)建了一個(gè)接口 :VideoGesureListener
public interface VideoGesureListener{
//亮度手勢(shì)嫂易,手指在SurfaceView左半部上下滑動(dòng)時(shí)候調(diào)用
public void onBrightnessGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//音量手勢(shì),手指在SurfaceView右半部上下滑動(dòng)時(shí)候調(diào)用
public void onVolumeGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//快進(jìn)快退手勢(shì)掐禁,手指在SurfaceView左右滑動(dòng)的時(shí)候調(diào)用
public void onFF_REWGesture(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
//按下手勢(shì)怜械,第一根手指按下時(shí)候調(diào)用
public void onDown(MotionEvent e);
//快進(jìn)快退執(zhí)行后的松開時(shí)候調(diào)用
public void onEndFF_REW(MotionEvent e);
}
接下來(lái)我們來(lái)看一下安卓給我們提供的手勢(shì)控制類
GestureDetector
接口
- OnGestureListener:監(jiān)聽一些手勢(shì),如單擊傅事、滑動(dòng)缕允、長(zhǎng)按等操作
1.onDown(MotionEvent e):按下屏幕的時(shí)候回調(diào)
2.onShowPress(MotionEvent e):按下后100ms內(nèi)未松開的時(shí)候回調(diào)(目前不知有何用)
3.onLongPress(MotionEvent e):用戶長(zhǎng)按后回調(diào)
4.onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY):手指滑動(dòng)的時(shí)候回調(diào),e1,e2分別是之前DOWN事件和當(dāng)前的MOVE事件蹭越,distanceX和distanceY就是當(dāng)前MOVE事件和上一個(gè)MOVE事件的位移量
5.onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY):滑動(dòng)后慣性操作回調(diào)
6.onSingleTapUp(MotionEvent e):點(diǎn)擊抬起時(shí)間障本,沒(méi)有長(zhǎng)按和滑動(dòng)時(shí)回調(diào) - OnDoubleTapListener:監(jiān)聽雙擊和單擊事件。
1.onSingleTapConfirmed(MotionEvent e):?jiǎn)螕羰录卣{(diào)
2.onDoubleTap(MotionEvent e):雙擊事件回調(diào)
3.onDoubleTapEvent(MotionEvent e):onDoubleTap雙擊后的回調(diào) - OnContextClickListener:監(jiān)聽鼠標(biāo)右鍵
1.onContextClick(MotionEvent e):當(dāng)鼠標(biāo)/觸摸板,右鍵點(diǎn)擊時(shí)候的回調(diào)驾霜。
內(nèi)部類 - SimpleOnGestureListener:實(shí)現(xiàn)上面三個(gè)接口的內(nèi)部類案训,擁有上面三個(gè)的所有回調(diào)
因?yàn)镾impleOnGestureListener不是抽象類,所以我們一般應(yīng)它進(jìn)行操縱粪糙,這樣我們就可以只寫我們用到的方法萤衰,就不用全部重寫所有的方法。
GestureDetector的使用與需要結(jié)合觸摸事件猜旬。只有感受到觸摸事件才能去進(jìn)行手指觸控脆栋。
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);
}
其中定義了四種狀態(tài) NONE = 0, VOLUME = 1, BRIGHTNESS = 2, FF_REW = 3;
接下來(lái)我們來(lái)看一下我們自定義的SurfaceViewOnGestureListener繼承 GestureDetector.SimpleOnGestureListener主要用到了
onDown(MotionEvent e)
onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)兩個(gè)方法
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的時(shí)候把狀態(tài)設(shè)置NONE
b.判斷橫向滑動(dòng)的距離大于縱向滑動(dòng)的距離,就把模式賦值為快進(jìn)和后退
c.在onScroll中進(jìn)行狀態(tài)賦值洒擦,根據(jù)滑動(dòng)的距離椿争,如果按下的點(diǎn)在屏幕的左半部分就吧狀態(tài)設(shè)置為調(diào)節(jié)亮度BRIGHTNESS,如果在右半部分就是調(diào)節(jié)音量
d.各種情況調(diào)用各自的接口方法
五 橫豎屏切換
- 不設(shè)置Activity的android:configChanges,或著設(shè)置Activity的android:configChanges="orientation"熟嫩,再或著設(shè)置Activity的android:configChanges="orientation|keyboardHidden"秦踪,切屏?xí)匦抡{(diào)用各個(gè)生命周期,切橫屏?xí)r會(huì)執(zhí)行一次掸茅,切豎屏?xí)r會(huì)執(zhí)行一次椅邓。
- 配置 android:configChanges="orientation|keyboardHidden|screenSize",不銷毀 activity昧狮,只調(diào)用 onConfigurationChanged方法景馁。
- 首先我們要先判斷當(dāng)前的屏幕是橫屏還是豎屏getResources().getConfiguration().orientation (獲取狀態(tài))進(jìn)行比較
Configuration.ORIENTATION_LANDSCAPE(橫屏)
Configuration.ORIENTATION_PORTRAIT(豎屏)
然后用 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);(setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);)進(jìn)行橫豎屏設(shè)置
接下來(lái)我們需要在onConfigurationChanged(Configuration newConfig)方法里面進(jìn)行設(shè)置屏幕切換后大小即可
//獲得屏幕寬高
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
width = displayMetrics.widthPixels;
height = displayMetrics.heightPixels;
//設(shè)置寬高,首先要找到外部包裹視頻界面的布局
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);
六 滑動(dòng)快進(jìn)逗鸣,后退
快進(jìn)和后退合住,我們需要知道的就是我們滑動(dòng)的距離如何與視頻的長(zhǎng)度關(guān)聯(lián)起來(lái)。
那么咱們就可以把視頻的總長(zhǎng)度與屏幕的總長(zhǎng)度相比撒璧,這樣就能知道你手指滑動(dòng)的距離占視頻的多少了透葛。
我們可以通過(guò) l = duration / mySurfaceView.getWidth();來(lái)獲得這個(gè)比例,然后用當(dāng)前的進(jìn)度加上指滑動(dòng)的距離占視頻的長(zhǎng)度就是要播放的視頻位置
float offset = e2.getX() - e1.getX();
if (offset>0){
float v = currentPosition + (offset * l) / 4;(除以4是為了讓快進(jìn)的量不是特別大)
aliyunVodPlayer.seekTo((int) v);
}else {
float v = currentPosition +(offset * l) / 4;
aliyunVodPlayer.seekTo((int) v);
}
}
抬起點(diǎn)的x坐標(biāo)與按下點(diǎn)的X坐標(biāo)所得的距離卿樱,大于0是快進(jìn)僚害,小于零是后退。
七 滑動(dòng)調(diào)節(jié)音量
系統(tǒng)的音量有很多繁调,包括通話音量值萨蚕,系統(tǒng)鈴聲值,音樂(lè)音量值涉馁,鬧鈴音量值门岔,等等吧爱致。
做一下筆記以備以后用到
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:系統(tǒng)音量值
- AudioManager.STREAM_RING:系統(tǒng)鈴聲值
- AudioManager.STREAM_MUSIC:音樂(lè)音量值
- AudioManager.STREAM_ALARM:鬧鈴音量值
- AudioManager.STREAM_NOTIFICATION:提示聲音音量值
視頻播放我們用的是音樂(lè)音量值烤送,同樣的道理,我們需要把音量和高度進(jìn)行關(guān)聯(lián)糠悯,我們可以控件的高度閉上最大音量得出比例后就可以知道你滑動(dòng)的距離占音量的多少了帮坚。
int value = mySurfaceView.getHeight()/maxVolume ;
int newVolume = (int) ((e1.getY()-e2.getY())/value+oldVolume);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,newVolume,AudioManager.FLAG_PLAY_SOUND);
八 滑動(dòng)調(diào)節(jié)亮度
1. 屏幕亮度的調(diào)節(jié)模式有兩種
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC:值為1妻往,自動(dòng)調(diào)節(jié)亮度。
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL:值為0试和,手動(dòng)模式讯泣。
2. 屏幕最大亮度為255, 屏幕最低亮度為0阅悍, 屏幕亮度值范圍必須位于:0~255好渠。
3. 在設(shè)置系統(tǒng)屏幕亮度前,需要保證AndroidManifest.xml中聲明如下權(quán)限(6.0需要?jiǎng)討B(tài)設(shè)置):<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
設(shè)置屏幕亮度有兩種方式
- 一種是通過(guò)WindowManager去設(shè)置當(dāng)前界面的亮度
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.screenBrightness = Float.valueOf(brightness) ;
activity.getWindow().setAttributes(lp);
這里有個(gè)注意點(diǎn)就是activity是當(dāng)前的這個(gè)界面节视,設(shè)置的是當(dāng)前的界面拳锚,離開這個(gè)界面后就不管用了。
- 第二種方式時(shí)通過(guò)修改系統(tǒng)數(shù)據(jù)庫(kù)來(lái)設(shè)置亮度
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);
相同的道理和滑動(dòng)調(diào)節(jié)音量一樣也是獲得屏幕的高度比上最大的亮度霍掺,然后計(jì)算滑動(dòng)的距離轉(zhuǎn)換成亮度是多少。(這里不多講了)
九 按Home鍵后重新點(diǎn)開黑屏問(wèn)題
為什么會(huì)出現(xiàn)黑屏拌蜘,就是按Home鍵再點(diǎn)App回來(lái)后杆烁,只有聲音沒(méi)有圖片的問(wèn)題,因?yàn)槲覀冇玫氖荢urfaceView简卧,每次點(diǎn)擊Home鍵時(shí)會(huì)銷毀這個(gè)SurfaceView兔魂,再回來(lái)時(shí)又會(huì)重新創(chuàng)建,這樣我們的阿里云播放器與SurfaceView就沒(méi)有綁定了举娩,畫面就沒(méi)有了入热。
這樣我們需要 給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;
}
我們?cè)诎磆ome鍵的時(shí)候會(huì)走surfaceDestroyed。這樣晓铆,我們就可以在這里做一個(gè)標(biāo)識(shí)勺良,讓他暫停,然后再回來(lái)的時(shí)候就會(huì)走surfaceCreated骄噪,判斷標(biāo)識(shí)尚困,然后進(jìn)行處理就可以了。切記一定要重新讓aliyunVodPlayer與SurfaceView進(jìn)行關(guān)聯(lián)链蕊,這樣才能有畫面也有聲音事甜。
十 Token過(guò)期問(wèn)題
由于我們的視頻在阿里云的服務(wù)器上存著,訪問(wèn)阿里云的服務(wù)器需要臨時(shí)憑證滔韵,我們通過(guò)STS來(lái)獲取Token逻谦,但是這個(gè)Token是有時(shí)間限制,正好阿里云的播放器給我們提供了播放視頻出錯(cuò)時(shí)候的回調(diào)接口陪蜻,我們只需要在這里面進(jìn)行重新請(qǐng)求Token就可以了
aliyunVodPlayer.setOnErrorListener(new IAliyunVodPlayer.OnErrorListener() {
@Override
public void onError(int arg0, int arg1, String msg) {
});