Internet, UNIX, Video, Leisure…
Stopping Rotter to crash (I Hope)
I talked about Rotter in a previous post. This litte app connect to a Jack plug and record the audio to a file, in mp3, wav or other depending how you compiled it.
This app can also be told to spead files in a tree depending of the date, create a new file every X minutes and remove files older than X hours. Great.
But I had some crashed or unexpected exit. A friend of mine, already using it for a month or two said having no problem with it. But digging in the code let us find what the problem may be :
As we are recording in stereo, we are getting 2 ports from Jack. Then we create one stereo file (in wave).
The process is simple :
Inside a loop, check the jack ring-buffer to see if enough data is here. If yes, write a new chunk of sound. If not, wait for some time and check again.
I first found that the Jack ring-buffer check was made only on the first channel. As we are having 2 channels for stereo, what will happen if the first channel is filled up but not the second ?
Let me guess… a crash ?
NO ![]()
This little app is so well built that it will raise a fatal error and exit gracefuly.
So, what should I do ??
To be sure to have something recorded whatever happen I made 2 changes to the code (CC to the author) :
- check for both channels to be filled before writing sound
- only raise an error and not a fatal error, so « no sound » is recorded instead and the app continues to run.
Since then, no exit or crashed occured.
I had another problem. As my rotter is writing on a NFS mount, I also started a rotter to write on the local filesystem, just in case something goes wrong.
Then I started to see defunct processes like this :
goa 2059 20746 0 11:00 ? 00:00:00 [rotter]
The parent of the defuct process was the local rotter, which was still running fine and writing data.
Cutting the story short, I found it was the « deletion » of old files which was causing the problem. Reading through the code, the deletion process is a fork of the main program. But the main process never check for the child to return… so the child process is always hanging around (in a prent point of view).
Adding a quick « wait() » in the code partly solved the problem. I plane to change this to a « waitpid() » but I had to do this quick before going live.
I finaly changed the « wait » time of the loop. By default, rotter read the Jack ringbuffer once every Rotter-buffer-size/4. I changed it to Rotter-buffer-size/10 as my buffer was 40 seconds (so I check the buffer every 4 seconds… not too many).
In this cas, one can ask « why having a such big buffer if you read it less ? »
I don’t have the answer of this good question. Maybe the secret is to have a 8 seconds buffer and check Jack every 2 seconds ?
So there are the diffs to tweak the code :
diff -u rotter-0.5/src/sndfile.c rotter-0.5-new/src/sndfile.c
--- rotter-0.5/src/sndfile.c 2007-03-22 17:02:37.000000000 +0100
+++ rotter-0.5-new/src/sndfile.c 2007-09-06 00:26:35.000000000 +0200
@@ -70,9 +70,12 @@
}
// Is the enough in the ring buffer?
- if (jack_ringbuffer_read_space( ringbuffer[0] ) < desired) {
+ for (c=0; c
+ {
+ if (jack_ringbuffer_read_space( ringbuffer[c] ) < desired) {
// Try again later
return 0;
+ }
}
// Get the audio out of the ring buffer
@@ -85,7 +88,7 @@
// Copy frames from ring buffer to temporary buffer
bytes_read = jack_ringbuffer_read( ringbuffer[c], (char*)tmp_buffer[c], desired);
if (bytes_read != desired) {
- rotter_fatal( "Failed to read desired number of bytes from ringbuffer." );
+ rotter_error( "Failed to read desired number of bytes %d from ringbuffer of %d on channel %d.", desired, bytes_read, c );
}
}
diff -ru rotter-0.5/src/rotter.c rotter-0.5-RTL/src/rotter.c
--- rotter-0.5/src/rotter.c 2007-06-01 03:57:38.000000000 +0200
+++ rotter-0.5-new/src/rotter.c 2008-03-06 12:06:46.000000000 +0100
@@ -200,7 +200,15 @@
return 0;
}
- if (mkdir(dir, DEFAULT_DIR_MODE) < 0) {
+ // added by prune
+ // get the umask value (by stting it to 0) then set it back
+ mode_t mask = umask (0);
+ umask (mask);
+
+ // Compute the right mode
+ mode_t DIR_MODE = ((0777) ^ mask);
+
+ if (mkdir(dir, DIR_MODE) < 0) {
if (errno == ENOENT) {
// ENOENT (a parent directory doesn't exist)
char* parent = strdup( dir );
@@ -219,7 +227,7 @@
// Try again to create the directory
if (result==0) {
- result = mkdir(dir, DEFAULT_DIR_MODE);
+ result = mkdir(dir, DIR_MODE);
}
} else {
@@ -382,9 +390,9 @@
// Write some audio to disk
int result = encoder->write();
if (result == 0) {
- // Sleep for 1/4 of the ringbuffer duration
- rotter_debug("Failed to write some audio, sleeping for %f sec.", rb_duration/4);
- usleep( (rb_duration/4) * 1000000 );
+ // Sleep for 1/10 of the ringbuffer duration
+ rotter_debug("Failed to write some audio, sleeping for %f sec.", rb_duration/10);
+ usleep( (rb_duration/10) * 1000000 );
} else if (result < 0) {
rotter_fatal("Shutting down, due to encoding error.");
break;
diff -u rotter-0.5/src/deletefiles.c rotter-0.5-new/src/deletefiles.c
--- rotter-0.5/src/deletefiles.c 2007-03-22 17:02:37.000000000 +0100
+++ rotter-0.5-new/src/deletefiles.c 2007-09-06 01:29:11.000000000 +0200
@@ -30,6 +30,7 @@
#include
#include
+#include
#include
#include
@@ -139,6 +140,9 @@
rotter_info( "Deleting files older than %d hours in %s.", hours, dirpath );
+ int mychild;
+ wait(&mychild);
+
// Fork a new process
child_pid = fork();
if (child_pid>0) {