zoukankan      html  css  js  c++  java
  • android之VideoView和视频播放View的扩展

    1.概念及扩展

      VideoView 是android 系统提供的一个媒体播放显示和控制的控件。其结构层次如下:

      原型:VideoView extends SurfaceView implements MediaController.MediaPlayerControl

      类结构:

          java.lang.Object
            ↳ android.view.View
              ↳ android.view.SurfaceView
                ↳ android.widget.VideoView

      通过VideoView 的原型可知:如果构建更为复杂和有特色个性的视频View,需要继承SurfaceView 和实现MediaPlayerControl接口其中SurfaceView 为显示提供支持,MediaPlayerControl则为媒体控制提供了支持。

    2.案例

    1)VideoView案例

    (我们没有管理MediaPalyer的各种状态,这些状态都让VideoView给封装了,并且,当VideoView创建的时候,MediaPalyer对象将会创建,当VideoView对象销毁的时候,MediaPlayer对象将会释放。)

    布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height
    ="fill_parent">
    <VideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent"
    android:layout_centerInParent
    ="true" />
    </LinearLayout>

    主程序:

    public class VideoPlayer extends Activity implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
    public static final String TAG = "VideoPlayer";
    private VideoView mVideoView;
    private Uri mUri;
    private int mPositionWhenPaused = -1;

    private MediaController mMediaController;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    //Set the screen to landscape.
    this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    mVideoView = (VideoView)findViewById(R.id.video_view);

    //Video file
    mUri = Uri.parse(Environment.getExternalStorageDirectory() + "/1.3gp");

    //Create media controller,组件可以控制视频的播放,暂停,回复,seek等操作,不需要你实现
    mMediaController = new MediaController(this);
    mVideoView.setMediaController(mMediaController);
    }

    public void onStart() {
    // Play Video
    mVideoView.setVideoURI(mUri);
    mVideoView.start();

    super.onStart();
    }

    public void onPause() {
    // Stop video when the activity is pause.
    mPositionWhenPaused = mVideoView.getCurrentPosition();
    mVideoView.stopPlayback();

    super.onPause();
    }

    public void onResume() {
    // Resume video player
    if(mPositionWhenPaused >= 0) {
    mVideoView.seekTo(mPositionWhenPaused);
    mPositionWhenPaused = -1;
    }

    super.onResume();
    }

    public boolean onError(MediaPlayer player, int arg1, int arg2) {
    return false;
    }

    public void onCompletion(MediaPlayer mp) {
    this.finish();
    }
    }

    2)自定义VideoView

    和VideoView实现类似,继承了SurfaceView并且实现了MediaPlayerControl。

    public class CustomerVideoView extends SurfaceView implements 
    MediaPlayerControl {
    private static String TAG = "customer.videoplayer";
    private boolean pause;
    private boolean seekBackward;
    private boolean seekForward;
    private Uri videoUri;
    private MediaPlayer mediaPlayer;
    private Context context;
    private OnPreparedListener onPreparedListener;
    private int videoWidth;
    private int videoHeight;
    private MediaController mediaController;
    protected SurfaceHolder surfaceHolder;
    private Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
    public void surfaceChanged(SurfaceHolder holder, int format, int w,
    int h) {
    }
    public void surfaceCreated(SurfaceHolder holder) {
    surfaceHolder = holder;
    if (mediaPlayer != null) {
    mediaPlayer.setDisplay(surfaceHolder);
    resume();
    } else {
    openVideo();
    }
    }
    public void surfaceDestroyed(SurfaceHolder holder) {
    surfaceHolder = null;
    if (mediaController != null) {
    mediaController.hide();
    }
    release(true);
    }
    };
    private void release(boolean cleartargetstate) {
    if (mediaPlayer != null) {
    mediaPlayer.reset();
    mediaPlayer.release();
    mediaPlayer = null;
    }
    }
    public void resume() {
    if (surfaceHolder == null) {
    return;
    }
    if (mediaPlayer != null) {
    return;
    }
    openVideo();
    }
    public CustomerVideoView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.context = context;
    this.initVideoView();
    }
    public CustomerVideoView(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.context = context;
    this.initVideoView();
    }
    public CustomerVideoView(Context context) {
    super(context);
    this.context = context;
    this.initVideoView();
    }
    @Override
    public boolean canPause() {
    return this.pause;
    }
    @Override
    public boolean canSeekBackward() {
    return this.seekBackward;
    }
    @Override
    public boolean canSeekForward() {
    return this.seekForward;
    }
    @Override
    public int getBufferPercentage() {
    return 0;
    }
    @Override
    public int getCurrentPosition() {
    return mediaPlayer!=null?mediaPlayer.getCurrentPosition():0;
    }
    @Override
    public int getDuration() {
    return mediaPlayer!=null?mediaPlayer.getDuration():0;
    }
    @Override
    public boolean isPlaying() {
    return false;
    }
    @Override
    public void pause() {
    }
    @Override
    public void seekTo(int mSec) {
    }
    @Override
    public void start() {
    }
    public void setVideoURI(Uri uri) {
    this.videoUri = uri;
    openVideo();
    requestLayout();
    invalidate();
    }
    private void openVideo() {
    this.mediaPlayer = new MediaPlayer();
    try {
    this.mediaPlayer.setDataSource(this.context, this.videoUri);
    } catch (Exception e) {
    Log.e(TAG, e.getMessage());
    throw new RuntimeException(e);
    }
    this.mediaPlayer.prepareAsync();
    this.mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    this.mediaPlayer.setOnPreparedListener(onPreparedListener);
    attachMediaController();
    }
    private void attachMediaController() {
    if (mediaPlayer != null && mediaController != null) {
    mediaController.setMediaPlayer(this);
    View anchorView = this.getParent() instanceof View ? (View) this
    .getParent() : this;
    mediaController.setAnchorView(anchorView);
    mediaController.setEnabled(true);
    }
    }
    public void setMediaController(MediaController controller) {
    if (mediaController != null) {
    mediaController.hide();
    }
    mediaController = controller;
    attachMediaController();
    }
    public void setOnPreparedListener(OnPreparedListener onPreparedListener) {
    this.onPreparedListener = onPreparedListener;
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width = getDefaultSize(videoWidth, widthMeasureSpec);
    int height = getDefaultSize(videoHeight, heightMeasureSpec);
    if (videoWidth > 0 && videoHeight > 0) {
    if (videoWidth * height > width * videoHeight) {
    height = width * videoHeight / videoWidth;
    } else if (videoWidth * height < width * videoHeight) {
    width = height * videoWidth / videoHeight;
    }
    }
    Log.i(TAG, "setting size: " + width + ‘x’ + height);
    setMeasuredDimension(width, height);
    }
    private void initVideoView() {
    videoWidth = 0;
    videoHeight = 0;
    getHolder().addCallback(surfaceHolderCallback);
    getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    setFocusable(true);
    setFocusableInTouchMode(true);
    requestFocus();
    }
    }

      一般情况下,android界面的绘制和更新,要交给主ui线程来操作,通过Handler机制。但是播放视频,需要比较优先和实时的改变和绘制界面。android提供了使用单独线程绘制UI的机制,就是SurfaceView。使用SurfaceView,需要实现SurfaceHolder.Callback接口:

    • surfaceCreated,在Surface(SurfaceView内部包含一个Surface实例)创建后,会立即调用该方法,可在该方法中做绘制界面相关的初始化工作;
    • surfaceChanged,当Surface的状态发生变化,比如大小,会调用该方法,在surfaceCreated方法调用过至少会调用一次该方法;
    • surfaceDestroyed,当销毁Surface的时候调用。

      开发者不能直接操作Surface实例,要通过SurfaceHandler,在SurfaceView中可以通过getHandler方法获取到SurfaceHandler实例。
    SurfaceHander有一些类型,用来标识Surface实例界面数据来源,可以通过setType来操作:

    • SURFACE_TYPE_NORMAL:RAM缓存的原生数据
    • SURFACE_TYPE_HARDWARE:通过DMA,direct memory access,就是直接写屏技术获取到的数据,或者其他硬件加速的数据
    • SURFACE_TYPE_GPU:通过GPU加速的数据
    • SURFACE_TYPE_PUSH_BUFFERS:标识数据来源于其他对象,比如照相机,比如视频播放服务器(android内部有视频播放的服务器,所有播放视频相当于客户端)


      CustomerVideoView的构造方法,使用超类的构造方法。都会执行initVideoView()方法用来初始化界面和参数。另外一个主要的内容是openVideo()方法:

    • mediaPlayer.prepareAsync(),用来异步准备播放,另外还有个prepare()方法,是同步的,也就是全部下载完毕才能播放,显然,在播放网上视频的时候需要用前者;
    • 通过attachMediaController()方法,把控制条附加到播放视频的SurfaceView上,这里实现的不完全,因此还不能使用,仅仅是把MediaPlayerControl实例通过setMediaPlayer方法设置一下,供OnPreparedListener用来得到加载成功的回调,另外供外面代码调用得到视频的时长和当前时长。
    •  --------------------------------------------------------------------

      PS: 欢迎关注公众号"Devin说",会不定期更新Java相关技术知识。

      --------------------------------------------------------------------

  • 相关阅读:
    群资料共享
    python 智能合约日志操作
    canvas绘制图片
    rgb随机变色
    直接用css生成三角形的问题
    纯css三层侧边栏效果
    清除浮动终极版本
    懒加载
    html5可以通用的几段代码
    jquery中animate()动画方法
  • 原文地址:https://www.cnblogs.com/devinzhang/p/2338125.html
Copyright © 2011-2022 走看看