Android使用SurfaceView和TextureView来播放视频

最近项目中在调试一个问题: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的用法吧。

一、surfaceview播放视频

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

猜你喜欢

转载自blog.csdn.net/wufeiqing/article/details/74139767
今日推荐