zoukankan      html  css  js  c++  java
  • android surfaView surfaHolder video 播放

    主文件

    package cn.com.sxp;

    import android.app.Activity;
    import android.media.AudioManager;
    import android.media.MediaPlayer;
    import android.os.Bundle;
    import android.view.SurfaceHolder;
    import android.view.SurfaceHolder.Callback;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.ImageButton;
    import android.widget.Toast;

    public class SurfaceView4VideoActivity extends Activity implements
    View.OnClickListener {
    // 下一步要实现的
    // 1. 活动onResume之后继续以前播放点播放
    // 2. 重新运行程序时,要能从上一次运行播放的时间点开始播放




    // ImageButton:
    // Displays a button with an image (instead of text 展示的是图片而不是文本) that can be
    // pressed or clicked by the user. By default, an ImageButton looks like a
    // regular Button看起来像常规的按钮, with the standard button background that changes
    // color during different button states. The image on the surface of the
    // button
    // is defined either by the android:src attribute in the XML element or by
    // the setImageResource(int) method. 图片来源要么来自于xml文件,要么来自方法
    // To remove the standard button background image, define your own
    // background image or set the background color to be transparent.
    // To indicate the different button states (focused, selected, etc.), you
    // can define a different image for each state.可以为按钮每一个不同状态选择不同的图片
    // E.g., a blue image by default, an orange one for when focused, and a
    // yellow one for when pressed. An easy way to do this is with an XML
    // drawable
    // 提供一种图片源的方法
    // "selector." For example:
    // <?xml version="1.0" encoding="utf-8"?>
    // <selector xmlns:android="http://schemas.android.com/apk/res/android">
    // <item android:state_pressed="true"
    // android:drawable="@drawable/button_pressed" /> <!-- pressed -->
    // <item android:state_focused="true"
    // android:drawable="@drawable/button_focused" /> <!-- focused -->
    // <item android:drawable="@drawable/button_normal" /> <!-- default -->
    // </selector>
    // Save the XML file in your project res/drawable/ folder and then reference
    // it as a drawable for the source of your ImageButton (in the
    // android:src attribute). Android will automatically change the image based
    // on the state of the button and the corresponding images defined in the
    // XML.
    // The order of the elements is important because they are evaluated in
    // order. This is why the "normal" button image comes last, because it will
    // only be
    // applied after android:state_pressed and android:state_focused have both
    // evaluated false.

    private ImageButton play = null, stop = null, pause = null;
    // SurfaceView:
    // Provides a dedicated drawing surface embedded inside of a view
    // hierarchy.在view里面内嵌了一个界面,界面显示绘画. You can control the format of this
    // surface and, if
    // you like, its size; the SurfaceView takes care of placing the surface at
    // the correct location on the screen .The surface is Z ordered so that it
    // is
    // behind the window holding its SurfaceView; the SurfaceView punches a hole
    // in its window to allow its surface to be displayed. The view hierarchy
    // will
    // take care of correctly compositing with the Surface any siblings of the
    // SurfaceView that would normally appear on top of it. This can be used to
    // place
    // overlays such as buttons on top of the Surface, though note however that
    // it can have an impact on performance since a full alpha-blended composite
    // will
    // be performed each time the Surface changes.Access to the underlying
    // surface is provided via the SurfaceHolder
    // interface,通过surfaceHolder接口来控制隐藏的
    // surafce界面. which can be retrieved by calling getHolder().The Surface will
    // be created for you while the SurfaceView's window is visible; you should
    // implement surfaceCreated(SurfaceHolder) and
    // surfaceDestroyed(SurfaceHolder) to discover when the Surface is created
    // and destroyed as the window is
    // shown and hidden.One of the purposes of this class is to provide a
    // surface in which a secondary thread can render into the screen. If you
    // are going to
    // use it this way, you need to be aware of some threading semantics: All
    // SurfaceView and SurfaceHolder.Callback methods will be called from the
    // thread
    // running the SurfaceView's window (typically the main thread of the
    // application). They thus need to correctly synchronize with any state that
    // is also
    // touched by the drawing thread.You must ensure that the drawing thread
    // only touches the underlying Surface while it is valid -- between
    // SurfaceHolder.Callback.surfaceCreated() and
    // SurfaceHolder.Callback.surfaceDestroyed().
    private SurfaceView avSurface = null;

    // MediaPlayer:
    // MediaPlayer class can be used to control playback of audio/video files
    // and streams. An example on how to use the methods in this class can be
    // found in
    // VideoView. Please see Audio and Video for additional help using
    // MediaPlayer.
    private MediaPlayer mplayer = null;
    int playbackPosition = 0;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    play = (ImageButton) this.findViewById(R.id.play);
    stop = (ImageButton) this.findViewById(R.id.play);
    pause = (ImageButton) this.findViewById(R.id.play);

    // setOnClickListener方法参数是View.OnClickListener.
    // 光添加this作为参数还不行,this是一个activity,又不是view,所以在类定义出要加上View.OnClickListener,表示在该类中实现这个接口
    // 其实setOnClickListener是要添加接口,而这个接口又实现了响应方法,所以,不管是this还是什么类,只要是实现了接口中响应方法的类,都可以作为参数
    // 很多接口都作为响应函数
    stop.setOnClickListener(this);
    play.setOnClickListener(this);
    pause.setOnClickListener(this);

    mplayer = new MediaPlayer();
    avSurface = (SurfaceView) this.findViewById(R.id.sv);

    // setType(): Sets the surface's
    // type.设置成SURFACE_TYPE_PUSH_BUFFERS,表示SurfaceView本身不包含原生数据,其数据来源于其它对象,比如Camera预览,就是由Camera对象向SurfaceView提供数据。
    // SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS
    avSurface.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    // addCallback添加的是实现了onCreated等方法的类,如果在这个activity中实现,参数就是this,如果没有在这个activity中实现,就添加一个new
    // 类,这个类实现了那几个响应函数接口
    // addCallback:
    // Add a Callback interface for this holder. There can several Callback
    // interfaces associated with a holder.
    // Parameters
    // callback The new Callback interface.
    // Callback:
    // A client may implement this interface to receive information about
    // changes to the surface. When used with a SurfaceView, the Surface
    // being held is
    // only available between calls to surfaceCreated(SurfaceHolder) and
    // surfaceDestroyed(SurfaceHolder). The Callback is set with
    // SurfaceHolder.addCallback
    // method.
    avSurface.getHolder().addCallback(new Callback() {
    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    // Toast.makeText(SurfaceView4VideoActivity.class, "我被摧毁啦~~",
    // 0);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
    if (playbackPosition > 0) {
    try {
    // 开始播放
    startPlay();
    // 并直接从指定位置开始播放
    // Seeks to specified time position.参数是毫秒
    mplayer.seekTo(playbackPosition);
    playbackPosition = 0;
    } catch (Exception e) {
    // TODO: handle exception
    }
    }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format,
    int width, int height) {

    }
    });
    }

    @Override
    // onClick:
    // Called when a view has been clicked.
    // Parameters
    // v The view that was clicked.
    public void onClick(View v) {
    switch (v.getId()) {// getid号在R文件中已经定义了
    case R.id.play:
    startPlay();
    break;
    case R.id.pause:
    if (mplayer.isPlaying()) {
    // pause:
    // Pauses playback. Call start() to resume.
    mplayer.pause();
    } else {
    // start:
    // Starts or resumes playback. If playback had previously been paused, playback will continue from where it was paused. If playback had been
    // stopped, or never started before, playback will start at the beginning.
    mplayer.start();
    }
    break;
    case R.id.stop:
    if (mplayer.isPlaying()) {
    // stop:
    // Stops playback after playback has been stopped or paused.
    mplayer.stop();
    }
    break;
    default:
    break;
    }

    }

    @Override
    protected void onPause() {
    // 先判断是否正在播放
    // Checks whether the MediaPlayer is playing.
    if (mplayer.isPlaying()) {
    // 如果正在播放我们就先保存这个播放位置
    playbackPosition = mplayer.getCurrentPosition();
    // Stops playback after playback has been stopped or paused.
    mplayer.stop();
    }
    super.onPause();
    }

    private void startPlay() {
    try {
    // Resets the MediaPlayer to its uninitialized state. After calling
    // this method, you will have to initialize it again by setting the
    // data source and calling prepare(). 官方的意思就是,调用这个方法后,状态就像恢复到出厂设置一样,要1. 重新设置数据源;
    // 2. 调用prepare方法,就这两件事情
    mplayer.reset();
    // Sets the audio stream type for this MediaPlayer. See AudioManager
    // for a list of stream types. Must call this method before
    // prepare() orn prepareAsync() in order for the target stream type to become
    // effective thereafter.Parameters streamtype the audio stream type
    // 这个方法必须得在prepare方法之前调用,因为要生效
    // AudioManager: AudioManager provides access to volume and ringer振铃
    // mode control.
    // STREAM_MUSIC : The audio stream for music playback播放
    mplayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    // 设置需要播放的视频
    // Sets the data source (file-path or http/rtsp URL) to use. 支持http与rtsp两种协议
    mplayer.setDataSource("/mnt/sdcard/sxp/android/test/test.3gp");
    // 把视频画面输出到SurfaceView
    // Sets the SurfaceHolder to use for displaying the video portion of
    // the media. Either a surface holder or surface must be set if a
    // display or video sink is needed. Not calling this method or
    // setSurface(Surface) when playing back a video will result in only
    // the audio track being played.
    // A null surface holder or surface will result in only the audio
    // track being played.
    // 这个方法是用来在surfaceView上显示视频的,不调用的话就只有音频数据
    mplayer.setDisplay(avSurface.getHolder());
    // Prepares the player for playback, synchronously同步. After setting
    // the datasource and the display surface 这句话很重要, you need to either
    // call prepare() or
    // prepareAsync(). For files, it is OK to call prepare(), which
    // blocks until MediaPlayer is ready for playback.
    mplayer.prepare();
    // 播放
    // Starts or resumes playback.开始或者恢复播放. If playback had previously
    // been paused, playback will continue from where it was paused. If
    // playback had
    // been stopped, or never started before, playback will start at the
    // beginning.
    mplayer.start();
    } catch (Exception e) {
    // TODO: handle exception
    }
    }
    }

    主文件代码不多,但是有很多地方值得研究,我喜欢这么几个地方:

    1. stop.setOnClickListener(this);

        该方法就是以一个接口为参数,官方叫注册。这个接口就是实现View.OnClickListener几个破方法,而本类刚好实现了,所以本类就可以作为这个接口参数。注册什么呀,说白了,就是指定一个人,当这个按钮被点击了,这个人实现的方法就能响应这个点击动作。这个人就是本类了;

    2. sv.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 真他妈的不理解,后来上网查了,大概意思是说SurfaceView本身不包含原生数据,其数据来源于其它对象,比如Camera预览,就是由Camera对象向SurfaceView提供数据。 还算好理解;

    3. sv.getHolder().addCallback(new Callback() ; 这个灰常的经典。surfaceHolder说是控制管理surfaceView,但它拿什么控制?屎吗?还不是靠着什么surfaceDestroyed几个烂方法吗。这几个方法谁实现?显然是new Callback()这么个隐含的类实现。与第1点相同,这个类要被注册, addCallback就干的这勾当。

    4. 精华部分来了,那就是播放器的使用方法了,一个个来:

       1) reset方法。英文解释如下:

            Resets the MediaPlayer to its uninitialized state. After calling this method, you will have to initialize it again by setting the data source and calling prepare(). 官方的意思就是,调用这个方法后,状态就像恢复到出厂设置一样,要1. 重新设置数据源;2. 调用prepare方法,就这两屁件事;

       2)setAudioStreamType方法。英文解释:

            Sets the audio stream type for this MediaPlayer. See AudioManager for a list of stream types. Must call this method before prepare() or prepareAsync() in order for the target stream type to become effective thereafter.Parameters streamtype the audio stream type

            这个方法必须得在prepare方法之前调用,因为要生效

            AudioManager: AudioManager provides access to volume and ringer振铃 mode control.STREAM_MUSIC : The audio stream for music playback播放

       3) 要播放的音乐或者视频路径,还可以是http网上的某个音乐和视频,牛叉~~

       4)  setDisplay方法:把视频画面输出到SurfaceView。Sets the SurfaceHolder to use for displaying the video portion of the media. Either a surface holder or surface must be set if a display or video sink is needed. Not calling this method or setSurface(Surface) when playing back a video will result in only the audio track being played. A null surface holder or surface will result in only the audio track being played.    // 这个方法是用来在surfaceView上显示视频的,不调用的话就只有音频数据;

       5) prepare方法:Prepares the player for playback, synchronously同步. After setting the datasource and the display surface 这句话很重要, you need to either call prepare() or prepareAsync(). For files, it is OK to call prepare(), which blocks until MediaPlayer is ready for playback. 

       6)start方法,这个很简单了,Starts or resumes playback.开始或者恢复播放. If playback had previously been paused, playback will continue from where it was paused. If playback had been stopped, or never started before, playback will start at the beginning. 是吗?要测试~~

    神了,接下来要测试的几个地方:

    1. 在播放状态时,再按下播放按钮,应该从开头重新播放;

    2. 在播放状态,按下暂停,应该是暂停;再按下暂停,应该从暂停处开始播放;

    3. 在暂停状态下,按下播放,应该是从暂停处播放,而不是从开头播放;

        在暂停状态下,按下停止,应该就停止;

    4. 在停止的状态下,按下开始,应该从头开始播放;

        在停止状态下,按下暂停,应该从头开始播放;

    5. 注释掉position = mp.getCurrentPosition();看看恢复播放时从哪里播放;

    昨天上传到sdcard卡的视频,今天在运行发现没了。今天再上传时,视频上传不了,而且无法创建目录。只有重新建立新的sdcard卡。

    xml文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width
    ="fill_parent"
    android:layout_height
    ="fill_parent"
    android:orientation
    ="vertical" >

    <SurfaceView
    android:id="@+id/sv"
    android:layout_width
    ="fill_parent"
    android:layout_height
    ="300px" />

    <LinearLayout
    android:layout_width="fill_parent"
    android:layout_height
    ="wrap_content"
    android:gravity
    ="center_horizontal"
    android:orientation
    ="horizontal" >

    <ImageButton
    android:id="@+id/play"
    android:layout_width
    ="wrap_content"
    android:layout_height
    ="wrap_content"
    android:src
    ="@drawable/ic_launcher" />

    <ImageButton
    android:id="@+id/pause"
    android:layout_width
    ="wrap_content"
    android:layout_height
    ="wrap_content"
    android:src
    ="@drawable/ic_launcher" />

    <ImageButton
    android:id="@+id/stop"
    android:layout_width
    ="wrap_content"
    android:layout_height
    ="wrap_content"
    android:src
    ="@drawable/ic_launcher" />
    </LinearLayout>

    </LinearLayout>

    xml文件就没有什么好分析的,希望谷歌哥尽早出个好用的界面设计工具,那么戳的ADT简直无法忍受,但是比起我国来,还是很牛逼的~~~

  • 相关阅读:
    小程序注册
    Webpack
    npm总结1
    js事件
    js高级程序2
    js高级程序
    索引
    将数据渲染到页面的方法
    前后端分离后,通讯问题 springboot + vue
    axios post 请求后端参数为null解决方案
  • 原文地址:https://www.cnblogs.com/itblog/p/2315170.html
Copyright © 2011-2022 走看看