Android开发 海康威视 多路视频播放(同时播放视频)
原文地址:Android开发 海康视频 多路视频播放 | Stars-One的杂货小窝
最近公司有个项目需要对接到海康监控摄像头来实现对应的实时播放和回放,但这两个不是我们今天要讨论的重点,APP首页,需要实现同时播放两个视频,全网搜集了下,都没有找到相关资源,于是便是自己研究,最终也是成功实现了功能
注:本文是基于海康视频SDK的demo项目进行功能的增加,默认各位研究阅读了海康SDK文档及已成功运行demo程序的前提下
效果图
首先放下效果图吧
上面的右边即是同时播放了两个视频,两个视频都是一个Fragment,然后各自放在了一个FrameLayout里面
实现
海康设备官方的demo中,是使用了Activity来实现视频播放的功能,但是由于我们这边需要播放多个,页面可以复用一个,只是传的相关参数不同,所以,需要先稍微改造一下官方的那个Activity的demo,改为Fragment
代码比较简单,都是基于官方的demo改了下,相信各位应该可以看懂
布局里只有个SurfaceView,然后需要配置下
Fragment需要在
onViewCreated()方法里设置
SurfaceView的配置选项
Fragment提供了个
startVideo()的方法,需要传递对应的设备参数进来即可实现播放
startVideo()这里的参数实体类是我自己定义的,各位可以看着改动下,具体是在
initVideoSdk()方法 中进行取值
布局代码:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:background="#021132" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <SurfaceView android:visibility="gone" android:id="@+id/svVideo" android:layout_width="match_parent" android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
注:源码里代码直接拷贝无法直接使用,需要各位看下然后稍微改动下,使用了EventBus,不需要的可以删除,然后就是视频播放的参数调整应该就没有啥问题了
使用
我这里是使用了两个FrameLayout,将Fragment设置了进去,即首页的右下角,两个FrameLayout是平分了width,我这里是直接使用了ConstraintLayout约束布局加辅助线来实现,代码如下所示
<androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/videoView" android:visibility="invisible" android:orientation="horizontal" android:layout_width="200dp" android:layout_height="200dp"> <FrameLayout android:id="@+id/fl1" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/gl" android:layout_width="0dp" android:layout_height="match_parent" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/gl" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintGuide_percent="0.5"/> <FrameLayout android:id="@+id/fl2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/gl" android:layout_width="0dp" android:layout_height="match_parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
使用的话,只需要创建new一个Fragment,之后将其添加到FrameLayout中
//初始化两个fragment for (int i = 0; i < 2; i++) { VideoPreviewFragment videoPreviewFragment = new VideoPreviewFragment(); videoPreviewFragments.add(videoPreviewFragment); if (i == 0) { FragmentUtils.add(getSupportFragmentManager(), videoPreviewFragment, R.id.fl1, "fragment" + i); } else { FragmentUtils.add(getSupportFragmentManager(), videoPreviewFragment, R.id.fl2, "fragment" + i); } }
然后再适应的时机,调用
startVideo(),传入对应的参数即可
Fragment的使用说明可以参考这一篇Android开发——Fragment的简单使用总结 - Stars-one - 博客园,这里不再过多赘述
首页的补充说明
首页其实底下是个WebView,然后右下角的是固定悬浮在上面的,由H5那边进行计算,将对应的坐标和长宽传了过来,由APP这边去设置View的宽高
设置View的宽高和大小(是以单位px):
private void setMargins(View v, int l, int t, int width, int height) { if (v.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) { ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); p.setMargins(l, t, 0, 0); p.height = height; p.width = width; v.requestLayout(); } }
使用的时候,需要改变View的显示和隐藏,如下代码
//要先隐藏,更改尺寸,再显示,更改尺寸才起作用 videoView.setVisibility(View.GONE); setMargins(videoView, event.getLeft(), event.getTop(), event.getWidth(), event.getHeight()); videoView.setVisibility(View.VISIBLE);
源码
VideoPreviewFragment源码package com.tyky.monitorboard.activity; import android.graphics.PixelFormat; import android.os.Bundle; import android.view.LayoutInflater; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import com.blankj.utilcode.util.ToastUtils; import com.hikvision.netsdk.NET_DVR_PREVIEWINFO; import com.socks.library.KLog; import com.tyky.monitorboard.R; import com.tyky.monitorboard.control.DevManageGuider; import com.tyky.monitorboard.control.SDKGuider; import com.tyky.monitorboard.event.ShowVideoViewEvent; import com.tyky.monitorboard.model.BaseVideoChannel; import com.tyky.monitorboard.utils.HkVideoHelper; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; /** * 视频实时预览 */ public class VideoPreviewFragment extends Fragment { private SurfaceView surfaceView; private int m_iPreviewHandle = -1; // playback private int m_iSelectChannel = 1; //0 main_stream 1 sub_stream 2 third_stream private int m_iSelectStreamType = 0; private int m_iUserID = -1; // return by NET_DVR_Login_v30 private BaseVideoChannel baseVideoChannel; private boolean isDeviceLogin; public VideoPreviewFragment() { } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { surfaceView = view.findViewById(R.id.svVideo); configSurface(); } public void startVideo(BaseVideoChannel baseVideoChannel) { if (this.baseVideoChannel != null) { return; } this.baseVideoChannel = baseVideoChannel; initVideoSdk(); //自动开始播放 new Thread(() -> { try { //稍微等待5s 视频播放的资源初始化 Thread.sleep(2*1000); videoPlay(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_video_preview, container, false); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); EventBus.getDefault().register(this); } private void initVideoSdk() { //String deviceName = "公司的"; //String ip = "192.9.11.72"; //int port = 8000; //String userName = "admin"; //String pwd = "tyky_1234"; //m_iSelectChannel = 1; //m_iSelectStreamType = 1; String deviceName = baseVideoChannel.getName(); String ip = baseVideoChannel.getIpAddress(); int port = Integer.parseInt(baseVideoChannel.getPort()); String userName = baseVideoChannel.getUserName(); String pwd = baseVideoChannel.getPwd(); m_iSelectChannel = baseVideoChannel.getChannel(); m_iSelectStreamType = Integer.valueOf(baseVideoChannel.getStream()); //设备登录 isDeviceLogin = HkVideoHelper.deviceLogin(deviceName, ip, port, userName, pwd, true); } /** * 初始化surface */ private void configSurface() { surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); if (-1 == m_iPreviewHandle) { return; } Surface surface = surfaceHolder.getSurface(); if (surface.isValid()) { if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni(m_iPreviewHandle, 0, surfaceHolder)) { ToastUtils.showShort("NET_DVR_PlayBackSurfaceChanged" + SDKGuider.g_sdkGuider.GetLastError_jni()); } } } @Override public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { if (-1 == m_iPreviewHandle) { return; } if (surfaceHolder.getSurface().isValid()) { if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni(m_iPreviewHandle, 0, null)) { ToastUtils.showShort("NET_DVR_RealPlaySurfaceChanged" + SDKGuider.g_sdkGuider.GetLastError_jni()); } } } }); surfaceView.setZOrderOnTop(true); } @Override public void onDestroy() { videoStop(); EventBus.getDefault().unregister(this); super.onDestroy(); } @Subscribe(threadMode = ThreadMode.MAIN) public void showVideoView(ShowVideoViewEvent event){ int type = event.getType(); if (type == 1) { //显示 surfaceView.setVisibility(View.VISIBLE); } if (type==0) { //隐藏 surfaceView.setVisibility(View.INVISIBLE); } } /** * 开始播放 */ private void videoPlay() { if (!isDeviceLogin) { ToastUtils.showShort("视频设备连接失败,请检查视频设备配置!"); return; } KLog.e("--test","视频开始播放"); //当前已连接的设备 ArrayList<DevManageGuider.DeviceItem> devList = SDKGuider.g_sdkGuider.m_comDMGuider.getDevList(); if (devList.size() > 0) { DevManageGuider.DeviceItem deviceInfo = devList.get(0); m_iUserID = deviceInfo.m_lUserID; } if (m_iPreviewHandle != -1) { SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(m_iPreviewHandle); } NET_DVR_PREVIEWINFO struPlayInfo = new NET_DVR_PREVIEWINFO(); struPlayInfo.lChannel = m_iSelectChannel; struPlayInfo.dwStreamType = m_iSelectStreamType; //bBlocked 0:非阻塞取流 1:阻塞取流 struPlayInfo.bBlocked = 1; struPlayInfo.hHwnd = surfaceView.getHolder(); m_iPreviewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni(m_iUserID, struPlayInfo, null); if (m_iPreviewHandle < 0) { ToastUtils.showShort("播放失败,原因::" + SDKGuider.g_sdkGuider.GetLastError_jni()); return; } ToastUtils.showShort("开始播放"); } /** * 停止播放 */ private void videoStop() { if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(m_iPreviewHandle)) { ToastUtils.showShort("NET_DVR_StopRealPlay m_iPreviewHandle:" + m_iPreviewHandle + " error:" + SDKGuider.g_sdkGuider.GetLastError_jni()); return; } m_iPreviewHandle = -1; ToastUtils.showShort("停止播放"); } }HkVideoHelper
public class HkVideoHelper { /** * 新增视频设备 * * @param isInsertInDb 是否将数据插入数据库 */ public static boolean deviceLogin(String devName, String ip, int port, String userName, String pwd, boolean isInsertInDb) { DevManageGuider.DeviceItem deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.new DeviceItem(); deviceItem.m_szDevName = devName; deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.new DevNetInfo( ip, port + "", userName, pwd); if (deviceItem.m_szDevName.isEmpty()) { deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp; } if (SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna(deviceItem.m_szDevName, deviceItem.m_struNetInfo)) { KLog.d("--HkVideoHelper", "设备连接成功"); return true; } else { KLog.d("--HkVideoHelper", "失败"); return false; } } }
- 关于android视频播放开发中 播放视频只有声音没有图像的问题解决方案
- Android开发之使用VideoView播放视频
- 同时RTSP_RTMP_HTTP_HLS_视频文件播放的Android安卓全功能播放器EasyPlayerPro
- Android开发 之 videoview循环播放视频
- ios开发视频播放后台下载功能实现 :1,ios播放视频 ,包含基于AVPlayer播放器,2,实现下载,iOS后台下载(多任务同时下载,单任务下载,下载进度,下载百分比,文件大小,下载状态)(真机调试功能正常)
- 关于android开发中使用VideoView切换视频源时同时改变大小会出现下一个视频第一帧为上一个视频最后显示帧问题解决
- android中的Mediaplayer------可以同时打开多个视频播放--------------
- Android开发之播放RTSP协议视频流
- android 视频开发 集成vitamio 播放rtsp m3u8 视频 有图有demo
- Android 音视频开发(三):使用 AudioTrack 播放PCM音频
- Android开发之初探视频的播放
- android开发视频播放相关使用
- android开发视频播放器------Vitamio第一个例子 播放url类型文件
- Android视频播放项目总结之 使用第三方Vitamio库,开发万能播放器(二)
- Android视频播放的开发资源
- [置顶] 【Android】Android开发实现进度条效果,SeekBar的简单使用。音量,音乐播放进度,视频播放进度等
- 【Android开发VR实战】二.播放360°全景视频(自用笔记)
- Android开发——播放视频
- [置顶] Android开发实战使用(VR技术实现360°全景视频播放功能)
- android开发SurfaceView+MediaPlayer实现视频播放