• cocos2dx 播放视频 on Android


    应一个朋友需求,研究了下 cocos2d-x 引擎在 android 平台上播放视频的方法,因为之前研究 Libgdx 播视频的时候有了经验,于是依葫芦画瓢

    首先你不要想到去用系统的 VideoView 控件,他不适合我们

    我们来用强大 SurfaceView 和 MediaPlayer 来组装一下,android 框架设计的很好啊

    其原理就是:

    MediaPlayer.setDisplay (SurfaceHolder sh)

    sh 来自于SurfaceView,这样MediaPlayer就可以看到画面了。

    不过这里面有几个细节需要注意:

    • 1.何时调用 setDisplay :

      一般可能会在构造函数里面就调用,这样系统会报错即使没报错也可能会出现 闻其声而不见其画面 的现象(大多数人遇到过),告诉你 Holder 无效,SurfaceHolder 是有一组回调接口的,通过 

      addCallback(SurfaceHolder.Callback callback)

    设置,Callback 里面有个函数:

      surfaceCreated(final SurfaceHolder holder)

    其参数是 SurfaceHolder 所以我们可以猜到这个接口用来告诉我们 SurfaceHolder 创建好啦,所以我们在这个回调里面调用 MediaPlayer.setDisplay 就没问题啦!

    • 2.如何播放视频文件:

    在 coco2d-x 中,资源文件肯定都在 assets 目录下,所以我们首先想到通过 URI 引用 assets 下文件,Like:

    Uri uri = Uri.parse("file:///android_asset/" + name); //不可取

    但是这样是不行的,播放不出来,于是我就上 StackOverFlow 上搜搜,还是有前辈解决了,所以我要说一句:StackOverFlow 是一个神奇的网站。

    正确的做法是调用这个接口:

    setDataSource (FileDescriptor fd, long offset, long length)

    assets 下可以通过:AssetFileDescriptor afd = getAssets().openFd(name); 方法得到 AssetFileDescriptor 对象,然后这样调用就OK:

    mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());

    你可能会想到:

    //直接调用 fd.getFileDescriptor() 是不行的
    mPlayer.setDataSource(fd.getFileDescriptor());

    这样也是不行的,具体原因没深入研究,此外再介绍用 res/raw 下资源的方法:

    Uri uri = Uri.parse("android.resource://" + this.getPackageName() + "/" + R.raw.video2); //可取

    这样一个 android 层视频播放器就封装好了,实现细节可以看源代码:

    View Code

    下面介绍如何调用这个播放器:

    • 1.native 层:native 调 java 我们肯定要用到 jni 技术,cocos2d-x 封装了一个 jni 帮助类在:cocos2dx\platform\android\jni\JniHelper.h ,我们需要在 Java 层定义一个静态方法,然后通过 jni->CallStaticVoidMethod 调用:
    View Code
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
            JniMethodInfo t;
    
            if (JniHelper::getStaticMethodInfo(t, 
                "com/yichou/demo/video/VideoDemo", 
                "playVideo", 
                "(Ljava/lang/String;)V"))
            {
                t.env->CallStaticVoidMethod(t.classID, t.methodID, 
                    t.env->NewStringUTF("video2.mp4"));
            }
    #endif
    • 2.这样重点还是来到 Java 层:
    View Code
        VideoView videoView;
        
        private void a(String name) {
            Log.i("", "name=" + name);
            
    //        Uri uri = Uri.parse("file:///android_asset/" + name); //不可取
    //        Uri uri = Uri.parse("android.resource://" + this.getPackageName() + "/" + R.raw.video2); //可取
            videoView = new VideoView(this);
            videoView.setOnFinishListener(this);
    //        videoView.setVideo(uri);
            try {
                AssetFileDescriptor afd = getAssets().openFd(name);
                videoView.setVideo(afd);
            } catch (IOException e) {
                e.printStackTrace();
            }
            group.addView(videoView);
            videoView.setZOrderMediaOverlay(true);
        }
        
        public static void playVideo(final String name) {
            if (instance != null) {
                instance.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        instance.a(name); 
                    }
                });
            }
        }

    为了方便起见,我们把方法定义为 static 这样我们就需要一个当前 activity 的实例,这里用了一个 instance 静态全局变量,类似于单例设计模式。

    完整代码:

    View Code
     1 public class VideoDemo extends Cocos2dxActivity implements OnFinishListener {
     2     ViewGroup group;
     3     static VideoDemo instance;
     4     
     5 
     6     protected void onCreate(Bundle savedInstanceState){
     7         super.onCreate(savedInstanceState);
     8         instance = this;
     9         
    10         group = (ViewGroup)getWindow().getDecorView();
    11     }
    12     
    13     
    14     VideoView videoView;
    15     
    16     private void a(String name) {
    17         Log.i("", "name=" + name);
    18         
    19 //        Uri uri = Uri.parse("file:///android_asset/" + name); //不可取
    20 //        Uri uri = Uri.parse("android.resource://" + this.getPackageName() + "/" + R.raw.video2); //可取
    21         videoView = new VideoView(this);
    22         videoView.setOnFinishListener(this);
    23 //        videoView.setVideo(uri);
    24         try {
    25             AssetFileDescriptor afd = getAssets().openFd(name);
    26             videoView.setVideo(afd);
    27         } catch (IOException e) {
    28             e.printStackTrace();
    29         }
    30         group.addView(videoView);
    31         videoView.setZOrderMediaOverlay(true);
    32     }
    33     
    34     public static void playVideo(final String name) {
    35         if (instance != null) {
    36             instance.runOnUiThread(new Runnable() {
    37                 @Override
    38                 public void run() {
    39                     instance.a(name); 
    40                 }
    41             });
    42         }
    43     }
    44     
    45     static {
    46          System.loadLibrary("game");
    47     }
    48 
    49     @Override
    50     public void onVideoFinish() {
    51         group.removeView(videoView);
    52         videoView = null;
    53     }
    54 }

    OK,核心方法介绍完毕,具体实现细节可以看我提供的 Demo 源码

    http://pan.baidu.com/share/link?shareid=505934&uk=4061068395

  • 相关阅读:
    Shell脚本实现用户数据导入
    Sping Cloud 微服务框架学习
    Redis学习笔记
    HTML+CSS实现简单三级菜单
    CSS Float浮动所带来的奇怪现象
    CSS如何清除浮动流的多种方案
    ECMAScript/JS 基础知识讲解
    Python的生成器
    Python包的导入说明
    Paramiko模块简单使用
  • 原文地址:https://www.cnblogs.com/yichouangle/p/3042121.html
Copyright © 2020-2023  润新知