vitamio视频播放学习

具体集成不作赘述

vitamio集成注意
1、4.2版本只需要倒入vitamio文件夹作lib,不过4.2版本有漏洞
2、minsdkversion-15小了报错
3、集成使用之后,第一次启动会出一个原生安卓暗色的加载界面(vitamio初始化)影响用户体验 得去掉
4、直接vedioview使用容易造成内存泄漏(我没用过)建议使用surfaceview+mediaplay播放
5、播放界面的长宽得设计好,太高画面会变形。我现在是固定200dp,得找个方式自适应比例 


xml布局

<RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <io.vov.vitamio.widget.CenterLayout
            android:id="@+id/surface_container"
            android:background="@android:color/black"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vedio_height"
            android:orientation="vertical" >

            <SurfaceView
                android:id="@+id/surface"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center" >
            </SurfaceView>
        </io.vov.vitamio.widget.CenterLayout>
        <ImageView
            android:id="@+id/first_frame"
            android:scaleType="fitXY"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vedio_height" />
        <ImageView
            android:layout_centerInParent="true"
            android:src="@drawable/mp_play_normal"
            android:alpha="0.7"
            android:scaleType="fitCenter"
            android:layout_width="@dimen/vedio_center_play_button"
            android:layout_height="@dimen/vedio_center_play_button"
            android:id="@+id/play" />

        <RelativeLayout
            android:alpha="0.7"
            android:layout_width="match_parent"
            android:layout_height="@dimen/vedio_height"
            android:id="@+id/control_ui">
            <ImageView
                android:layout_margin="@dimen/vedio_button_margin"
                android:src="@drawable/mp_back_normal"
                android:layout_width="@dimen/vedio_back_size"
                android:layout_height="@dimen/vedio_back_size"
                android:id="@+id/vedio_back" />
            <LinearLayout
                android:orientation="horizontal"
                android:layout_alignParentBottom="true"
                android:layout_width="match_parent"
                android:background="#50f5f5f5"
                android:gravity="center_vertical"
                android:layout_height="@dimen/vedio_bottom_actionbar_size">
                <android.support.v7.widget.AppCompatSeekBar
                    android:layout_width="0dp"
                    android:layout_weight="1"
                    style="@style/Widget.AppCompat.SeekBar"
                    android:layout_height="wrap_content"
                    android:id="@+id/vedio_seekbar" />
                <TextView
                    android:layout_marginLeft="@dimen/button_margin"
                    android:layout_marginRight="@dimen/button_margin"
                    android:textColor="@color/white"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:id="@+id/vedio_fulltime" />
                <ImageView
                    android:id="@+id/full_player"
                    android:layout_margin="@dimen/button_margin"
                    android:src="@drawable/mp_square_fullscreen_normal"
                    android:layout_width="20dp"
                    android:layout_height="20dp" />
            </LinearLayout>
        </RelativeLayout>
    </RelativeLayout>


vitamio初始化&播放初始化

isInitialized方法的调用必须要放在setcontentview之前
Vitamio.isInitialized(this);
        setContentView(R.layout.activity_video_play);

SurfaceView播放初始化
/*
    * 初始化播放 & ui初始
    * */
    private void initVatmioPlayer(){
        mPreview = (SurfaceView) findViewById(R.id.surface);
        mPreviewContainer = (CenterLayout) findViewById(R.id.surface_container);
        holder = mPreview.getHolder();
        holder.addCallback(this);
        holder.setFormat(PixelFormat.RGBA_8888);
        path= Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/super.mp4";///Download
        Log.e("path2",path);
        if (!FileUtils.isFileExists(path))
            new AlertDialog.Builder(VideoPlay.this).setMessage("当前文件路径\n"+path+"\n设置失败!").show();

        setPlayURI(path);
    }

播放前的准备
    /*
    * 播放前的工作
    * */
    private void playVideo() {
        doCleanUp();//释放
        try {
            // Create a new media player and set the listeners
            mMediaPlayer = new MediaPlayer();
            Log.e("load","load");
            mMediaPlayer.setDataSource(path);//资源
            Log.e("load","load2");
            mMediaPlayer.setDisplay(holder);
            Log.e("load","load3");
            mMediaPlayer.prepareAsync();
            Log.e("load","load4");
            mMediaPlayer.setOnBufferingUpdateListener(this);
            Log.e("load","load5");
            mMediaPlayer.setOnCompletionListener(this);//播放完成
            Log.e("load","load6");
            mMediaPlayer.setOnPreparedListener(this);
            Log.e("load","load7");
            mMediaPlayer.setOnVideoSizeChangedListener(this);
            Log.e("load","load8");
            setVolumeControlStream(AudioManager.STREAM_MUSIC);//声音
            Log.e("load","load9");
        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage(), e);
        }
    }
prepareAsync 
    /*
    * prepareAsync 
    * */
    public void onPrepared(MediaPlayer mediaplayer) {
        Log.e(TAG, "onPrepared called");
        mIsVideoReadyToBePlayed = true;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();//播放
        }

        initVedioMsg();//更新视频信息
    }

播放
使用mediaplayer控制播放
    /*
    * 播放
    * */
    private void startVideoPlayback() {
        Log.e(TAG, "startVideoPlayback");
        holder.setFixedSize(mVideoWidth, mVideoHeight);

        MediaHelper.setSurfaceViewMeasure(mPreviewContainer,mPreview,
                mMediaPlayer.getVideoWidth(),mMediaPlayer.getVideoHeight());

        //如果是继续播放则继续 return
        if (CONTINUE_PLAY){
            continuePlay((int)currentPosition);
            return;
        }

        //如果正在播放 return
        if (!START_PLAY)
            return;

        mMediaPlayer.start();
    }

播放联动ui的控制,在播放开始之后设置播放按钮的状态以及操作栏的显示/隐藏
操作栏控制
    private Handler hideControlUI=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case HIDE_PLAY_ACTION:
                    setPlayBtnStatus(0);
                    break;
                case HIDE_CONTROL_ACTION:
                    setControlUI(0);
                    break;
                default:
                    Log.e(TAG, "handleMessage - empty msg!");
                    break;
            }
            super.handleMessage(msg);
        }
    };
    /*
    * 设置上下的ui显示状态
    * */
    private void setControlUI(int visible){
        switch (visible){
            case 0://invisile
                controlUI.setVisibility(View.INVISIBLE);
                break;
            case 1://visible
                controlUI.setVisibility(View.VISIBLE);
                break;
            default:
                break;
        }
    }


播放按钮
/*
    * 设置中间播放按钮的状态
    * */
    private void setPlayBtnStatus(int visible){
        switch (visible){
            case 0://invisile
                play.setVisibility(View.INVISIBLE);

                removeFirstFrame();//移除第一贞(后文有详细赘述)
                break;
            case 1://visible
                play.setVisibility(View.VISIBLE);
                break;
            default:
                break;
        }

        //改变播放按钮状态的同时该变控制UI的状态
        setControlUI(visible);
    }

 

视频信息

每1s更新一次

private void upDatePlayingTimeInfo(){
        Log.e(TAG, "update vedio info timer called!" );
        mTimer = new Timer();
        mTimer.schedule(new playingTimeUpdate(),0,1000);
    }

timetask

/*
    * timer 更新进度条以及时长倒计时
    * */
    class playingTimeUpdate extends TimerTask{

        @Override
        public void run() {
            //seekbar在拖动,不在播放中,对象为空都不更新进度(用户体验)
            if(SEEKBAR_IN_MOVING==true
                    |START_PLAY==false|mMediaPlayer==null)
                return;

            progressCahnge();
            timeTextChange();
        }

        private void progressCahnge(){
            //防止进度条后跳
            if (vedioSeekbar.getProgress()<currentPosition)
                vedioSeekbar.setProgress((int)mMediaPlayer.getCurrentPosition());
            currentPosition=mMediaPlayer.getCurrentPosition();
        }

        private void timeTextChange(){
            //倒计时
            int leftDuration = (int)mMediaPlayer.getDuration() - (int)mMediaPlayer.getCurrentPosition();
            if (vedioFullTime!=null&&leftDuration>=0)
                updateTimeText.sendEmptyMessage(leftDuration);
        }
    }
工具

大多数视频app都会在列表界面有个第一贞预览的小细节,我的实现方法是在视频未开始播放时默认在surfaceview上一层添加一个与surfaceview一样大小的imageview。将获取到的第一贞的bitmap设置上imageview,以达到一贞预览的效果。

public static Bitmap createVideoThumbnail(String filePath) {
        // MediaMetadataRetriever is available on API Level 8
        // but is hidden until API Level 10
        try {
            MediaMetadataRetriever media = new MediaMetadataRetriever();
            media.setDataSource(filePath);

            Bitmap bitmap = media.getFrameAtTime();
            if (bitmap != null)
                Log.e(TAG, "createVideoThumbnail - media.getFrameAtTime() success!");
            return bitmap;
        }catch (IllegalArgumentException ex){
            Log.e(TAG, "createVideoThumbnail: ", ex);
        }
        return null;
    }

在视频播放的时候,因为源视频的长宽都是未知的,而且xml中surfaceview大小不一,不一定能恰好匹配上视频的大小。所以这时候就需要根据surface大小及视频大小来缩放视频,达到正常的播放状态

    //计算缩放比例
    public static void setSurfaceViewMeasure(CenterLayout container, SurfaceView surfaceView,float videoWith,float videoHeight){
        if (container==null||surfaceView==null)
            return;

        Log.e(TAG, "setSurfaceViewMeasure: "+videoWith+"/"+videoHeight);
        int VIDEO_TYPE=0;

        if (videoWith>videoHeight) VIDEO_TYPE=0;//横条型
        if (videoWith<videoHeight) VIDEO_TYPE=1;//竖条型
        if (videoWith==videoHeight) VIDEO_TYPE=2;//方型

        int measuredWidth = container.getMeasuredWidth();
        int measuredHeight = container.getMeasuredHeight();
        float scale=videoWith/videoHeight;
        Log.e(TAG, "setSurfaceViewMeasure: scale = "+(scale) );
        Log.e(TAG, "setSurfaceViewMeasure: measuredWidth="+measuredWidth+" measuredHeight="+measuredHeight );

        float surfaceWith=0;
        float surfaceHeight=0;
        switch (VIDEO_TYPE){
            case 0://以宽为准
                //scale=measuredWidth/videoWith;
                surfaceWith = measuredWidth;
                surfaceHeight = surfaceWith/scale;
                break;
            case 1://以高为准
                //scale=measuredHeight/videoHeight;
                surfaceHeight = measuredHeight;
                surfaceWith = surfaceHeight*scale;
                break;
            case 2://以高为准
                //scale=measuredHeight/videoHeight;
                surfaceHeight = measuredHeight;
                surfaceWith = surfaceHeight*scale;
                break;
        }

        Log.e(TAG, "setSurfaceViewMeasure: videoWith="+surfaceWith+" videoHeight="+surfaceHeight);
        if (surfaceWith==0|surfaceHeight==0)
            return;

        surfaceView.setLayoutParams(new CenterLayout.LayoutParams((int)surfaceWith,(int)surfaceHeight,0,0));
    }


在视频信息的更新时候,视频的时长需要经过转换,换算成时间类型再进行ui上的展示
    //计算时间
    public static String formatDuring(long mss,boolean withOutHours) {
        long hours = (mss % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
        long minutes = (mss % (1000 * 60 * 60)) / (1000 * 60);
        long seconds = (mss % (1000 * 60)) / 1000;
        String hour = "";
        if(hours < 10) {
            hour = "0" + hours;
        }else{
            hour = hours+"";
        }
        String minute = "";
        if(minutes < 10) {
            minute = "0" + minutes;
        }else{
            minute = hours+"";
        }
        String second = "";
        if(seconds < 10) {
            second = "0" + seconds;
        }else{
            second = seconds+"";
        }

        //不需要显示小时
        if (withOutHours){
            minutes=hours*60+minutes;

            if (minutes<10)
                minute = "0" + minutes;
            else
                minute = "" + minutes;

            return minute + ":" + second ;
        }
        return hour + ":" + minute + ":" + second ;
    }

    public static int duration2seconds(long mss){
        int seconds=(int)(mss/1000);
        return seconds;
    }


其他

数据释放

/*
    * 释放
    * */
    private void releaseMediaPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    /*
    * mediaplayer数据源清除
    * */
    private void doCleanUp() {
        mVideoWidth = 0;
        mVideoHeight = 0;
        mIsVideoReadyToBePlayed = false;
        mIsVideoSizeKnown = false;
    }



源代码


个人demo合集

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值