手游项目需要播放视频,首先需要在android下播放。
以下记录遇到的坑。
第一:在项目中需要调用java代码
#include "platform/android/jni/JniHelper.h"后报错,发现JniHelper里有一句
#include "Jni.h"而项目中找不到。
解决办法:找到jdk目录下的include,复制jni.h到VC目录下的include
复制jdk/include/win32下的jni_md.h到目录下的include
问题解决
第二个坑:System.Loadbiray传入什么参数?
static
{
System.loadLibrary("cocos2dcpp");
}
这里的cocos2dcpp是在cocos2d-x工程里proj.android/jni/Android.mk里LOCAL_MODULE_FILENAME := libcocos2dcpp指定的名字,而且不能要前面的lib
接下来说说具体实现
首先在proj.android\src\org\cocos2dx\cpp 目录下创建文件VideoView.java,代码如下
package org.cocos2dx.cpp;
import java.io.FileDescriptor;
import java.io.IOException;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.net.Uri;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
public class VideoView extends SurfaceView implements
SurfaceHolder.Callback,
View.OnTouchListener,
MediaPlayer.OnPreparedListener,
MediaPlayer.OnErrorListener,
MediaPlayer.OnInfoListener,
MediaPlayer.OnCompletionListener {
private static final String TAG = "VideoView";
private MediaPlayer mPlayer; // MediaPlayer对象
private Activity gameActivity;
private Uri resUri;
private AssetFileDescriptor fd;
private boolean surfaceCreated;
private OnFinishListener onFinishListener;
public VideoView(Activity context) {
super(context);
this.gameActivity = context;
final SurfaceHolder holder = getHolder();
holder.addCallback(this); // 设置回调接口
holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置为Buffer类型(播放视频&Camera预览)
setOnTouchListener(this);
mPlayer = new MediaPlayer();
// mPlayer.setDisplay(getHolder()); //此时holder并未创建,不能在此处设置
mPlayer.setScreenOnWhilePlaying(true);
mPlayer.setOnPreparedListener(this);
mPlayer.setOnCompletionListener(this);
mPlayer.setOnErrorListener(this);
mPlayer.setOnInfoListener(this);
}
public VideoView setOnFinishListener(OnFinishListener onFinishListener) {
this.onFinishListener = onFinishListener;
return this;
}
public void setVideo(Uri resUri) {
this.resUri = resUri;
try {
mPlayer.setDataSource(gameActivity, resUri);
} catch (Exception e) {
}
}
public void setVideo(AssetFileDescriptor fd) {
this.fd = fd;
try {
//直接调用 fd.getFileDescriptor() 是不行的
mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceCreated(final SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
surfaceCreated = true;
mPlayer.setDisplay(holder); // 指定SurfaceHolder
try {
mPlayer.prepare();
} catch (Exception e1) {
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(TAG, "surfaceDestroyed");
surfaceCreated = false;
if(mPlayer != null){
mPlayer.stop();
mPlayer.reset();
}
}
@Override
public void onPrepared(MediaPlayer player) {
Log.i(TAG, "onPrepared");
int wWidth = getWidth();
int wHeight = getHeight();
/* 获得视频宽长 */
int vWidth = mPlayer.getVideoWidth();
int vHeight = mPlayer.getVideoHeight();
/* 最适屏幕 */
float wRatio = (float) vWidth / (float) wWidth; // 宽度比
float hRatio = (float) vHeight / (float) wHeight; // 高度比
float ratio = Math.max(wRatio, hRatio); // 较大的比
vWidth = (int) Math.ceil((float) vWidth / ratio); // 新视频宽度
vHeight = (int) Math.ceil((float) vHeight / ratio); // 新视频高度
// 改变SurfaceHolder大小
getHolder().setFixedSize(vWidth, vHeight);
mPlayer.seekTo(posttion);
mPlayer.start();
}
private void dispose() {
mPlayer.release();
mPlayer = null;
resUri = null;
if (fd != null) {
try {
fd.close();
} catch (IOException e) {
e.printStackTrace();
}
fd = null;
}
}
@Override
public void onCompletion(MediaPlayer mp) {
Log.i(TAG, "onCompletion");
dispose();
if(onFinishListener != null)
onFinishListener.onVideoFinish();
}
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra) {
return true;
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
stop();
}
return true;
}
public void stop() {
mPlayer.stop(); // 大概不会调用 MediaPlayer.onCompletion
dispose();
if(onFinishListener != null)
onFinishListener.onVideoFinish();
}
int posttion;
public void pause() {
posttion = mPlayer.getCurrentPosition();
mPlayer.pause();
}
/**
* 暂停的时候,系统会销毁 SurfaceView ,所以在resume的时候相对于重新设置MediaPlayer
*/
public void resume() {
if(surfaceCreated){
mPlayer.start();
}else {
try {
if(resUri != null)
mPlayer.setDataSource(gameActivity, resUri);
else if (fd != null) {
mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
}
} catch (Exception e) {
}
}
}
public interface OnFinishListener {
public void onVideoFinish();
}
}
然后修改Cocos2dxActivity.java
package org.cocos2dx.cpp;
import android.app.NativeActivity;
import android.os.Bundle;
import android.view.WindowManager;
import java.io.FileDescriptor;
import java.io.IOException;
import android.content.res.AssetFileDescriptor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.ViewGroup;
import org.cocos2dx.cpp.VideoView.OnFinishListener;
public class Cocos2dxActivity extends NativeActivity implements OnFinishListener {
static Cocos2dxActivity instance;
VideoView videoView;
ViewGroup group;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 这一句是屏幕常亮
//For supports translucency
//1.change "attribs" in cocos\2d\platform\android\nativeactivity.cpp
/*const EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
//EGL_BLUE_SIZE, 5, -->delete
//EGL_GREEN_SIZE, 6, -->delete
//EGL_RED_SIZE, 5, -->delete
EGL_BUFFER_SIZE, 32, //-->new field
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
EGL_NONE
};*/
//2.Set the format of window
// getWindow().setFormat(PixelFormat.TRANSLUCENT);
instance = this;
group = (ViewGroup)getWindow().getDecorView();
}
static
{
System.loadLibrary("cocos2dcpp");
}
private void play(String name) {
Log.i("", "----- name=" + name);
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.play(name);
}
});
}
}
@Override
public void onVideoFinish() {
group.removeView(videoView);
videoView = null;
}
}
Java的代码就是这些了。接下来是C++层
在需要播放视频的地方加入:
#include "platform/android/jni/JniHelper.h"
JniMethodInfo t;
if (JniHelper::getStaticMethodInfo(t,
"org/cocos2dx/cpp/Cocos2dxActivity",
"playVideo",
"(Ljava/lang/String;)V"))
{
t.env->CallStaticVoidMethod(t.classID, t.methodID,
t.env->NewStringUTF("a.mp4"));
}
文件名随意,必须放到Resource目录,当然可以放到子文件夹下,那么文件名就改成
目录/文件名.后缀名
getStaticMethodInfo的参数:
第一个是JniMehodInfo类型,就是上面创建的
第二个是你要调用的Java文件类
第三个是要调用的静态函数名称
第四个是返回值和参数说明,这里的意思是返回值为void,参数有一个string
具体的可以百度搜索Jni和JniHelper
到这里,视频应该可以正常播放了,但还没研究如何控制视频的播放,不过对于项目来说够了。