我的需求是播放rtsp视频流,最开始用了Android原生播放器MediaPlayer,但是延迟太大了,感觉要将近10秒,而且不能修改缓冲大小,因为底层写死的(根据Android版本设置了固定的缓冲大小),最终用了开源的vlc

目前项目中使用vlc播放了rtsp视频流,并且有拍照,录制的功能

使用vlc播放,vlc使用的是3.0.0的版本

截图和录制需要在aar中加入Android可调用的方法

需要将aar放到libs文件下,在gradle中配置

 

 
libvlcaar

 

 
配置aar

然后添加依赖
compile(name:’libvlc-release-3′,ext:’aar’)

具体代码实现如下

final ArrayList args =new ArrayList<>();//VLC参数

args.add(“–rtsp-tcp”);//强制rtsp-tcp,加快加载视频速度

args.add(“–aout=opensles”);

args.add(“–audio-time-stretch”);

args.add(“–sub-source=marq{marquee=\”%Y-%m-%d,%H:%M:%S\”,position=10,color=0xFF0000,size=40}”);//这行是可以再vlc窗口右下角添加当前时间的

args.add(“-vvv”);

mLibVLC =new LibVLC(this, args);

mMediaPlayer =new MediaPlayer(mLibVLC);

Rect surfaceFrame =textureView.getHolder().getSurfaceFrame();

//设置vlc视频铺满布局

mMediaPlayer.getVLCVout().setWindowSize(layout_video.getWidth(), layout_video.getHeight());//宽,高  播放窗口的大小

mMediaPlayer.setAspectRatio(layout_video.getWidth()+”:”+layout_video.getHeight());//宽,高  画面大小

mMediaPlayer.setScale(0);//这行必须加,为了让视图填满布局

//添加视图

IVLCVout vout =mMediaPlayer.getVLCVout();

vout.setVideoView(textureView);

vout.attachViews();

        Uri uri = Uri.parse(Path.RTSP_URL);//rtsp流地址或其他流地址//”https://media.w3.org/2010/05/sintel/trailer.mp4″

final Media media =new Media(mLibVLC, uri);

int cache =10;

media.addOption(“:network-caching=” + cache);

media.addOption(“:file-caching=” + cache);

media.addOption(“:live-cacheing=” + cache);

media.addOption(“:sout-mux-caching=” + cache);

media.addOption(“:codec=mediacodec,iomx,all”);

mMediaPlayer.setMedia(media);//  

media.setHWDecoderEnabled(false, false);//设置后才可以录制和截屏,这行必须放在mMediaPlayer.setMedia(media)后面,因为setMedia会设置setHWDecoderEnabled为true

mMediaPlayer.play();

这样就可以使用vlc播放rtsp视频了

vlc截图

String path1 = Environment.getExternalStorageDirectory().getAbsolutePath()+”/” +”wy”;//路径最后不用/,vlc会自己补充

boolean snapshot =mMediaPlayer.getSnapshot(path1);//这个是vlc  arr里面添加的方法

//通知系統相冊刷新保持的錄屏,拍照后的图片存放在本地相册

Intent intent =new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

intent.setData(Uri.fromFile(new File(path1)));

sendBroadcast(intent);//不添加的话,相册不会更新,手机图库看不到

vlc 录制

boolean onvideo =false;//onvideo是否开始录制false是未录制

String path = Environment.getExternalStorageDirectory().getAbsolutePath()+”/” +”wy”;

                        if (!onvideo){

Thread myThread =new Thread(new Runnable() {

@Override

                                public void run() {

Log.d(“record_video”,Thread.currentThread().getName());

                                    if (!isStopThread) {

                                        onvideo =true;

                                        boolean record =mMediaPlayer.record(path);//此方法是vlc  源码添加的,开始录制

                                        Log.d(“record1″,record+””);

                                    }

}

});

                            myThread.start();

                                tv_recordvideo.setText(R.string.textvideo);

                        }else {

onvideo =false;

                            boolean record =mMediaPlayer.record(null);//结束录制,路径传null

                            Log.d(“record2″,record+””);

                            tv_recordvideo.setText(“”);

                            //通知系統相冊刷新保持的錄屏

                                    Intent intent1 =new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);

                                    intent1.setData(Uri.fromFile(new File(path)));

                                    sendBroadcast(intent1);

                        }

oppo手机出现不更新画面

E/libEGL: eglCreateWindowSurfaceTmpl:729 error 3003 (EGL_BAD_ALLOC)

 E/VLC: [c637e3b0/4338] libvlc gl: cannot create EGL window surface

 E/VLC: [c5a38530/4338] libvlc window: request 1 not implemented

 E/VLC: [bf07af30/3829] libvlc video output: video output creation failed

 E/VLC: [c645ee30/3829] libvlc decoder: failed to create video output

 E/VLC: [c637e430/4339] libvlc window: request 1 not implemented

 E/BufferQueueProducer: [SurfaceView – com.example.activity.MainActivity#0] connect: already connected (cur=1 req=1)

E/libEGL: eglCreateWindowSurface: native_window_api_connect (win=0xc5a8e808) failed (0xffffffea) (already connected to another API?)

解决方案:

android:hardwareAccelerated=”true”//在清单文件中添加了这行,开启了硬件加速,不报上面的错了

然而我用华为手机测试没有这个问题。。。。。

2021.1.25

使用vlc播放器播放smb远程共享文件中的mp4视频

Uri uri = Uri.parse(直接传入smb视频地址);

获取视频长度:

mMediaPlayer.setEventListener,设置监听

IMedia media1 =mMediaPlayer.getMedia();

media1.parseAsync();//这里同步方法拿到的是0,或者-1,我用了异步解决问题

chapter = (int)mMediaPlayer.getLength();

mediaplaye.play();//核心代码,在播放前获取视频长度

 

mMediaPlayer.setEventListener(new MediaPlayer.EventListener() {

@Override

                public void onEvent(MediaPlayer.Event event) {

//                    Log.d(“mMediaPlayer”,”event.type==”+ event.type);

                    switch (event.type){

case Opening:

Log.d(“mMediaPlayer”,”Opening==”+Opening);

break;

                        case MediaChanged:

Log.d(“mMediaPlayer”,”MediaChanged==”+MediaChanged);

break;

                        case  Buffering:

Log.d(“mMediaPlayer”,”Buffering==”+Buffering);

break;

                        case  Playing:

Log.d(“mMediaPlayer”,”Playing==”+Playing);

break;

                        case  Paused:

Log.d(“mMediaPlayer”,”Paused==”+Paused);

break;

                        case  Stopped:

Log.d(“mMediaPlayer”,”Stopped==”+Stopped);

break;

                        case  EndReached:

//                            textureView.setVisibility(View.INVISIBLE);

//                            network_layout.setVisibility(View.VISIBLE);

//                            tv_network.setText(“网络连接断开,请重试”);

                            Log.d(“mMediaPlayer”,”EndReached==”+EndReached);

break;

                        case  EncounteredError:

Log.d(“mMediaPlayer”,”EncounteredError==”+EncounteredError);

break;

                        case  TimeChanged:

Log.d(“mMediaPlayer”,”TimeChanged==”+TimeChanged);

break;

                        case  PositionChanged:

if (isPositionChanged){

handler.sendEmptyMessageDelayed(2,0);

                                isPositionChanged =false;

                            }

Log.d(“mMediaPlayer”,”PositionChanged==”+PositionChanged);

break;

                        case  SeekableChanged:

IMedia media1 =mMediaPlayer.getMedia();

                            media1.parseAsync();

                            chapter = (int)mMediaPlayer.getLength();

                            int time =chapter /1000;

                            int miao = time /60;

                            int i1 = time – (miao *60);

                            if (miao<10){

if (i1<10){

String i =”0″ + miao +”:” +”0″ + i1;

                                    pb_seek.setMax(chapter /1000);

                                    tv_pro_right.setText(i);

                                }else if (i1<60){

String i =”0″ + miao +”:” + i1;

                                    pb_seek.setMax(chapter /1000);

                                    tv_pro_right.setText(i);

                                }

}else if (miao<60){

if (i1<10){

String i = miao +”:” +”0″ + i1;

                                    pb_seek.setMax(chapter /1000);

                                    tv_pro_right.setText(i);

                                }else if (i1<60){

String i = miao +”:” + i1;

                                    pb_seek.setMax(chapter /1000);

                                    tv_pro_right.setText(i);

                                }

}

Log.d(“time”,time+””);

//                            Message message = new Message();

//                            Bundle bundle = new Bundle();

//                            bundle.putInt(“time”,chapter);

//

//                            message.setData(bundle);

//                            handler.sendMessageDelayed(message,0);

                            Log.d(“chapter”, chapter +”===”+  media1.getDuration()+”===”+mMediaPlayer.getChapter()+”===”+mMediaPlayer.getTime()+”===”+mMediaPlayer.getLength()+”===”+mMediaPlayer.getPosition()+”===”+mMediaPlayer.getTitle());

                            Log.d(“mMediaPlayer”,”SeekableChanged==”+SeekableChanged);

break;

                        case  PausableChanged:

Log.d(“mMediaPlayer”,”PausableChanged==”+PausableChanged);

break;

                        case  LengthChanged:

Log.d(“mMediaPlayer”,”LengthChanged==”+LengthChanged);

break;

                        case  Vout:

Log.d(“mMediaPlayer”,”Vout==”+Vout);

break;

                        case  ESAdded:

Log.d(“mMediaPlayer”,”ESAdded==”+ESAdded);

break;

                        case  ESDeleted:

Log.d(“mMediaPlayer”,”ESDeleted==”+ESDeleted);

break;

                        case  ESSelected :

Log.d(“mMediaPlayer”,”ESSelected==”+ESSelected);

break;

                        case  RecordChanged:

Log.d(“mMediaPlayer”,”RecordChanged==”+RecordChanged);

break;

                    }

}

});

            mMediaPlayer.play();

        }catch (Exception e){

}

handler中拿到消息设置进度条

private void setTimer(){

timer =new Timer();

        timerTask =new TimerTask() {

@Override

            public void run() {

if (mMediaPlayer !=null) {

if (mMediaPlayer.isPlaying()) {

runOnUiThread(new Runnable() {

@Override

                            public void run() {

Log.d(“totalTime”,totalTime+””);

                                pb_seek.setProgress(totalTime);

                                int i =totalTime /60;

                                if (totalTime <=chapter /1000) {

int miao =totalTime – (i *60);

                                    if (i <10) {

if (miao <10) {

tv_pro_left.setText(“0″+i+”:0″ + miao +””);

                                        }else if (miao <60) {

tv_pro_left.setText(“0″+i+”:” + miao);

                                        }

}else if (i <60) {

if (miao <10) {

tv_pro_left.setText(i+”:0″ + miao +””);

                                        }else if (miao <60) {

tv_pro_left.setText(i+”:” + miao);

                                        }

}

}else {

return;

                                }

totalTime =totalTime +1;

                            }

});

                    }

}

}

};

        timer.schedule(timerTask, 0, 1000);

    }

我的progress布局

<androidx.constraintlayout.widget.ConstraintLayout

    app:layout_constraintHorizontal_chainStyle=”spread_inside”

    android:layout_width=”match_parent”

    android:layout_height=”wrap_content”

    app:layout_constraintBottom_toBottomOf=”parent”

    tools:ignore=”MissingConstraints”>

        app:layout_constraintHorizontal_weight=”1″

        android:layout_marginLeft=”@dimen/dp_3″

        android:id=”@+id/tv_pro_left”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        app:layout_constraintLeft_toLeftOf=”parent”

        android:text=”00:00″/>

        style=”?android:attr/progressBarStyleHorizontal”

        android:layout_gravity=”center_horizontal”

        android:layout_marginTop=”10dp”

        app:layout_constraintHorizontal_weight=”4″

        android:id=”@+id/pb_seek”

        app:layout_constraintRight_toLeftOf=”@id/tv_pro_right”

        app:layout_constraintLeft_toRightOf=”@id/tv_pro_left”

        android:layout_width=”@dimen/dp_300″

        android:layout_height=”wrap_content”

        tools:ignore=”MissingConstraints” />

        app:layout_constraintHorizontal_weight=”1″

        android:layout_marginRight=”@dimen/dp_3″

        app:layout_constraintRight_toRightOf=”parent”

        android:id=”@+id/tv_pro_right”

        android:layout_width=”wrap_content”

        android:layout_height=”wrap_content”

        android:text=”00:00″/>

</androidx.constraintlayout.widget.ConstraintLayout>

效果图:

 
效果图

在activity中关闭资源,避免oom

private void cancelTimer(){

if (timer !=null) {

timer.cancel();

        timer =null;

    }

if (timerTask !=null) {

timerTask.cancel();

        timerTask =null;

    }

}

public void releasePlayer() {

// mMediaPlayer.setVideoCallback(null, null);

    if (mMediaPlayer!=null){

mMediaPlayer.stop();

        mMediaPlayer.setEventListener(null);

    }

if (vout!=null){

vout.detachViews();

  }

if (mLibVLC!=null){

mLibVLC.release();

        mLibVLC =null;

    }

}

目前还有一个问题,就是录制视频的时候要讲当前时间录制进去,我的vlc窗口已经添加了时间,知道的大神可以告知一下。

 

作者:怪咖小青年_56e8
链接:https://www.jianshu.com/p/4e3d49e5da42
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。