mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 07:01:30 +00:00
[client] audio: conceal failed USB packets
This commit is contained in:
@@ -99,7 +99,8 @@ enum
|
|||||||
USB_AUDIO_RECORD_STREAM_DESC_SIZE = 46,
|
USB_AUDIO_RECORD_STREAM_DESC_SIZE = 46,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define USB_AUDIO_RECORD_DEBUG_INTERVAL_NS UINT64_C(5000000000)
|
#define USB_AUDIO_RECORD_DEBUG_INTERVAL_NS UINT64_C(5000000000)
|
||||||
|
#define USB_AUDIO_PLAYBACK_DEBUG_INTERVAL_NS UINT64_C(5000000000)
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@@ -370,6 +371,11 @@ struct LG_USBAudio
|
|||||||
uint8_t playbackBatchPackets;
|
uint8_t playbackBatchPackets;
|
||||||
uint8_t playbackBatchFrameSize;
|
uint8_t playbackBatchFrameSize;
|
||||||
uint16_t playbackBatchFrames;
|
uint16_t playbackBatchFrames;
|
||||||
|
uint32_t playbackPacketPhase;
|
||||||
|
bool playbackErrorWarned;
|
||||||
|
uint64_t playbackConcealedPackets;
|
||||||
|
uint64_t playbackInvalidPackets;
|
||||||
|
uint64_t playbackNextDebugTime;
|
||||||
atomic_bool feedbackStreaming;
|
atomic_bool feedbackStreaming;
|
||||||
uint64_t feedbackPacketId;
|
uint64_t feedbackPacketId;
|
||||||
uint64_t feedbackNextPacketTime;
|
uint64_t feedbackNextPacketTime;
|
||||||
@@ -493,6 +499,14 @@ static void resetFeedbackRate(LG_USBAudio * audio)
|
|||||||
encodeFeedbackRate(audio->playbackSampleRate), memory_order_release);
|
encodeFeedbackRate(audio->playbackSampleRate), memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t nextPlaybackPacketFrames(LG_USBAudio * audio)
|
||||||
|
{
|
||||||
|
const uint64_t phase = audio->playbackPacketPhase +
|
||||||
|
atomic_load_explicit(&audio->feedbackValue, memory_order_acquire);
|
||||||
|
audio->playbackPacketPhase = phase & UINT32_C(0xffff);
|
||||||
|
return (uint32_t)(phase >> 16);
|
||||||
|
}
|
||||||
|
|
||||||
static uint32_t nearestSampleRate(uint32_t sampleRate)
|
static uint32_t nearestSampleRate(uint32_t sampleRate)
|
||||||
{
|
{
|
||||||
uint32_t selected = l_sampleRates[0];
|
uint32_t selected = l_sampleRates[0];
|
||||||
@@ -570,9 +584,12 @@ static void queuePlaybackPacket(LG_USBAudio * audio,
|
|||||||
|
|
||||||
if (frames)
|
if (frames)
|
||||||
{
|
{
|
||||||
memcpy(audio->playbackBatch +
|
uint8_t * destination = audio->playbackBatch +
|
||||||
(size_t)audio->playbackBatchFrames * frameSize,
|
(size_t)audio->playbackBatchFrames * frameSize;
|
||||||
data, frames * frameSize);
|
if (data)
|
||||||
|
memcpy(destination, data, frames * frameSize);
|
||||||
|
else
|
||||||
|
memset(destination, 0, frames * frameSize);
|
||||||
audio->playbackBatchFrameSize = frameSize;
|
audio->playbackBatchFrameSize = frameSize;
|
||||||
audio->playbackBatchFrames += (uint16_t)frames;
|
audio->playbackBatchFrames += (uint16_t)frames;
|
||||||
}
|
}
|
||||||
@@ -582,6 +599,7 @@ static void queuePlaybackPacket(LG_USBAudio * audio,
|
|||||||
static void stopPlayback(LG_USBAudio * audio)
|
static void stopPlayback(LG_USBAudio * audio)
|
||||||
{
|
{
|
||||||
flushPlayback(audio);
|
flushPlayback(audio);
|
||||||
|
audio->playbackPacketPhase = 0;
|
||||||
if (!audio->playbackStreaming)
|
if (!audio->playbackStreaming)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -613,7 +631,12 @@ static void startPlayback(LG_USBAudio * audio)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
resetFeedbackRate(audio);
|
resetFeedbackRate(audio);
|
||||||
audio->playbackStreaming = true;
|
audio->playbackPacketPhase = 0;
|
||||||
|
audio->playbackErrorWarned = false;
|
||||||
|
audio->playbackConcealedPackets = 0;
|
||||||
|
audio->playbackInvalidPackets = 0;
|
||||||
|
audio->playbackNextDebugTime = 0;
|
||||||
|
audio->playbackStreaming = true;
|
||||||
if (audio->events && audio->events->playbackStart)
|
if (audio->events && audio->events->playbackStart)
|
||||||
audio->events->playbackStart(
|
audio->events->playbackStart(
|
||||||
audio->eventOpaque, audio->playbackSampleRate,
|
audio->eventOpaque, audio->playbackSampleRate,
|
||||||
@@ -1629,13 +1652,6 @@ static void controlPacket(void * opaque, uint64_t id,
|
|||||||
usbredirparser_free_packet_data(audio->parser, data);
|
usbredirparser_free_packet_data(audio->parser, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stallPlayback(LG_USBAudio * audio)
|
|
||||||
{
|
|
||||||
stopPlayback(audio);
|
|
||||||
sendISOStatus(audio, 0,
|
|
||||||
USB_AUDIO_PLAYBACK_DATA_ENDPOINT, usb_redir_stall);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void isoPacket(void * opaque, uint64_t id,
|
static void isoPacket(void * opaque, uint64_t id,
|
||||||
struct usb_redir_iso_packet_header * packet,
|
struct usb_redir_iso_packet_header * packet,
|
||||||
uint8_t * data, int dataLength)
|
uint8_t * data, int dataLength)
|
||||||
@@ -1648,20 +1664,42 @@ static void isoPacket(void * opaque, uint64_t id,
|
|||||||
{
|
{
|
||||||
const uint16_t frameSize =
|
const uint16_t frameSize =
|
||||||
layout ? layout->channelCount * USB_AUDIO_SAMPLE_SIZE : 0;
|
layout ? layout->channelCount * USB_AUDIO_SAMPLE_SIZE : 0;
|
||||||
if (packet->endpoint != USB_AUDIO_PLAYBACK_DATA_ENDPOINT ||
|
if (packet->endpoint != USB_AUDIO_PLAYBACK_DATA_ENDPOINT || !layout)
|
||||||
packet->status != usb_redir_success || dataLength < 0 ||
|
|
||||||
packet->length != dataLength ||
|
|
||||||
(dataLength && !data) ||
|
|
||||||
!layout || dataLength > layoutPacketSize(layout) ||
|
|
||||||
dataLength % frameSize != 0)
|
|
||||||
{
|
{
|
||||||
DEBUG_WARN("Invalid USB audio isochronous packet");
|
++audio->playbackInvalidPackets;
|
||||||
stallPlayback(audio);
|
if (!audio->playbackErrorWarned)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("USB audio packet loss detected; "
|
||||||
|
"keeping the playback stream active");
|
||||||
|
audio->playbackErrorWarned = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
queuePlaybackPacket(
|
const uint32_t expectedFrames = nextPlaybackPacketFrames(audio);
|
||||||
audio, data, dataLength / frameSize, frameSize);
|
const bool valid = packet->status == usb_redir_success &&
|
||||||
|
dataLength > 0 && packet->length == dataLength && data &&
|
||||||
|
dataLength <= layoutPacketSize(layout) &&
|
||||||
|
dataLength % frameSize == 0;
|
||||||
|
|
||||||
|
if (valid)
|
||||||
|
queuePlaybackPacket(
|
||||||
|
audio, data, dataLength / frameSize, frameSize);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (packet->status == usb_redir_success)
|
||||||
|
++audio->playbackInvalidPackets;
|
||||||
|
++audio->playbackConcealedPackets;
|
||||||
|
queuePlaybackPacket(audio, NULL, expectedFrames, frameSize);
|
||||||
|
|
||||||
|
if (!audio->playbackErrorWarned)
|
||||||
|
{
|
||||||
|
DEBUG_WARN("USB audio packet loss detected; "
|
||||||
|
"keeping the playback stream active");
|
||||||
|
audio->playbackErrorWarned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (audio->playbackBatchPackets == USB_AUDIO_PLAYBACK_BATCH_PACKETS)
|
if (audio->playbackBatchPackets == USB_AUDIO_PLAYBACK_BATCH_PACKETS)
|
||||||
flushPlayback(audio);
|
flushPlayback(audio);
|
||||||
}
|
}
|
||||||
@@ -1755,12 +1793,43 @@ static void reportRecordDebug(LG_USBAudio * audio)
|
|||||||
discontinuities);
|
discontinuities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reportPlaybackDebug(LG_USBAudio * audio)
|
||||||
|
{
|
||||||
|
if (!audio->debug)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint64_t now = streamTime();
|
||||||
|
if (!audio->playbackNextDebugTime)
|
||||||
|
{
|
||||||
|
audio->playbackNextDebugTime =
|
||||||
|
now + USB_AUDIO_PLAYBACK_DEBUG_INTERVAL_NS;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (now < audio->playbackNextDebugTime)
|
||||||
|
return;
|
||||||
|
audio->playbackNextDebugTime =
|
||||||
|
now + USB_AUDIO_PLAYBACK_DEBUG_INTERVAL_NS;
|
||||||
|
|
||||||
|
if (!audio->playbackConcealedPackets &&
|
||||||
|
!audio->playbackInvalidPackets)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DEBUG_INFO("USB playback: concealed %" PRIu64
|
||||||
|
" packets, invalid %" PRIu64,
|
||||||
|
audio->playbackConcealedPackets,
|
||||||
|
audio->playbackInvalidPackets);
|
||||||
|
audio->playbackConcealedPackets = 0;
|
||||||
|
audio->playbackInvalidPackets = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void processDevice(void * opaque)
|
static void processDevice(void * opaque)
|
||||||
{
|
{
|
||||||
LG_USBAudio * audio = opaque;
|
LG_USBAudio * audio = opaque;
|
||||||
sendFeedbackPacketsDue(audio);
|
sendFeedbackPacketsDue(audio);
|
||||||
flushPlayback(audio);
|
flushPlayback(audio);
|
||||||
sendRecordPackets(audio);
|
sendRecordPackets(audio);
|
||||||
|
reportPlaybackDebug(audio);
|
||||||
reportRecordDebug(audio);
|
reportRecordDebug(audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user