Я пытаюсь транслировать видео, отправленное камерой, по Wi-Fi. Официальное приложение имеет хорошую производительность, и мне интересно, почему моя реализация отстает.
Камера отправляет данные по UDP, и каждый пакет содержит одно изображение JPEG + несколько метаданных.
Мой вопрос здесь об общемструктура моего бесконечного цикла. Как вы думаете, этот код может быть более эффективным и / или более надежным?
Я также получаю много poll timed out error
(частота зависит, конечно, от времени ожидания, которое я положил в сокет).
Кроме того, метод run
выполняется в фоновом потоке.
override fun run() {
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND)
socket = null
try {
socket = DatagramSocket(udpPort)
socket?.soTimeout = 1000
} catch (error: Exception) {
error.message?.let {
Log.e("$TAG/run", "Can't create the socket for streaming: ${error.message}")
}
}
socket?.let { socket ->
// The camera sends each image in one UDP packet, normally between 25000 and 30000 bytes.
// We set 35000 here to be safe.
var udpPacketBuffer: ByteArray
var currentFrame: Bitmap?
var receivedPacket: DatagramPacket? = null
currentFrame = null
while (!Thread.interrupted()) {
try {
udpPacketBuffer = ByteArray(35000)
receivedPacket = DatagramPacket(
udpPacketBuffer, udpPacketBuffer.size,
address, udpPort
)
socket?.receive(receivedPacket)
imageExecutor.submit {
currentFrame = getImage(receivedPacket)
currentFrame?.let { currentFrame ->
// Display the image in the app.
// Commenting this does not remove the lag.
imageConsumer(currentFrame)
}
}
} catch (error: Exception) {
error.message?.let { Log.e("$TAG/run", it) }
}
}
}
imageExecutor.shutdown()
socket?.close()
Thread.interrupted()
}
private fun getImage(receivedPacket: DatagramPacket): Bitmap? {
var currentFrame: Bitmap? = null
try {
var imageData = getImageData(receivedPacket)
currentFrame = BitmapFactory.decodeByteArray(imageData, 0, imageData.size)
} catch (error: Exception) {
error.message?.let { Log.e("$TAG/getImage", it) }
onLoading?.let { it() }
}
return currentFrame
}
private fun getImageData(receivedPacket: DatagramPacket): ByteArray {
val udpData = receivedPacket.data
// The camera adds some kind of header to each packet, which we need to ignore
val videoDataStart = getImageDataStart(receivedPacket, udpData)
return Arrays.copyOfRange(udpData, videoDataStart, receivedPacket.length)
}
private fun getImageDataStart(receivedPacket: DatagramPacket, udpData: ByteArray): Int {
var videoDataStart = 130
// The image data starts somewhere after the first 130 bytes, but at last in 320 bytes
var k = 130
while (k < 320 && k < receivedPacket.length - 1) {
// The bytes FF and D8 signify the start of the jpeg data, see https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format
if (udpData[k] == 0xFF.toByte() && udpData[k + 1] == 0xD8.toByte()) {
videoDataStart = k
}
k++
}
return videoDataStart
}