Android中视频播放以及解码

本博文主要介绍Android中视频的播放形式,以及Android中音视频编解码库。

一、Android中视频播放的三种方式:

        1、MediaPlayer

              如果只是播放音频,直接使用MediaPlayer即可。如果播放视频的话,则需要MediaPlayer+Surface。

              关于MdiaPlayer详解:

              1.1  MediaPlayer的实例化:两种方式:MediaPlayer mp = new MediaPlayer();//MediaPlayer只有这么一个无参的构造方法

              或者 MediaPlayer mp = MediaPlayer.create(this,R.raw.test); //此时不需要调用setDataSource()方法了

              1.2  设置要播放的文件:用户在应用中事先自带的resource资源    MediaPlayer.create(this,R.raw.test);

                                               存储在sd卡或者其他路径下的媒体文件    mp.setDataSource("/sdcard/test.mp3");

                                               网络上的媒体文件

                   MediaPlayer的setDataSource一共四个方法:
                           setDataSource (String path) 
                           setDataSource (FileDescriptor fd) 
                           setDataSource (Context context, Uri uri) 
                           setDataSource (FileDescriptor fd, long offset, long length)

                   其中使用FileDescriptor时,需要将文件放到与res文件夹平级的assets文件夹里,然后使用:
                   AssetFileDescriptor fileDescriptor = getAssets().openFd("rain.mp3");
                   mp.setDataSource(fileDescriptor.getFileDescriptor(),fileDescriptor.getStartOffset(),                              fileDescriptor.getLength());来设置datasource

             1.3 对播放器的主要控制方法

                   Android通过控制播放器的状态的方式来控制媒体文件的播放,其中:prepare()和prepareAsync() 提供了同步和异步两种方式设置播放器进入prepare状态。

                   如果是采用create的方式创建的mediaPlayer,则第一次启动播放前是不需要调用prepare方法的,因为create方法中已经调用了。

                   启动文件播放:start()

                   暂停:pause()

                   停止播放:stop()

                   定位方法:seekTo()  可以让播放器从指定的位置开始播放,需要注意的是该方法是个异步方法,也就是说该方法返回时并不意味着定位完成,尤其是播放的网络文件,真正定位完成时会触发OnSeekComplete.onSeekComplete(),如果需要是可以调用setOnSeekCompleteListener(OnSeekCompleteListener)设置监听器来处理的

                   释放资源:release()  一旦确定不再使用播放器,应尽早释放资源

                   从错误状态中恢复:reset()  

             1.4 设置播放器的监听器

                   MediaPlayer提供了一些设置不同监听器的方法来更好地对播放器的工作状态进行监听,以期及时处理各种情况,如: setOnCompletionListener(MediaPlayer.OnCompletionListener listener)、
setOnErrorListener(MediaPlayer.OnErrorListener listener)、setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
等,设置播放器时需要考虑到播放器可能出现的情况设置好监听和处理逻辑,以保持播放器的健壮性。

             1.5 MediaPlayer除了可以播放本地音视频、网络媒体文件以外,还支持rtsp实时流的播放。

                   mp.setDataSource(rtsp流地址); 

                   mp.setDisplay(surfaceView.getHolder());

             1.6 用MediaPlayer播放视频时,需要注意得在surface被创建好了后再setDisplay(surfaceView.getHolder());

可以在surfaceCreated(SurfaceHolder holder)方法中进行此操作,如下:

surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        player = new MediaPlayer();
        try {
            player.setDataSource(rtspUri);
            player.setDisplay(surfaceView.getHolder());
            player.prepare();
            player.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
});

            1.7 也可以通过MediaPlayer+TextureView实现播放视频。此时应该是mp.setSurface(surface);

           

       2、VideoView

            使用VideoView来播放。在布局文件中使用VideoView结合MediaController来实现对其控制。

            示例代码:

                           VideoView videoView = (VideoView)this.findViewById(R.id.videoView);

                           videoView.setMediaController(new MediaController(this));

                           videoView.setVideoURI(uri);

                           videoView.start();

                           videoView.requestFocus();

            当我们用VideoView播放网络视频的时候,可能需要等待一段时间,尤其当网络不好的时候。为了提高用户体验,此时我们增加缓冲条,并且通过setOnPreparedListener这个方法给VideoView设置监听。代码如下:

        videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                progressBar.setVisibility(View.GONE);
            }
        });

关于该方法的官方解释如下:

    /**
     * Register a callback to be invoked when the media file
     * is loaded and ready to go.
     *
     * @param l The callback that will be run
     */
    public void setOnPreparedListener(MediaPlayer.OnPreparedListener l)
    {
        mOnPreparedListener = l;
    }

即,注册一个回调,当媒体文件已经被加载并且准备播放时 ,这个回调被调用。

     3、调用安卓自带的播放器

            Intent intent = new Intent(Intent.ACTION_VIEW);

            intent.setDataAndType(uri,"video/mp4");

            startActivity(intent);

二、Android中音视频编解码库

       MediaCodec类可以用于访问底层媒体编解码器。它是Android底层多媒体支持基础设施的一部分。通常和MediaExtractor,MediaSync,MediaMuxer,MediaCrypto,MediaDrm,Image,Surface, AudioTrack一起使用。

工作机制示意图:

其生命周期示意图:

在项目中使用MediaCodec进行实时视频解码的代码示例:

首先,我们得创建解码器对象

MediaCodec decoder = MediaCodec.createCodecByName("OMX.google.h264.decoder");  //Uninitialized State

//这里使用的是软解

//MediaCodec既支持硬解,也支持软解

当然,也可以采用下面的代码进行实例化:MediaCodec.createDecoderByType(String);

然后,进行配置configure:

MediaFormat format = MediaFormat.createVideoFormat("video/avc",mSurfaceWidth,mSurfaceHeight);

decoder.configure(format,surface,null,0);  //Configured State 第三个参数是加密

接着,调用start()开启解码器

decoder.start();  //Executing state--->Flushed sub-state

ByteBuffer[] inputBuffers = decoder.getInputBuffers();  //这里使用的是Buffer Array的方式,这种方式在Android 5.0(API 21)以上已经过时,这里我为了向下兼容,故而采用此方式。

int index = decoder.dequeueInputBuffer(-1);  //Running sub-state

ByteBuffer buffer = inputBuffers[index];

buffer.clear();

buffer.put(array,0,array.length);  //把需要解码的数据拷贝到empty buffer

decoder.queueInputBuffer(index,0,array.length,timestamp,MediaCodec.BUFFER_FLAG_CODEC_CONFIG);  //End-of-Stream sub-stream

最后,从解码器中拿解码后的数据:

MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

int index2 = decoder.dequeueOutputBuffer(info,0);

ByteBuffer outputBuffer;

byte[] array_output;

while(index2 >= 0)

{

     ByteBuffer[] outputBuffers = decoder.getOutputBuffers();

     outputBuffer = outputBuffers[index2];  //解码后数据

     array_output = new byte[info.size];

     outputBuffer.get(array_output);

     decoder.releaseOutputBuffer(index2,true);  //返回缓冲区给解码器,并且在surface上渲染它

     index2 = decoder.dequeueOutputBuffer(info,0);

}

MediaFormat format_output = decoder.getOutputFormat();  

int color_format = format_output.getInteger(KEY_COLOR_FORMAT);  //获得解码后的数据格式

int width_format = format_output.getInteger(KEY_WIDTH);  //获得解码后的视频宽

int height_format = format_output.getInteger(KEY_HEIGHT);  //获得解码后的视频高

//得到这些后就可以实现抓取实时视频的一帧图像的功能了

最后别忘了,decoder.stop();  //Uninitialized State

decoder.release();

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android解码MJPEG格式的视频流,你可以使用以下步骤: 1. 添加必要的依赖项。在你的Android项目,添加对OpenCV的依赖项,以便使用其图像处理功能。在项目的build.gradle文件,添加以下依赖项: ```groovy implementation 'org.opencv:opencv-android:3.4.13' ``` 2. 创建一个自定义的SurfaceView类,并实现SurfaceHolder.Callback接口,以监听SurfaceView的生命周期事件。 ```java public class MJPEGSurfaceView extends SurfaceView implements SurfaceHolder.Callback { private MJPEGDecoder mjpegDecoder; public MJPEGSurfaceView(Context context) { super(context); getHolder().addCallback(this); } @Override public void surfaceCreated(SurfaceHolder holder) { // 在Surface创建时初始化解码器 mjpegDecoder = new MJPEGDecoder(holder.getSurface()); mjpegDecoder.startDecoding(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 在Surface尺寸发生变化时更新解码器 mjpegDecoder.updateSurface(holder.getSurface()); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 在Surface销毁时停止解码器 mjpegDecoder.stopDecoding(); } } ``` 3. 创建一个MJPEGDecoder类,负责接收和解码MJPEG格式的视频流。 ```java public class MJPEGDecoder { private Surface surface; private boolean isDecoding; public MJPEGDecoder(Surface surface) { this.surface = surface; isDecoding = true; } public void startDecoding() { // 开始解码线程 new Thread(new Runnable() { @Override public void run() { while (isDecoding) { // 从MJPEG视频获取数据并解码 byte[] frameData = getFrameDataFromStream(); // 使用OpenCV解码图像 Bitmap frame = decodeFrame(frameData); // 将解码后的帧绘制到Surface上 drawFrameToSurface(frame); } } }).start(); } public void updateSurface(Surface surface) { this.surface = surface; } public void stopDecoding() { isDecoding = false; } private byte[] getFrameDataFromStream() { // 从网络或其他来源获取MJPEG视频流数据 // 返回帧的字节数组 } private Bitmap decodeFrame(byte[] frameData) { // 使用OpenCV解码MJPEG帧 Mat frameMat = Imgcodecs.imdecode(new MatOfByte(frameData), Imgcodecs.IMREAD_UNCHANGED); Bitmap frameBitmap = null; if (!frameMat.empty()) { frameBitmap = Bitmap.createBitmap(frameMat.cols(), frameMat.rows(), Bitmap.Config.ARGB_8888); Utils.matToBitmap(frameMat, frameBitmap); } return frameBitmap; } private void drawFrameToSurface(Bitmap frame) { if (frame != null) { Canvas canvas = surface.lockCanvas(null); if (canvas != null) { canvas.drawBitmap(frame, 0, 0, null); surface.unlockCanvasAndPost(canvas); } } } } ``` 4. 在你的Activity使用自定义的MJPEGSurfaceView来显示MJPEG视频流。 ```java public class MainActivity extends AppCompatActivity { private MJPEGSurfaceView surfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surface_view); } } ``` 确保在布局文件添加一个MJPEGSurfaceView元素: ```xml <com.example.MJPEGSurfaceView android:id="@+id/surface_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 这样,你就可以在Android解码和显示MJPEG格式的视频流了。请确保根据你的需求和网络库进行必要的调整和配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值