http://blog.csdn.net/tyxkzzf/article/details/39081045
(头注:Cocos2d-x3.2引擎已经集成了MP4播放功能了,本篇文章写于2.x版本)
游戏需要播放mp4文件展示游戏背景,在网上搜了好久,IOS平台比较容易实现,Android就不敢恭维了;
播放mp4需要分平台实现,悲催的是IOS和Android的原生开发都没有做过,所以只能从网上找资料;
目前实现的功能播放,跳过和播放完成之后回到游戏,IOS和Android一样都已经实现;
1、IOS实现:
首先,参考文章:点击打开链接,我是完全按照这篇文章的介绍做的,里面有源代码地址,相信各位了看了之后就回明白,至少也有方向了;
- 类说明:
- LHVideoPlayerImplCpp.h/mm // cocos2dx中使用的播放MP4接口
- LHVideoPlayerImpl.h/m // videoPlayer的oc接口
- LHVideoPlayer.h/m // videoPlayer的实现,调用MPMoviePlayerController播放MP4
- LHVideoOverlayView.h/m // videoPlayer的上层操作层,有跳过影片按钮。
第一个是LHVideoPlayerImplCpp.h/mm文件,这个是负责给2dx调用的。该类有两个静态方法:
- class LHVideoPlayerImplCpp {
- public:
- /**
- * 开始播放MP4视频
- * name 视频名称,不要带后缀".mp4"。(比如文件是test.mp4, 那么name就传"test")
- * frameRect 视频显示的区域。全屏(Rect(0, 0, visibleSize.width, visibleSize.height))
- */
- static void playMP4WithName(const char* name, cocos2d::Rect frameRect);
- /**
- * 设置跳过影片按钮title,默认无跳过影片功能
- */
- static void setSkipTitle(const char* title);
- };
a、这个方法是播放MP4。注释很清楚。
- - (void)playMP4WithName: (NSString *)name VideoFrame:(CGRect)rect
- {
- UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
- // 获取视频文件的名称
- NSString *url = [[NSBundle mainBundle]pathForResource:name ofType:@"mp4"];
- // 初始化player
- _player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];
- [keyWindow.rootViewController.view addSubview: [_player view]];
- // 设置player样式
- [_player setControlStyle: MPMovieControlStyleNone];
- [[_player view] setFrame: rect];
- // 当MP4完成播放的回调
- [[NSNotificationCenter defaultCenter]
- addObserver:self selector:@selector(movieFinishedCallback:)
- name:MPMoviePlayerPlaybackDidFinishNotification object:_player];
- // 开始播放影片
- [_player play];
- // 上层操作层
- _videoOverlayView = [ [LHVideoOverlayView alloc] initWithFrame: rect];
- [keyWindow.rootViewController.view addSubview: _videoOverlayView];
- }
- - (void)removePlayer:(MPMoviePlayerController*)player
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:player];
- [player.view removeFromSuperview];
- [_player release];
- _player = nil;
- [_videoOverlayView removeFromSuperview];
- [_videoOverlayView release];
- _videoOverlayView = nil;
- }
3、播放完成,通知外界。playerPlayFinished 这个方法是空的,没有通知外界。我看了下,感觉没什么需要,所以没加。(原作者没加,不过考虑到我播放的mp4是挂在一个layer里面实现的,所以还是希望在播放结束的时候该layer能知道;所以,我另外添加了一个函数):
- bool LHVideoPlayerImplCpp::getFinshState() {
- return [LHVideoPlayerImpl getFinshState];
- }
- void ShowMovieStart::visit() {
- GUILayer::visit();
- bool isfinish = false;
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
- //
- #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- isfinish = LHVideoPlayerImplCpp::getFinshState();
- #endif
- if (isfinish) {
- CCLog("视频播放结束了");
- //做相关处理
- }
- }
使用方法:
a、导入头文件
- #include “LHVideoPlayerImplCpp.h”
- Size visibleSize = Director::getInstance()->getVisibleSize();
- LHVideoPlayerImplCpp::playMP4WithName(“loading”, Rect(0, 0, visibleSize.width, visibleSize.height));
- LHVideoPlayerImplCpp::setSkipTitle(“Skip”);
git地址: 点击打开链接
IOS实现相对于Android来说比较容易,而且上诉源代码还提供了跳过功能,非常不错;
2、Android部分,这个比较复杂,一开始我根本不知道从何下手,只能从网上找,但是相关资料也不是很多,而且大多数不容易解决问题,有些可能是写给Android老手看的,像我Android开发只见皮毛者完全云里雾里,不过,看得资料多了,代码还是可以理解的,虽然有时候一知半解,但还是一步步实现了;之前只用过C++和脚本,lua Js什么的,java代码也没写过,不过语言从来不是问题,主要的是原理。
a、首先,如何把mp4播放出来;
视频播放Veiw实现VideoView.java文件:
- package org.cocos2dx.common;
- 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 = null;
- 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);
- setOnTouchListener(this);
- if (mPlayer != null) {
- mPlayer.reset();
- mPlayer.release();
- mPlayer = null;
- }
- mPlayer = new MediaPlayer();
- mPlayer = new MediaPlayer();
- mPlayer.setScreenOnWhilePlaying(true);
- mPlayer.setOnPreparedListener(this);
- mPlayer.setOnCompletionListener(this);
- mPlayer.setOnErrorListener(this);
- mPlayer.setOnInfoListener(this);
- //不应该在这里设置holder
- //mPlayer.setDisplay(holder);
- //mPlayer.prepareAsync();
- }
- 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 {
- 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);
- 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); //
- //
- 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(); //
- dispose();
- if(onFinishListener != null) {
- onFinishListener.onVideoFinish();
- }
- }
- int posttion;
- public void pause() {
- posttion = mPlayer.getCurrentPosition();
- mPlayer.pause();
- }
- /**
- *
- */
- 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();
- }
- }
下面是PlayeVideo文件:
- package org.cocos2dx.common;
- import java.io.*;
- import org.cocos2dx.lib.Cocos2dxActivity;
- import org.cocos2dx.common.VideoView.OnFinishListener;
- import android.content.res.AssetFileDescriptor;
- import android.net.Uri;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.LinearLayout;
- import android.widget.RelativeLayout;
- import android.widget.Toast;
- import android.content.Context;
- import android.view.LayoutInflater;
- public class playvideo implements OnFinishListener {
- ViewGroup viewgroup = null;
- private static playvideo pv = null;
- private static XXXX instance = null;
- VideoView videoView;
- boolean isvideofinished = false;
- //跳过按钮的view
- private View layout = null;
- //跳过按钮
- private Button skipbtn = null;
- public static playvideo shareInstance() {
- if(null == pv) {
- pv = new playvideo();
- }
- return pv;
- }
- public void ShowVideo(String name) {
- System.out.println("Android ShowVideo 111111");
- if (null == instance) {
- return;
- }
- Log.i("", "name=" + name);
- videoView = new VideoView(instance);
- videoView.setOnFinishListener(this);
- viewgroup = (ViewGroup)instance.getWindow().getDecorView();
- try {
- AssetFileDescriptor afd = instance.getAssets().openFd(name);
- videoView.setVideo(afd);
- } catch (IOException e) {
- e.printStackTrace();
- }
- viewgroup.addView(videoView);
- videoView.setZOrderMediaOverlay(true);
- //加一个新的界面
- Context mContext = ShenMoJie.getContext();
- //LAYOUT_INFLATER_SERVICE表示从xml文件中加载布局
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- layout = inflater.inflate(R.layout.video, null);
- //添加一层
- viewgroup.addView(layout);
- //找到对应的按钮
- skipbtn = (Button)layout.findViewById(R.id.skipbutton);
- skipbtn.setOnClickListener(listener);
- //用相对布局定义控件的位置
- int width = instance.getWindowManager().getDefaultDisplay().getWidth();
- int height = instance.getWindowManager().getDefaultDisplay().getHeight();
- System.out.println("width: "+width+"height: "+height);
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)skipbtn.getLayoutParams();
- //left, top, right, bottom
- params.setMargins(width*5/6, 10, 0, 0);// 通过自定义坐标来放置你的控件
- skipbtn.setLayoutParams(params);
- }
- private OnClickListener listener = new OnClickListener() {
- @Override
- public void onClick(View v) {
- Button btn=(Button)v;
- System.out.println("Android listener");
- switch (btn.getId()) {
- case R.id.skipbutton:
- System.out.println("Android listener->skipbutton");
- skipVideo();
- break;
- default:
- break;
- }
- }
- };
- public static void playVideo(final String name) {
- System.out.println("Android playVideo 111111");
- if (instance != null ) {
- System.out.println("Android playVideo 22222");
- instance.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- System.out.println("Android playVideo 333333");
- playvideo.shareInstance().ShowVideo(name);
- }
- });
- }
- }
- public static boolean isVideoFinished() {
- //System.out.println("Android isVideoFinished");
- return playvideo.shareInstance().isvideofinished;
- }
- public static void skipVideo() {
- playvideo.shareInstance().videoView.stop();
- }
- @Override
- public void onVideoFinish() {
- viewgroup.removeView(videoView);
- viewgroup.removeView(layout);
- videoView = null;
- layout = null;
- isvideofinished = true;
- System.out.println("Android onVideoFinish");
- }
- public static void SetActivity(XXXX ptActivity) {
- instance = ptActivity;
- }
- }
到这一步还比较简单,由于之前没有做过Android原生开发,所以连怎么添加一个button都不知道(跳过按钮),不过了解了一下之后,还是实现了,虽然代码不知道写得合不合理;
- //加一个新的界面
- Context mContext = ShenMoJie.getContext();
- //LAYOUT_INFLATER_SERVICE表示从xml文件中加载布局
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- layout = inflater.inflate(R.layout.video, null);
- //添加一层
- viewgroup.addView(layout);
- //找到对应的按钮
- skipbtn = (Button)layout.findViewById(R.id.skipbutton);
- skipbtn.setOnClickListener(listener);
- //用相对布局定义控件的位置
- int width = instance.getWindowManager().getDefaultDisplay().getWidth();
- int height = instance.getWindowManager().getDefaultDisplay().getHeight();
- System.out.println("width: "+width+"height: "+height);
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)skipbtn.getLayoutParams();
- //left, top, right, bottom
- params.setMargins(width*5/6, 10, 0, 0);// 通过自定义坐标来放置你的控件
- skipbtn.setLayoutParams(params);
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent">
- <Button android:text="@string/video_skip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/skipbutton" />
- </LinearLayout>