Android VR视频

话不多说,先上图(眼睛模式)
这里写图片描述
参考的开源库——传送门 MD360Player4Android
在开源库的基础上,做了菜单和播放器逻辑。
VR功能的部分通过依赖vrlib来实现,视频播放的部分集成ijkPlayer
这里写图片描述
这里写图片描述
这里对MD360Player4Android中的一些代码做解释。
初始化VR播放器

    @Override
    protected MDVRLibrary createVRLibrary() {

        return MDVRLibrary.with(this)
                .displayMode(MDVRLibrary.DISPLAY_MODE_NORMAL) //默认360度全景
                .interactiveMode(MDVRLibrary.INTERACTIVE_MODE_MOTION_WITH_TOUCH)//触摸和重力
                .asVideo(new MDVRLibrary.IOnSurfaceReadyCallback() {
                    @Override
                    public void onSurfaceReady(Surface surface) {
                        mMediaPlayerWrapper.setSurface(surface);
                    }
                })
                .ifNotSupport(new MDVRLibrary.INotSupportCallback() {
                    @Override
                    public void onNotSupport(int mode) {
                        String tip = mode == MDVRLibrary.INTERACTIVE_MODE_MOTION
                                ? "onNotSupport:MOTION" : "onNotSupport:" + String.valueOf(mode);
                        Toast.makeText(VrPlayerActivity.this, tip, Toast.LENGTH_SHORT).show();
                    }
                })
                .pinchConfig(new MDPinchConfig().setMin(1.0f).setMax(8.0f).setDefaultValue(0.1f))
                .pinchEnabled(true)
                .directorFactory(new MD360DirectorFactory() {
                    @Override
                    public MD360Director createDirector(int index) {
                        return MD360Director.builder().setPitch(90).build();
                    }
                })
                .projectionFactory(new CustomProjectionFactory()).listenGesture(new MDVRLibrary.IGestureListener() {
                    @Override
                    public void onClick(MotionEvent e) {
                        touchRl.setVisibility(View.VISIBLE);
                        if(mMediaPlayerWrapper.getPlayer().isPlaying()){
                            img_start_vr.setVisibility(View.GONE);
                            img_stop_vr.setVisibility(View.VISIBLE);
                        }else {
                            img_start_vr.setVisibility(View.VISIBLE);
                            img_stop_vr.setVisibility(View.GONE);
                        }
                        mHandler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);
                        hasMenuAction();
                        verticalSeekBar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 100 / mMaxVolume);
                    }
                })
                .barrelDistortionConfig(new BarrelDistortionConfig().setDefaultEnabled(false).setScale(0.95f))
                .build(findViewById(R.id.gl_view));

    }

主要的设置有displayMode——眼睛模式和全景模式,interactiveMode——这个字面上理解是交互模式,可以设置重力感应,触摸,或者禁止重力感应,禁止触摸等。
其实在M360PlayerActivity中,各种设置选项已经陈列出来了

public abstract class M360PlayerActivity extends AppCompatActivity {

    private static final SparseArray<String> sDisplayMode = new SparseArray<>();
    private static final SparseArray<String> sInteractiveMode = new SparseArray<>();
    private static final SparseArray<String> sProjectionMode = new SparseArray<>();
    private static final SparseArray<String> sAntiDistortion = new SparseArray<>();
    private static final SparseArray<String> sPitchFilter = new SparseArray<>();
    private static final SparseArray<String> sFlingEnabled = new SparseArray<>();

    static {
        sDisplayMode.put(MDVRLibrary.DISPLAY_MODE_NORMAL,"NORMAL");
        sDisplayMode.put(MDVRLibrary.DISPLAY_MODE_GLASS,"GLASS");

        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_MOTION,"MOTION");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_TOUCH,"TOUCH");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_MOTION_WITH_TOUCH,"M & T");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_CARDBORAD_MOTION,"CARDBOARD M");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_CARDBORAD_MOTION_WITH_TOUCH,"CARDBOARD M&T");

        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_SPHERE,"SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME180,"DOME 180");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME230,"DOME 230");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME180_UPPER,"DOME 180 UPPER");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME230_UPPER,"DOME 230 UPPER");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_STEREO_SPHERE_HORIZONTAL,"STEREO H SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_STEREO_SPHERE_VERTICAL,"STEREO V SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_FIT,"PLANE FIT");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_CROP,"PLANE CROP");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_FULL,"PLANE FULL");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_MULTI_FISH_EYE_HORIZONTAL,"MULTI FISH EYE HORIZONTAL");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_MULTI_FISH_EYE_VERTICAL,"MULTI FISH EYE VERTICAL");

        sAntiDistortion.put(1,"ANTI-ENABLE");
        sAntiDistortion.put(0,"ANTI-DISABLE");

        sPitchFilter.put(1,"FILTER PITCH");
        sPitchFilter.put(0,"FILTER NOP");

        sFlingEnabled.put(1, "FLING ENABLED");
        sFlingEnabled.put(0, "FLING DISABLED");
    }
}

在onSurfaceReady(Surface surface)的回调中,调用了 mMediaPlayerWrapper.setSurface(surface); 这里的MediaPlayerWrapper是一个封住了ijkPlayer的播放器。这里可以理解成在VR功能接入完毕后,又把视频的处理交还给了ijkPlayer。而ijkPlayer的使用,跟VR功能就没关系了。(理解有点生硬,完全是看单词翻译的有木有- -!)
项目实现大概就是这样,形容确切一点,就是一个具有VR功能的ijjPlayer
页面布局activity_md_using_surface_view.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
   <FrameLayout
       android:id="@+id/framelayout_gl"
       android:orientation="horizontal"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
      <android.opengl.GLSurfaceView
          android:id="@+id/gl_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />

   </FrameLayout>
   <!--视频菜单-->
   <include layout="@layout/include_menu"/>

   <ProgressBar
       android:layout_centerInParent="true"
       android:id="@+id/progress"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content" />

</RelativeLayout>

菜单布局include_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_touch_menu"
    android:background="#33000000"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="44dp"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        >

        <ImageView
            android:id="@+id/img_back"
            android:layout_marginLeft="15dp"
            android:src="@drawable/back_normal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <TextView
            android:layout_marginLeft="26dp"
            android:gravity="center_vertical"
            android:id="@+id/tv_vr_title"
            android:textSize="16sp"
            android:text="视频名字哦"
            android:textColor="#ffffff"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />

    </LinearLayout>

    <LinearLayout
        android:layout_marginTop="13dp"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center_vertical"
        android:layout_marginRight="18dp">

        <Button
            android:id="@+id/btn_vr_nor"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="360°全景" />
        <Button
            android:id="@+id/btn_vr_glass"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="眼睛模式" />

    </LinearLayout>

    <LinearLayout
        android:layout_centerVertical="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:gravity="center_horizontal">

        <RelativeLayout
            android:id="@+id/rl_voice_add"
            android:padding="7dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">

            <TextView
                android:textColor="@color/white"
                android:text="音量+"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </RelativeLayout>

        <com.vrplayerdemo.view.VerticalSeekBar
            android:layout_marginTop="15dp"
            android:id="@+id/seekbar_voice"
            android:layout_width="100dp"
            android:layout_height="160dp" />
        <RelativeLayout
            android:layout_marginTop="15dp"
            android:id="@+id/rl_voice_sub"
            android:padding="7dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <TextView
                android:textColor="@color/white"
                android:text="音量-"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </RelativeLayout>

    </LinearLayout>

    <ImageView
        android:clickable="true"
        android:layout_marginRight="45dp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"
        android:id="@+id/img_next_video"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/vr_next_nor"/>
    <ImageView
        android:id="@+id/img_stop_vr"
        android:src="@drawable/btn_vr_stop"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/img_start_vr"
        android:src="@drawable/btn_vr_start_nor"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="gone"/>

    <LinearLayout
        android:layout_marginLeft="28dp"
        android:layout_marginRight="28dp"
        android:layout_marginBottom="17dp"
        android:layout_alignParentBottom="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/tv_current_play_time"
            android:text="00:00"
            android:textColor="#FFFFFF"
            android:textSize="14sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <SeekBar
            style="@style/SeekBarAppTheme"
            android:layout_marginLeft="15dp"
            android:layout_marginRight="15dp"
            android:id="@+id/seekbar_play_video"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            />

        <TextView
            android:id="@+id/tv_end_play_time"
            android:text="12:05"
            android:textColor="#FFFFFF"
            android:textSize="14sp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />


    </LinearLayout>


</RelativeLayout>

M360PlayerActivity代码

package com.vrplayerdemo.view;

import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseArray;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import com.asha.vrlib.MDDirectorCamUpdate;
import com.asha.vrlib.MDVRLibrary;
import com.asha.vrlib.plugins.hotspot.IMDHotspot;
import com.asha.vrlib.texture.MD360BitmapTexture;
import com.vrplayerdemo.R;
import com.vrplayerdemo.VrPlayerActivity;
import com.vrplayerdemo.model.VideoModel;

import java.io.FileNotFoundException;


/**
 * using MD360Renderer
 *
 * Created by hzqiujiadi on 16/1/22.
 * hzqiujiadi ashqalcn@gmail.com
 */
public abstract class M360PlayerActivity extends AppCompatActivity {

    private static final SparseArray<String> sDisplayMode = new SparseArray<>();
    private static final SparseArray<String> sInteractiveMode = new SparseArray<>();
    private static final SparseArray<String> sProjectionMode = new SparseArray<>();
    private static final SparseArray<String> sAntiDistortion = new SparseArray<>();
    private static final SparseArray<String> sPitchFilter = new SparseArray<>();
    private static final SparseArray<String> sFlingEnabled = new SparseArray<>();

    static {
        sDisplayMode.put(MDVRLibrary.DISPLAY_MODE_NORMAL,"NORMAL");
        sDisplayMode.put(MDVRLibrary.DISPLAY_MODE_GLASS,"GLASS");

        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_MOTION,"MOTION");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_TOUCH,"TOUCH");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_MOTION_WITH_TOUCH,"M & T");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_CARDBORAD_MOTION,"CARDBOARD M");
        sInteractiveMode.put(MDVRLibrary.INTERACTIVE_MODE_CARDBORAD_MOTION_WITH_TOUCH,"CARDBOARD M&T");

        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_SPHERE,"SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME180,"DOME 180");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME230,"DOME 230");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME180_UPPER,"DOME 180 UPPER");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_DOME230_UPPER,"DOME 230 UPPER");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_STEREO_SPHERE_HORIZONTAL,"STEREO H SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_STEREO_SPHERE_VERTICAL,"STEREO V SPHERE");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_FIT,"PLANE FIT");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_CROP,"PLANE CROP");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_PLANE_FULL,"PLANE FULL");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_MULTI_FISH_EYE_HORIZONTAL,"MULTI FISH EYE HORIZONTAL");
        sProjectionMode.put(MDVRLibrary.PROJECTION_MODE_MULTI_FISH_EYE_VERTICAL,"MULTI FISH EYE VERTICAL");

        sAntiDistortion.put(1,"ANTI-ENABLE");
        sAntiDistortion.put(0,"ANTI-DISABLE");

        sPitchFilter.put(1,"FILTER PITCH");
        sPitchFilter.put(0,"FILTER NOP");

        sFlingEnabled.put(1, "FLING ENABLED");
        sFlingEnabled.put(0, "FLING DISABLED");
    }

//    public static void startVideo(Context context, Uri uri){
//        start(context, uri,VideoPlayerActivity.class);
//    }
    // uri在userAlbumModel里,不是本地数据,不需要Uri格式
    public static void startVideo(Context context, VideoModel videoModel, int playType){
        start(context,videoModel,playType, VrPlayerActivity.class);
    }
//    public static void startBitmap(Context context, Uri uri){
//        start(context, uri, BitmapPlayerActivity.class);
//    }
    private static void start(Context context,VideoModel videoModel,int playType,Class<? extends Activity> clz){
        Intent i = new Intent(context,clz);
//        i.setData(uri);
        i.putExtra("videoModel",videoModel);
        i.putExtra("playType",playType);
        context.startActivity(i);
    }

    private MDVRLibrary mVRLibrary;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // no title
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        // full screen
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // set content view
        setContentView(R.layout.activity_md_using_surface_view);

        // init VR Library
        mVRLibrary = createVRLibrary();

        final Activity activity = this;


        getVRLibrary().setEyePickChangedListener(new MDVRLibrary.IEyePickListener() {
            @Override
            public void onHotspotHit(IMDHotspot hotspot, long hitTimestamp) {

                if (System.currentTimeMillis() - hitTimestamp > 5000){
                    getVRLibrary().resetEyePick();
                }
            }
        });


    }


    private ValueAnimator animator;

    private void startCameraAnimation(final MDDirectorCamUpdate cameraUpdate, PropertyValuesHolder... values){
        if (animator != null){
            animator.cancel();
        }

        animator = ValueAnimator.ofPropertyValuesHolder(values).setDuration(2000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float near = (float) animation.getAnimatedValue("near");
                float eyeZ = (float) animation.getAnimatedValue("eyeZ");
                float pitch = (float) animation.getAnimatedValue("pitch");
                float yaw = (float) animation.getAnimatedValue("yaw");
                float roll = (float) animation.getAnimatedValue("roll");
                cameraUpdate.setEyeZ(eyeZ).setNearScale(near).setPitch(pitch).setYaw(yaw).setRoll(roll);
            }
        });
        animator.start();
    }

    abstract protected MDVRLibrary createVRLibrary();

    public MDVRLibrary getVRLibrary() {
        return mVRLibrary;
    }

    @Override
    protected void onResume() {
        super.onResume();
        mVRLibrary.onResume(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        mVRLibrary.onPause(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mVRLibrary.onDestroy();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mVRLibrary.onOrientationChanged(this);
    }

    protected Uri getUri() {
        Intent i = getIntent();
        if (i == null || i.getData() == null){
            return null;
        }
        return i.getData();
    }

    public void cancelBusy(){
        findViewById(R.id.progress).setVisibility(View.GONE);
    }

    public void busy(){
        findViewById(R.id.progress).setVisibility(View.VISIBLE);
    }

    // android impl
    private class AndroidProvider implements MDVRLibrary.IImageLoadProvider {

        Activity activity;

        public AndroidProvider(Activity activity) {
            this.activity = activity;
        }

        @Override
        public void onProvideBitmap(Uri uri, MD360BitmapTexture.Callback callback) {
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(activity.getContentResolver().openInputStream(uri));
                callback.texture(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }

        }
    }
}

最后就是我们页面的主要逻辑,也是区别于MD360Player4Android,自己做的页面逻辑,让demo更像实际开发的项目。这里页面风格主要仿照尤果圈。

package com.vrplayerdemo;

import android.content.Context;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.asha.vrlib.MD360Director;
import com.asha.vrlib.MD360DirectorFactory;
import com.asha.vrlib.MDVRLibrary;
import com.asha.vrlib.model.BarrelDistortionConfig;
import com.asha.vrlib.model.MDPinchConfig;
import com.vrplayerdemo.model.VideoModel;
import com.vrplayerdemo.view.CustomProjectionFactory;
import com.vrplayerdemo.view.M360PlayerActivity;
import com.vrplayerdemo.view.MediaPlayerWrapper;
import com.vrplayerdemo.view.VerticalSeekBar;

import tv.danmaku.ijk.media.player.IMediaPlayer;



public class VrPlayerActivity extends M360PlayerActivity implements View.OnClickListener{
    /**
     * 是否在拖动进度条中,默认为停止拖动,true为在拖动中,false为停止拖动
     */
    private boolean isDragging;
    /**
     * 播放缓冲监听
     */
    private int mCurrentBufferPercentage=0;
    /**
     * 同步进度
     */
    private static final int MESSAGE_SHOW_PROGRESS = 1;
    /**
     * 5秒菜单操作,隐藏菜单面板
     */
    private static final int MESSAGE_TOUCH_MENU = 3;
    /**
     * 开启倒计时
     */
    private static final int MESSAGE_START_COUNTDOWN = 4;
    /**
     * 重新播放
     */
    private static final int MESSAGE_PLAY_NEXT = 5;
    private MediaPlayerWrapper mMediaPlayerWrapper = new MediaPlayerWrapper();

    //显示菜单的全屏区域
    private RelativeLayout touchRl;
    //音量的seekbar
    private VerticalSeekBar verticalSeekBar;
    private RelativeLayout addVoice;
    private RelativeLayout subVoice;
    private Button btn_vr_nor;//全景模式
    private Button btn_vr_glass; //眼镜模式
    private ImageView img_stop_vr; //停止vr按钮
    private ImageView img_start_vr;//继续vr按钮
    private TextView endTimeTv; //总时长
    private TextView currentTimeTv; //当前时长
    private SeekBar playSeekbar; //视频播放进度
    private TextView titleTv;  //视频标题
    private ImageView img_back; //返回键
    private ImageView img_next_video; //下一部视频
    //当前声音大小
    private int volume;
    //设备最大音量
    private int mMaxVolume;
    //音频管理器
    private AudioManager audioManager;
    /**
     * 播放总时长
     */
    private VideoModel videoModel;
    private int playType=MDVRLibrary.DISPLAY_MODE_NORMAL;


    private boolean isFirstGlass;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        videoModel= (VideoModel) getIntent().getSerializableExtra("videoModel");
        playType =getIntent().getIntExtra("playType",MDVRLibrary.DISPLAY_MODE_NORMAL);
        initView();

        //初始化页面动画
        touchRl.setVisibility(View.VISIBLE);
        initMediaPlayer();
        mHandler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);

    }
    //初始化播放器
    private void initMediaPlayer() {
        mMediaPlayerWrapper.init();
        mMediaPlayerWrapper.setPreparedListener(new IMediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(IMediaPlayer mp) {
                cancelBusy();
                if (getVRLibrary() != null){
                    getVRLibrary().notifyPlayerChanged();
                }
                if(isFirstGlass){
                    isFirstGlass=false;
                    mMediaPlayerWrapper.pause();
                }
            }
        });
        mMediaPlayerWrapper.getPlayer().setOnErrorListener(new IMediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(IMediaPlayer mp, int what, int extra) {
                String error = String.format("Play Error what=%d extra=%d",what,extra);
                Toast.makeText(VrPlayerActivity.this, error, Toast.LENGTH_SHORT).show();
                return true;
            }
        });
        mMediaPlayerWrapper.getPlayer().setOnVideoSizeChangedListener(new IMediaPlayer.OnVideoSizeChangedListener() {
            @Override
            public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
                getVRLibrary().onTextureResize(width, height);
            }
        });

        if(videoModel.getVideoUrl()!=null){
            mMediaPlayerWrapper.openRemoteFile(videoModel.getVideoUrl());
            mMediaPlayerWrapper.prepare();
        }
        mMediaPlayerWrapper.getPlayer().setOnBufferingUpdateListener(new IMediaPlayer.OnBufferingUpdateListener() {
            @Override
            public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int percent) {
                mCurrentBufferPercentage = percent;
            }
        });
        mMediaPlayerWrapper.getPlayer().setOnCompletionListener(new IMediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(IMediaPlayer iMediaPlayer) {
                mHandler.sendEmptyMessage(MESSAGE_START_COUNTDOWN);
            }
        });
    }

    //初始化页面菜单功能
    private void initView() {
        img_back= (ImageView) findViewById(R.id.img_back);
        img_back.setOnClickListener(this);
        touchRl= (RelativeLayout) findViewById(R.id.rl_touch_menu);
        touchRl.setOnClickListener(this);
        verticalSeekBar= (VerticalSeekBar) findViewById(R.id.seekbar_voice);
        addVoice= (RelativeLayout) findViewById(R.id.rl_voice_add);
        subVoice= (RelativeLayout) findViewById(R.id.rl_voice_sub);
        addVoice.setOnClickListener(this);
        subVoice.setOnClickListener(this);
        //不要显示进度球
        verticalSeekBar.setThumbSize(1,1);
        verticalSeekBar.setUnSelectColor(Color.parseColor("#ff707070"));
        verticalSeekBar.setSelectColor(Color.parseColor("#ffffffff"));
        //单位px
        verticalSeekBar.setmInnerProgressWidth(3);
        verticalSeekBar.setOnSlideChangeListener(slideChangeListener);
        titleTv= (TextView) findViewById(R.id.tv_vr_title);
        titleTv.setText(videoModel.getName());

        btn_vr_nor= (Button) findViewById(R.id.btn_vr_nor);
        btn_vr_glass= (Button) findViewById(R.id.btn_vr_glass);
        btn_vr_nor.setOnClickListener(this);
        btn_vr_glass.setOnClickListener(this);
        img_stop_vr= (ImageView) findViewById(R.id.img_stop_vr);
        img_start_vr= (ImageView) findViewById(R.id.img_start_vr);
        img_stop_vr.setOnClickListener(this);
        img_start_vr.setOnClickListener(this);
        endTimeTv = (TextView) findViewById(R.id.tv_end_play_time);
        currentTimeTv = (TextView) findViewById(R.id.tv_current_play_time);
        img_next_video= (ImageView) findViewById(R.id.img_next_video);
        img_next_video.setOnClickListener(this);

        audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        mMaxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        verticalSeekBar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 100 / mMaxVolume);

        playSeekbar= (SeekBar) findViewById(R.id.seekbar_play_video);
        playSeekbar.setMax(1000);
        playSeekbar.setOnSeekBarChangeListener(mSeekListener);
        if(playType==MDVRLibrary.DISPLAY_MODE_GLASS){
            //因为默认是360全景,所有只有当playType为眼镜模式时,才需要改变默认.在M360PlayerActivity中,createVRLibrary带不过参数,所有是先设置全景,再改变
            changeToglass();
        }


    }

    @Override
    protected MDVRLibrary createVRLibrary() {

        return MDVRLibrary.with(this)
                .displayMode(MDVRLibrary.DISPLAY_MODE_NORMAL) //默认360度全景
                .interactiveMode(MDVRLibrary.INTERACTIVE_MODE_MOTION_WITH_TOUCH)//触摸和重力
                .asVideo(new MDVRLibrary.IOnSurfaceReadyCallback() {
                    @Override
                    public void onSurfaceReady(Surface surface) {
                        mMediaPlayerWrapper.setSurface(surface);
                    }
                })
                .ifNotSupport(new MDVRLibrary.INotSupportCallback() {
                    @Override
                    public void onNotSupport(int mode) {
                        String tip = mode == MDVRLibrary.INTERACTIVE_MODE_MOTION
                                ? "onNotSupport:MOTION" : "onNotSupport:" + String.valueOf(mode);
                        Toast.makeText(VrPlayerActivity.this, tip, Toast.LENGTH_SHORT).show();
                    }
                })
                .pinchConfig(new MDPinchConfig().setMin(1.0f).setMax(8.0f).setDefaultValue(0.1f))
                .pinchEnabled(true)
                .directorFactory(new MD360DirectorFactory() {
                    @Override
                    public MD360Director createDirector(int index) {
                        return MD360Director.builder().setPitch(90).build();
                    }
                })
                .projectionFactory(new CustomProjectionFactory()).listenGesture(new MDVRLibrary.IGestureListener() {
                    @Override
                    public void onClick(MotionEvent e) {
                        touchRl.setVisibility(View.VISIBLE);
                        if(mMediaPlayerWrapper.getPlayer().isPlaying()){
                            img_start_vr.setVisibility(View.GONE);
                            img_stop_vr.setVisibility(View.VISIBLE);
                        }else {
                            img_start_vr.setVisibility(View.VISIBLE);
                            img_stop_vr.setVisibility(View.GONE);
                        }
                        mHandler.sendEmptyMessage(MESSAGE_SHOW_PROGRESS);
                        hasMenuAction();
                        verticalSeekBar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 100 / mMaxVolume);
                    }
                })
                .barrelDistortionConfig(new BarrelDistortionConfig().setDefaultEnabled(false).setScale(0.95f))
                .build(findViewById(R.id.gl_view));

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
        mMediaPlayerWrapper.destroy();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mMediaPlayerWrapper.pause();
    }

    @Override
    protected void onResume() {
        super.onResume();
//        mMediaPlayerWrapper.resume();
    }

    int curProgress;
    @Override
    public void onClick(View v) {
        switch (v.getId()){

            case R.id.rl_touch_menu://点击菜单无点击事件的区域,菜单消失
                touchRl.setVisibility(View.GONE);
                mHandler.removeMessages(MESSAGE_TOUCH_MENU);
                break;
            case R.id.rl_voice_add://增加音量
                curProgress=verticalSeekBar.getProgress();
                Log.d("vrdemo","音量增加="+verticalSeekBar.getProgress());
                if(curProgress<100){
                    curProgress=curProgress+5;
                    verticalSeekBar.setProgress(curProgress);
                }
                volume = (int) (mMaxVolume * curProgress * 0.01);
                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
                hasMenuAction();
                break;
            case R.id.rl_voice_sub://减少音量
                Log.d("vrdemo","音量减少="+verticalSeekBar.getProgress());
                curProgress=verticalSeekBar.getProgress();
                if(curProgress>0){
                    curProgress=curProgress-5;
                    verticalSeekBar.setProgress(curProgress);
                }
                volume = (int) (mMaxVolume * curProgress * 0.01);
                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
                hasMenuAction();
                break;
            case R.id.btn_vr_nor://360全景模式
                if(playType!=MDVRLibrary.DISPLAY_MODE_NORMAL){
                    changeTo360nor();
                }
                hasMenuAction();
                break;
            case R.id.btn_vr_glass://眼镜模式
                if(playType!=MDVRLibrary.DISPLAY_MODE_GLASS){
                    changeToglass();
                }
                hasMenuAction();
                break;
            case R.id.img_stop_vr://暂停
                img_stop_vr.setVisibility(View.GONE);
                img_start_vr.setVisibility(View.VISIBLE);
                mMediaPlayerWrapper.pause();
                mHandler.removeMessages(MESSAGE_TOUCH_MENU);
                break;
            case R.id.img_start_vr://继续播放
                img_start_vr.setVisibility(View.GONE);
                img_stop_vr.setVisibility(View.VISIBLE);
                mMediaPlayerWrapper.resume();
                hasMenuAction();
                break;
            case R.id.img_back://返回
                finish();
                break;
            case R.id.img_next_video://下一部
                playNext();
                break;
        }
    }

    private void playNext() {
        //获取下一步内容
        //TODO 获取新片资源
        if(videoModel.getId()==15){
            videoModel=MainActivity.videoModels.get(0);
        }else {
            videoModel=MainActivity.videoModels.get(videoModel.getId());
        }
        titleTv.setText(videoModel.getName());
        mMediaPlayerWrapper.pause();
        mMediaPlayerWrapper.destroy();
        mMediaPlayerWrapper.init();
        mMediaPlayerWrapper.openRemoteFile(videoModel.getVideoUrl());
        mMediaPlayerWrapper.prepare();
    }



    //切换成360全景模式
    public void changeTo360nor(){
        playType=MDVRLibrary.DISPLAY_MODE_NORMAL;
        getVRLibrary().switchDisplayMode(VrPlayerActivity.this, MDVRLibrary.DISPLAY_MODE_NORMAL);
    }
    public void changeToglass(){
        playType=MDVRLibrary.DISPLAY_MODE_GLASS;
        getVRLibrary().switchDisplayMode(VrPlayerActivity.this, MDVRLibrary.DISPLAY_MODE_GLASS);
    }
    private VerticalSeekBar.SlideChangeListener slideChangeListener=new VerticalSeekBar.SlideChangeListener() {
        @Override
        public void onStart(VerticalSeekBar slideView, int progress) {

        }

        @Override
        public void onProgress(VerticalSeekBar slideView, int progress) {
            volume= (int) (mMaxVolume * progress * 0.01);
            if (volume > mMaxVolume)
                volume = mMaxVolume;
            else if (volume < 0)
                volume = 0;
            // 变更声音
            audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
        }

        @Override
        public void onStop(VerticalSeekBar slideView, int progress) {

        }
    };
    /**
     * 时长格式化显示
     */
    private String generateTime(long time) {
        int totalSeconds = (int) (time / 1000);
        int seconds = totalSeconds % 60;
        int minutes = (totalSeconds / 60) % 60;
        int hours = totalSeconds / 3600;
        return hours > 0 ? String.format("%02d:%02d:%02d", hours, minutes, seconds) : String.format("%02d:%02d", minutes, seconds);
    }
    /**
     * 进度条滑动监听
     */
    private final SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {

        /**数值的改变*/
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            long duration = mMediaPlayerWrapper.getPlayer().getDuration();
            int position = (int) ((duration * progress * 1.0) / 1000);
            String time = generateTime(position);
            currentTimeTv.setText(time);

        }

        /**开始拖动*/
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            isDragging = true;
            mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
        }

        /**停止拖动*/
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            long duration = mMediaPlayerWrapper.getPlayer().getDuration();
            mMediaPlayerWrapper.getPlayer().seekTo((int) ((duration * seekBar.getProgress() * 1.0) / 1000));

            mHandler.removeMessages(MESSAGE_SHOW_PROGRESS);
            isDragging = false;
            mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_PROGRESS, 1000);
            hasMenuAction();
        }
    };

    /**
     * 消息处理
     */
    private Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                /**滑动中,同步播放进度*/
                case MESSAGE_SHOW_PROGRESS:
                    long pos = syncProgress();
                    if (!isDragging) {
                        msg = obtainMessage(MESSAGE_SHOW_PROGRESS);
                        sendMessageDelayed(msg, 1000 - (pos % 1000));
                    }
                    break;
                /**隐藏菜单面板*/
                case MESSAGE_TOUCH_MENU:
                    touchRl.setVisibility(View.GONE);
                    //防止点触面引起的显示冲突
                    break;
                case MESSAGE_PLAY_NEXT:
                    playNext();
                    break;
                case MESSAGE_START_COUNTDOWN://播放完成时,开始倒计时,抛出handler是不明确完成时是否有线程安全问题
                    playNext();
                    break;
            }
        }
    };

    /**
     * 同步进度
     */
    private long syncProgress() {
        if (isDragging) {
            return 0;
        }
        long position = mMediaPlayerWrapper.getPlayer().getCurrentPosition();
        long duration = mMediaPlayerWrapper.getPlayer().getDuration();
        if (playSeekbar != null) {
            if (duration > 0) {
                long pos = 1000L * position / duration;
                playSeekbar.setProgress((int) pos);
            }
            int percent =mCurrentBufferPercentage;
            playSeekbar.setSecondaryProgress(percent * 10);
        }

        currentTimeTv.setText(generateTime(position));
        endTimeTv.setText(generateTime(duration));
        verticalSeekBar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 100 / mMaxVolume);

        return position;
    }
    //有菜单操作
    public void hasMenuAction(){
        if(isDragging){
            mHandler.removeMessages(MESSAGE_TOUCH_MENU);
        }else {
            mHandler.removeMessages(MESSAGE_TOUCH_MENU);
            mHandler.sendEmptyMessageDelayed(MESSAGE_TOUCH_MENU, 5000);
        }
    }

}

代码中用到的VideoModel是一个视频资源的model,可以根据实际项目需求传入,大致可以有标题,视频的url,封面图片,视频时长等字段。
这里讲下普通视频资源跟VR视频资源。拍摄VR视频的时候,是那种360°的机器,拍出来的视频就是360°的,如果你在浏览器上放VR视频,看到的一个很长的视频,你把VR视频用VR播放器播放,就相当于把长视频又卷到一个球上,而观看者在球中间。
demo中放置的几个测试url都是普通视频的url,所以看起来想过就很畸形了。

代码传送门

https://download.csdn.net/download/qq_31390699/10610824

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页