从本地读取视频以及播放(3)——学习笔记

来源:互联网 发布:c语言异步调用 编辑:程序博客网 时间:2024/05/16 05:14

首先是如何让其他软件能调起自己写的播放器

可以参照系统源代码
将下面这些代码拷贝到SystemVideoPlayer的Activity配置清单处:

        <intent-filter>                <action android:name="android.intent.action.VIEW" />                <category android:name="android.intent.category.DEFAULT" />                <category android:name="android.intent.category.BROWSABLE" />                <data android:scheme="rtsp" />             </intent-filter>             <intent-filter>                <action android:name="android.intent.action.VIEW" />                <category android:name="android.intent.category.DEFAULT" />                <data android:mimeType="video/*" />                <data android:mimeType="application/sdp" />             </intent-filter>             <intent-filter>                <action android:name="android.intent.action.VIEW" />                <category android:name="android.intent.category.DEFAULT" />                <category android:name="android.intent.category.BROWSABLE" />                <data android:scheme="http" />                <data android:mimeType="video/mp4" />                <data android:mimeType="video/3gp" />                <data android:mimeType="video/3gpp" />                <data android:mimeType="video/3gpp2" />             </intent-filter>

然后点击一个视频文件的时候,系统会传递这个视频文件的地址,这样就可以调用这个播放器了。

接下来判断一下如果有网络视频的话,地址是本地的还是网络的。在Utils.class中添加下面的方法:

public boolean isNetUri(String uri) {        boolean reault = false;        if (uri != null) {            if (uri.toLowerCase().startsWith("http") || uri.toLowerCase().startsWith("rtsp") || uri.toLowerCase().startsWith("mms")) {                reault = true;            }        }        return reault;    }

然后在下面这些地方添加判断:

private boolean isNetUri;    setData(){     if (mediaItems != null && mediaItems.size() > 0){                ...                isNetUri = utils.isNetUri(mediaItem.getData());                ...            } else if (uri != null){                ...                isNetUri = utils.isNetUri(uri.toString());                ...            }        }    private void playPreVideo(){        if(position >= 0){            ...            isNetUri = utils.isNetUri(mediaItem.getData());            ...        }    }    private void playNextVideo(){        if (position < mediaItems.size()){            ...            isNetUri = utils.isNetUri(mediaItem.getData());            ...        }    }

接下来如果判断视频来源于网络,就去为视频做缓冲,在每秒发一次消息的handler中添加:

case PROGRESS:... //缓存进度的更新                    if (isNetUri) {                        //只有网络资源才有缓存效果                        int buffer = videoview.getBufferPercentage();//0~100                        int totalBuffer = buffer * seekbarVideo.getMax();                        int secondaryProgress = totalBuffer / 100;                        seekbarVideo.setSecondaryProgress(secondaryProgress);                    } else {                        //本地视频没有缓冲效果                        seekbarVideo.setSecondaryProgress(0);                    }...

这样播放网络视频就可以显示缓冲进度了,接下来去监听网络视频播放是否卡顿。

设置监听播放网络视频卡setOnInfoListener

前提:播放视频的时候,网络比较慢才会出现

1.Android2.3,在MediaPlayer引入的监听卡–自定义VideoView,把监听卡封装一下

2.Android4.2.2左右才把监听卡封装在VideoView中

在setListener()中添加:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {                videoview.setOnInfoListener(new MyOnInfoListener());            }

然后准备MyOnInfoListener()之前先去为卡顿设置一下布局,在中添加:

    ... <include layout="@layout/ll_buffer" android:id="@+id/ll_buffer"/> <include layout="@layout/ll_loading" android:id="@+id/ll_loading"/>

下面是布局详情,先是ll_buffer的:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:layout_width="wrap_content"              android:layout_height="wrap_content"              android:layout_centerInParent="true"              android:background="#33000000"              android:gravity="center"              android:orientation="horizontal"              android:padding="3dp"              android:visibility="gone">    <ProgressBar        android:layout_width="30dp"        android:layout_height="30dp" />    <TextView        android:id="@+id/tv_buffer_netspeed"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_marginLeft="3dp"        android:gravity="center"        android:text="正在缓冲...30kb/s"        android:textColor="#ffffff"        android:textSize="10sp" /></LinearLayout>

再是ll_loading的:

<?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:background="@drawable/bg_player_loading_background"              android:gravity="center"              android:orientation="vertical">    <ProgressBar        android:layout_width="30dp"        android:layout_height="30dp" />    <TextView        android:layout_marginTop="3dp"        android:id="@+id/tv_laoding_netspeed"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_marginLeft="3dp"        android:gravity="center"        android:text="玩命加载中...30kb/s"        android:textColor="#ffffff"        android:textSize="10sp" /></LinearLayout>

接着将上面布局的控件都实例化,然后是监听MyOnInfoListener():

class MyOnInfoListener implements MediaPlayer.OnInfoListener {        @Override        public boolean onInfo(MediaPlayer mp, int what, int extra) {            switch (what) {                case MediaPlayer.MEDIA_INFO_BUFFERING_START://视频卡了,拖动卡                    ll_buffer.setVisibility(View.VISIBLE);                    break;                case MediaPlayer.MEDIA_INFO_BUFFERING_END://视频不卡了,拖动卡结束了                    ll_buffer.setVisibility(View.GONE);                    break;            }            return true;        }    }

除了使用上面的方法外,还可以自己去校验视频的播放进度来判断是否卡顿,原理就是

当播放进度 -上一次播放进度 < 0 ——>卡顿

不过这个不适用于直播,所以一般结合这种方法和之前的方法使用
然后就去编写代码,首先添加这个判断,判断是否使用系统的监听卡:

private boolean isUseSystem = true;

在setListener中修改:

    if (isUseSystem) {            //监听视频播放卡-系统的api            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {                videoview.setOnInfoListener(new MyOnInfoListener());            }        }

校验进度前先添加一个记录上一次播放进度的int数据:

private int preCurrentPosition; //上一次的播放进度

在handler中去校验:

switch (msg.what){...case PROGRESS:...                    //视频播放的时候去监听                    if (!isUseSystem && videoview.isPlaying()) {                        if(videoview.isPlaying()){                            int buffer = currentPosition - preCurrentPosition;                            if (buffer < 500) {                                //视频卡了                                ll_buffer.setVisibility(View.VISIBLE);                            } else {                                //视频不卡了                                ll_buffer.setVisibility(View.GONE);                            }                        }else{                            ll_buffer.setVisibility(View.GONE);                        }                    }...}

这样监听视频是否卡顿就完成了,接下来继续完成之前布局的loading效果,也就是加载效果。
首先在onPrepared中去设置让loading隐藏:

...ll_loading.setVisibility(View.GONE); //隐藏加载界面

在playNextVideo和playPreVideo的判断方法中去设置loading显示:

ll_loading.setVisibility(View.VISIBLE);

然后去获取网速并且显示到控件上面,在Utils.class中添加下面这个方法:

    private long lastTimeStamp;    private long lastTotalRxBytes;    /**     * 得到网络速度     * 每隔两秒调用一次     * @param context     * @return     */    public String getNetSpeed(Context context) {        String netSpeed = "0 kb/s";        long nowTotalRxBytes = TrafficStats.getUidRxBytes(context.getApplicationInfo().uid)==                TrafficStats.UNSUPPORTED ? 0 :(TrafficStats.getTotalRxBytes()/1024);//转为KB;        long nowTimeStamp = System.currentTimeMillis();        long speed = ((nowTotalRxBytes - lastTotalRxBytes) * 1000 / (nowTimeStamp - lastTimeStamp));//毫秒转换        lastTimeStamp = nowTimeStamp;        lastTotalRxBytes = nowTotalRxBytes;        netSpeed  = String.valueOf(speed) + " kb/s";        return  netSpeed;    }

接下来在SystemVideoPlayer中添加

private static final int SHOW_SPEED = 3;//显示网速

然后在handler中判断:

case SHOW_SPEED: //显示网速                    String netSpeed = utils.getNetSpeed(SystemVideoPlayer.this);//得到网速                    //在控件上显示网速                    tv_laoding_netspeed.setText("玩命加载中..." + netSpeed);                    tv_buffer_netspeed.setText("正在缓冲..." + netSpeed);                    removeMessages(SHOW_SPEED);                    sendEmptyMessageDelayed(SHOW_SPEED, 2000); //每两秒更新一次                    break;

在findViews()中去发送消息:

...         //开始更新网络速度        handler.sendEmptyMessage(SHOW_SPEED);

有的时候,系统播放器遇到有些视频无法播放,解决办法就是在项目中去集成vitamio万能播放器

由于官网打不开,所以在github上面去下载

地址是https://github.com/yixia/VitamioBundle.git

下载完成后,导入vitamio库并且关联,然后复制一份SystemVideoPlayer,粘贴为VitamioVideoPlayer;将MyVedioView复制粘贴一份为VitamioVideoView,同时将VitamioVideoPlayer和VitamioVideoView中的MediaPlayer,VideoView的包都换成Vitamio的,并且里面的实例也要替换掉。同时SystemVideoPlayer的布局文件也复制一份,粘贴为activity_vitmiao_video_player.xml,将VitamioVideoPlayer的布局文件替换掉,并且布局文件中的一些控件也替换为Vitamio的控件。
别忘记给VitamioVideoPlayer在功能清单中注册活动。

接下来将下面这些权限添加到功能清单文件中:

<uses-permission android:name="android.permission.WAKE_LOCK" />    <uses-permission android:name="android.permission.INTERNET" />    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

然后下面这些代码也添加进去:

 <!-- Don't forgot InitActivity -->        <activity            android:name="io.vov.vitamio.activity.InitActivity"            android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"            android:launchMode="singleTop"            android:theme="@android:style/Theme.NoTitleBar"            android:windowSoftInputMode="stateAlwaysHidden" />

还需要在布局文件加载之前初始化Vitamio库,在VitamioVideoPlayer的findViews()方法的第一行添加:

Vitamio.isInitialized(this);

接着在SystemVideoPlayer的MyOnErrorListener监听里面添加startVitamioPlayer();

 private void startVitamioPlayer() {        if(videoview != null){            videoview.stopPlayback();        }        Intent intent = new Intent(this,VitamioVideoPlayer.class);        if(mediaItems != null && mediaItems.size() > 0){            Bundle bundle = new Bundle();            bundle.putSerializable("videolist", mediaItems);            intent.putExtras(bundle);            intent.putExtra("position", position);        }else if(uri != null){            intent.setData(uri);        }        startActivity(intent);        finish();//关闭页面    }

系统播放器出错的时候就去跳转到万能播放器,同时在VitamioVideoPlayer中的MyOnErrorListener监听里面添加showErrorDialog();

private void showErrorDialog() {        AlertDialog.Builder builder = new AlertDialog.Builder(this);        builder.setTitle("提示");        builder.setMessage("抱歉,无法播放该视频!!");        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                finish();            }        });        builder.show();    }

由于有些视频也能播放但是没有画面,只有声音,这时候是不会调用万能播放器的,接下来去设置手动切换万能播放器。
在SystemVideoPlayer中的按钮btnSwichPlayer点击事件中添加showSwichPlayerDialog();

private void showSwichPlayerDialog() {        AlertDialog.Builder builder = new AlertDialog.Builder(this);        builder.setTitle("系统播放器提醒您");        builder.setMessage("当您播放视频,有声音没有画面的时候,请切换万能播放器播放");        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                startVitamioPlayer();            }        });        builder.setNegativeButton("取消",null);        builder.show();    }

同时也在VitamioVideoPlayer的btnSwichPlayer点击事件中添加showSwichPlayerDialog();

private void showSwichPlayerDialog() {        AlertDialog.Builder builder = new AlertDialog.Builder(this);        builder.setTitle("万能播放器提醒您");        builder.setMessage("当您播放一个视频,有花屏的时候,可以尝试使用系统播放器播放");        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                startSystemPlayer();            }        });        builder.setNegativeButton("取消",null);        builder.show();    }

然后就可以手动切换了,不过每次切换播放器的时候会有动画效果,接下来去除这个动画效果可以在values的styles.xml中添加

    <style name="noAnimation_Theme" parent="android:Theme">        <item name="android:windowAnimationStyle">@style/noAnimation</item>        <item name="android:windowNoTitle">true</item>        <item name="android:windowFullscreen">true</item>        <item name="android:windowContentOverlay">@null</item>    </style>    <style name="noAnimation">        <item name="android:activityOpenEnterAnimation">@null</item>        <item name="android:activityOpenExitAnimation">@null</item>        <item name="android:activityCloseEnterAnimation">@null</item>        <item name="android:activityCloseExitAnimation">@null</item>        <item name="android:taskOpenEnterAnimation">@null</item>        <item name="android:taskOpenExitAnimation">@null</item>        <item name="android:taskCloseEnterAnimation">@null</item>        <item name="android:taskCloseExitAnimation">@null</item>        <item name="android:taskToFrontEnterAnimation">@null</item>        <item name="android:taskToFrontExitAnimation">@null</item>        <item name="android:taskToBackEnterAnimation">@null</item>        <item name="android:taskToBackExitAnimation">@null</item>    </style>

之后在功能清单文件中的SystemVideoPlayer活动和VitamioVideoPlayer活动中添加

android:theme="@style/noAnimation_Theme"
原创粉丝点击