After recording two and a half hours of video with one channel of the
stereo audio missing, I decided that lavrec should display audio level
meters while recording.
(I also decided that I hate 1/8" plugs even more than I used to).
The attached patch adds a "--vu-meter" option to lavrec from
mjpegtools-1.6.2. The option makes the status output output look somthing
like this when recording stereo audio:
0.00.10:00 int:033 lst: 0 ins: 0 del: 0 ae: 0 td1=0.050 td2=0.009
L:###################- R:##################--
Although the option is called "vu-meter" the actual meter is linear, not
logarithmic, and responds to the peak sample values seen. The
peak-metering code is inspired by that in arecord, from alsa-utils.
Steve
--- mjpegtools-1.6.2-orig/lavtools/audiolib.h 2002-03-23 17:47:57.000000000
-0500
+++ mjpegtools-1.6.2/lavtools/audiolib.h 2004-11-04 22:04:37.000000000
-0500
@@ -28,6 +28,7 @@
int audio_read( uint8_t *buf, int size, int swap,
struct timeval *tmstmp, int *status);
int audio_write( uint8_t *buf, int size, int swap);
+void audio_compute_peaks(uint8_t *buf, int size, int swap, signed int *peaks);
void audio_start(void);
--- mjpegtools-1.6.2-orig/lavtools/audiolib.c 2003-12-01 01:42:51.000000000
-0500
+++ mjpegtools-1.6.2/lavtools/audiolib.c 2004-11-06 22:58:33.018970794
-0500
@@ -627,6 +627,44 @@
}
/*
+ * audio_compute_peaks:
+ * scan buffer of audio samples, and determine
+ * the peak levels found.
+ *
+ * buf Buffer with audio data
+ * size Size of the buffer
+ * swap Flag if to swap the endian-ness of 16 bit data
+ * peaks pointer to pair of integers holding running peak values
+ */
+
+void audio_compute_peaks(uint8_t *buf, int size, int swap, signed int *peaks)
+{
+ signed int val;
+ size_t step;
+ int ch = 0;
+
+ if(audio_size == 0)
+ return;
+
+ while (size > 0) {
+ switch(audio_size) {
+ case 8: val = *(unsigned char *)buf - 128;
+ step = 1; break;
+ case 16: val = *(signed short *)buf;
+ step = 2; break;
+ default: val = 0; step = 1; break;
+ }
+ buf += step;
+ size -= step;
+ val = abs(val);
+ if(peaks[ch] < val)
+ peaks[ch] = val;
+ if(stereo)
+ ch ^= 1;
+ }
+}
+
+/*
* The audio task
*/
--- mjpegtools-1.6.2-orig/lavtools/liblavrec.h 2003-11-14 22:36:17.000000000
-0500
+++ mjpegtools-1.6.2/lavtools/liblavrec.h 2004-11-06 23:08:00.554574371
-0500
@@ -66,6 +66,8 @@
struct timeval prev_sync;
struct timeval cur_sync;
+ int audio_peaks[2];
+ int audio_handled; /* any audio buffers handled ? */
} video_capture_stats;
--- mjpegtools-1.6.2-orig/lavtools/liblavrec.c 2003-08-26 14:09:11.000000000
-0400
+++ mjpegtools-1.6.2/lavtools/liblavrec.c 2004-11-07 00:04:22.077503105
-0500
@@ -1865,6 +1865,7 @@
int nerr = 0;
video_capture_setup *settings = (video_capture_setup *)info->settings;
video_capture_stats *stats = settings->stats;
+ stats->audio_handled = 0;
while (info->audio_size)
{
@@ -1896,6 +1897,8 @@
stats->num_aerr++;
stats->stats_changed = 1;
}
+ stats->audio_handled = 1;
+ audio_compute_peaks((unsigned char*)settings->AUDIO_buff, x, 0,
stats->audio_peaks);
/* Adjust for difference at start */
if (settings->audio_offset >= x)
@@ -1930,6 +1933,7 @@
/* output_statistics */
if (info->output_statistics) info->output_statistics(stats);
stats->stats_changed = 0;
+ stats->audio_peaks[0] = stats->audio_peaks[1] = 0;
stats->prev_sync = stats->cur_sync;
@@ -2048,6 +2052,9 @@
stats.num_aerr = 0;
stats.tdiff1 = 0.;
stats.tdiff2 = 0.;
+ stats.audio_peaks[0] = 0;
+ stats.audio_peaks[1] = 0;
+ stats.audio_handled = 0;
if (info->software_encoding);
settings->bsync.frame = -1;
gettimeofday( &(stats.prev_sync), NULL );
--- mjpegtools-1.6.2-orig/lavtools/lavrec.c 2004-01-09 22:23:53.000000000
-0500
+++ mjpegtools-1.6.2/lavtools/lavrec.c 2004-11-06 23:59:30.737300638 -0500
@@ -215,6 +215,7 @@
static int batch_mode = 0;
static char input_source;
static pthread_t signal_thread;
+static int vumeter = 0;
static void Usage(char *progname)
{
@@ -250,6 +251,7 @@
fprintf(stderr, " -C/--channel LIST:CHAN When using a TV tuner,
channel list/number\n");
fprintf(stderr, " -F/--frequency KHz When using a TV tuner,
frequency in KHz\n");
fprintf(stderr, " -U/--use-read Use read instead of mmap
for recording\n");
+ fprintf(stderr, " --vu-meter show audio level
meter\n");
fprintf(stderr, " --software-encoding Use software
JPEG-encoding (for BTTV-capture)\n");
fprintf(stderr, " --num-procs num Number of encoding
processes (default: 1)\n");
fprintf(stderr, " --max-file-size num Maximum size per file
(in MB)\n");
@@ -538,6 +540,25 @@
}
#endif
+static void draw_vumeter(char label, int level)
+{
+ int max, i, percent;
+ if(info->audio_size == 16) {
+ max = 32767;
+ percent = level / (max/100);
+ } else {
+ max = 127;
+ percent = level * 100 / max;
+ }
+ printf(" %c:", label);
+ for(i = 0; i < 20; i++) {
+ if(percent && (i <= percent/5))
+ putc('#', stdout);
+ else
+ putc('-', stdout);
+ }
+}
+
static void output_stats(video_capture_stats *stats)
{
num_frames = stats->num_frames;
@@ -546,6 +567,8 @@
num_del = stats->num_del;
num_aerr = stats->num_aerr;
+ static int peaks[2];
+
if (show_stats > 0 && !batch_mode)
{
MPEG_timecode_t tc;
@@ -559,12 +582,28 @@
else {
printf(
"%d.%2.2d.%2.2d:%2.2d int:%03ld lst:%3d ins:%3d del:%3d "
- "ae:%3d td1=%.3f td2=%.3f\r",
+ "ae:%3d td1=%.3f td2=%.3f",
tc.h, tc.m, tc.s, tc.f,
(stats->cur_sync.tv_usec - stats->prev_sync.tv_usec)/1000,
stats->num_lost,
stats->num_ins, stats->num_del, stats->num_aerr, stats->tdiff1,
stats->tdiff2);
- if(verbose) printf("\n"); // Keep lines from overlapping
- }
+ if(vumeter) {
+ if(stats->audio_handled) {
+ // if no audio samples handled this frame
+ // (due to low audio bit rate) draw meter using peak
+ // level from previous frame.
+ peaks[0] = stats->audio_peaks[0];
+ peaks[1] = stats->audio_peaks[1];
+ }
+ if(info->stereo) {
+ draw_vumeter('L', peaks[0]);
+ draw_vumeter('R', peaks[1]);
+ } else {
+ draw_vumeter('V', peaks[0]);
+ }
+ }
+ putchar('\r');
+ if(verbose) printf("\n"); // Keep lines from overlapping
+ }
fflush(stdout);
}
}
@@ -739,6 +778,10 @@
nerr++;
}
}
+ else if (strcmp(name, "vu-meter")==0)
+ {
+ vumeter = 1;
+ }
else if (strcmp(name, "mjpeg-buffers")==0 || strcmp(name, "n")==0)
{
info->MJPG_numbufs = atoi(value);
@@ -879,6 +922,7 @@
{"max-file-frames" ,1,0,0}, /* --max-file-frames */
{"file-flush" ,1,0,0}, /* --file-flush */
{"frequency" ,1,0,0}, /* --frequency/-F */
+ {"vu-meter" ,0,0,0}, /* --vu-meter */
{0,0,0,0}
};
#endif
@@ -947,6 +991,9 @@
if(optind>=argc) nerr++;
if(nerr) Usage(argv[0]);
+ if(info->audio_size == 0)
+ vumeter = 0;
+
mjpeg_default_handler_verbosity(verbose);
/* disable audio for jpeg */
--- mjpegtools-1.6.2-orig/docs/lavrec.1 2003-11-14 22:36:17.000000000 -0500
+++ mjpegtools-1.6.2/docs/lavrec.1 2004-11-07 00:20:39.465737975 -0500
@@ -138,6 +138,10 @@
at all.
.TP 8
+.B \-\-vu\-meter
+If this option is given, lavrec will display audio level meters.
+
+.TP 8
.B \-m/\-\-mute
Mute sound output while recording. This can be useful when recording
sound from the microphone to disable echos. This option is disabled by