package com.usbwebcam import java.io.OutputStream import java.net.ServerSocket import java.net.Socket import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.atomic.AtomicBoolean class MjpegServer(private val port: Int) { private var serverSocket: ServerSocket? = null private var serverThread: Thread? = null private val clients = CopyOnWriteArrayList() private val running = AtomicBoolean(false) private var currentFrame: ByteArray? = null private val frameLock = Any() fun start(onStarted: () -> Unit) { serverThread = Thread { try { serverSocket = ServerSocket(port) running.set(true) onStarted() while (running.get()) { val client = try { serverSocket?.accept() } catch (e: Exception) { null } ?: continue val handler = ClientHandler(client) clients.add(handler) handler.start() // 发送当前帧 val frameToSend = synchronized(frameLock) { currentFrame } if (frameToSend != null) { handler.sendFrame(frameToSend) } } } catch (e: Exception) { if (running.get()) { e.printStackTrace() } } }.apply { name = "MjpegServerThread" start() } } fun updateFrame(frame: ByteArray) { synchronized(frameLock) { currentFrame = frame } // 广播给所有客户端 val iterator = clients.iterator() while (iterator.hasNext()) { val handler = iterator.next() if (handler.isClientAlive()) { handler.sendFrame(frame) } else { iterator.remove() } } } fun stop() { if (!running.getAndSet(false)) return try { serverSocket?.close() } catch (e: Exception) { // ignore } clients.forEach { it.close() } clients.clear() serverSocket = null serverThread = null } private class ClientHandler(private val socket: Socket) : Thread() { private var clientStream: OutputStream? = null @Volatile private var initialized = false override fun run() { try { val os = socket.getOutputStream() clientStream = os socket.setSoTimeout(5000) // 读取HTTP请求 val buffer = ByteArray(1024) socket.getInputStream().read(buffer) // 发送HTTP响应头 val header = byteArrayOf( *("HTTP/1.1 200 OK\r\n").toByteArray(), *("Content-Type: multipart/x-mixed-replace; boundary=boundary\r\n").toByteArray(), *("Cache-Control: no-cache\r\n").toByteArray(), *("Connection: close\r\n").toByteArray(), *("\r\n").toByteArray() ) os.write(header) os.flush() initialized = true } catch (e: Exception) { close() } } fun sendFrame(frame: ByteArray) { if (!initialized) return try { // MJPEG frame header val header = byteArrayOf( *("--boundary\r\n").toByteArray(), *("Content-Type: image/jpeg\r\n").toByteArray(), *("Content-Length: ${frame.size}\r\n").toByteArray(), *("\r\n").toByteArray() ) clientStream?.write(header) clientStream?.write(frame) clientStream?.write("\r\n".toByteArray()) clientStream?.flush() } catch (e: Exception) { close() } } fun close() { try { socket.close() } catch (e: Exception) { e.printStackTrace() } } fun isClientAlive(): Boolean { return !socket.isClosed && socket.isConnected } } }