Skip to content
This repository has been archived by the owner on Aug 19, 2023. It is now read-only.

Commit

Permalink
Improve device reconnection behavior.
Browse files Browse the repository at this point in the history
  • Loading branch information
d4rken committed May 16, 2021
1 parent 5dfeff2 commit 97327ad
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,13 @@ class FpvGogglesV1(
if (device == null) {
Timber.tag(TAG).w("Device disconnected!")

if (videoFeedInternal.value != null) {
wasVideoActive = true
val wasActive = videoFeedInternal.value != null
if (wasActive) {
Timber.tag(TAG).d("Video feed was active on disconnect, will restart after reconnect.")
}

stopVideoFeed()
wasVideoActive = wasActive
eventsInternal.value = Gear.Event.GearDetached(this)
} else if (reconnect) {
Timber.tag(TAG).w("Device reconnected!")
Expand Down Expand Up @@ -71,20 +72,22 @@ class FpvGogglesV1(
}

connection = device.openConnection()

return FpvGogglesV1VideoFeed(connection!!).also { feed ->
videoFeedInternal.value = feed
}
}

override suspend fun stopVideoFeed() {
Timber.tag(TAG).i("stopVideoFeed()")
videoFeedInternal.value?.close()
videoFeedInternal.value?.let {
it.close()
wasVideoActive = false
}
videoFeedInternal.value = null

connection?.close()
connection = null

wasVideoActive = true
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,78 +16,86 @@ import timber.log.Timber

class FpvGogglesV1VideoFeed(
private val connection: DVCAConnection,
) : Goggles.VideoFeed, DataSource {
) : Goggles.VideoFeed {
private val intf = connection.getInterface(3)
private val cmdEndpoint = intf.getEndpoint(0)
private val videoEndpoint = intf.getEndpoint(1)
private var cmdSink: BufferedSink? = null
private var videoSource: BufferedSource? = null

override val exoDataSource: DataSource = this
override val exoDataSource: DataSource = object : DataSource {
override fun getUri(): Uri? = Uri.EMPTY

override fun getUri(): Uri? = Uri.EMPTY
override fun addTransferListener(transferListener: TransferListener) {}

override fun addTransferListener(transferListener: TransferListener) {
// NOOP
}
override fun open(dataSpec: DataSpec): Long {
Timber.tag(TAG).v("open(dataSpec=%s) this=%s", dataSpec, this)

override fun open(dataSpec: DataSpec): Long {
Timber.tag(TAG).v("open(dataSpec=%s) this=%s", dataSpec, this)
intf.claim(forced = false)

intf.claim(forced = true)
var newCmdSink = cmdEndpoint.sink()
var newVideoSource = videoEndpoint.source()

var newCmdSink = cmdEndpoint.sink()
var newVideoSource = videoEndpoint.source()
try {
Timber.tag(TAG).v("Waiting for video feed to start.")
newCmdSink.apply {
Timber.tag(TAG).d("Writing magic packet.")
write(MAGIC_FPVOUT_PACKET)
flush()
}
newVideoSource.readByteArray(64)
Timber.tag(TAG).v("Waiting for video feed has started.")
} catch (e: Exception) {
// java.io.InterruptedIOException: timeout ?
Timber.tag(TAG).v(e, "Failed to open video feed, needs magic packet.")

try {
Timber.tag(TAG).v("Waiting for video feed to start.")
newVideoSource.readByteArray(64)
Timber.tag(TAG).v("Waiting for video feed has started.")
} catch (e: Exception) {
// java.io.InterruptedIOException: timeout ?
Timber.tag(TAG).v(e, "Failed to open video feed, needs magic packet.")
newCmdSink.close()
newVideoSource.close()

newCmdSink.close()
newVideoSource.close()
intf.apply {
release()
claim(forced = true)
}

intf.apply {
release()
claim(forced = true)
}
newCmdSink = cmdEndpoint.sink().apply {
Timber.tag(TAG).d("Writing new magic packet.")
write(MAGIC_FPVOUT_PACKET)
flush()
}

newCmdSink = cmdEndpoint.sink().apply {
Timber.tag(TAG).d("Writing magic packet.")
write(MAGIC_FPVOUT_PACKET)
flush()
}
Thread.sleep(100)

newVideoSource = videoEndpoint.source()
newVideoSource = videoEndpoint.source()

Timber.tag(TAG).d("Video feed restart attempt done.")
}
Timber.tag(TAG).d("Video feed restart attempt done.")
}

cmdSink = newCmdSink
videoSource = newVideoSource

this.cmdSink = newCmdSink
this.videoSource = newVideoSource
return if (dataSpec.length != C.LENGTH_UNSET.toLong()) {
dataSpec.length
} else {
C.LENGTH_UNSET.toLong()
}
}

return if (dataSpec.length != C.LENGTH_UNSET.toLong()) {
dataSpec.length
} else {
C.LENGTH_UNSET.toLong()
override fun read(target: ByteArray, offset: Int, length: Int): Int {
return videoSource?.read(target, offset, length) ?: -1
}
}

override fun read(target: ByteArray, offset: Int, length: Int): Int {
return videoSource!!.read(target, offset, length)
override fun close() {
Timber.tag(TAG).d("close(), source, this=%s", this)
cmdSink?.close()
cmdSink = null
videoSource?.close()
videoSource = null
}
}

override fun close() {
Timber.tag(TAG).d("close() this=%s", this)
cmdSink?.close()
cmdSink = null
videoSource?.close()
videoSource = null

Timber.tag(TAG).v("close() feed, this=%s", this)
exoDataSource.close()
intf.release()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint
import android.hardware.usb.UsbInterface
import eu.darken.fpv.dvca.App
import eu.darken.fpv.dvca.usb.connection.io.AndroidUSBInputStream2
import eu.darken.fpv.dvca.usb.connection.io.UsbDataSink
import eu.darken.fpv.dvca.usb.connection.io.UsbDataSource
import okio.BufferedSink
import okio.BufferedSource
import okio.buffer
import okio.source
import timber.log.Timber
import java.io.Closeable

Expand Down Expand Up @@ -84,16 +83,16 @@ class DVCAConnection(
rawConnection
).buffer()

fun source(): BufferedSource = AndroidUSBInputStream2(
rawEndpoint,
rawConnection
).source().buffer()

// TODO Tweak to gain better USB read performance and less delay
// fun source(): BufferedSource = UsbDataSource(
// fun source(): BufferedSource = AndroidUSBInputStream2(
// rawEndpoint,
// rawConnection
// ).buffer()
// ).source().buffer()

// TODO Tweak to gain better USB read performance and less delay
fun source(): BufferedSource = UsbDataSource(
rawEndpoint,
rawConnection
).buffer()

companion object {
private val TAG = App.logTag("Usb", "Device", "Connection", "Interface", "Endpoint")
Expand Down

0 comments on commit 97327ad

Please sign in to comment.