應一個朋友需求,研究了下 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 層視頻播放器就封裝好了,實現細節可以看源代碼:

下面介紹如何調用這個播放器:
- 1.native 層:native 調 java 我們肯定要用到 jni 技術,cocos2d-x 封裝了一個 jni 幫助類在:cocos2dx\platform\android\jni\JniHelper.h ,我們需要在 Java 層定義一個靜態方法,然后通過 jni->CallStaticVoidMethod 調用:

#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 層:

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 靜態全局變量,類似於單例設計模式。
完整代碼:

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