Internet, UNIX, Video, Leisure…
naming of Darkice’s Jack port
Darkice is a really good free product when talking about live encoding and streaming. It is the best friend of Icecast, which is the deflector module (ie : the link between live stream and customers).
I used to use Darkice directly from ALSA, the Linux sound layer, and encode my live audio from the soundcard to MP3. But for my new radio project I had to use Jack. I already talked about Jack earlier. It is so damn easy you won’t believe it :
Start jack (or have Jack start automaticaly when you start something using jack – see /etc/jackd.conf)
Start your favorite app, let’s say Darkice
Connect your Darkice plug to the soundcard
and you’re done !
In the special case of Darkice, you can’t specify the name of the Jack plug you will get. This is hardcoded to « darkice-$PID » where $PID is the Darkice process number once started.
This means your Darkice plug name will change at every restart… not really conveniant.
Reading the Darkice Track, I found an opened bug related to this. The writer gives a solution to change that changing name to a static name, which is fine if you don’t have to run multiple Darkice on the same host. I do !
In fact, having a static name is also easyer when you are on a server (not a desktop) and you want to have Darkice connected to another Jack plug automaticaly. Else, you’ll have to find the PID of Darkice, then connect it. Ugly !
So I made another (dirty) patch to achieve that. As Darkice is C++ and is programmed in a way I even can’t read it I wasn’t able to add a new config parameter. Digging in the code showed me that only the first 4 letters of the
So you have plenty of room to add anything you like after that !
In fact it seems the code already use this feature to detect if Darkice have to auto connect to the first 2 Jack inputs -> don’t name your device as « jack_auto » so
My change keeps the same code. I just add something so the « name » defined in the « device » config parameter is accessible by the « open » function of the Jack layer of Darkice.
diff -ru darkice-0.18.1/src/JackDspSource.cpp darkice-0.18.1-new-rbsize/src/JackDspSource.cpp
--- darkice-0.18.1/src/JackDspSource.cpp 2007-02-19 08:56:49.000000000 +0100
+++ darkice-0.18.1-new-rbsize/src/JackDspSource.cpp 2008-03-07 10:21:40.000000000 +0100
@@ -99,6 +99,7 @@
client = NULL;
auto_connect = false; // Default is to not auto connect the JACK ports
tmp_buffer = NULL; // Buffer big enough for one 'read' of audio
+ snprintf(jack_name, 255, "%s", name);
// Auto connect the ports ?
if ( Util::strEq( name, "jack_auto", 9) ) {
@@ -188,7 +189,7 @@
}
// Register client with Jack
- snprintf(client_name, 255, "darkice-%d", getpid());
+ snprintf(client_name, 255, "darkice-%s", jack_name);
if ((client = jack_client_new(client_name)) == NULL) {
throw Exception( __FILE__, __LINE__, "JACK server not running?");
}
@@ -236,7 +237,7 @@
// Create a ring buffer for each channel
- rb_size = 2
+ rb_size = 32
* jack_get_sample_rate(client)
* sizeof (jack_default_audio_sample_t);
for (c=0; c
@@ -277,26 +278,42 @@
JackDspSource :: canRead ( unsigned int sec,
unsigned int usec ) throw ( Exception )
{
- size_t available=0;
-
+ const unsigned int max_wait_time = sec * 1000000;
+ const unsigned int wait_increment = 10000;
+
if ( !isOpen() ) {
return false;
}
- // How many bytes available in ring buffer ?
- available = jack_ringbuffer_read_space( rb[0] );
- if (available) return true;
-
- // Sleep and check again
- // FIXME: should we really sleep the full duration ?
- usleep( (sec*1000000) + usec );
-
- available = jack_ringbuffer_read_space( rb[0] );
- if (available) {
- return true;
- } else {
- return false;
+ unsigned int cur_wait = 0;
+
+ // wait for the buffer to be filled
+ while ( max_wait_time > cur_wait ){
+ bool canRead = true;
+ for (unsigned int c = 0 ; c < getChannel() ; c++)
+ if ( jack_ringbuffer_read_space( rb[c] ) <= 2048 )
+ canRead = false;
+
+ if (canRead)
+ return true;
+
+ cur_wait += wait_increment;
+ usleep ( wait_increment );
+ }
+
+ // impossible to get the buffer after max_wait_time
+ Reporter::reportEvent( 1, "Jack ringbuffer still not ready, waiting ", usec);
+ usleep( usec );
+
+ // check one last time
+ bool canRead = true;
+ for (unsigned int c = 0 ; c < getChannel() ; c++) {
+ if ( jack_ringbuffer_read_space( rb[c] ) <= 0 ) {
+ canRead = false;
+ Reporter::reportEvent( 1, "Jack ringbuffer still not ready on channel", c);
+ }
}
+ return canRead;
}
@@ -324,6 +341,14 @@
throw Exception( __FILE__, __LINE__, "realloc on tmp_buffer failed");
}
+ // We must be sure to fetch as many data on both channels
+ // get minBytesAvailable samples or a smaller size available
+ int minBytesAvailable = samples * sizeof( jack_default_audio_sample_t );
+ for (c=0; c
+ int readable = jack_ringbuffer_read_space( rb[c] );
+ if (readable < minBytesAvailable)
+ minBytesAvailable = readable;
+ }
for (c=0; c
{
@@ -331,7 +356,7 @@
// and then convert samples to output buffer
int bytes_read = jack_ringbuffer_read(rb[c],
(char*)tmp_buffer,
- samples * sizeof( jack_default_audio_sample_t ));
+ minBytesAvailable);
samples_read[c] = bytes_read / sizeof( jack_default_audio_sample_t );
@@ -418,7 +443,8 @@
char *buf = (char*)jack_port_get_buffer(self->ports[c], nframes);
size_t len = jack_ringbuffer_write(self->rb[c], buf, to_write);
if (len < to_write) {
- Reporter::reportEvent( 1, "failed to write to ring ruffer");
+ Reporter::reportEvent( 1, "failed to write to ring ruffer. wrote on channel ", len, to_write, c);
+ usleep( 1000000 );
return 1;
}
}
root@dorado:/opt/src# diff -u darkice-0.18.1/src/JackDspSource.h darkice-0.18.1-new/src/JackDspSource.h
--- darkice-0.18.1/src/JackDspSource.h 2007-02-19 08:56:49.000000000 +0100
+++ darkice-0.18.1-new/src/JackDspSource.h 2007-09-03 19:21:39.000000000 +0200
@@ -93,6 +93,9 @@
*/
bool auto_connect;
+
+ char jack_name[255];
+
protected:
/**
Compile Darkice and install it.
Change your config file so the « device » name start by « jack » and add anything you want the name to be.
For exemple, if the input section is :
[input]
device = jack_in_1
sampleRate = 48000
bitsPerSample = 16 # bits per sample. try 16
channel = 2 # channels. 1 = mono, 2 = stereo
The resulting Jack plugs will be :
darkice-jack_in_1:left
darkice-jack_in_1:right
31 octobre 2008 - 15:58
j’aime beaucoup ton sens critiqu
13 mars 2009 - 22:58
I achived the same thing with some backtick shenanigans and no patches to darkice.
which is cool because i can just the deb from debianmultimedia and get updates without
having to rebuild anything.
i just have a shell script called stream that starts jack , then darkice
and then connects them up using those commands.
jack_connect system:capture_1 `jack_lsp | grep darkice | grep left`
jack_connect system:capture_2 `jack_lsp | grep darkice | grep right`
kudos for taking it to the source though
17 mars 2009 - 11:20
You are using the standard way, which is fine and working, but not clear enought when you are running many darkice at the same time. Then you need to know which process ID is which darkice. It can get quite complicated.
Using my solution I know exactly which is which in a second. That saved my life (or the one of the company i’m working for) multiple times
31 juillet 2011 - 06:21
Amias,
Could you please explain your shell script more? I’m running a single instance of darkice on a headless server, and I think your solution is what I need. Thanks.
26 août 2011 - 13:55
This is not a shell script. it’s a path generated with « diff -u ».
You can either use it with « patch -p0″ but sometimes it fail, moreover if your darkice version is not the exact same as mine.
The other solution is search for lines around the ones starting with + or -.
Add the lines with a + and remove lines with a –
This is as easy as this…
I’m sorry I’m late to answer but let me know if that helped.
26 août 2011 - 14:18
Elliot,
those are two command lines that are run within a script . everything within the backticks is run first and the output from it (the name of the jack ports the darkice accepts input from) is substituted in .
As prune says this will not be so good if you have more than one darkice streamer connected to jack.
if you wanted more darkice instances i’d imagine they would be darkice1 , darkice2 etc in jack so just adjust the first grep to match that instead.
however i’ve never heard of anyone doing more than stereo streaming and multiple bitrate streams can be generated by the icecast server more efficiently so i’d guess it should be fine.
Toodle-pip
Amias