Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/yangkun19921001/memoryfilewritebytesyuv
解决执法仪本地录制和第三方软件同时使用摄像头资源不被占用的问题
https://github.com/yangkun19921001/memoryfilewritebytesyuv
Last synced: about 2 months ago
JSON representation
解决执法仪本地录制和第三方软件同时使用摄像头资源不被占用的问题
- Host: GitHub
- URL: https://github.com/yangkun19921001/memoryfilewritebytesyuv
- Owner: yangkun19921001
- Created: 2019-06-01T16:05:31.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2019-08-27T09:22:00.000Z (over 5 years ago)
- Last Synced: 2023-10-20T23:54:43.196Z (about 1 year ago)
- Language: Java
- Homepage:
- Size: 5.51 MB
- Stars: 19
- Watchers: 5
- Forks: 11
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
1. ## 执法仪相机视频流传输协议说明
### 简介说明
由于项目需求,需要在执法仪本地录像的时候,执法软件能正常的使用设备本身的 Camera 资源。由于 Android 系统自身不允许多个软件同时使用 Camera 资源,故开发一套**内存共享**子码流传输协议,当执法软件需要视频流的时候,向执法仪设备请求往 MemoryFile 中写入 YUV 格式的视频流,执法软件每隔一段时间循环的去指定的内存中 read YUV 视频流。
[完整代码入口]()
### 流程图
[![cVXYm.png](https://storage1.cuntuku.com/2019/08/27/cVXYm.png)](https://cuntuku.com/image/cVXYm)
### 效果图
- 本地相机未打开
![]()
[效果图未显示请点击这里]()- 本地相机打开
![]()
[效果图未显示请点击这里](https://ws3.sinaimg.cn/large/005BYqpgly1g3m75vcjohg30b60mw1l5.jpg?referrer=https://cdn.sinaimg.cn.52ecy.cn)### 简要流程及示例代码说明
1. 执法仪服务端收到客户端的开启视频流写入内存的指令
```java /**
//处理客服端发送过来的需要子码流的数据
private void onHandleAction(Context context, Intent intent) {
switch (intent.getAction()) {
/**
* 需要子码流
*/
case Constants.ACTION_CAMERE_CORE_SHOW:
//如果正在发送视频流,就不需要执行后面代码了
if (!MemoryFileServiceManager.getInsta(context).isSendVideoFrame())
MemoryFileServiceManager.getInsta(context).setSendVideoFrame(true, intent);
break;
}
}
```
2. 服务端获取开启视频的条件
```java
private void sendVideoFrame(Intent intent) {
if (intent != null && intent.getExtras() != null) {
//获取需要预览的宽
Constants.PREVIEWHEIGHT = extras.getInt(Constants.Config.PREVIEW_WIDTH, 1280);
//获取需要预览的高
Constants.PREVIEWHEIGHT = extras.getInt(Constants.Config.PREVIEW_HEIGHT, 720);
//需要绑定对方服务的进程
Constants.BIND_OTHER_SERVICE_PCK = extras.getString(Constants.Config.BIND_OTHER_SERVICE_PCK, "");
//需要绑定对方服务的全路径
Constants.BIND_OTHER_SERVICE_CLASS = extras.getString(Constants.Config.BIND_OTHER_SERVICE_CLASS, "");
//需要绑定对方广播的进程
Constants.BIND_OTHER_BROADCAST_PCK = extras.getString(Constants.Config.BIND_OTHER_BROADCAST_PCK, "");
//需要绑定对方广播的全路径
Constants.BIND_OTHER_BROADCAST_CLASS = extras.getString(Constants.Config.BIND_OTHER_BROADCAST_CLASS, "");
//需要开启 Camera ID 的前置还是后置 0:后置 1:前置
Constants.CAMERA_ID = extras.getInt(Constants.Config.CAMERA_ID, 0);
}
}
```3. 服务器是否开启相机,如果已经开启则不需要开启
```java
//是否开启摄像头
if (mCamera == null)
openCamera();
```4. 服务端初始化一块内存,用于写入 YUV 视频流。
```java
mMemoryFile = initMemoryFile(Constants.MEMORY_FILE_NAME, Constants.MEMORY_SIZE);
```5. 绑定对方服务,提供文件描述符号
```java
/**
* 绑定对方服务,提供 文件描述符
*/
private void bindOtherService() {
try {
if (TextUtils.isEmpty(Constants.BIND_OTHER_SERVICE_PCK) || TextUtils.isEmpty(Constants.BIND_OTHER_SERVICE_CLASS))
throw new NullPointerException("PCK or CLSS is null ?");
Intent intent = new Intent();
ComponentName cmp = new ComponentName(Constants.BIND_OTHER_SERVICE_PCK, Constants.BIND_OTHER_SERVICE_CLASS);
intent.setComponent(cmp);
context.bindService(intent, mCameraServiceConnection, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
```6. 绑定对方服务成功,交于文件描述符 ParcelFileDescriptor
```java
mCameraService = ICameraCoreService.Stub.asInterface(binder);
if (mMemoryFile != null) {
try {
//反射拿到文件描述符号
mParcelFileDescriptor = MemoryFileHelper.getParcelFileDescriptor(mMemoryFile);
if (mParcelFileDescriptor != null) {
mCameraService.addExportMemoryFile(mParcelFileDescriptor, Constants.PREVIEWWIDTH, Constants.PREVIEWHEIGHT, Constants.MEMORY_SIZE);
}
```7. 发送数据,当标志位为 byte[0] == 0 代表服务端可将 YUV 写入内存, == 1 ,代表客服端可以读取可用的 YUV 数据。
```java
/**
* 读标志位 写入视频流
*
* @param memoryFile
*/
public void writeBytes(MemoryFile memoryFile) {
try {
if (mYUVQueue.size() > 0) {
BufferBean mBufferBean = new BufferBean(Constants.BUFFER_SIZE);
//读取标志符号
memoryFile.readBytes(mBufferBean.isCanRead, 0, 0, 1);
//当第一位为 0 的时候,代表客服端已经读取了,可以正常将视频流写入内存中
if (mBufferBean.isCanRead[0] == 0) {
//拿到视频流
byte[] video = mYUVQueue.poll();
if (video != null)
//将视频流写入内存中
memoryFile.writeBytes(video, 0, 0, video.length);
//标志位复位,等待客服端读取视频流
mBufferBean.isCanRead[0] = 1;
memoryFile.writeBytes(mBufferBean.isCanRead, 0, 0, 1);
} else {
Log.d(TAG, "readShareBufferMsg isCanRead:" + mBufferBean.isCanRead[0] + ";length:"
+ mBufferBean.mBuffer.length);
}
}
} catch (IOException e) {
e.printStackTrace();
sendBroadcast(Constants.ACTION_FEEDBACK, e.getMessage());
}
}
```8. 开启成功或者失败等其他错误消息反馈给客服端
```java
/**
* 把错误或者成功的消息反馈给客服端
* @param action
* @param content
*/
public void sendBroadcast(String action, String content) {
Intent intent = new Intent();
intent.setAction(action);
ComponentName componentName = new ComponentName(Constants.Config.BIND_OTHER_BROADCAST_PCK,
Constants.Config.BIND_OTHER_BROADCAST_CLASS);
intent.setComponent(componentName);
Bundle extras = new Bundle();
extras.putString(Constants.ACTION_FEEDBACK_CONTENT, content);
intent.putExtras(extras);
context.sendBroadcast(intent);
}
```9. 收到来至客服端停止写入 YUV 的命令
```java
private void onHandleAction(Context context, Intent intent) {
switch (intent.getAction()) {
/**
* 不需要子码流
*/
case Constants.ACTION_CAMERE_CORE_HIDE:
MemoryFileServiceManager.getInsta(context).setSendVideoFrame(false, intent);
break;
}
}```