[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