最近项目中在调试一个问题:app端呈现摄像头画面的调整,很坑的一个问题。项目经理估计搞不定,就把这个问题扔给我了,有点坑啊。因为播放摄像头视频的控件是继承于surfaceview的,于是乎就从surfaceview入手,因为这个控件是继承于surfaceview,又稍微做了些封装,结果机智的我就直接写了如下代码:
ViewGroup.LayoutParams params = surfaceView.getLayoutParams();
params.width = 你期望的宽度;
params.height = 你期望的高度;
surfaceView.setLayoutParams(params);
写完之后心里暗自窃喜,心里在想这个问题那么简单,项目经理都搞不定,我竟然搞定了,哈哈,看来我技术要比经理好呀!于是呼,赶紧把项目编译运行了下,准备调试了。跑到大厅的门口机呼一下“5652”,马上要见证奇迹的时候了,发现视频高度是对的,可两边有阴影,有点辣眼睛啊,额头冒出了大汗。高兴心情立马就消失的无影无踪,有点伤心。
后来还是屁diandian回到了座位上,开始新一轮的百度之旅。然后认真的看了下代码,发现了一点点规律。其中源码的代码是这样写的:
int screenWidth = SDUtil.getScreenWidth();
int height= screenWidth*240/320;
//int height = screenWidth*720/1280;
ViewGroup.LayoutParams params = surfaceView.getLayoutParams();
params.height = height;
surfaceView.setLayoutParams(params);
看了一下,那行注释最亮眼了。为什么呢?因为这个肯定是项目经理做了修改嘛,道理就是这么简单,不用考虑太多。于是呼,又把这行代码打开,把它之上的那行代码注释掉,重新编译运行下,又跑到门口机那里测试了下,结果是两边有阴影,再次亮瞎了我的双眼。呜呼,怎么可以有这样结果,想不通,实在是想不通。
再一次回到座位上,思考了好久,仿佛在思考人生。
之后改了测,测了改,次数估计有几十次吧,具体次数我也记不清了,因为太认真了,哈哈。第二天开早会,项目经理叫每个人讲一下昨天的情况,轮到我了,我说昨天在研究这个视频高度的问题。项目经理说解决了吗?我呜咽了一下,有点反应迟钝啊,说还没有。项目经理说没关系,这个问题不要紧,你就当熟悉代码。我心里暗自窃喜,知道项目经理也搞不定的,他心里有底。晨会结束后,不服输的我还是坚决要解决这个问题。
机智的我又开始重新思考起来,仔细琢磨一番,发现这不是环信的API吗?忽然一道灵光从我脑海中闪过-——”从环信的SDK入手吧”。于是看了下它的api。发现有个setScalMode方法,它是用来设置视频的填充模式的,感觉终于找到你了,有点相见恨晚的感觉。于是在代码里加了这个设置方法,运行测试下,果真可以填充整个布局了,哈哈,这个问题终于解决了,好像感觉是解决了。于是向项目经理汇报了下,我解决了这个问题,项目经理兴奋地站了起来,这是真的吗?感觉他比我还开心,我都不敢相信我自己了。
第二天,项目经理有时间了,拿手机测了一下,然后截图让我比对了下手机端跟门口机端的两张照片,他说还是有点不一样,视频没有缩放,是遮住了。我的大脑仿佛被石头怦然一击,香菇蓝瘦,此处省略一万字……
最终的结果是此问题没有解决,后续再研究下环信的api吧。
不好意思,说了那么多才步入今天博客主题,实在是抱歉、抱歉、抱歉,重要的事要说三遍。
android原生播放视频的api有surfaceview、videoview、textureview等,其他的我也不一一列举了,可能我也没用过。下面就讲下surfaceview与textureview的用法吧。
1、先看下工程的布局文件(activity_main.xml)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
RelativeLayout>
这布局太简单了,就写了个surfaceview控件,没毛病,因为用的就是它啊。
2、逻辑主代码
import java.io.IOException;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.widget.Button;
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private SurfaceView surfaceview;
private SurfaceHolder surfaceHolder;
private Button btnGo;
private MediaPlayer mediaPlayer;
private int postion = 0;
private int screenWidth;
private int screenHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getScreenParams();
findViewById();
initView();
}
/**
* 获取屏幕宽高
*/
private void getScreenParams() {
DisplayMetrics dm = new DisplayMetrics();
// 获取屏幕信息
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
Log.e(TAG, "screenWidth==" + screenWidth + ",screenHeight=="
+ screenHeight);
}
protected void findViewById() {
surfaceview = (SurfaceView) findViewById(R.id.surfaceView);
}
protected void initView() {
mediaPlayer = new MediaPlayer();
surfaceHolder = surfaceview.getHolder();
surfaceHolder.setKeepScreenOn(true);
surfaceHolder.addCallback(new SurfaceViewLis());
// 调整surfaceView的大小
ViewGroup.LayoutParams params = surfaceview.getLayoutParams();
params.width = screenWidth;
params.height = screenHeight;
surfaceview.setLayoutParams(params);
}
private class SurfaceViewLis implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (postion == 0) {
try {
play();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
public void play() throws IllegalArgumentException, SecurityException,
IllegalStateException, IOException {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
AssetFileDescriptor fd = this.getAssets().openFd("video.mp4");
mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(),
fd.getLength());
mediaPlayer.setLooping(true);
mediaPlayer.setDisplay(surfaceHolder);
// 通过异步的方式装载媒体资源
mediaPlayer.prepareAsync();
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// 装载完毕回调
mediaPlayer.start();
}
});
}
}
这里主要是播放本地assets资源下的一个视频文件,用到的技术主要是SurfaceView+MediaPlayer,其实没啥技术含量,主要是用来测试控制视频的大小,发现之前的那段代码还是可以控制的
ViewGroup.LayoutParams params = surfaceView.getLayoutParams();
params.width = 你期望的宽度;
params.height = 你期望的高度;
surfaceView.setLayoutParams(params);
没错就是上面这段代码,什么情况啊?为何原生的可以控制,环信封装的那个就不行啊。哎,累呀!不想那么多了,接着看TextureView来播放视频了。
二、TextureView播放本地视频
1、布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextureView
android:id="@+id/textureview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
RelativeLayout>
2、逻辑代码主体
package com.example.textureviewdemo;
import java.io.IOException;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.view.Menu;
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
public class MainActivity extends Activity implements SurfaceTextureListener,
OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener,
OnVideoSizeChangedListener {
private TextureView mTextureView;
private MediaPlayer mMediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById();
init();
}
private void init() {
mTextureView.setSurfaceTextureListener(this);
}
private void findViewById() {
mTextureView = (TextureView) findViewById(R.id.textureview);
}
@Override
public void onVideoSizeChanged(MediaPlayer arg0, int arg1, int arg2) {
}
@Override
public void onPrepared(MediaPlayer arg0) {
}
@Override
public void onCompletion(MediaPlayer arg0) {
}
@Override
public void onBufferingUpdate(MediaPlayer arg0, int arg1) {
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int arg1,
int arg2) {
Surface s = new Surface(surface);
try {
mMediaPlayer = new MediaPlayer();
AssetFileDescriptor fd = this.getAssets().openFd("video.mp4");
mMediaPlayer.setDataSource(fd.getFileDescriptor(),
fd.getStartOffset(), fd.getLength());
mMediaPlayer.setSurface(s);
mMediaPlayer.prepare();
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnVideoSizeChangedListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setLooping(true);
mMediaPlayer.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
mMediaPlayer.stop();
return true;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,
int arg2) {
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
}
}
好了,用这两个控件播放视频的功能基本实现了。具体他们之间的区别是:1、TextureView必须在Android4.0及以上才可用;2、TextureView要比SurfaceView方便好用;3、其他
另外附上源码下载地址:
SurfaceViewDemo
TextureView播放视频Demo