[FFmpeg-devel] [PATCH] libavdevice: JACK demuxer

Olivier Guilyardi list
Fri Feb 27 18:58:55 CET 2009


Hi Jack and FFmpeg developers,

below is a patch against ffmpeg svn r17646 for using JACK as an audio input
device in ffmpeg, as in:

ffmpeg -f jack -ac 2 -i ffmpeg out.wav

This connects to JACK with two input ports: ffmpeg:input_1 and ffmpeg:input_2.
You still need to connect these ports using jack_connect or qjackctl.

This demuxer is already being used in pre-production for teaching lessons
broadcasting, using a DV camera, several mics and icecast.

A downloadable version is available here:
http://www.samalyse.com/code/misc/patches/ffmpeg-r17646-jack-0.3.diff

Index: configure
===================================================================
--- configure	(revision 17646)
+++ configure	(working copy)
@@ -1095,6 +1095,8 @@
 bktr_demuxer_deps_any="dev_bktr_ioctl_bt848_h machine_ioctl_bt848_h
dev_video_bktr_ioctl_bt848_h dev_ic_bt8xx_h"
 dirac_demuxer_deps="dirac_parser"
 dv1394_demuxer_deps="dv1394 dv_demuxer"
+jack_demuxer_deps="jack_jack_h"
+jack_demuxer_extralibs="-ljack -lpthread -lrt"
 libdc1394_demuxer_deps="libdc1394"
 libnut_demuxer_deps="libnut"
 libnut_muxer_deps="libnut"
@@ -2111,6 +2113,8 @@
 check_header alsa/asoundlib.h &&
 check_lib2 alsa/asoundlib.h snd_pcm_htimestamp -lasound

+check_header jack/jack.h
+
 # deal with the X11 frame grabber
 enabled x11grab                         &&
 check_header X11/Xlib.h                 &&
Index: libavdevice/alldevices.c
===================================================================
--- libavdevice/alldevices.c	(revision 17646)
+++ libavdevice/alldevices.c	(working copy)
@@ -48,6 +48,7 @@
     REGISTER_MUXDEMUX (AUDIO_BEOS, audio_beos);
     REGISTER_DEMUXER  (BKTR, bktr);
     REGISTER_DEMUXER  (DV1394, dv1394);
+    REGISTER_DEMUXER  (JACK, jack);
     REGISTER_MUXDEMUX (OSS, oss);
     REGISTER_DEMUXER  (V4L2, v4l2);
     REGISTER_DEMUXER  (V4L, v4l);
Index: libavdevice/jack_audio.c
===================================================================
--- libavdevice/jack_audio.c	(revision 0)
+++ libavdevice/jack_audio.c	(revision 0)
@@ -0,0 +1,316 @@
+/*
+ * JACK Audio Connection Kit interface
+ * Copyright (c) 2009 Samalyse
+ * Author: Olivier Guilyardi <olivier samalyse com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _BSD_SOURCE 1
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+
+#include "libavutil/log.h"
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
+
+// Size of the internal ringbuffer as a number of jack buffers
+#define AV_JACK_RING_NBUFFERS 4
+
+typedef float AVJackSampleType;
+
+typedef enum AVJackOverrunType {
+    AV_JACK_CTL_OVERRUN   = 1,
+    AV_JACK_DATA_OVERRUN  = 2,
+    AV_JACK_XRUN          = 4
+} AVJackOverrunType;
+
+typedef struct {
+    jack_client_t * client;
+    jack_nframes_t  sample_rate;
+    double          frame_ms;
+    jack_nframes_t  buffer_size;
+    int             period;
+    jack_port_t **  ports;
+    int             nports;
+    jack_ringbuffer_t *ctl_rb;
+    jack_ringbuffer_t *data_rb;
+    float *         channel_buffer;
+    AVJackOverrunType volatile overrun;
+} AVJackData;
+
+typedef struct {
+    int64_t         pts;
+    jack_nframes_t  size;
+} AVJackPacketInfo;
+
+static void av_jack_create_ringbuffers(AVJackData *self);
+static int  av_jack_start(AVFormatContext *context, AVFormatParameters *params);
+static void av_jack_stop(AVJackData *self);
+
+static int  av_jack_read_header(AVFormatContext *context, AVFormatParameters
*params);
+static int  av_jack_read_packet(AVFormatContext *context, AVPacket *pkt);
+static int  av_jack_read_close(AVFormatContext *context);
+
+static int  av_jack_process_callback(jack_nframes_t nframes, void *arg);
+static void av_jack_shutdown_callback(void *arg);
+static int  av_jack_xrun_callback(void *arg);
+
+static void av_jack_create_ringbuffers(AVJackData *self)
+{
+    int rb_size;
+
+    rb_size = AV_JACK_RING_NBUFFERS * sizeof(AVJackPacketInfo);
+    self->ctl_rb = jack_ringbuffer_create(rb_size + 1);
+
+    rb_size = self->nports * self->buffer_size * AV_JACK_RING_NBUFFERS *
sizeof(float);
+    self->data_rb = jack_ringbuffer_create(rb_size + 1);
+}
+
+static int av_jack_start(AVFormatContext *context, AVFormatParameters *params)
+{
+    AVJackData *self = context->priv_data;
+    jack_status_t status;
+    char str[16];
+    int i;
+
+    self->client = jack_client_open(context->filename, 0, &status);
+    if (!self->client) {
+        av_log(context, AV_LOG_ERROR, "Unable to register as a JACK client\n");
+        return AVERROR(EIO);
+    }
+    self->sample_rate = jack_get_sample_rate(self->client);
+    self->frame_ms = (double) 1000000.0 / self->sample_rate;
+
+    self->nports = params->channels;
+    self->ports = av_malloc(self->nports * sizeof(jack_port_t *));
+    self->buffer_size = jack_get_buffer_size(self->client);
+    self->period = self->buffer_size * self->frame_ms;
+
+    for (i = 0; i < self->nports; i++) {
+        snprintf(str, 16, "input_%d", i + 1);
+        self->ports[i] = jack_port_register(self->client, str,
+                                            JACK_DEFAULT_AUDIO_TYPE,
+                                            JackPortIsInput, 0);
+        if (!self->ports[i]) {
+            av_log(context, AV_LOG_ERROR, "Unable to register port %s:%s\n",
+                   context->filename, str);
+            jack_client_close(self->client);
+            return AVERROR(EIO);
+        }
+    }
+
+    av_jack_create_ringbuffers(self);
+
+    jack_set_process_callback(self->client, av_jack_process_callback, (void *)
self);
+    jack_on_shutdown(self->client, av_jack_shutdown_callback, (void *) self);
+    jack_set_xrun_callback(self->client, av_jack_xrun_callback, (void *) self);
+
+    if (jack_activate(self->client) != 0) {
+          av_log(context, AV_LOG_ERROR, "Unable to activate JACK client\n");
+          jack_client_close(self->client);
+          return AVERROR(EIO);
+    }
+
+    av_log(context, AV_LOG_INFO, "JACK client registered and activated
(rate=%dHz, buffer_size=%d frames)\n",
+           self->sample_rate, self->buffer_size);
+
+    return 0;
+
+}
+
+static void av_jack_stop(AVJackData *self)
+{
+    if (self->client) {
+        jack_deactivate(self->client);
+        jack_client_close(self->client);
+    }
+    jack_ringbuffer_free(self->ctl_rb);
+    jack_ringbuffer_free(self->data_rb);
+    av_free(self->ports);
+}
+
+static int av_jack_read_header(AVFormatContext *context, AVFormatParameters
*params)
+{
+    AVJackData *self = context->priv_data;
+    AVStream *stream;
+    int test;
+
+    if (params->sample_rate <= 0 || params->channels <= 0)
+        return -1;
+
+    self->overrun = 0;
+
+    if ((test = av_jack_start(context, params)) != 0)
+        return test;
+
+    self->channel_buffer = av_malloc(self->buffer_size * sizeof(float));
+
+    stream = av_new_stream(context, 0);
+    if (!stream) {
+        av_jack_stop(self);
+        return AVERROR(ENOMEM);
+    }
+
+    stream->codec->codec_type = CODEC_TYPE_AUDIO;
+#ifdef WORDS_BIGENDIAN
+    stream->codec->codec_id = CODEC_ID_PCM_F32BE;
+#else
+    stream->codec->codec_id = CODEC_ID_PCM_F32LE;
+#endif
+    stream->codec->sample_rate = self->sample_rate;
+    stream->codec->channels = self->nports;
+
+    av_set_pts_info(stream, 64, 1, 1000000);  /* 64 bits pts in us */
+    return 0;
+}
+
+static int av_jack_process_callback(jack_nframes_t nframes, void *arg)
+{
+    int i;
+    AVJackData *self = arg;
+    void * buffer;
+    AVJackPacketInfo info;
+    double latency, cycle_delay;
+
+    if (!self->client)
+        return 0;
+
+    if (jack_ringbuffer_write_space(self->ctl_rb) >= sizeof(AVJackPacketInfo)) {
+        info.size = jack_ringbuffer_write_space(self->data_rb) / sizeof(float)
/ self->nports;
+
+        if (info.size > nframes)
+            info.size = nframes;
+        else if (info.size < nframes)
+            self->overrun |= AV_JACK_DATA_OVERRUN;
+
+        if (info.size > 0) {
+            latency = 0;
+            for (i = 0; i < self->nports; i++) {
+                latency += jack_port_get_total_latency(self->client,
self->ports[i]);
+                buffer = jack_port_get_buffer(self->ports[i], info.size);
+                jack_ringbuffer_write(self->data_rb, (char *) buffer, info.size
* sizeof(float));
+            }
+
+            latency = latency / self->nports;
+            cycle_delay = jack_frames_since_cycle_start(self->client);
+            info.pts = av_gettime() - (latency + cycle_delay) * self->frame_ms;
+
+            jack_ringbuffer_write(self->ctl_rb, (char *) &info,
sizeof(AVJackPacketInfo));
+        }
+    } else {
+        self->overrun |= AV_JACK_CTL_OVERRUN;
+    }
+    return 0;
+}
+
+static void av_jack_shutdown_callback(void *arg)
+{
+    ((AVJackData *) arg)->client = NULL;
+}
+
+static int  av_jack_xrun_callback(void *arg)
+{
+    ((AVJackData *) arg)->overrun |= AV_JACK_XRUN;
+    return 0;
+}
+
+static int av_jack_read_packet(AVFormatContext *context, AVPacket *pkt)
+{
+    AVJackData *self = context->priv_data;
+    AVJackPacketInfo info;
+    int pkt_size, i, j, timeout;
+    AVJackSampleType *output_data;
+
+    if (self->overrun & AV_JACK_CTL_OVERRUN)
+        av_log(context, AV_LOG_WARNING, "Control ringbuffer overrun\n");
+
+    if (self->overrun & AV_JACK_DATA_OVERRUN)
+        av_log(context, AV_LOG_WARNING, "Data ringbuffer overrun\n");
+
+    if (self->overrun & AV_JACK_XRUN)
+        av_log(context, AV_LOG_WARNING, "JACK xrun\n");
+
+    self->overrun = 0;
+    timeout = 2000000;
+
+    while ((timeout > 0) && self->client
+           && (jack_ringbuffer_read_space(self->ctl_rb) <
sizeof(AVJackPacketInfo))) {
+        usleep(self->period);
+        timeout -= self->period;
+    }
+    if (!self->client) {
+        av_log(context, AV_LOG_ERROR, "Input error: JACK server is gone\n");
+        return AVERROR(EIO);
+    }
+    if (timeout <= 0) {
+        av_log(context, AV_LOG_ERROR,
+               "Input error: timed out (%.2fms) when waiting for JACK process
callback output\n",
+               timeout / 1000.0);
+        return AVERROR(EIO);
+    }
+
+    jack_ringbuffer_read(self->ctl_rb, (char *) &info, sizeof(AVJackPacketInfo));
+
+    pkt_size = info.size * self->nports * sizeof(AVJackSampleType);
+    if (av_new_packet(pkt, pkt_size) < 0) {
+        av_log(context, AV_LOG_ERROR, "Could not create packet of size %d\n",
pkt_size);
+        return AVERROR(EIO);
+    }
+
+    pkt->pts = info.pts;
+
+    output_data = (AVJackSampleType *) pkt->data;
+
+    for (i = 0; i < self->nports; i++) {
+        jack_ringbuffer_read(self->data_rb, (char *) self->channel_buffer,
+                             info.size * sizeof(float));
+        for (j = 0; j < info.size; j++) {
+            output_data[j * self->nports + i] = self->channel_buffer[j];
+        }
+    }
+
+    return 0;
+}
+
+static int av_jack_read_close(AVFormatContext *context)
+{
+    AVJackData *self = context->priv_data;
+    av_jack_stop(self);
+    av_free(self->channel_buffer);
+    return 0;
+}
+
+#if CONFIG_JACK_DEMUXER
+AVInputFormat jack_demuxer = {
+    "jack",
+    NULL_IF_CONFIG_SMALL("JACK Audio Connection Kit"),
+    sizeof(AVJackData),
+    NULL,
+    av_jack_read_header,
+    av_jack_read_packet,
+    av_jack_read_close,
+    .flags = AVFMT_NOFILE,
+};
+#endif
Index: libavdevice/Makefile
===================================================================
--- libavdevice/Makefile	(revision 17646)
+++ libavdevice/Makefile	(working copy)
@@ -12,6 +12,7 @@
 OBJS-$(CONFIG_ALSA_MUXER)                += alsa-audio-common.o alsa-audio-enc.o
 OBJS-$(CONFIG_BKTR_DEMUXER)              += bktr.o
 OBJS-$(CONFIG_DV1394_DEMUXER)            += dv1394.o
+OBJS-$(CONFIG_JACK_DEMUXER)              += jack_audio.o
 OBJS-$(CONFIG_OSS_DEMUXER)               += oss_audio.o
 OBJS-$(CONFIG_OSS_MUXER)                 += oss_audio.o
 OBJS-$(CONFIG_V4L2_DEMUXER)              += v4l2.o


--
  Olivier





More information about the ffmpeg-devel mailing list