Cocos2dx播放mp4文件(IOS和Android)

来源:互联网 发布:深入理解大数据微盘 编辑:程序博客网 时间:2024/04/29 08:09

http://blog.csdn.net/tyxkzzf/article/details/39081045


(头注:Cocos2d-x3.2引擎已经集成了MP4播放功能了,本篇文章写于2.x版本)

游戏需要播放mp4文件展示游戏背景,在网上搜了好久,IOS平台比较容易实现,Android就不敢恭维了;

播放mp4需要分平台实现,悲催的是IOS和Android的原生开发都没有做过,所以只能从网上找资料;

目前实现的功能播放,跳过和播放完成之后回到游戏,IOS和Android一样都已经实现;

1、IOS实现:

首先,参考文章:点击打开链接,我是完全按照这篇文章的介绍做的,里面有源代码地址,相信各位了看了之后就回明白,至少也有方向了;

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. 类说明:  
  2. LHVideoPlayerImplCpp.h/mm // cocos2dx中使用的播放MP4接口  
  3. LHVideoPlayerImpl.h/m // videoPlayer的oc接口  
  4. LHVideoPlayer.h/m // videoPlayer的实现,调用MPMoviePlayerController播放MP4  
  5. LHVideoOverlayView.h/m // videoPlayer的上层操作层,有跳过影片按钮。  
我就功能点而言,介绍其中的两个类。
第一个是LHVideoPlayerImplCpp.h/mm文件,这个是负责给2dx调用的。该类有两个静态方法:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. class LHVideoPlayerImplCpp {  
  2. public:  
  3.     /** 
  4.      * 开始播放MP4视频 
  5.      * name 视频名称,不要带后缀".mp4"。(比如文件是test.mp4, 那么name就传"test") 
  6.      * frameRect 视频显示的区域。全屏(Rect(0, 0, visibleSize.width, visibleSize.height)) 
  7.      */  
  8.     static void playMP4WithName(const char* name, cocos2d::Rect frameRect);  
  9.   
  10.     /** 
  11.      * 设置跳过影片按钮title,默认无跳过影片功能 
  12.      */  
  13.     static void setSkipTitle(const char* title);  
  14. };  
第二个是LHVideoPlayer.h/m,这个是负责播放MP4。
a、这个方法是播放MP4。注释很清楚。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. - (void)playMP4WithName: (NSString *)name VideoFrame:(CGRect)rect  
  2. {  
  3.     UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];  
  4.   
  5.     // 获取视频文件的名称  
  6.     NSString *url = [[NSBundle mainBundle]pathForResource:name ofType:@"mp4"];  
  7.     // 初始化player  
  8.     _player = [[MPMoviePlayerController alloc] initWithContentURL:[NSURL fileURLWithPath:url]];  
  9.     [keyWindow.rootViewController.view addSubview: [_player view]];  
  10.   
  11.     // 设置player样式  
  12.     [_player setControlStyle: MPMovieControlStyleNone];  
  13.     [[_player view] setFrame: rect];  
  14.   
  15.     // 当MP4完成播放的回调  
  16.     [[NSNotificationCenter defaultCenter]  
  17.      addObserver:self selector:@selector(movieFinishedCallback:)  
  18.      name:MPMoviePlayerPlaybackDidFinishNotification object:_player];  
  19.   
  20.     // 开始播放影片  
  21.     [_player play];  
  22.   
  23.     // 上层操作层  
  24.     _videoOverlayView = [ [LHVideoOverlayView alloc] initWithFrame: rect];  
  25.     [keyWindow.rootViewController.view addSubview: _videoOverlayView];  
  26. }  
b、这个方法是播放结束之后,移除播放view。

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. - (void)removePlayer:(MPMoviePlayerController*)player  
  2. {  
  3.     [[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:player];  
  4.   
  5.     [player.view removeFromSuperview];  
  6.     [_player release];  
  7.     _player = nil;  
  8.   
  9.     [_videoOverlayView removeFromSuperview];  
  10.     [_videoOverlayView release];  
  11.     _videoOverlayView = nil;  
  12. }  

3、播放完成,通知外界。playerPlayFinished 这个方法是空的,没有通知外界。我看了下,感觉没什么需要,所以没加。(原作者没加,不过考虑到我播放的mp4是挂在一个layer里面实现的,所以还是希望在播放结束的时候该layer能知道;所以,我另外添加了一个函数):

[objc] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. bool LHVideoPlayerImplCpp::getFinshState() {  
  2.     return [LHVideoPlayerImpl getFinshState];  
  3. }  
至于里面调用的函数就不写了,用于获取视频已经播放结束,然后在layer的visit里面处理:

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. void ShowMovieStart::visit() {  
  2.     GUILayer::visit();  
  3.     bool isfinish = false;  
  4. #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)  
  5.     //  
  6. #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  7.     isfinish = LHVideoPlayerImplCpp::getFinshState();  
  8. #endif  
  9.     if (isfinish) {  
  10.         CCLog("视频播放结束了");  
  11.         //做相关处理  
  12.     }  
  13. }  
注意看ios实现的部分,当视频结束时,做相关处理;

使用方法:
a、导入头文件

[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. #include “LHVideoPlayerImplCpp.h”  
b、开始调用接口,假设你要播放的是“loading.mp4”
[cpp] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. Size visibleSize = Director::getInstance()->getVisibleSize();  
  2. LHVideoPlayerImplCpp::playMP4WithName(“loading”, Rect(0, 0, visibleSize.width, visibleSize.height));  
  3. LHVideoPlayerImplCpp::setSkipTitle(“Skip”);  
c、影片结束之后,会自动移除视图;
git地址:点击打开链接

IOS实现相对于Android来说比较容易,而且上诉源代码还提供了跳过功能,非常不错;

2、Android部分,这个比较复杂,一开始我根本不知道从何下手,只能从网上找,但是相关资料也不是很多,而且大多数不容易解决问题,有些可能是写给Android老手看的,像我Android开发只见皮毛者完全云里雾里,不过,看得资料多了,代码还是可以理解的,虽然有时候一知半解,但还是一步步实现了;之前只用过C++和脚本,lua Js什么的,java代码也没写过,不过语言从来不是问题,主要的是原理。

a、首先,如何把mp4播放出来;

视频播放Veiw实现VideoView.java文件:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package org.cocos2dx.common;  
  2.   
  3. import java.io.FileDescriptor;  
  4. import java.io.IOException;  
  5.   
  6. import android.app.Activity;  
  7. import android.content.res.AssetFileDescriptor;  
  8. import android.media.MediaPlayer;  
  9. import android.net.Uri;  
  10. import android.util.Log;  
  11. import android.view.MotionEvent;  
  12. import android.view.SurfaceHolder;  
  13. import android.view.SurfaceView;  
  14. import android.view.View;  
  15.   
  16. public class VideoView extends SurfaceView implements   
  17.             SurfaceHolder.Callback,   
  18.             View.OnTouchListener,   
  19.             MediaPlayer.OnPreparedListener,   
  20.             MediaPlayer.OnErrorListener,   
  21.             MediaPlayer.OnInfoListener,  
  22.             MediaPlayer.OnCompletionListener {  
  23.     private static final String TAG = "VideoView";  
  24.       
  25.     private MediaPlayer mPlayer = null;  
  26.     private Activity gameActivity;  
  27.     private Uri resUri;  
  28.     private AssetFileDescriptor fd;  
  29.     private boolean surfaceCreated;  
  30.     private OnFinishListener onFinishListener;  
  31.       
  32.     public VideoView(Activity context) {  
  33.         super(context);  
  34.         this.gameActivity = context;  
  35.         final SurfaceHolder holder = getHolder();  
  36.         holder.addCallback(this);  
  37.         holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);  
  38.         setOnTouchListener(this);  
  39.         if (mPlayer != null) {    
  40.             mPlayer.reset();    
  41.             mPlayer.release();    
  42.             mPlayer = null;    
  43.         }   
  44.         mPlayer = new MediaPlayer();  
  45.           
  46.         mPlayer = new MediaPlayer();  
  47.         mPlayer.setScreenOnWhilePlaying(true);  
  48.         mPlayer.setOnPreparedListener(this);  
  49.         mPlayer.setOnCompletionListener(this);  
  50.         mPlayer.setOnErrorListener(this);  
  51.         mPlayer.setOnInfoListener(this);  
  52.         //不应该在这里设置holder  
  53.         //mPlayer.setDisplay(holder);  
  54.         //mPlayer.prepareAsync();  
  55.     }  
  56.       
  57.     public VideoView setOnFinishListener(OnFinishListener onFinishListener) {  
  58.         this.onFinishListener = onFinishListener;  
  59.         return this;  
  60.     }  
  61.   
  62.     public void setVideo(Uri resUri) {  
  63.         this.resUri = resUri;  
  64.         try {  
  65.             mPlayer.setDataSource(gameActivity, resUri);  
  66.         } catch (Exception e) {  
  67.         }  
  68.     }  
  69.       
  70.     public void setVideo(AssetFileDescriptor fd) {  
  71.         this.fd = fd;  
  72.   
  73.         try {  
  74.             mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());  
  75.         } catch (IOException e) {  
  76.             e.printStackTrace();  
  77.         }  
  78.     }  
  79.   
  80.     @Override  
  81.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  82.           
  83.     }  
  84.   
  85.     @Override  
  86.     public void surfaceCreated(final SurfaceHolder holder) {  
  87.         Log.i(TAG, "surfaceCreated");  
  88.   
  89.         surfaceCreated = true;  
  90.   
  91.         mPlayer.setDisplay(holder);  
  92.         try {  
  93.             mPlayer.prepare();  
  94.         } catch (Exception e1) {  
  95.               
  96.         }  
  97.     }  
  98.   
  99.     @Override  
  100.     public void surfaceDestroyed(SurfaceHolder holder) {  
  101.         Log.i(TAG, "surfaceDestroyed");  
  102.         surfaceCreated = false;  
  103.           
  104.         if(mPlayer != null){  
  105.             mPlayer.stop();  
  106.             mPlayer.reset();  
  107.         }  
  108.     }  
  109.   
  110.     @Override  
  111.     public void onPrepared(MediaPlayer player) {  
  112.         Log.i(TAG, "onPrepared");  
  113.   
  114.         int wWidth = getWidth();  
  115.         int wHeight = getHeight();  
  116.   
  117.         //  
  118.         int vWidth = mPlayer.getVideoWidth();  
  119.         int vHeight = mPlayer.getVideoHeight();  
  120.   
  121.         //  
  122.         float wRatio = (float) vWidth / (float) wWidth; //  
  123.         float hRatio = (float) vHeight / (float) wHeight; //  
  124.         float ratio = Math.max(wRatio, hRatio); //  
  125.         vWidth = (int) Math.ceil((float) vWidth / ratio); //  
  126.         vHeight = (int) Math.ceil((float) vHeight / ratio); //  
  127.   
  128.         //  
  129.         getHolder().setFixedSize(vWidth, vHeight);  
  130.         mPlayer.seekTo(posttion);  
  131.         mPlayer.start();  
  132.     }  
  133.       
  134.     private void dispose() {  
  135.         mPlayer.release();  
  136.         mPlayer = null;  
  137.         resUri = null;  
  138.         if (fd != null) {  
  139.             try {  
  140.                 fd.close();  
  141.             } catch (IOException e) {  
  142.                 e.printStackTrace();  
  143.             }  
  144.             fd = null;  
  145.         }  
  146.     }  
  147.   
  148.     @Override  
  149.     public void onCompletion(MediaPlayer mp) {  
  150.         Log.i(TAG, "onCompletion");  
  151.   
  152.         dispose();  
  153.           
  154.         if(onFinishListener != null)  
  155.             onFinishListener.onVideoFinish();  
  156.     }  
  157.   
  158.     @Override  
  159.     public boolean onInfo(MediaPlayer mp, int what, int extra) {  
  160.         return true;  
  161.     }  
  162.   
  163.     @Override  
  164.     public boolean onError(MediaPlayer mp, int what, int extra) {  
  165.         return true;  
  166.     }  
  167.   
  168.     @Override  
  169.     public boolean onTouch(View v, MotionEvent event) {  
  170.         if (event.getAction() == MotionEvent.ACTION_DOWN) {  
  171.             //不响应点击事件  
  172.             //stop();  
  173.         }  
  174.   
  175.         return true;  
  176.     }  
  177.   
  178.     public void stop() {  
  179.         mPlayer.stop(); //  
  180.         dispose();  
  181.         if(onFinishListener != null) {  
  182.             onFinishListener.onVideoFinish();  
  183.         }  
  184.     }  
  185.       
  186.     int posttion;  
  187.     public void pause() {  
  188.         posttion = mPlayer.getCurrentPosition();  
  189.         mPlayer.pause();  
  190.     }  
  191.   
  192.     /** 
  193.      *  
  194.      */  
  195.     public void resume() {  
  196.         if(surfaceCreated){  
  197.             mPlayer.start();  
  198.         }else {  
  199.             try {  
  200.                 if(resUri != null)  
  201.                     mPlayer.setDataSource(gameActivity, resUri);  
  202.                 else if (fd != null) {  
  203.                     mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());  
  204.                 }  
  205.             } catch (Exception e) {  
  206.             }  
  207.         }  
  208.     }  
  209.       
  210.     public interface OnFinishListener {  
  211.         public void onVideoFinish();  
  212.     }  
  213. }  
主要用到MediaPlayer来实现,至于MediaPlayer怎么用,我刚开始也不大懂,从网上找资料,很多,有兴趣google之,这里实现了一个视频播放的主要功能,另外有停止(跳过)的功能;

下面是PlayeVideo文件:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. package org.cocos2dx.common;  
  2.   
  3. import java.io.*;  
  4.   
  5. import org.cocos2dx.lib.Cocos2dxActivity;  
  6.   
  7. import org.cocos2dx.common.VideoView.OnFinishListener;  
  8.   
  9. import android.content.res.AssetFileDescriptor;  
  10. import android.net.Uri;  
  11. import android.os.Bundle;  
  12. import android.util.Log;  
  13. import android.view.View;  
  14. import android.view.ViewGroup;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.Button;  
  17. import android.widget.LinearLayout;  
  18. import android.widget.RelativeLayout;  
  19. import android.widget.Toast;  
  20. import android.content.Context;  
  21. import android.view.LayoutInflater;  
  22.   
  23. public class playvideo implements OnFinishListener {  
  24.     ViewGroup viewgroup = null;  
  25.     private static playvideo pv = null;  
  26.     private static XXXX instance = null;  
  27.     VideoView videoView;  
  28.     boolean isvideofinished = false;  
  29.     //跳过按钮的view  
  30.     private View layout = null;  
  31.     //跳过按钮  
  32.     private Button skipbtn = null;  
  33.       
  34.     public static playvideo shareInstance() {  
  35.         if(null == pv) {  
  36.             pv = new playvideo();  
  37.         }  
  38.           
  39.         return pv;  
  40.     }  
  41.       
  42.     public void ShowVideo(String name) {  
  43.         System.out.println("Android ShowVideo 111111");  
  44.         if (null == instance) {  
  45.             return;  
  46.         }  
  47.           
  48.         Log.i("""name=" + name);  
  49.           
  50.         videoView = new VideoView(instance);  
  51.         videoView.setOnFinishListener(this);  
  52.           
  53.         viewgroup = (ViewGroup)instance.getWindow().getDecorView();  
  54.         try {  
  55.             AssetFileDescriptor afd = instance.getAssets().openFd(name);  
  56.             videoView.setVideo(afd);  
  57.         } catch (IOException e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.         viewgroup.addView(videoView);  
  61.         videoView.setZOrderMediaOverlay(true);  
  62.           
  63.         //加一个新的界面  
  64.         Context mContext = ShenMoJie.getContext();  
  65.         //LAYOUT_INFLATER_SERVICE表示从xml文件中加载布局  
  66.         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  67.         layout = inflater.inflate(R.layout.video, null);  
  68.         //添加一层  
  69.         viewgroup.addView(layout);  
  70.         //找到对应的按钮  
  71.         skipbtn = (Button)layout.findViewById(R.id.skipbutton);  
  72.         skipbtn.setOnClickListener(listener);  
  73.         //用相对布局定义控件的位置  
  74.         int width = instance.getWindowManager().getDefaultDisplay().getWidth();   
  75.         int height = instance.getWindowManager().getDefaultDisplay().getHeight();  
  76.         System.out.println("width: "+width+"height: "+height);  
  77.         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)skipbtn.getLayoutParams();  
  78.         //left, top, right, bottom  
  79.         params.setMargins(width*5/61000);// 通过自定义坐标来放置你的控件  
  80.         skipbtn.setLayoutParams(params);  
  81.     }  
  82.       
  83.     private OnClickListener listener = new OnClickListener() {  
  84.         @Override  
  85.         public void onClick(View v) {  
  86.             Button btn=(Button)v;  
  87.             System.out.println("Android listener");  
  88.             switch (btn.getId()) {  
  89.                 case R.id.skipbutton:  
  90.                     System.out.println("Android listener->skipbutton");  
  91.                     skipVideo();  
  92.                     break;  
  93.                 default:  
  94.                     break;  
  95.             }  
  96.         }  
  97.     };  
  98.       
  99.     public static void playVideo(final String name) {  
  100.         System.out.println("Android playVideo 111111");  
  101.         if (instance != null ) {  
  102.             System.out.println("Android playVideo 22222");  
  103.             instance.runOnUiThread(new Runnable() {  
  104.                 @Override  
  105.                 public void run() {  
  106.                     System.out.println("Android playVideo 333333");  
  107.                     playvideo.shareInstance().ShowVideo(name);  
  108.                 }  
  109.             });  
  110.         }  
  111.     }  
  112.       
  113.     public static boolean isVideoFinished() {  
  114.         //System.out.println("Android isVideoFinished");  
  115.         return playvideo.shareInstance().isvideofinished;  
  116.     }  
  117.       
  118.     public static void skipVideo() {  
  119.         playvideo.shareInstance().videoView.stop();  
  120.     }  
  121.   
  122.     @Override  
  123.     public void onVideoFinish() {  
  124.         viewgroup.removeView(videoView);  
  125.         viewgroup.removeView(layout);  
  126.         videoView = null;  
  127.         layout = null;  
  128.         isvideofinished = true;  
  129.         System.out.println("Android onVideoFinish");  
  130.     }  
  131.       
  132.     public static void SetActivity(XXXX ptActivity) {  
  133.         instance = ptActivity;  
  134.     }  
  135. }  
其中XXXX是继承之Cocos2dxActivity的实例,用SetActivity方法,将之传进来,播放PlayVideo,传一个视频文件名,这里需要用到JNI,C++调用java函数,网上也是一大堆的资料,知之为知之,不知百度之,再不知谷歌之,然后就可以实现实现游戏的播放了,注意在VideoView中实现了,播放完成之后会将试图从viewgourp中移除;

到这一步还比较简单,由于之前没有做过Android原生开发,所以连怎么添加一个button都不知道(跳过按钮),不过了解了一下之后,还是实现了,虽然代码不知道写得合不合理;

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. //加一个新的界面  
  2.         Context mContext = ShenMoJie.getContext();  
  3.         //LAYOUT_INFLATER_SERVICE表示从xml文件中加载布局  
  4.         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  5.         layout = inflater.inflate(R.layout.video, null);  
  6.         //添加一层  
  7.         viewgroup.addView(layout);  
  8.         //找到对应的按钮  
  9.         skipbtn = (Button)layout.findViewById(R.id.skipbutton);  
  10.         skipbtn.setOnClickListener(listener);  
  11.         //用相对布局定义控件的位置  
  12.         int width = instance.getWindowManager().getDefaultDisplay().getWidth();   
  13.         int height = instance.getWindowManager().getDefaultDisplay().getHeight();  
  14.         System.out.println("width: "+width+"height: "+height);  
  15.         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)skipbtn.getLayoutParams();  
  16.         //left, top, right, bottom  
  17.         params.setMargins(width*5/61000);// 通过自定义坐标来放置你的控件  
  18.         skipbtn.setLayoutParams(params);  
详情请看PlayVideo文件,下面是vidio.xml

[html] view plaincopyprint?在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent">  
  3.  <Button android:text="@string/video_skip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/skipbutton" />  
  4. </LinearLayout>  
就这样,Android版也实现了播放跳过功能;
0 0