Skip to content

Fix screen mirroring audio, video freeze on reconnect, volume control, and improve lossless codec support - #4

Merged
YimingZhanshen merged 16 commits into
mainfrom
copilot/improve-audio-synchronization
Feb 10, 2026
Merged

YimingZhanshen merged 16 commits into
mainfrom
copilot/improve-audio-synchronization

Conversation

Copilot AI commented Feb 9, 2026

Copy link
Copy Markdown

Screen mirroring had no audio (SharpJaad doesn't support AAC-ELD), video froze on reconnect (stale session/cipher state), volume control crashed (DirectSoundOut.Volume unsupported), and only AAC/AAC-ELD codecs were advertised (no lossless).

AAC-ELD audio decoding

  • SharpJaad throws "unsupported profile: ER_AAC_LD" for AAC-ELD. The fdk-aac NuGet binary also fails with error 0x5 (missing ER format support).
  • Added FFmpegAacEldDecoder: pipes ADTS-wrapped AAC-ELD frames through an ffmpeg subprocess (stdin → PCM S16LE stdout), matching the approach used by UxPlay/shairport-sync.
  • Removed fdk-aac and FFmpeg.AutoGen NuGet dependencies.

Video/mirroring fixes

  • NALU parsing: Fixed inverted length check that prevented valid H.264 frames from being processed.
  • PPS length parsing: & 2040& 0xFF for correct high byte extraction.
  • Reconnect: TEARDOWN now nulls session listener refs and resets cipher state (_nextDecryptCount, _og). Old AudioListener is stopped before recreating to release UDP ports 7002/7003.
  • NTP timestamps: Fixed Int32.MaxValue>> 32 bit-shift for fractional seconds in MirroringHeader.
  • Removed unnecessary Task.Delay calls in mirroring loop.

Volume control

  • DirectSoundOut.set_Volume throws InvalidOperationException. Volume is now applied by scaling PCM samples directly in StreamingWaveProvider.Read().

Lossless codec support

  • Advertise cn=0,1,2,3 (PCM, ALAC, AAC, AAC-ELD) instead of cn=2,3.
  • Parse ct (compression type) and spf (samples per frame) from SETUP plist for dynamic decoder configuration.
  • Use Convert.ToInt32() for plist integer values (parser returns Int16 for small values, direct (int) unbox fails).

Audio latency (prior commit, included in PR)

  • Prebuffer reduced from 50 → 10 packets, DirectSoundOut latency 100ms → 40ms, queue cap 500 → 250.
  • Fixed RaopBufferInit slice bug: Skip(i)Skip(i * audio_buffer_size).
  • Replaced LINQ allocations with Array.Copy/Array.Clear in hot path.

Video output

  • Added VideoOutputService: writes H.264 Annex B to a named pipe (\\.\pipe\AirPlayVideo) for consumption by ffplay/mpv/vlc.
Original prompt

此项目目前能实现ALAC的纯音频扭转了(虽然延迟很大,具体表现为连接了好久才会响声音,手机歌词和音频不匹配,跳转歌词也会延迟几秒,但是如果手机点击暂停电脑也会立马暂停)。你能参考https://github.com/FDH2/UxPlayhttps://github.com/Frank-Friemel/Shairport4w 以及 https://github.com/mikebrady/shairport-sync 来完善相关功能(屏幕扭转可能是通过FFmpeg来实现的)吗?


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 2 commits February 9, 2026 22:43
…ix buffer init bug, fix NTP sync, optimize dequeue

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
…ialization

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title [WIP] Improve ALAC audio synchronization and delay issues Fix audio latency: buffer init bug, NTP sync, and playback tuning Feb 9, 2026
Copilot AI requested a review from YimingZhanshen February 9, 2026 22:46
Copilot AI and others added 2 commits February 9, 2026 22:58
…ngth, add VideoOutputService

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
…ll exception in logs

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix audio latency: buffer init bug, NTP sync, and playback tuning Fix screen mirroring pipeline: NALU parsing, NTP sync, PPS parsing, and add video output Feb 9, 2026
Copilot AI and others added 2 commits February 9, 2026 23:19
…ledException

- Stop old AudioListener before creating new one (release UDP ports 7002/7003)
- Null out session listener references in TEARDOWN for proper reconnect
- Fix SessionManager to allow intentional null/reset of session fields
- Handle TaskCanceledException in MirroringListener gracefully
- Reset AES-CTR cipher state on new mirroring connection
- Reset video state (SpsPps, StreamConnectionId, MirroringSession) on teardown

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
… document SessionManager

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix screen mirroring pipeline: NALU parsing, NTP sync, PPS parsing, and add video output Fix mirroring audio silence, video freeze on reconnect, and session state leaks Feb 9, 2026
… iOS volume control

- Audio during screen mirroring arrives as type 0x56 packets on the control socket
  but was only queued and never dequeued/played. Add dequeue loop after queue.
- Initialize AAC-ELD decoder in OnRawCSocketAsync (was only initialized in data socket handler)
- Add SetVolume method to AudioOutputService with AirPlay dB-to-linear conversion
- Wire up OnSetVolumeReceived event to AudioOutputService.SetVolume

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix mirroring audio silence, video freeze on reconnect, and session state leaks Fix mirroring audio pipeline, video reconnection, and iOS volume control Feb 9, 2026
Copilot AI and others added 3 commits February 9, 2026 23:57
Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Root cause: SharpJaad.AAC throws "unsupported profile: ER_AAC_LD" for AAC-ELD,
causing the decoder init to fail and audio socket handler to die silently.
Solution: Use fdk-aac native library (libAACdec.dll) which natively supports AAC-ELD.

Volume fix: DirectSoundOut.Volume throws InvalidOperationException.
Apply volume via StreamingWaveProvider PCM sample scaling instead.

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix mirroring audio pipeline, video reconnection, and iOS volume control Fix mirroring audio (AAC-ELD decoder) and volume control Feb 10, 2026
Copilot AI and others added 2 commits February 10, 2026 00:18
…ASC format

Error 8204 (0x200C) = AAC_DEC_OUTPUT_BUFFER_TOO_SMALL. The FDK AAC library
requires an output buffer of at least 2048 * channels samples for internal
processing, but only 480 * channels = 960 samples were allocated.

Also updated the AudioSpecificConfig to match the format used by the original
airplayreceiver project (which is known to work with FDK AAC).

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
…ix PCM decoder

- Advertise cn=0,1,2,3 (PCM, ALAC, AAC, AAC-ELD) instead of cn=2,3
- Parse ct (compression type) and spf (samples per frame) from SETUP request
- Use spf from client to configure decoder frame length dynamically
- Fix PCMDecoder.GetOutputStreamLength() returning -1 (could crash buffer allocation)
- Add PCM format description to GetAudioFormatDescription
- Increase RaopBuffer audio_buffer_size to 1024*4 for AAC-main support
- Add fallback decoder selection from ct when audioFormat is unknown

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix mirroring audio (AAC-ELD decoder) and volume control Fix screen mirroring audio, AAC-ELD decoding, volume control, and lossless codec support Feb 10, 2026
…s (ct, spf, audioFormat)

Binary plist parser returns Int16 for small values like ct=8, but (int) unboxing
cast fails on boxed Int16. Use Convert.ToInt32() which handles all numeric types.

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI and others added 2 commits February 10, 2026 00:58
The fdk-aac NuGet package returns error 0x5 (AAC_DEC_UNSUPPORTED_ER_FORMAT)
because the pre-built binary doesn't include Error Resilient format support
which AAC-ELD requires.

Solution: Use FFmpeg as a subprocess (piping ADTS-wrapped AAC frames through
stdin and reading PCM S16LE from stdout). This works with any FFmpeg version
and is the approach UxPlay/shairport-sync use (via GStreamer/libavcodec).

Also removed the fdk-aac and FFmpeg.AutoGen NuGet packages as they're no
longer needed.

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
…eedback

- Rename class and file to accurately reflect FFmpeg subprocess implementation
- Extract magic numbers to named constants (MAX_READ_ATTEMPTS, PROCESS_EXIT_TIMEOUT_MS)
- Implement graceful shutdown: close stdin first, wait for exit, then force kill

Co-authored-by: YimingZhanshen <76594627+YimingZhanshen@users.noreply.github.com>
Copilot AI changed the title Fix screen mirroring audio, AAC-ELD decoding, volume control, and lossless codec support Fix screen mirroring audio, video freeze on reconnect, volume control, and improve lossless codec support Feb 10, 2026
@YimingZhanshen
YimingZhanshen marked this pull request as ready for review February 10, 2026 01:16
Copilot AI review requested due to automatic review settings February 10, 2026 01:16
@YimingZhanshen
YimingZhanshen merged commit 9f5a45a into main Feb 10, 2026
1 check passed
@YimingZhanshen
YimingZhanshen deleted the copilot/improve-audio-synchronization branch February 10, 2026 01:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses multiple AirPlay runtime issues (mirroring audio/video reliability, volume control stability, and codec advertisement/handling), and adds a video output path for external players via named pipes/FIFO.

Changes:

  • Add FFmpeg-subprocess AAC-ELD decoding and expand advertised/parsed audio codec parameters (cn/ct/spf) to improve compatibility (incl. lossless).
  • Fix mirroring reconnect/cipher reset and H.264 parsing/timestamp handling to prevent freezes and improve frame processing.
  • Implement safe volume control by scaling PCM samples in the wave provider, and add a video output service that writes Annex B H.264 to a pipe/FIFO.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
AirPlay/Services/VideoOutputService.cs New named pipe/FIFO video writer for mirrored H.264 output.
AirPlay/Services/StreamingWaveProvider.cs Add per-sample PCM volume scaling in the audio read path.
AirPlay/Services/SessionManager.cs Change session updates to replace the stored session object (supports intentional nulling).
AirPlay/Services/AudioOutputService.cs Reduce latency/buffer sizes and route volume changes into StreamingWaveProvider.
AirPlay/Models/Session.cs Add ct/spf session fields to support dynamic decoder configuration.
AirPlay/Models/Mirroring/MirroringHeader.cs Fix NTP fractional conversion to PTS.
AirPlay/Listeners/MirroringListener.cs Reset cipher state on reconnect, improve header/payload reading, and fix NALU/PPS parsing.
AirPlay/Listeners/AudioListener.cs Initialize decoder for mirroring audio on control socket; adjust buffering/dequeueing and decoder selection.
AirPlay/Listeners/AirTunesListener.cs Parse ct/spf and stop/recreate listeners cleanly on reconnect/teardown.
AirPlay/Decoders/Implementations/PCMDecoder.cs Make PCM output sizing/configurable and avoid over-copying.
AirPlay/Decoders/Implementations/FFmpegAacEldDecoder.cs New FFmpeg-based AAC-ELD decoder via stdin/stdout pipes.
AirPlay/AirPlayService.cs Wire volume events and forward received H.264 frames to VideoOutputService.
AirPlay/AirPlayReceiver.cs Advertise additional codecs via RAOP cn=0,1,2,3 (PCM/ALAC/AAC/AAC-ELD).
Comments suppressed due to low confidence (31)

AirPlay/Listeners/AirTunesListener.cs:353

  • stream["controlPort"] is unboxed as short. The BinaryPlistReader can return short or int depending on the encoded integer size; unboxing to short will throw if the value is boxed as int. Use Convert.ToUInt16/ToInt32 (consistent with the audioFormat/ct/spf changes) to avoid InvalidCastException.
                                {
                                    session.AudioSamplesPerFrame = Convert.ToInt32(stream["spf"]);
                                }
                                if (stream.ContainsKey("controlPort"))
                                {
                                    // Use this port to request resend lost packet? (remote port)
                                    var controlPort = (ushort)((short)stream["controlPort"]);

AirPlay/AirPlayReceiver.cs:67

  • Disposable 'ServiceDiscovery' is created but not disposed.
            var sd = new ServiceDiscovery(_mdns);

AirPlay/Listeners/AudioListener.cs:452

  • This comparison is always false.
                for (seqnum = raop_buffer.FirstSeqNum; Utilities.SeqNumCmp(seqnum, raop_buffer.LastSeqNum) < 0; seqnum++)

AirPlay/Listeners/AirTunesListener.cs:538

  • Empty block without comment.
            {

            }

AirPlay/Services/AudioOutputService.cs:99

  • Poor error handling: empty catch block.
                        catch { }

AirPlay/Services/AudioOutputService.cs:229

  • Poor error handling: empty catch block.
                catch { }

AirPlay/Services/AudioOutputService.cs:263

  • Poor error handling: empty catch block.
                catch { }

AirPlay/Listeners/AudioListener.cs:448

  • Condition always evaluates to 'false'.
            if (Utilities.SeqNumCmp(raop_buffer.FirstSeqNum, raop_buffer.LastSeqNum) < 0)

AirPlay/Listeners/AirTunesListener.cs:538

  • If-statement with an empty then-branch and no else-branch.
            if(request.Type == RequestType.ANNOUNCE)
            {

            }

AirPlay/Listeners/AirTunesListener.cs:488

  • Inefficient use of 'ContainsKey' and indexer.
                if (request.Headers.ContainsKey("Content-Type"))

AirPlay/Listeners/AirTunesListener.cs:543

  • Inefficient use of 'ContainsKey' and indexer.
                if (request.Headers.ContainsKey("RTP-Info"))

AirPlay/Listeners/AudioListener.cs:174

  • This assignment to type_d is useless, since its value is never read.
                int type_d = packet[1] & ~0x80;

AirPlay/Listeners/AudioListener.cs:186

  • This assignment to buf_ret is useless, since its value is never read.
                    buf_ret = RaopBufferQueue(_raopBuffer, packet, (ushort)dret, session);

AirPlay/Listeners/AirTunesListener.cs:201

  • This assignment to signatureBuffer is useless, since its value is never read.
                        signatureBuffer = aesCtr128Encrypt.ProcessBytes(signatureBuffer);

AirPlay/Listeners/AirTunesListener.cs:227

  • This assignment to mode is useless, since its value is never read.
                        var mode = body[14];

AirPlay/Listeners/AirTunesListener.cs:353

  • This assignment to controlPort is useless, since its value is never read.
                                    var controlPort = (ushort)((short)stream["controlPort"]);

AirPlay/Listeners/AirTunesListener.cs:412

  • This assignment to timingPort is useless, since its value is never read.
                                var timingPort = (ushort)((short)plist["timingPort"]);

AirPlay/Listeners/AirTunesListener.cs:510

  • This assignment to start is useless, since its value is never read.
                                var start = long.Parse(pVals[0]);

AirPlay/Listeners/AirTunesListener.cs:511

  • This assignment to current is useless, since its value is never read.
                                var current = long.Parse(pVals[1]);

AirPlay/Listeners/AirTunesListener.cs:512

  • This assignment to end is useless, since its value is never read.
                                var end = long.Parse(pVals[2]);

AirPlay/Listeners/AirTunesListener.cs:520

  • This assignment to image is useless, since its value is never read.
                        var image = request.Body;

AirPlay/Listeners/AirTunesListener.cs:527

  • This assignment to output is useless, since its value is never read.
                        var output = dmap.Decode(request.Body);

AirPlay/Listeners/AirTunesListener.cs:530

  • These 'if' statements can be combined.
            if (request.Type == RequestType.SET_PARAMETER)
            {
                if (request.Headers.ContainsKey("Content-Type"))
                {
                    var contentType = request.Headers["Content-Type"];

                    if (contentType.Equals("text/parameters", StringComparison.OrdinalIgnoreCase))
                    {
                        var body = Encoding.ASCII.GetString(request.Body);
                        var keyPair = body.Split(":", StringSplitOptions.RemoveEmptyEntries).Select(b => b.Trim(' ', '\r', '\n')).ToArray();
                        if(keyPair.Length == 2)
                        {
                            var key = keyPair[0];
                            var val = keyPair[1];

                            if (key.Equals("volume", StringComparison.OrdinalIgnoreCase))
                            {
                                // request.Body contains 'volume: N.NNNNNN'
                                _receiver.OnSetVolume(decimal.Parse(val, CultureInfo.InvariantCulture));
                            }
                            else if (key.Equals("progress", StringComparison.OrdinalIgnoreCase))
                            {
                                var pVals = val.Split("/", StringSplitOptions.RemoveEmptyEntries);

                                var start = long.Parse(pVals[0]);
                                var current = long.Parse(pVals[1]);
                                var end = long.Parse(pVals[2]);

                                // DO SOMETHING W/ PROGRESS
                            }
                        }
                    }
                    else if (contentType.Equals("image/jpeg", StringComparison.OrdinalIgnoreCase))
                    {
                        var image = request.Body;

                        // DO SOMETHING W/ IMAGE
                    }
                    else if (contentType.Equals("application/x-dmap-tagged", StringComparison.OrdinalIgnoreCase))
                    {
                        var dmap = new DMapTagged();
                        var output = dmap.Decode(request.Body);
                    }
                }
            }

AirPlay/Models/Session.cs:48

  • The expression 'A ? B : false' can be simplified to 'A && B'.
        public bool MirroringSessionReady => StreamConnectionId != null && MirroringSession.HasValue ? MirroringSession.Value : false;

AirPlay/AirPlayReceiver.cs:27

  • Field '_airTunesListener' can be 'readonly'.
        private AirTunesListener _airTunesListener = null;

AirPlay/Listeners/AudioListener.cs:26

  • Field '_aesCbcDecrypt' can be 'readonly'.
        private IBufferedCipher _aesCbcDecrypt;

AirPlay/Listeners/AudioListener.cs:32

  • Field '_controlSequenceNumber' can be 'readonly'.
        private ushort _controlSequenceNumber = 0;

AirPlay/Listeners/AudioListener.cs:33

  • Field '_raopBuffer' can be 'readonly'.
        private RaopBuffer _raopBuffer;

AirPlay/Listeners/MirroringListener.cs:25

  • Field '_og' can be 'readonly'.
        private byte[] _og = new byte[16];

AirPlay/Models/Session.cs:9

  • Field '_sessionId' can be 'readonly'.
        private string _sessionId;

AirPlay/Services/SessionManager.cs:12

  • Field '_sessions' can be 'readonly'.
        private ConcurrentDictionary<string, Session> _sessions;

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 74 to 78
if ((headerBuffer[0] == 80 && headerBuffer[1] == 79 && headerBuffer[2] == 83 && headerBuffer[3] == 84) || (headerBuffer[0] == 71 && headerBuffer[1] == 69 && headerBuffer[2] == 84))
{
// Request is POST or GET (skip)
continue;
}

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The POST/GET detection continues after reading only the first 4 bytes, leaving the rest of the HTTP request in the stream. On the next iteration those bytes will be misinterpreted as a mirroring header, which can corrupt parsing and potentially lead to huge PayloadSize allocations. Consume and discard the entire HTTP request (e.g., read until \r\n\r\n) before continuing, or handle HTTP requests on a separate path before attempting to parse 128-byte mirroring headers.

Copilot uses AI. Check for mistakes.
if (nc_len <= 0 || offset + 4 + nc_len > payload.Length)
{
return;
break;

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the AVCC->AnnexB conversion, when an invalid NALU length is encountered you break the loop but still emit the (partially converted) payload. That can produce an invalid H.264 stream. Prefer returning (drop the frame) when nc_len is invalid/out-of-range rather than sending partially converted data.

Suggested change
break;
// Invalid NALU length: drop this frame instead of emitting partially converted data
return;

Copilot uses AI. Check for mistakes.
Comment on lines +561 to 565
else if (audioFormat == AudioFormat.PCM)
{
// (PCM -> PCM)
// Not used
// Raw PCM audio - no decoding needed
_decoder = new PCMDecoder();
}

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PCMDecoder is instantiated but never configured. PCMDecoder.GetOutputStreamLength() depends on Config() to set _pcmSize; otherwise the default size is used and DecodeFrame may truncate PCM frames that are larger than the default. Call Config() here using the negotiated spf (or another correct frame size) so output sizing is correct.

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +150
private void WaitForUnixPipeConnection(CancellationToken token)
{
try
{
// This will block until a reader connects
_unixPipeStream = new FileStream(UNIX_PIPE_PATH, FileMode.Open, FileAccess.Write);
lock (_lock)

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitForUnixPipeConnection blocks on new FileStream(..., FileAccess.Write) until a reader connects and does not honor the cancellation token. This can leave the accept task stuck during shutdown/dispose. Consider opening the FIFO in non-blocking mode (or open ReadWrite to avoid the writer-open block) and/or use a cancellation-aware approach so disposal can complete reliably.

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +110
try
{
byte[] payload = new byte[header.PayloadSize];

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

header.PayloadSize is used directly to allocate a payload buffer with no upper bound. If the stream is desynced or malformed (including due to the POST/GET handling above), this can cause excessive allocations / OOM. Add a reasonable maximum payload size check and close/skip when exceeded.

Copilot uses AI. Check for mistakes.
{
session.AudioCompressionType = Convert.ToInt32(stream["ct"]);
}
if (stream.ContainsKey("spf"))

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inefficient use of 'ContainsKey' and indexer.

Copilot uses AI. Check for mistakes.
{
session.AudioSamplesPerFrame = Convert.ToInt32(stream["spf"]);
}
if (stream.ContainsKey("controlPort"))

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inefficient use of 'ContainsKey' and indexer.

Copilot uses AI. Check for mistakes.
@@ -87,9 +90,22 @@ public override async Task OnRawCSocketAsync(Socket cSocket, CancellationToken c
var data = reader.ReadBytes(cret - 4);

var ret = RaopBufferQueue(_raopBuffer, data, (ushort)data.Length, session);

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to ret is useless, since its value is never read.

Suggested change
var ret = RaopBufferQueue(_raopBuffer, data, (ushort)data.Length, session);
RaopBufferQueue(_raopBuffer, data, (ushort)data.Length, session);

Copilot uses AI. Check for mistakes.
uint ntp_fraction = (uint)reader.ReadInt32();
ulong ntp_time = ((ulong)ntp_seconds * 1000000UL) + (((ulong)ntp_fraction * 1000000UL) >> 32);
uint rtp_timestamp = (uint)((packet[4] << 24) | (packet[5] << 16) | (packet[6] << 8) | packet[7]);
uint next_timestamp = (uint)((packet[16] << 24) | (packet[17] << 16) | (packet[18] << 8) | packet[19]);

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to next_timestamp is useless, since its value is never read.

Suggested change
uint next_timestamp = (uint)((packet[16] << 24) | (packet[17] << 16) | (packet[18] << 8) | packet[19]);

Copilot uses AI. Check for mistakes.
Comment on lines +191 to +199
if (_ffmpegProcess != null && !_ffmpegProcess.HasExited)
{
// Wait for graceful exit, then force kill if needed
if (!_ffmpegProcess.WaitForExit(PROCESS_EXIT_TIMEOUT_MS))
{
_ffmpegProcess.Kill();
_ffmpegProcess.WaitForExit(PROCESS_EXIT_TIMEOUT_MS);
}
}

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 'if' statements can be combined.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants