package com.runt.live.media
|
|
import android.content.Context
|
import android.content.Intent
|
import android.graphics.Point
|
import android.hardware.usb.UsbConstants
|
import android.hardware.usb.UsbDevice
|
import android.util.Log
|
import android.view.SurfaceHolder
|
import com.jiangdg.usb.USBMonitor
|
import com.jiangdg.utils.HandlerThreadHandler
|
import com.jiangdg.uvc.UVCCamera
|
import com.runt.live.cpp.LiveMiniView
|
import com.runt.live.cpp.LiveMiniView.mainStreamCode
|
import com.runt.live.data.StreamWindow
|
import com.runt.live.data.UVCMonitor
|
import com.runt.live.enum.ErrorCode
|
import com.runt.live.enum.LiveState
|
import com.runt.live.ui.stream.LiveLayoutView
|
import java.util.Date
|
import kotlin.concurrent.thread
|
|
|
/**
|
* @author Runt(qingingrunt2010@qq.com)
|
* @purpose usb相机,视频采集卡等
|
* @date 3/6/25
|
*/
|
class UVCHelper : USBHelper {
|
|
data class BytesData(val bytes:ByteArray,val timestamp : Long)
|
|
var sync = Object();
|
var uvcMonitors = HashMap<String,UVCMonitor>()
|
val usbMonitor : USBMonitor
|
constructor(context : Context) : super(context) {
|
usbMonitor = USBMonitor(mContext , onDeviceConnectListener);
|
}
|
|
|
val onDeviceConnectListener = object : USBMonitor.OnDeviceConnectListener {
|
override fun onAttach(device : UsbDevice?) {
|
}
|
|
override fun onDetach(p0 : UsbDevice?) {
|
}
|
|
override fun onConnect(device : UsbDevice? , ctrlBlock : USBMonitor.UsbControlBlock? , createNew : Boolean) {
|
Log.d(TAG , "Requesting onConnect " + uvcMonitors.contains(device!!.deviceName))
|
Log.d(TAG , "Requesting onConnect " + device!!.deviceName)
|
Log.d(TAG , "Requesting onConnect " + uvcMonitors.keys)
|
HandlerThreadHandler.createHandler(TAG).postDelayed({
|
|
var monitor = uvcMonitors.get(device!!.deviceName)!!
|
val camera : UVCCamera = monitor.uvcCamera;
|
camera.open(ctrlBlock)
|
|
Log.i(TAG , "supportedSize:" + camera.getSupportedSize())
|
var type = 0 ;
|
try {
|
type = UVCCamera.FRAME_FORMAT_MJPEG;
|
camera.setPreviewSize(LiveMiniView.FRAME_HEIGHT , LiveMiniView.FRAME_WIDTH , UVCCamera.FRAME_FORMAT_MJPEG)
|
} catch (e : IllegalArgumentException) {
|
try { // fallback to YUV mode
|
type = UVCCamera.FRAME_FORMAT_MJPEG;
|
camera.setPreviewSize(LiveMiniView.FRAME_HEIGHT/3*2 , LiveMiniView.FRAME_WIDTH/3*2 , UVCCamera.FRAME_FORMAT_MJPEG)
|
} catch (e1 : IllegalArgumentException) {
|
camera.destroy()
|
return@postDelayed
|
}
|
}
|
monitor.streamWindow.sizeState.value = Point(camera.previewSize.width,camera.previewSize.height )
|
monitor.streamWindow.hasVideoState.value = true;
|
monitor.streamWindow.listener?.onStarted?.invoke()
|
if (monitor.streamWindow.surfaceHolder != null) {
|
camera.setPreviewDisplay(monitor.streamWindow.surfaceHolder!!.surface)
|
camera.startPreview()
|
var t2 = Date().time;
|
var t_fps = 0
|
camera.setFrameCallback({
|
t_fps += 1;
|
if (Date().time - t2 > 1000) {
|
Log.e(TAG , "uvc ${device.deviceName} fps: ${t_fps}" )
|
t_fps = 0
|
t2 = Date().time
|
}
|
val bytes = ByteArray(it.remaining()) // 确保数组大小等于缓冲区剩余大小
|
it.get(bytes)
|
monitor.imagesQueue.put(BytesData(bytes, Date().time))
|
}, UVCCamera.PIXEL_FORMAT_YUV420SP)
|
|
monitor.streamWindow.surfaceHolder!!.addCallback(object : SurfaceHolder.Callback{
|
override fun surfaceCreated(surfaceHolder : SurfaceHolder) {
|
}
|
|
override fun surfaceChanged(surfaceHolder : SurfaceHolder , p1 : Int , p2 : Int , p3 : Int) {
|
camera.setPreviewDisplay(surfaceHolder.surface)
|
}
|
|
override fun surfaceDestroyed(p0 : SurfaceHolder) {
|
camera.stopCapture()
|
}
|
|
})
|
}
|
},0)
|
}
|
|
override fun onDisconnect(device : UsbDevice? , ctrlBlock : USBMonitor.UsbControlBlock?) {
|
synchronized(sync){
|
Log.d(TAG , "Requesting onDisconnect " + device?.deviceName)
|
if(uvcMonitors.contains(device!!.deviceName)){
|
var monitor = uvcMonitors[device.deviceName]!!;
|
monitor.uvcCamera.close()
|
monitor.streamWindow?.listener?.onFailed?.invoke(-1)
|
uvcMonitors.remove(device.deviceName)
|
}
|
}
|
}
|
|
override fun onCancel(device : UsbDevice?) {
|
synchronized(sync){
|
if(device != null && uvcMonitors.contains(device!!.deviceName)){
|
uvcMonitors[device.deviceName]!!.uvcCamera.destroy()
|
uvcMonitors[device.deviceName]!!.streamWindow!!.listener!!.onFailed!!.invoke(-1)
|
uvcMonitors.remove(device.deviceName)
|
}
|
}
|
}
|
}
|
|
override fun register() {
|
super.register()
|
usbMonitor.register()
|
}
|
|
override fun unRegister() {
|
super.unRegister()
|
usbMonitor.unregister()
|
}
|
|
override fun onUsbRgister(intent : Intent) {
|
for ( device in usbManager!!.deviceList.values){
|
if(hasCamera(device) && ! LiveLayoutView.usbDevices.value.contains(device)){
|
LiveLayoutView.usbDevices.value+=device;
|
}
|
}
|
}
|
|
override fun onDeviceOut(device : UsbDevice) {
|
Log.i(TAG , "onReceive: 外设已经移除")
|
if(LiveLayoutView.usbDevices.value.contains(device)){
|
LiveLayoutView.usbDevices.value-=device;
|
}
|
}
|
|
override fun onDeviceIn(device : UsbDevice) {
|
printDeviceInfo(device);
|
// 检查设备接口
|
var isCamera = hasCamera(device)
|
|
if (isCamera) {
|
Log.d(TAG , "This device is a USB Camera.")
|
//判断外设
|
Log.i(TAG , "onReceive: 外设连接")
|
if(! LiveLayoutView.usbDevices.value.contains(device)){
|
LiveLayoutView.usbDevices.value+=device;
|
}
|
}
|
}
|
|
fun openDevice(streamWindow : StreamWindow,deviceName:String){
|
getDeviceByName(deviceName)?.let {
|
openDevice(streamWindow,it)
|
return
|
}
|
streamWindow.listener?.onFailed?.invoke(ErrorCode.NOT_FOUND.value)
|
}
|
|
override fun openDevice(streamWindow : StreamWindow , device : UsbDevice) {
|
synchronized(sync){
|
if(!uvcMonitors.contains(device.deviceName)){
|
var camera = UVCCamera();
|
var uvcMonitor = UVCMonitor(streamWindow, camera)
|
uvcMonitors.put(device.deviceName,uvcMonitor)
|
openDevice(device)
|
startPushData(uvcMonitor,device)
|
}else{
|
streamWindow.listener?.onFailed?.invoke(ErrorCode.REUSE.value)
|
}
|
}
|
}
|
|
override protected fun openDevice(device : UsbDevice) {
|
usbMonitor.requestPermission(device);
|
}
|
|
fun startPushData(uvcMonitor : UVCMonitor,device : UsbDevice){
|
thread {
|
while (uvcMonitors.contains(device.deviceName)) {
|
|
var imageData = uvcMonitor.imagesQueue.take();
|
uvcMonitor.streamWindow?.let { streamWindow ->
|
if (streamWindow.videoDelay > 0) {
|
var tempStamp = Date().time;
|
var c = (imageData.timestamp + streamWindow.videoDelay) - tempStamp;
|
Log.i(TAG , "startPushData: ${streamWindow.videoDelay} ${c}")
|
if (c < streamWindow.videoDelay && c > 0) {
|
Thread.sleep(c);
|
}
|
}
|
if (streamWindow !!.videoState.value == LiveState.IN_LIVE || streamWindow !!.id == mainStreamCode) {
|
LiveMiniView.native_push_nv21(uvcMonitor.streamWindow.id,imageData.bytes)
|
}
|
}
|
}
|
uvcMonitor.imagesQueue.clear();
|
}
|
}
|
|
override fun closeDevice(streamId : Int) {
|
synchronized(sync){
|
for (key in uvcMonitors.keys){
|
if(uvcMonitors[key]!!.streamWindow.id == streamId){
|
var monitor = uvcMonitors[key]!!;
|
uvcMonitors.remove(key)
|
monitor.uvcCamera.close()
|
break;
|
}
|
}
|
}
|
}
|
|
private fun hasCamera(device : UsbDevice):Boolean{
|
var hasCamera = false;
|
for (i in 0 until device.interfaceCount) {
|
val usbInterface = device.getInterface(i) // 检查接口的类值是否为 0x0E (UVC)
|
if (usbInterface.interfaceClass == UsbConstants.USB_CLASS_VIDEO) {
|
hasCamera = true
|
break
|
}
|
}
|
return hasCamera;
|
}
|
}
|