Android VideoView播放网络视频简介

最近项目中用到了很多视频播放的地方,不管是聊天发送的视频消息,还是类似内涵段子的视频列表,都会涉及这些知识,不过网上的知识都很零散,一会找缓存方法,一会找预览图片的方法,一会找视频动态修改尺寸的方法,总之找的人好烦,所以自己写一篇来记录这些知识点,也方便别人查阅

  • 获取视频首帧当预览图(MediaMetadataRetriever

在VideoView中,如果直接设置播放路径,然后seekTo(1)当然也能产生预览效果,但是,如果VideoView较多,设置播放路径的方法会产生几个问题,设置路径后VideoView会取网上拉取视频(缓冲池大小),这样造成流量浪费,而且,多个VideoView会造成显示首帧非常非常慢,且有严重的卡顿

那如何解决这个问题,我的想法是,还是用首帧当预览图,不过我是在ImageView里面显示预览图,所以预览的时候不用VideoView了,获取预览图也是变的简单化,省流量,还快捷,下来我们了解下MediaMetadataRetriever类如何获取视频的首帧。MediaMetadataRetriever类不但可以获取视频首帧,还可以获取标题,时长,作者等信息,大家根据需要可以获取,我在这里就不一一举例,在获取到首帧后,我们做下缓存处理,以便下一次预览不用每次从网上拉取,然后用Glide加载显示

ThreadPoolUtils.execute(new Runnable() {
            @Override
            public void run() {
                MediaMetadataRetriever retriever = new MediaMetadataRetriever();
                Bitmap bitmap = null;
                try {
                    //这里要用FileProvider获取的Uri
                    if (url.contains("http")) {
                        retriever.setDataSource(url, new HashMap<String, String>());
                    } else {
                        retriever.setDataSource(url);
                    }
                    bitmap = retriever.getFrameAtTime();
                } catch (Exception ex) {
                    ex.printStackTrace();
                } finally {
                    try {
                        retriever.release();
                    } catch (RuntimeException ex) {
                        ex.printStackTrace();
                    }
                }
                showImageMessage(bitmap, positionTag, vv);
            }
        });

后记:其实还有一种办法来做预览显示,就是让后台将预览图处理好,然后拿到图片地址直接用Glide显示,都不用自己缓存,而且后台可以生成GIF,也可以用Glide显示,且显得高大上

预览图加载完毕后,点击预览图,然后我们可以做各种处理,如隐藏ImageView且显示VideoView,或者跳到视频播放界面等,各种加载逻辑大家可以发挥自己得想象

  • VideoView加载一个网络视频

VideoView加载视频其实很简单,我们直接看代码吧

<?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">

    <VideoView
        android:id="@+id/mVideoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

/**
     * 香港卫视:http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8
     * CCTV1高清:http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8
     * CCTV3高清:http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8
     * CCTV5高清:http://ivi.bupt.edu.cn/hls/cctv5hd.m3u8
     * CCTV5+高清:http://ivi.bupt.edu.cn/hls/cctv5phd.m3u8
     * CCTV6高清:http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8
     * 苹果提供的测试源(点播):http://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear2/prog_index.m3u8
     */
    private void initView() {
        String url="http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8";
        VideoView videoView=findViewById(R.id.mVideoView);
        videoView.setVideoPath(url);
        videoView.requestFocus();
        videoView.start();
    }

这样一个网络视频就可以播放了

  • 视频控件长宽的大小调整

视频是播放出来了,怎么看都有点不和谐,大白边框太丑了,那缩小吧,一不小心缩变形了,看起来更别扭,怎么才能按照视频的比例来显示呢?我们上面不是讲过MediaMetadataRetriever吗?我们可以根据获取的首帧图片的大小确定视频的大小,MediaMetadataRetriever还可以采用

int videoWidth=retriever.METADATA_KEY_VIDEO_WIDTH;
int videoHeight=retriever.METADATA_KEY_VIDEO_HEIGHT;

来确定视频的大小。从而动态设置VideoView的大小,咦,设置那么大的控件,怎么才显示那么小的视频?哈哈,问题来了,小视频怎么动态适配控件大小?

  • 小视频适配大控件(动态调整视频显示的大小)

不说原理了,我也是百度的,普通的LayoutParams只能调整控件的大小,当视频比控件小时,视频只能显示大默认大小,可是怎么来调整呢?请看代码↓

自定义VideoView控件CustomVideoView.java

/**
 * @author  Created by MrRight on 2017/10/24.
 */
public class CustomVideoView extends VideoView{
    private Context mContext;
    final int defaultHeight=200; //单位DP

    public CustomVideoView(Context context) {
        super(context);
        mContext=context;
    }

    public CustomVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext=context;
    }

    public CustomVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext=context;
    }

    //widthMeasureSpec 和 heightMeasureSpec的值 由父容器决定
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super .onMeasure(widthMeasureSpec,heightMeasureSpec);
        // 默认高度,为了自动获取到focus
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = width;
        // 这个之前是默认的拉伸图像
        if (this.width > 0 && this.height > 0) {
            width = this.width;
            height = this.height;
        }
        setMeasuredDimension(width, height);
    }


    private int width,height;

    public void setMeasure(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

怎么用呢!!很简单,继续看代码↓

videoViewParent.post(new Runnable() {
       @Override
       public void run() {
            int[] widthAndHeight=getWidthAndHeight(holder.videoViewParent,dynamicsBean.getWeight(),dynamicsBean.getHeight());
            videoView.getHolder().setFixedSize(widthAndHeight[0], widthAndHeight[1]);
            // 重绘VideoView大小,这个方法是在重写VideoView时对外抛出方法
            videoView.setMeasure(widthAndHeight[0], widthAndHeight[1]);
            // 请求调整
            videoView.requestLayout();
        }
});

就这样,视频可以按你的需求行进动态调整了!!

  • VideoView的常用监听和作用

VideoView有好多监听,真的是好多,许多监听是重复的,至于怎么重复的?为什么重复?有兴趣的自己去看看!首先看第一个非常重要的一个监听:点击事件和双击事件的监听,你们有没有试过设置OnClick事件?是不是没有什么用啊?没用就对了,点击事件的正确姿势是↓↓↓

/*
 * 对VideoView setOnClickListener时,发现无效,搜索一番后找到解决方案;
 * 同时监听VideoView的点击双击和滑动事件,通过对VideoView的OnTouchListener设置进行监听,
 * 首先实例化一个手势识别器,并返回它的onTouchEvent。
 * 然后初始化GestureDetector ,这里面有一个坑,如果单纯的设置OnGestureListener,发现当onDown的返回值为true的
 * 时候可以响应单击长摁和滑动事件,为false的时候只会响应长摁事件;如果想要监听双击事件,就要对GestureDetector设
 * 置OnDoubleTapListener,需要注意的的是,在OnGestureListener的onDown返回值为false的时候OnDoubleTapListener
 * 里面所有的回调是不会去响应的
 */
holder.videoView.setOnTouchListener(new View.OnTouchListener() {
    GestureDetector mGesture;
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (mGesture == null) {
            mGesture = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onDown(MotionEvent e) {
                    //返回false的话只能响应长摁事件
                    return true;
                }

                @Override
                public void onLongPress(MotionEvent e) {
                    super.onLongPress(e);
                }

                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                    return super.onScroll(e1, e2, distanceX, distanceY);
                }
            });
            mGesture.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
                @Override
                public boolean onSingleTapConfirmed(MotionEvent e) {
                    controlLayoutShowAndHiden(holder.controlLayout,holder.cancheImage);
                    return true;
                }

                @Override
                public boolean onDoubleTap(MotionEvent e) {
                    return true;
                }

                @Override
                public boolean onDoubleTapEvent(MotionEvent e) {
                    return false;
                }
            });
        }

        return mGesture.onTouchEvent(event);
    }
});

OK!点击事件看完之后,我们看下剩下的其他的监听方法,剩下的比较简单,光看名字就知道是干什么用的,我们只写下方法和作用,不再赘述

videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        if(what==MediaPlayer.MEDIA_ERROR_UNKNOWN //未指定的媒体播放器错误。
                ||what==MediaPlayer.MEDIA_ERROR_SERVER_DIED //媒体服务器死了。在这种情况下,应用程序必须释放
                ||what==MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK//视频流,其容器对逐行扫描无效。
                ||what==MediaPlayer.MEDIA_ERROR_MALFORMED//文件或网络操作错误
                ||what==MediaPlayer.MEDIA_ERROR_UNSUPPORTED//比特流符合相关的编码标准或文件规范,但 媒体框架不支持该功能。
                ||what==MediaPlayer.MEDIA_ERROR_TIMED_OUT//超时
                ||what==MediaPlayer.MEDIA_ERROR_IO){ //IO刘错误
            if(controlImageBig.getVisibility()==View.VISIBLE){
                controlImageBig.setBackgroundResource(R.drawable.vodeo_retry);
            }
        }
        return true;//如果设置true就可以防止他弹出错误的提示框!
    }
});


videoView.setOnInfoListener(new MediaPlayer.OnInfoListener() {
    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {
        if (what==MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START){
            controlImageBig.setVisibility(View.GONE);
            cancheImage.setVisibility(View.GONE);
            controlImageBig.setBackgroundResource(R.drawable.eventdynamics_play_big);
        }
        LogUtils.i(TAG,"\n extra is "+extra
                                +"\n what is "+what);
        return false;
    }
});


videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        Log.d(TAG,"onPrepared methmod is called and position is "+position);
        int duration=holder.videoView.getDuration();
        totleTime.setText(intTimeToString(duration));
        seekBar.setMax(duration);
        videoViewParent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                controlLayoutShowAndHiden(holder.controlLayout,holder.cancheImage);
            }
        });
    }
});


videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    @Override
    public void onCompletion(MediaPlayer mp) {
        threadPoolUtils.shutDownNow();
        if(cancheImage.getVisibility()==View.GONE){
            cancheImage.setVisibility(View.VISIBLE);
        }
        if(controlImageBig.getVisibility()==View.GONE){
            controlImageBig.setVisibility(View.VISIBLE);
        }
        playControl.setImageResource(R.drawable.eventdynamics_play);
        seekBar.setProgress(0);
    }
});

好了,大概就这么多,后续有新东西还会持续更新,大家有什么好的建议也可以留言交流

  • 20
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
Android VideoView 是一个可以用来播放视频的视图控件,但是有时候会遇到无法播放某些视频的情况。 造成 Android VideoView 无法播放视频的原因有很多,下面是一些可能的原因: 1. 文件格式不受支持:Android VideoView支持播放视频格式有限,例如常见的MP4、3GP等格式,但是某些特殊格式如MKV、FLV等可能无法播放。 2. 编码方式不受支持:即使是支持的文件格式,如果视频使用了不受支持的编码方式,也无法播放Android支持的编码方式包括H.264、H.263等。 3. 视频文件损坏:如果视频文件本身损坏或者缺失必要的编码信息,也会导致无法播放。 4. 网络问题:如果播放视频来自网络,可能是网络连接问题导致无法播放。例如服务器问题、网络限速等。 解决这个问题的方法有几种: 1. 转码视频:将不受支持的视频格式或编码方式转换为支持的格式,可以使用一些视频转换工具来完成。 2. 使用其他视频播放器:如果VideoView无法播放某个视频,可以尝试其他的视频播放器,如ExoPlayer、VLC等。 3. 检查视频文件:确保视频文件没有损坏,尝试播放其他已知可以正常播放视频文件来检查。 4. 检查网络连接:如果视频来自网络,确保网络连接正常,并尝试使用其他网络播放其他视频来排除网络问题。 总之,如果Android VideoView无法播放某个视频,我们需要先检查视频文件格式、编码方式、文件完整性以及网络连接等方面的问题,然后采取相应的解决方法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baoolong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值