我现在做的一款游戏使用 Libgdx ,其中需要播放小段视频过度游戏场景,想到传统的办法是跳转 Activity ,显然这样做不是我会选择的!
我想直接在当前游戏 Activity 中插播视频。
播放视频其实只需要:MediaPlayer 和 SurfaceView
刚开始在网上找到了一段这样的实例代码,拿来用没问题
1: public class VideoPlayerActivity extends Activity implements
2: MediaPlayer.OnCompletionListener,
3: MediaPlayer.OnPreparedListener,
4: SurfaceHolder.Callback,
5: OnClickListener {
6:
7: private SurfaceView surfaceView;
8: private MediaPlayer mPlayer; // MediaPlayer对象
9: private SurfaceHolder mSurfaceHolder; // SurfaceHolder对象
10:
11: private Button mSkipVideoBtn;
12:
13: @Override
14: protected void onCreate(Bundle savedInstanceState) {
15: super.onCreate(savedInstanceState);
16: requestWindowFeature(Window.FEATURE_NO_TITLE);
17: getWindow().setFlags(0x400, 0x400);
18:
19: setContentView(R.layout.video_player);
20:
21: surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
22:
23: /* 初始化mSurfaceHolder */
24: mSurfaceHolder = surfaceView.getHolder();
25: mSurfaceHolder.addCallback(this); // 设置回调接口
26: mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置为Buffer类型(播放视频&Camera预览)
27:
28: /* 初始化MediaPlayer */
29: mPlayer = MediaPlayer.create(this, getIntent().getIntExtra("resId", R.raw.video2));
30: mPlayer.setOnPreparedListener(this);
31: mPlayer.setOnCompletionListener(this);
32:
33: mSkipVideoBtn = (Button) findViewById(R.id.button1);
34: mSkipVideoBtn.setOnClickListener(this);
35: }
36:
37: @Override
38: protected void onDestroy() {
39: mPlayer.release();
40: super.onDestroy();
41: }
42:
43: @Override
44: public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
45: // TODO Auto-generated method stub
46:
47: String s = "#E0D8C8";
48: int c = Integer.parseInt(s, 16);
49: surfaceView.setBackgroundColor(Color.rgb(c>>24, (c>>16)&0x0000ff00, c&0x000000ff));
50: }
51:
52: @Override
53: public void surfaceCreated(SurfaceHolder holder) {
54: mPlayer.setDisplay(holder); // 指定SurfaceHolder
55: // mPlayer.prepareAsync(); // 异步准备(将回调OnPreparedListener接口)
56: }
57:
58: @Override
59: public void surfaceDestroyed(SurfaceHolder holder) {
60: // TODO Auto-generated method stub
61:
62: }
63:
64: @Override
65: public void onPrepared(MediaPlayer player) {
66: /* 获得窗口宽长 */
67: Display display = getWindowManager().getDefaultDisplay();
68: int wWidth = display.getWidth();
69: int wHeight = display.getHeight();
70:
71: /* 获得视频宽长 */
72: int vWidth = mPlayer.getVideoWidth();
73: int vHeight = mPlayer.getVideoHeight();
74:
75: /* 最适屏幕 */
76: float wRatio = (float) vWidth / (float) wWidth; // 宽度比
77: float hRatio = (float) vHeight / (float) wHeight; // 高度比
78: float ratio = Math.max(wRatio, hRatio); // 较大的比
79: vWidth = (int) Math.ceil((float) vWidth / ratio); // 新视频宽度
80: vHeight = (int) Math.ceil((float) vHeight / ratio); // 新视频高度
81:
82: // 改变SurfaceHolder大小
83: mSurfaceHolder.setFixedSize(vWidth, vHeight);
84: // 设置新布局参数(这在samsung i9088上出现stretch的错误==)
85: // surfaceView.setLayoutParams(new LinearLayout.LayoutParams(vWidth,
86: // vHeight));
87: // 启动播放
88: mPlayer.start();
89: }
90:
91: @Override
92: public void onCompletion(MediaPlayer mp) {
93: // mSkipVideoBtn.setText("开始游戏");
94: // Toast.makeText(this, "播放完毕!", Toast.LENGTH_SHORT).show();
95: finish();
96: }
97:
98: @Override
99: public void onClick(View v) {
100: mPlayer.stop();
101: finish();
102: }
103: }
这里最主要的就是: MediaPlayer.setDisplay(),新建一个SurfaceView,将 SurfaceView 的 SurfaceHolder 设置给 MediaPlayer 然后设置播放视频,prepare() 、 start() 就可以正常播放视频了,不过据说只支持 .mp4 .3gp,我没亲测。
起初这段源代码给我带来帮助,在感谢原作者之余他也在代码里设下了一个陷阱,作者当时估计没测试到就是!
如果你使用这段代码,在有些手机上是可以正常播放视频的,而有些手机就是不行(你只能听见声音、看不到画面),系统没给任何提示,只有2个警告信息,如下图:
于是百度“mediaPlayer错误信息”得知 35 在 native 层是这样定义的:const PVMFStatus PVMFInfoTrackDisable = 35;
于是 Google “PVMFInfoTrackDisable ” 果然有人跟我遇到同样的问题,在 stackoverflow 论坛有一篇这样的帖子:
这里不得不说下,stackoverflow 论坛能够找到你遇到的各种疑难杂症,嘿嘿!
这里面说法很多,但没找到根本原因,倒是在另外一篇帖子找到了答案:
这哥们才发现了问题的根本,大概意思就是:在 surfaceCreated 没被系统调用之前,SurfaceView 的 SurfaceHolder 是无效的,而我们一般习惯在 MediaPlayer 构造完后就给他设置数据,而我在 surfaceCreated 才调用了 mPlayer.setDisplay(holder); 这样 MediaPlayer 就没有设置显示器就开始播放了,这样自然看不到画面,而后来系统调用 surfaceCreated 的时候是于事无补的!
这也难怪看到有些人回复说第二次在播放视频的时候就可以,因为前面第一次设置了 holder 嘛!
知道了问题的根本就好办了, 在 surfaceCreated 里设置 holder,然后设置视频数据,然后调用 prepare,这样总是没问题了吧!
下面贴上我封装的一个 VideoPlayer :
1: public class VideoPlayer implements
2: MediaPlayer.OnCompletionListener,
3: SurfaceHolder.Callback,
4: OnClickListener,
5: OnErrorListener,
6: OnInfoListener,
7: OnTouchListener,
8: OnKeyListener,
9: MediaPlayer.OnPreparedListener {
10: private SurfaceView surfaceView;
11: private MediaPlayer mPlayer; // MediaPlayer对象
12: private SurfaceHolder mSurfaceHolder; // SurfaceHolder对象
13: private ViewGroup viewGroup;
14: private GameActivity gameActivity;
15: private ImageButton button;
16:
17: private boolean surfaceCreated = false;
18:
19:
20: public VideoPlayer(GameActivity gameActivity) {
21: this.gameActivity = gameActivity;
22:
23: LayoutInflater inflater = (LayoutInflater) gameActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
24: // LayoutInflater inflater = context.getLayoutInflater();
25: viewGroup = (ViewGroup) inflater.inflate(R.layout.video_player, null);
26:
27: button = (ImageButton) viewGroup.findViewById(R.id.button1);
28: button.setOnClickListener(this);
29: button.setVisibility(View.INVISIBLE);
30:
31: surfaceView = (SurfaceView) viewGroup.findViewById(R.id.surfaceView);
32: surfaceView.setOnTouchListener(this);
33: surfaceView.setOnKeyListener(this);
34: surfaceView.setFocusableInTouchMode(true);
35: surfaceView.requestFocus();
36:
37: mSurfaceHolder = surfaceView.getHolder();
38: mSurfaceHolder.addCallback(this); // 设置回调接口
39: mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // 设置为Buffer类型(播放视频&Camera预览)
40:
41: mPlayer = new MediaPlayer();
42: mPlayer.setScreenOnWhilePlaying(true);
43:
44: mPlayer.setOnPreparedListener(this);
45: mPlayer.setOnCompletionListener(this);
46: mPlayer.setOnErrorListener(this);
47: mPlayer.setOnInfoListener(this);
48: }
49:
50: public void pause() {
51: mPlayer.pause();
52: }
53:
54: public void resume() {
55: mPlayer.start();
56: }
57:
58: private int resId;
59: public void setVideo(int resId) {
60: Log.i(PobabyGame.TAG, "== setVideo ! == ");
61:
62: this.resId = resId;
63: Uri uri = Uri.parse("android.resource://"
64: + gameActivity.getPackageName()
65: + "/" + resId);
66:
67: try {
68: mPlayer.setDataSource(gameActivity, uri);
69: // mPlayer.prepare();
70: } catch (IllegalArgumentException e) {
71: } catch (SecurityException e) {
72: } catch (IllegalStateException e) {
73: } catch (IOException e) {
74: }
75: }
76:
77: public SurfaceView getSurfaceView() {
78: return surfaceView;
79: }
80:
81: public ViewGroup getViewGroup() {
82: return viewGroup;
83: }
84:
85: @Override
86: public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
87: }
88:
89: @Override
90: public void surfaceCreated(final SurfaceHolder holder) {
91: Log.i(PobabyGame.TAG, "== surfaceCreated ! == " + "holder" + holder.toString() + ", h2" + mSurfaceHolder);
92:
93: gameActivity.handler.post(new Runnable() {
94: @Override
95: public void run() {
96: mPlayer.setDisplay(holder); // 指定SurfaceHolder
97: surfaceCreated = true;
98:
99: // Uri uri = Uri.parse("android.resource://"
100: // + gameActivity.getPackageName()
101: // + "/" + resId);
102:
103: try {
104: // mPlayer.setDataSource(gameActivity, uri);
105: mPlayer.prepare();
106: } catch (IllegalArgumentException e) {
107: } catch (SecurityException e) {
108: } catch (IllegalStateException e) {
109: } catch (IOException e) {
110: }
111: }
112: });
113: }
114:
115: @Override
116: public void surfaceDestroyed(SurfaceHolder holder) {
117: surfaceCreated = false;
118: }
119:
120: @Override
121: public void onPrepared(MediaPlayer player) {
122: int wWidth = Gdx.graphics.getWidth();
123: int wHeight = Gdx.graphics.getHeight();
124:
125: /* 获得视频宽长 */
126: int vWidth = mPlayer.getVideoWidth();
127: int vHeight = mPlayer.getVideoHeight();
128:
129: /* 最适屏幕 */
130: float wRatio = (float) vWidth / (float) wWidth; // 宽度比
131: float hRatio = (float) vHeight / (float) wHeight; // 高度比
132: float ratio = Math.max(wRatio, hRatio); // 较大的比
133: vWidth = (int) Math.ceil((float) vWidth / ratio); // 新视频宽度
134: vHeight = (int) Math.ceil((float) vHeight / ratio); // 新视频高度
135:
136: // 改变SurfaceHolder大小
137: mSurfaceHolder.setFixedSize(vWidth, vHeight);
138: mPlayer.start();
139:
140: gameActivity.gameView.setVisibility(View.INVISIBLE);
141: }
142:
143: @Override
144: public void onCompletion(MediaPlayer mp) {
145: gameActivity.videoFinish(resId);
146: }
147:
148: @Override
149: public void onClick(View v) {
150: gameActivity.videoFinish(resId);
151: mPlayer.release();
152: }
153:
154: @Override
155: public boolean onInfo(MediaPlayer mp, int what, int extra) {
156: Log.i(PobabyGame.TAG, String.format("onInfo(%d, %d)", what, extra));
157: return true;
158: }
159:
160: @Override
161: public boolean onError(MediaPlayer mp, int what, int extra) {
162: Log.e(PobabyGame.TAG, String.format("onError(%d, %d)", what, extra));
163: return true;
164: }
165:
166: @Override
167: public boolean onTouch(View v, MotionEvent event) {
168: if(event.getAction() == MotionEvent.ACTION_DOWN){
169: button.setVisibility(View.VISIBLE);
170: return true;
171: }
172:
173: return false;
174: }
175:
176: @Override
177: public boolean onKey(View v, int keyCode, KeyEvent event) {
178: if(event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
179: gameActivity.videoFinish(resId);
180: mPlayer.release();
181: }
182:
183: return true;
184: }
185: }