Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Jack StreamReader #1241

Merged
merged 23 commits into from
Jun 19, 2024
Merged

Add Jack StreamReader #1241

merged 23 commits into from
Jun 19, 2024

Conversation

mawe42
Copy link
Contributor

@mawe42 mawe42 commented May 30, 2024

This PR adds a Jack StreamReader to SnapCast.

I have successfully tested this with a Jack2 server on Linux and would like to get more testing feedback and possibly a code-review before finalizing this.

doc/configuration.md contains information on how to use the jack stream.

Please note: I'm more at home with C and haven't got much C++ experience, so I would welcome any pointers about weird code constructs or things that could be expressed more cleanly in C++.

@mawe42 mawe42 marked this pull request as draft May 30, 2024 14:18
@badaix
Copy link
Owner

badaix commented Jun 1, 2024

Thanks, I had to updated the ci.yml and some cmake files in the develop branch to make the CI "green" again (GitHub removed gcc13 from their ubuntu 22.04 images and there was a hickup on Windows with boost).
Can you please rebase your changed on the current HEAD of develop?

A general question from my side: I would like to test the PR, but I don't know how to setup a Jack server and how to feed audio into a Jack stream. Can you please provide me a basic a how-to for this?

common/message/pcm_chunk.hpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.cpp Show resolved Hide resolved
server/streamreader/jack_stream.cpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.cpp Show resolved Hide resolved
server/streamreader/jack_stream.cpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.hpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.hpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.hpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.hpp Outdated Show resolved Hide resolved
server/streamreader/jack_stream.hpp Outdated Show resolved Hide resolved
@mawe42
Copy link
Contributor Author

mawe42 commented Jun 1, 2024

Great, thanks for the review! I will address all comments and provide some instructions on how to set up a Jack test environment in the next few days.

@mawe42
Copy link
Contributor Author

mawe42 commented Jun 2, 2024

A general question from my side: I would like to test the PR, but I don't know how to setup a Jack server and how to feed audio into a Jack stream. Can you please provide me a basic a how-to for this?

Do you have a preferred Linux distribution that you would like to test this on?

@badaix
Copy link
Owner

badaix commented Jun 2, 2024

A general question from my side: I would like to test the PR, but I don't know how to setup a Jack server and how to feed audio into a Jack stream. Can you please provide me a basic a how-to for this?

Do you have a preferred Linux distribution that you would like to test this on?

I'm using Linux Mint on my dev machine.

@mawe42
Copy link
Contributor Author

mawe42 commented Jun 3, 2024

To test this, you can install Jack and a music player that can output to Jack, for example mpv:

sudo apt-get install --no-install-recommends jackd mpv

Then start a Jack server with "dummy" backend (we don't need Jack to access the soundcard) and choose a sample rate and buffer size, for example:

$ jackd -d dummy -r 48000 -p 2048

Then, in another shell but with the same user, play an audio file via that Jack server:

$ mpv --ao=jack <filename>

mpv will create a Jack client called "mpv" by default, with a number of output ports according to the number of channels in the soundfile called "out_0", "out_1", ...

If you define the SnapCast Jack streams with &autoconnect=mpv:out_ (and optionally autoconnect_skip), they will automatically connect to the mpv ports if and when they become available. snapserver needs to run as the same user that has started the Jack server. It doesn't matter if Jack is already started when snapserver starts, it will keep attempting to connect until it is available.

@badaix
Copy link
Owner

badaix commented Jun 6, 2024

jack_stream.zip

Please find attached the jack_stream files with slightly modified timestamping.
With this line the client often "resyncs", drops samples and fast forwards:

        tvEncodedChunk_ = std::chrono::time_point<std::chrono::steady_clock>(static_cast<chrono::microseconds>(current_usecs + jackTimeAdjust_));

@mawe42
Copy link
Contributor Author

mawe42 commented Jun 11, 2024

jack_stream.zip

Please find attached the jack_stream files with slightly modified timestamping. With this line the client often "resyncs", drops samples and fast forwards:

        tvEncodedChunk_ = std::chrono::time_point<std::chrono::steady_clock>(static_cast<chrono::microseconds>(current_usecs + jackTimeAdjust_));

Thanks for the modifications! I can see that they work, but they don't solve my use-case: splitting a 24+ channel Jack output into 24 individual SnapCast streams with exact time sync. Would you be open to more discussion/experimentation and possibly code-changes in other parts of SnapCast to make this possible? The submitted code actually works well in my setup... but it would be interesting to find out why it's breaking at your end.

@mawe42
Copy link
Contributor Author

mawe42 commented Jun 11, 2024

With this line the client often "resyncs", drops samples and fast forwards:

With a "normal" and continuous soundfile or music being played via Jack? That's interesting. I saw some hard syncs, but also with the ALSA streamreader and output of Jack audio via the ALSA loopback device. I think the handling of silence in audio streams (with the default silence_threshold) has some edge-cases that are a little buggy (in snapclient). See also #1126 .

@badaix
Copy link
Owner

badaix commented Jun 11, 2024

With this line the client often "resyncs", drops samples and fast forwards:

With a "normal" and continuous soundfile or music being played via Jack? That's interesting. I saw some hard syncs, but also with the ALSA streamreader and output of Jack audio via the ALSA loopback device. I think the handling of silence in audio streams (with the default silence_threshold) has some edge-cases that are a little buggy (in snapclient). See also #1126 .

Just ran again the unpatched version, it actually looks like this, including the FLAC decoder error (don't know where this comes from):

2024-06-11 13-11-10.219 [Error] (FlacDecoder) Got error callback: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
2024-06-11 13-11-10.219 [Error] (FlacDecoder) FLAC decode error: FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC
2024-06-11 13-11-10.231 [Info] (Controller) diff to server [ms]: 0.001
2024-06-11 13-11-10.575 [Debug] (Stream) Silent frames: 602, frames: 889, age: -13.669
2024-06-11 13-11-11.011 [Debug] (Stats) Chunk: 0	145	116	116	21	79	0
2024-06-11 13-11-12.018 [Debug] (Stats) Chunk: 173	144	116	116	70	79	0
2024-06-11 13-11-12.646 [Info] (Stream) pShortBuffer->full() && (abs(shortMedian_) > 5): 11644
2024-06-11 13-11-12.667 [Debug] (Stream) age > 0: 20ms, dropping old chunks
2024-06-11 13-11-12.667 [Debug] (Stream) age: -11, requested chunk_duration: 20, duration: 26
2024-06-11 13-11-12.667 [Debug] (Stream) Silent frames: 521, frames: 909, age: -11.817
2024-06-11 13-11-13.002 [Debug] (Stats) Chunk: 143	114	114	114	16	80	0
2024-06-11 13-11-14.017 [Debug] (Stats) Chunk: 113	142	114	114	66	79	0
2024-06-11 13-11-14.728 [Info] (Stream) pShortBuffer->full() && (abs(shortMedian_) > 5): 11441
2024-06-11 13-11-14.748 [Debug] (Stream) age > 0: 20ms, dropping old chunks
2024-06-11 13-11-14.748 [Debug] (Stream) age: -20, requested chunk_duration: 20, duration: 26
2024-06-11 13-11-14.748 [Debug] (Stream) Silent frames: 910, frames: 910, age: -20.823
2024-06-11 13-11-14.768 [Debug] (Stream) Silent frames: 11, frames: 889, age: -0.253
2024-06-11 13-11-15.013 [Debug] (Stats) Chunk: 28	116	116	116	12	79	0
2024-06-11 13-11-16.007 [Debug] (Stats) Chunk: 203	145	116	116	61	79	0
2024-06-11 13-11-16.815 [Info] (Stream) pShortBuffer->full() && (abs(shortMedian_) > 5): 11617
2024-06-11 13-11-16.835 [Debug] (Stream) age > 0: 20ms, dropping old chunks
2024-06-11 13-11-16.835 [Debug] (Stream) age: -23, requested chunk_duration: 20, duration: 26
2024-06-11 13-11-16.835 [Debug] (Stream) Silent frames: 891, frames: 891, age: -23.403
2024-06-11 13-11-16.855 [Debug] (Stream) Silent frames: 143, frames: 889, age: -3.262
2024-06-11 13-11-17.016 [Debug] (Stats) Chunk: 173	115	115	115	8	79	0
2024-06-11 13-11-18.015 [Debug] (Stats) Chunk: 146	146	117	117	57	80	0
2024-06-11 13-11-18.926 [Info] (Stream) pShortBuffer->full() && (abs(shortMedian_) > 5): 11793
2024-06-11 13-11-18.946 [Debug] (Stream) age > 0: 20ms, dropping old chunks
2024-06-11 13-11-18.946 [Debug] (Stream) age: -1, requested chunk_duration: 20, duration: 26
2024-06-11 13-11-18.946 [Debug] (Stream) Silent frames: 87, frames: 911, age: -1.974
2024-06-11 13-11-19.007 [Debug] (Stats) Chunk: 57	57	57	57	3	80	0
2024-06-11 13-11-20.006 [Debug] (Stats) Chunk: 231	144	144	144	52	80	0
2024-06-11 13-11-21.005 [Info] (Stream) pShortBuffer->full() && (abs(shortMedian_) > 5): 14463
2024-06-11 13-11-21.005 [Debug] (Stats) Chunk: 204	146	144	144	101	80	0
2024-06-11 13-11-21.025 [Debug] (Stream) age > 0: 20ms, dropping old chunks
2024-06-11 13-11-21.026 [Debug] (Stream) age: -11, requested chunk_duration: 20, duration: 26
2024-06-11 13-11-21.026 [Debug] (Stream) Silent frames: 523, frames: 884, age: -11.881
2024-06-11 13-11-22.019 [Debug] (Stats) Chunk: 173	115	115	115	48	80	0
^C2024-06-11 13-11-22.452 [Info] (Snapclient) Received signal 2: Interrupt
2024-06-11 13-11-22.468 [Debug] (Connection) Disconnecting
2024-06-11 13-11-22.468 [Debug] (Connection) Disconnected
2024-06-11 13-11-22.468 [Notice] (Snapclient) Snapclient terminated.

And same setup with patch:

2024-06-11 13-14-32.143 [Debug] (Alsa) Waiting for chunk
2024-06-11 13-14-33.562 [Info] (Alsa) PCM name: default, sample rate: 44100 Hz, channels: 2, buffer time: 80000 us, periods: 4, period time: 20000 us, period frames: 882
2024-06-11 13-14-33.835 [Debug] (Stream) Silent frames: 438, frames: 887, age: -9.948
2024-06-11 13-14-33.855 [Debug] (Stats) Chunk: 0	0	0	0	1	80	0
2024-06-11 13-14-34.026 [Debug] (Stats) Chunk: 0	0	0	0	9	79	0
2024-06-11 13-14-35.005 [Debug] (Stats) Chunk: 0	0	0	0	57	79	0
2024-06-11 13-14-36.009 [Debug] (Stats) Chunk: 0	0	0	0	106	80	0
2024-06-11 13-14-37.004 [Debug] (Stats) Chunk: -2	-2	0	0	155	79	0
2024-06-11 13-14-38.018 [Debug] (Stats) Chunk: -2	-2	-2	0	205	79	0
2024-06-11 13-14-39.017 [Debug] (Stats) Chunk: -2	-2	-2	-1	254	79	4
2024-06-11 13-14-40.020 [Debug] (Stats) Chunk: 0	0	-2	0	303	79	4
2024-06-11 13-14-41.022 [Debug] (Stats) Chunk: 0	1	0	0	352	79	1
2024-06-11 13-14-42.004 [Debug] (Stats) Chunk: 0	0	0	0	400	79	0
2024-06-11 13-14-43.009 [Debug] (Stats) Chunk: 1	1	1	0	450	80	0
2024-06-11 13-14-44.021 [Debug] (Stats) Chunk: 1	1	1	0	500	80	-2
2024-06-11 13-14-45.006 [Debug] (Stats) Chunk: 0	0	1	0	500	80	-4
2024-06-11 13-14-46.020 [Debug] (Stats) Chunk: 0	0	0	0	500	80	-3
2024-06-11 13-14-47.014 [Debug] (Stats) Chunk: 1	1	0	0	500	79	0
2024-06-11 13-14-48.018 [Debug] (Stats) Chunk: 1	1	1	0	500	79	0
2024-06-11 13-14-49.018 [Debug] (Stats) Chunk: 1	1	1	1	500	80	-3
2024-06-11 13-14-50.019 [Debug] (Stats) Chunk: 0	0	1	1	500	79	-3
2024-06-11 13-14-51.002 [Debug] (Stats) Chunk: 0	0	0	1	500	79	-2
2024-06-11 13-14-52.012 [Debug] (Stats) Chunk: 0	0	0	1	500	79	0
2024-06-11 13-14-53.002 [Debug] (Stats) Chunk: 0	0	0	1	500	79	0
^C2024-06-11 13-14-53.579 [Info] (Snapclient) Received signal 2: Interrupt
2024-06-11 13-14-53.594 [Debug] (Connection) Disconnecting
2024-06-11 13-14-53.594 [Debug] (Connection) Disconnected
2024-06-11 13-14-53.594 [Notice] (Snapclient) Snapclient terminated.

@mawe42
Copy link
Contributor Author

mawe42 commented Jun 11, 2024

Just ran again the unpatched version, it actually looks like this, including the FLAC decoder error (don't know where this comes from):

Ah, thanks! I only tested with codec = pcm. I see similar errors when I switch to FLAC and it gets even worse when using Ogg. I will try to make it work with all codecs while still retaining the same timesamp for all jack streams that I need for my use-case.

@mawe42 mawe42 marked this pull request as ready for review June 18, 2024 13:35
@mawe42 mawe42 changed the title [WIP] Add Jack StreamReader Add Jack StreamReader Jun 18, 2024
This brings it into line with all other StreamReaders.
doc/configuration.md Outdated Show resolved Hide resolved
@badaix badaix merged commit 71e5bee into badaix:develop Jun 19, 2024
31 checks passed
@badaix
Copy link
Owner

badaix commented Jun 19, 2024

Merged, thanks!

@mawe42 mawe42 deleted the jack-streamreader branch June 20, 2024 11:34
@mawe42
Copy link
Contributor Author

mawe42 commented Jun 20, 2024

Excellent, thanks for reviewing, testing and your feedback!

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.

2 participants