您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-x3.0 android播放视频

2014-07-11 19:27 495 查看
手游项目需要播放视频,首先需要在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

到这里,视频应该可以正常播放了,但还没研究如何控制视频的播放,不过对于项目来说够了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: