All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] qv4l2: add ALSA audio playback
@ 2013-08-02 12:05 Bård Eirik Winther
  2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
  0 siblings, 1 reply; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

The qv4l2 test utility now supports ALSA playback of audio.
This allows for PCM playback during capture for supported devices.

This requires at least the OpenGL patch series' "qv4l2: add Capture menu" patch.
A device must be ALSA compatible in order to be used with the qv4l2.
The ALSA implementation requires ALSA on the system. If ALSA support is not present,
then this feature will not be compiled in.

Some of the changes/improvements:
- Capturing will also capture audio
- Added audio controls to the capture menu
- Selectable audio devices (can also have no audio)
- Automatically find corresponding audio source for a given video device if applicable
- Supports both radio, video and audio devices that uses PCM.
- Bug fixes

Known issues:
- Sometimes when generating the audio in and out device lists,
  it may take some time for the combo boxes to render correctly.
- If the audio causes underruns/overruns, try increase the audio buffer.
- Not all audio input/output combination will work, depending on system and devices.
- The A-V difference in ms is not always correct, but should still help as an indicator


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] qv4l2: alter capture menu
  2013-08-02 12:05 [PATCH 0/5] qv4l2: add ALSA audio playback Bård Eirik Winther
@ 2013-08-02 12:05 ` Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 2/5] qv4l2: new ALSA stream source code Bård Eirik Winther
                     ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

Corrected Use OpenGL Render to Rendering and removed the separation line.

Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
 utils/qv4l2/qv4l2.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index 4dc5a3e..275b399 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -131,12 +131,11 @@ ApplicationWindow::ApplicationWindow() :
 	QMenu *captureMenu = menuBar()->addMenu("&Capture");
 	captureMenu->addAction(m_capStartAct);
 	captureMenu->addAction(m_showFramesAct);
-	captureMenu->addSeparator();
 
 	if (CaptureWinGL::isSupported()) {
 		m_renderMethod = QV4L2_RENDER_GL;
 
-		m_useGLAct = new QAction("Use Open&GL Render", this);
+		m_useGLAct = new QAction("Use Open&GL Rendering", this);
 		m_useGLAct->setStatusTip("Use GPU with OpenGL for video capture if set.");
 		m_useGLAct->setCheckable(true);
 		m_useGLAct->setChecked(true);
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/5] qv4l2: new ALSA stream source code
  2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
@ 2013-08-02 12:05   ` Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 3/5] qv4l2: add ALSA stream to qv4l2 Bård Eirik Winther
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

Code copied from xawtv3

Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
 utils/qv4l2/alsa_stream.c | 645 ++++++++++++++++++++++++++++++++++++++++++++++
 utils/qv4l2/alsa_stream.h |   5 +
 2 files changed, 650 insertions(+)
 create mode 100644 utils/qv4l2/alsa_stream.c
 create mode 100644 utils/qv4l2/alsa_stream.h

diff --git a/utils/qv4l2/alsa_stream.c b/utils/qv4l2/alsa_stream.c
new file mode 100644
index 0000000..3e33b5e
--- /dev/null
+++ b/utils/qv4l2/alsa_stream.c
@@ -0,0 +1,645 @@
+/*
+ *  ALSA streaming support
+ *
+ *  Originally written by:
+ *      Copyright (c) by Devin Heitmueller <dheitmueller@kernellabs.com>
+ *	for usage at tvtime
+ *  Derived from the alsa-driver test tool latency.c:
+ *    Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *  Copyright (c) 2011 - Mauro Carvalho Chehab <mchehab@redhat.com>
+ *	Ported to xawtv, with bug fixes and improvements
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include "config.h"
+
+#ifdef HAVE_ALSA_ASOUNDLIB_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <alsa/asoundlib.h>
+#include <sys/time.h>
+#include <math.h>
+#include "alsa_stream.h"
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
+
+/* Private vars to control alsa thread status */
+static int stop_alsa = 0;
+
+/* Error handlers */
+snd_output_t *output = NULL;
+FILE *error_fp;
+int verbose = 0;
+
+struct final_params {
+    int bufsize;
+    int rate;
+    int latency;
+    int channels;
+};
+
+static int setparams_stream(snd_pcm_t *handle,
+			    snd_pcm_hw_params_t *params,
+			    snd_pcm_format_t format,
+			    int *channels,
+			    const char *id)
+{
+    int err;
+
+    err = snd_pcm_hw_params_any(handle, params);
+    if (err < 0) {
+	fprintf(error_fp,
+		"alsa: Broken configuration for %s PCM: no configurations available: %s\n",
+		snd_strerror(err), id);
+	return err;
+    }
+
+    err = snd_pcm_hw_params_set_access(handle, params,
+				       SND_PCM_ACCESS_RW_INTERLEAVED);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Access type not available for %s: %s\n", id,
+		snd_strerror(err));
+	return err;
+    }
+
+    err = snd_pcm_hw_params_set_format(handle, params, format);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Sample format not available for %s: %s\n", id,
+	       snd_strerror(err));
+	return err;
+    }
+
+retry:
+    err = snd_pcm_hw_params_set_channels(handle, params, *channels);
+    if (err < 0) {
+	if (strcmp(id, "capture") == 0 && *channels == 2) {
+	    *channels = 1;
+	    goto retry; /* Retry with mono capture */
+	}
+	fprintf(error_fp, "alsa: Channels count (%i) not available for %s: %s\n",
+		*channels, id, snd_strerror(err));
+	return err;
+    }
+
+    return 0;
+}
+
+static void getparams_periods(snd_pcm_t *handle,
+		      snd_pcm_hw_params_t *params,
+		      unsigned int *usecs,
+		      unsigned int *count,
+		      const char *id)
+{
+    unsigned min = 0, max = 0;
+
+    snd_pcm_hw_params_get_periods_min(params, &min, 0);
+    snd_pcm_hw_params_get_periods_max(params, &max, 0);
+    if (min && max) {
+	if (verbose)
+	    fprintf(error_fp, "alsa: %s periods range between %u and %u. Want: %u\n",
+		    id, min, max, *count);
+	if (*count < min)
+	    *count = min;
+	if (*count > max)
+	    *count = max;
+    }
+
+    min = max = 0;
+    snd_pcm_hw_params_get_period_time_min(params, &min, 0);
+    snd_pcm_hw_params_get_period_time_max(params, &max, 0);
+    if (min && max) {
+	if (verbose)
+	    fprintf(error_fp, "alsa: %s period time range between %u and %u. Want: %u\n",
+		    id, min, max, *usecs);
+	if (*usecs < min)
+	    *usecs = min;
+	if (*usecs > max)
+	    *usecs = max;
+    }
+}
+
+static int setparams_periods(snd_pcm_t *handle,
+		      snd_pcm_hw_params_t *params,
+		      unsigned int *usecs,
+		      unsigned int *count,
+		      const char *id)
+{
+    int err;
+
+    err = snd_pcm_hw_params_set_period_time_near(handle, params, usecs, 0);
+    if (err < 0) {
+	    fprintf(error_fp, "alsa: Unable to set period time %u for %s: %s\n",
+		    *usecs, id, snd_strerror(err));
+	    return err;
+    }
+
+    err = snd_pcm_hw_params_set_periods_near(handle, params, count, 0);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to set %u periods for %s: %s\n",
+		*count, id, snd_strerror(err));
+	return err;
+    }
+
+    if (verbose)
+	fprintf(error_fp, "alsa: %s period set to %u periods of %u time\n",
+		id, *count, *usecs);
+
+    return 0;
+}
+
+static int setparams_set(snd_pcm_t *handle,
+			 snd_pcm_hw_params_t *params,
+			 snd_pcm_sw_params_t *swparams,
+			 snd_pcm_uframes_t start_treshold,
+			 const char *id)
+{
+    int err;
+
+    err = snd_pcm_hw_params(handle, params);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to set hw params for %s: %s\n",
+		id, snd_strerror(err));
+	return err;
+    }
+    err = snd_pcm_sw_params_current(handle, swparams);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to determine current swparams for %s: %s\n",
+		id, snd_strerror(err));
+	return err;
+    }
+    err = snd_pcm_sw_params_set_start_threshold(handle, swparams,
+						start_treshold);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to set start threshold mode for %s: %s\n",
+		id, snd_strerror(err));
+	return err;
+    }
+
+    err = snd_pcm_sw_params_set_avail_min(handle, swparams, 4);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to set avail min for %s: %s\n",
+		id, snd_strerror(err));
+	return err;
+    }
+    err = snd_pcm_sw_params(handle, swparams);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Unable to set sw params for %s: %s\n",
+		id, snd_strerror(err));
+	return err;
+    }
+    return 0;
+}
+
+static int alsa_try_rate(snd_pcm_t *phandle, snd_pcm_t *chandle,
+                        snd_pcm_hw_params_t *p_hwparams,
+                        snd_pcm_hw_params_t *c_hwparams,
+                        int allow_resample, unsigned *ratep, unsigned *ratec)
+{
+    int err;
+
+    err = snd_pcm_hw_params_set_rate_near(chandle, c_hwparams, ratec, 0);
+    if (err)
+        return err;
+
+    *ratep = *ratec;
+    err = snd_pcm_hw_params_set_rate_near(phandle, p_hwparams, ratep, 0);
+    if (err)
+        return err;
+
+    if (*ratep == *ratec)
+        return 0;
+
+    if (verbose)
+        fprintf(error_fp,
+                "alsa_try_rate: capture wanted %u, playback wanted %u%s\n",
+                *ratec, *ratep, allow_resample ? " with resample enabled": "");
+
+    return 1; /* No error, but also no match */
+}
+
+static int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
+		     snd_pcm_format_t format,
+		     int latency, int allow_resample,
+		     struct final_params *negotiated)
+{
+    int i;
+    unsigned ratep, ratec = 0;
+    unsigned ratemin = 32000, ratemax = 96000, val;
+    int err, channels = 2;
+    snd_pcm_hw_params_t *p_hwparams, *c_hwparams;
+    snd_pcm_sw_params_t *p_swparams, *c_swparams;
+    snd_pcm_uframes_t c_size, p_psize, c_psize;
+    /* Our latency is 2 periods (in usecs) */
+    unsigned int c_periods = 2, p_periods;
+    unsigned int c_periodtime, p_periodtime;
+    const unsigned int prefered_rates[] = { 44100, 48000, 32000 };
+
+    snd_pcm_hw_params_alloca(&p_hwparams);
+    snd_pcm_hw_params_alloca(&c_hwparams);
+    snd_pcm_sw_params_alloca(&p_swparams);
+    snd_pcm_sw_params_alloca(&c_swparams);
+
+    if (setparams_stream(chandle, c_hwparams, format, &channels, "capture"))
+	return 1;
+
+    if (setparams_stream(phandle, p_hwparams, format, &channels, "playback"))
+	return 1;
+
+    if (allow_resample) {
+	err = snd_pcm_hw_params_set_rate_resample(chandle, c_hwparams, 1);
+	if (err < 0) {
+	    fprintf(error_fp, "alsa: Resample setup failed: %s\n", snd_strerror(err));
+	    return 1;
+	} else if (verbose)
+	   fprintf(error_fp, "alsa: Resample enabled.\n");
+    }
+
+    err = snd_pcm_hw_params_get_rate_min(c_hwparams, &ratemin, 0);
+    if (err >= 0 && verbose)
+	fprintf(error_fp, "alsa: Capture min rate is %d\n", ratemin);
+    err = snd_pcm_hw_params_get_rate_max(c_hwparams, &ratemax, 0);
+    if (err >= 0 && verbose)
+	fprintf(error_fp, "alsa: Capture max rate is %u\n", ratemax);
+
+    err = snd_pcm_hw_params_get_rate_min(p_hwparams, &val, 0);
+    if (err >= 0) {
+	if (verbose)
+	    fprintf(error_fp, "alsa: Playback min rate is %u\n", val);
+	if (val > ratemin)
+		ratemin = val;
+    }
+    err = snd_pcm_hw_params_get_rate_max(p_hwparams, &val, 0);
+    if (err >= 0) {
+	if (verbose)
+	    fprintf(error_fp, "alsa: Playback max rate is %u\n", val);
+	if (val < ratemax)
+		ratemax = val;
+    }
+
+    if (verbose)
+	fprintf(error_fp,
+	        "alsa: Will search a common rate between %u and %u\n",
+		ratemin, ratemax);
+
+    /* First try a set of common rates */
+    err = -1;
+    for (i = 0; i < ARRAY_SIZE(prefered_rates); i++) {
+        if (prefered_rates[i] < ratemin || prefered_rates[i] > ratemax)
+            continue;
+        ratep = ratec = prefered_rates[i];
+        err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
+                            allow_resample, &ratep, &ratec);
+        if (err == 0)
+            break;
+    }
+
+    if (err != 0) {
+        if (ratemin >= 44100) {
+            for (i = ratemin; i <= ratemax; i += 100) {
+                ratep = ratec = i;
+                err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
+                                    allow_resample, &ratep, &ratec);
+                if (err == 0)
+                    break;
+            }
+        } else {
+            for (i = ratemax; i >= ratemin; i -= 100) {
+                ratep = ratec = i;
+                err = alsa_try_rate(phandle, chandle, p_hwparams, c_hwparams,
+                                    allow_resample, &ratep, &ratec);
+                if (err == 0)
+                    break;
+            }
+        }
+    }
+
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Failed to set a supported rate: %s\n",
+		snd_strerror(err));
+	return 1;
+    }
+    if (ratep != ratec) {
+	if (verbose || allow_resample)
+	    fprintf(error_fp,
+		    "alsa: Couldn't find a rate that it is supported by both playback and capture\n");
+	return 2;
+    }
+    if (verbose)
+	fprintf(error_fp, "alsa: Using Rate %d\n", ratec);
+
+    /* Negotiate period parameters */
+
+    c_periodtime = latency * 1000 / c_periods;
+    getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture");
+    p_periods = c_periods * 2;
+    p_periodtime = c_periodtime;
+    getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback");
+    c_periods = p_periods / 2;
+
+    /*
+     * Some playback devices support a very limited periodtime range. If the user needs to
+     * use a higher latency to avoid overrun/underrun, use an alternate algorithm of incresing
+     * the number of periods, to archive the needed latency
+     */
+    if (p_periodtime < c_periodtime) {
+	c_periodtime = p_periodtime;
+	c_periods = round (latency * 1000.0 / c_periodtime + 0.5);
+	getparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture");
+	p_periods = c_periods * 2;
+	p_periodtime = c_periodtime;
+	getparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback");
+	c_periods = p_periods / 2;
+    }
+
+    if (setparams_periods(chandle, c_hwparams, &c_periodtime, &c_periods, "capture"))
+	return 1;
+
+    /* Note we use twice as much periods for the playback buffer, since we
+       will get a period size near the requested time and we don't want it to
+       end up smaller then the capture buffer as then we could end up blocking
+       on writing to it. Note we will configure the playback dev to start
+       playing as soon as it has 2 capture periods worth of data, so this
+       won't influence latency */
+    if (setparams_periods(phandle, p_hwparams, &p_periodtime, &p_periods, "playback"))
+	return 1;
+
+    snd_pcm_hw_params_get_period_size(p_hwparams, &p_psize, NULL);
+    snd_pcm_hw_params_get_period_size(c_hwparams, &c_psize, NULL);
+    snd_pcm_hw_params_get_buffer_size(c_hwparams, &c_size);
+
+    latency = c_periods * c_psize;
+    if (setparams_set(phandle, p_hwparams, p_swparams, latency, "playback"))
+	return 1;
+
+    if (setparams_set(chandle, c_hwparams, c_swparams, c_psize, "capture"))
+	return 1;
+
+    if ((err = snd_pcm_prepare(phandle)) < 0) {
+	fprintf(error_fp, "alsa: Prepare error: %s\n", snd_strerror(err));
+	return 1;
+    }
+
+    if (verbose) {
+	fprintf(error_fp, "alsa: Negociated configuration:\n");
+	snd_pcm_dump_setup(phandle, output);
+	snd_pcm_dump_setup(chandle, output);
+	fprintf(error_fp, "alsa: Parameters are %iHz, %s, %i channels\n",
+		ratep, snd_pcm_format_name(format), channels);
+	fprintf(error_fp, "alsa: Set bitrate to %u%s, buffer size is %u\n", ratec,
+		allow_resample ? " with resample enabled at playback": "",
+		(unsigned int)c_size);
+    }
+
+    negotiated->bufsize = c_size;
+    negotiated->rate = ratep;
+    negotiated->channels = channels;
+    negotiated->latency = latency;
+    return 0;
+}
+
+/* Read up to len frames */
+static snd_pcm_sframes_t readbuf(snd_pcm_t *handle, char *buf, long len)
+{
+    snd_pcm_sframes_t r;
+
+    r = snd_pcm_readi(handle, buf, len);
+    if (r < 0 && r != -EAGAIN) {
+	r = snd_pcm_recover(handle, r, 0);
+	if (r < 0)
+	    fprintf(error_fp, "alsa: overrun recover error: %s\n", snd_strerror(r));
+    }
+    return r;
+}
+
+/* Write len frames (note not up to len, but all of len!) */
+static snd_pcm_sframes_t writebuf(snd_pcm_t *handle, char *buf, long len)
+{
+    snd_pcm_sframes_t r;
+
+    while (1) {
+	r = snd_pcm_writei(handle, buf, len);
+	if (r == len)
+	    return 0;
+	if (r < 0) {
+	    r = snd_pcm_recover(handle, r, 0);
+	    if (r < 0) {
+		fprintf(error_fp, "alsa: underrun recover error: %s\n",
+			snd_strerror(r));
+		return r;
+	    }
+	}
+	buf += r * 4;
+	len -= r;
+	snd_pcm_wait(handle, 100);
+    }
+}
+
+static int alsa_stream(const char *pdevice, const char *cdevice, int latency)
+{
+    snd_pcm_t *phandle, *chandle;
+    char *buffer;
+    int err;
+    ssize_t r;
+    struct final_params negotiated;
+    snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
+    char pdevice_new[32];
+
+    err = snd_output_stdio_attach(&output, error_fp, 0);
+    if (err < 0) {
+	fprintf(error_fp, "alsa: Output failed: %s\n", snd_strerror(err));
+	return 0;
+    }
+
+    /* Open the devices */
+    if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK,
+			    0)) < 0) {
+	fprintf(error_fp, "alsa: Cannot open playback device %s: %s\n",
+		pdevice, snd_strerror(err));
+	return 0;
+    }
+    if ((err = snd_pcm_open(&chandle, cdevice, SND_PCM_STREAM_CAPTURE,
+			    SND_PCM_NONBLOCK)) < 0) {
+	fprintf(error_fp, "alsa: Cannot open capture device %s: %s\n",
+		cdevice, snd_strerror(err));
+	snd_pcm_close(phandle);
+	return 0;
+    }
+
+    err = setparams(phandle, chandle, format, latency, 0, &negotiated);
+
+    /* Try to use plughw instead, as it allows emulating speed */
+    if (err == 2 && strncmp(pdevice, "hw", 2) == 0) {
+
+	snd_pcm_close(phandle);
+
+	sprintf(pdevice_new, "plug%s", pdevice);
+	pdevice = pdevice_new;
+	if (verbose)
+	    fprintf(error_fp, "alsa: Trying %s for playback\n", pdevice);
+	if ((err = snd_pcm_open(&phandle, pdevice, SND_PCM_STREAM_PLAYBACK,
+				0)) < 0) {
+	    fprintf(error_fp, "alsa: Cannot open playback device %s: %s\n",
+		    pdevice, snd_strerror(err));
+	    snd_pcm_close(chandle);
+	    return 0;
+	}
+
+	err = setparams(phandle, chandle, format, latency, 1, &negotiated);
+    }
+
+    if (err != 0) {
+	fprintf(error_fp, "alsa: setparams failed\n");
+	snd_pcm_close(phandle);
+	snd_pcm_close(chandle);
+	return 1;
+    }
+
+    buffer = malloc((negotiated.bufsize * snd_pcm_format_width(format) / 8)
+		    * negotiated.channels);
+    if (buffer == NULL) {
+	fprintf(error_fp, "alsa: Failed allocating buffer for audio\n");
+	snd_pcm_close(phandle);
+	snd_pcm_close(chandle);
+	return 0;
+    }
+
+    if (verbose)
+        fprintf(error_fp,
+	    "alsa: stream started from %s to %s (%i Hz, buffer delay = %.2f ms)\n",
+	    cdevice, pdevice, negotiated.rate,
+	    negotiated.latency * 1000.0 / negotiated.rate);
+
+    while (!stop_alsa) {
+	/* We start with a read and not a wait to auto(re)start the capture */
+	r = readbuf(chandle, buffer, negotiated.bufsize);
+	if (r == 0)   /* Succesfully recovered from an overrun? */
+	    continue; /* Force restart of capture stream */
+	if (r > 0)
+	    writebuf(phandle, buffer, r);
+	/* use poll to wait for next event */
+	while (!stop_alsa && !snd_pcm_wait(chandle, 50))
+	    ;
+    }
+
+    snd_pcm_drop(chandle);
+    snd_pcm_drop(phandle);
+
+    snd_pcm_unlink(chandle);
+    snd_pcm_hw_free(phandle);
+    snd_pcm_hw_free(chandle);
+
+    snd_pcm_close(phandle);
+    snd_pcm_close(chandle);
+
+    return 0;
+}
+
+struct input_params {
+    char *pdevice;
+    char *cdevice;
+    int latency;
+};
+
+static void *alsa_thread_entry(void *whatever)
+{
+    struct input_params *inputs = (struct input_params *) whatever;
+
+    if (verbose)
+	fprintf(error_fp, "alsa: starting copying alsa stream from %s to %s\n",
+		inputs->cdevice, inputs->pdevice);
+    alsa_stream(inputs->pdevice, inputs->cdevice, inputs->latency);
+    if (verbose)
+        fprintf(error_fp, "alsa: stream stopped\n");
+
+    free(inputs->pdevice);
+    free(inputs->cdevice);
+    free(inputs);
+
+    return NULL;
+}
+
+/*************************************************************************
+ Public functions
+ *************************************************************************/
+
+static int alsa_is_running = 0;
+static pthread_t alsa_thread;
+
+int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
+			FILE *__error_fp, int __verbose)
+{
+    int ret;
+    struct input_params *inputs;
+
+    if ((strcasecmp(pdevice, "disabled") == 0) ||
+	(strcasecmp(cdevice, "disabled") == 0))
+	return 0;
+
+    if (__error_fp)
+	error_fp = __error_fp;
+    else
+	error_fp = stderr;
+
+    verbose = __verbose;
+
+    if (alsa_is_running) {
+        fprintf(error_fp, "alsa: Already running\n");
+        return EBUSY;
+    }
+
+    inputs = malloc(sizeof(struct input_params));
+    if (inputs == NULL) {
+	fprintf(error_fp, "alsa: failed allocating memory for inputs\n");
+	return ENOMEM;
+    }
+
+    inputs->pdevice = strdup(pdevice);
+    inputs->cdevice = strdup(cdevice);
+    inputs->latency = latency;
+
+    stop_alsa = 0;
+    ret = pthread_create(&alsa_thread, NULL,
+			 &alsa_thread_entry, (void *) inputs);
+    if (ret == 0)
+        alsa_is_running = 1;
+
+    return ret;
+}
+
+void alsa_thread_stop(void)
+{
+    if (!alsa_is_running)
+        return;
+
+    stop_alsa = 1;
+    pthread_join(alsa_thread, NULL);
+    alsa_is_running = 0;
+}
+
+int alsa_thread_is_running(void)
+{
+    return alsa_is_running;
+}
+
+#endif
diff --git a/utils/qv4l2/alsa_stream.h b/utils/qv4l2/alsa_stream.h
new file mode 100644
index 0000000..c68fd6d
--- /dev/null
+++ b/utils/qv4l2/alsa_stream.h
@@ -0,0 +1,5 @@
+int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
+			FILE *__error_fp,
+			int __verbose);
+void alsa_thread_stop(void);
+int alsa_thread_is_running(void);
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/5] qv4l2: add ALSA stream to qv4l2
  2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 2/5] qv4l2: new ALSA stream source code Bård Eirik Winther
@ 2013-08-02 12:05   ` Bård Eirik Winther
  2013-08-02 12:25     ` Hans Verkuil
  2013-08-02 22:19     ` Gregor Jasny
  2013-08-02 12:05   ` [PATCH 4/5] qv4l2: fix a bug where the alsa thread never stops Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 5/5] qv4l2: add ALSA audio playback Bård Eirik Winther
  3 siblings, 2 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

Changes the ALSA streaming code to work with qv4l2 and allows it to
be compiled in. qv4l2 does not use the streaming function yet.

Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
 configure.ac              |  6 ++++++
 utils/qv4l2/Makefile.am   |  9 ++++++++-
 utils/qv4l2/alsa_stream.c | 21 +++++++++++++++------
 utils/qv4l2/alsa_stream.h | 13 ++++++++++---
 4 files changed, 39 insertions(+), 10 deletions(-)

diff --git a/configure.ac b/configure.ac
index d74da61..e12507e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,6 +136,11 @@ if test "x$qt_pkgconfig_gl" = "xfalse"; then
    AC_MSG_WARN(Qt4 OpenGL or higher is not available)
 fi
 
+PKG_CHECK_MODULES(ALSA, [alsa], [alsa_pkgconfig=true], [alsa_pkgconfig=false])
+if test "x$alsa_pkgconfig" = "xfalse"; then
+   AC_MSG_WARN(ALSA library not available)
+fi
+
 AC_SUBST([JPEG_LIBS])
 
 # The dlopen() function is in the C library for *BSD and in
@@ -243,6 +248,7 @@ AM_CONDITIONAL([WITH_LIBV4L], [test x$enable_libv4l != xno])
 AM_CONDITIONAL([WITH_V4LUTILS], [test x$enable_v4lutils != xno])
 AM_CONDITIONAL([WITH_QV4L2], [test ${qt_pkgconfig} = true -a x$enable_qv4l2 != xno])
 AM_CONDITIONAL([WITH_QV4L2_GL], [test WITH_QV4L2 -a ${qt_pkgconfig_gl} = true])
+AM_CONDITIONAL([WITH_QV4L2_ALSA], [test WITH_QV4L2 -a ${alsa_pkgconfig} = true])
 AM_CONDITIONAL([WITH_V4L_PLUGINS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
 AM_CONDITIONAL([WITH_V4L_WRAPPERS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
 
diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
index 22d4c17..eed25b0 100644
--- a/utils/qv4l2/Makefile.am
+++ b/utils/qv4l2/Makefile.am
@@ -4,7 +4,8 @@ qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp
   capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h \
   raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h v4l2-api.h raw2sliced.h
 nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
-qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la
+qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la \
+  ../libmedia_dev/libmedia_dev.la
 
 if WITH_QV4L2_GL
 qv4l2_CPPFLAGS = $(QTGL_CFLAGS) -DENABLE_GL
@@ -14,6 +15,12 @@ qv4l2_CPPFLAGS = $(QT_CFLAGS)
 qv4l2_LDFLAGS = $(QT_LIBS)
 endif
 
+if WITH_QV4L2_ALSA
+qv4l2_CPPFLAGS += $(ALSA_CFLAGS) -DENABLE_ALSA
+qv4l2_LDFLAGS += $(ALSA_LIBS) -pthread
+qv4l2_SOURCES += alsa_stream.c alsa_stream.h
+endif
+
 EXTRA_DIST = exit.png fileopen.png qv4l2_24x24.png qv4l2_64x64.png qv4l2.png qv4l2.svg snapshot.png \
   video-television.png fileclose.png qv4l2_16x16.png qv4l2_32x32.png qv4l2.desktop qv4l2.qrc record.png \
   saveraw.png qv4l2.pro
diff --git a/utils/qv4l2/alsa_stream.c b/utils/qv4l2/alsa_stream.c
index 3e33b5e..90d3afb 100644
--- a/utils/qv4l2/alsa_stream.c
+++ b/utils/qv4l2/alsa_stream.c
@@ -26,9 +26,7 @@
  *
  */
 
-#include "config.h"
-
-#ifdef HAVE_ALSA_ASOUNDLIB_H
+#include "alsa_stream.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -40,12 +38,12 @@
 #include <alsa/asoundlib.h>
 #include <sys/time.h>
 #include <math.h>
-#include "alsa_stream.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
 
 /* Private vars to control alsa thread status */
 static int stop_alsa = 0;
+static snd_htimestamp_t timestamp;
 
 /* Error handlers */
 snd_output_t *output = NULL;
@@ -422,7 +420,8 @@ static int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
 static snd_pcm_sframes_t readbuf(snd_pcm_t *handle, char *buf, long len)
 {
     snd_pcm_sframes_t r;
-
+    snd_pcm_uframes_t frames;
+    snd_pcm_htimestamp(handle, &frames, &timestamp);
     r = snd_pcm_readi(handle, buf, len);
     if (r < 0 && r != -EAGAIN) {
 	r = snd_pcm_recover(handle, r, 0);
@@ -453,6 +452,7 @@ static snd_pcm_sframes_t writebuf(snd_pcm_t *handle, char *buf, long len)
 	len -= r;
 	snd_pcm_wait(handle, 100);
     }
+    return -1;
 }
 
 static int alsa_stream(const char *pdevice, const char *cdevice, int latency)
@@ -642,4 +642,13 @@ int alsa_thread_is_running(void)
     return alsa_is_running;
 }
 
-#endif
+void alsa_thread_timestamp(struct timeval *tv)
+{
+	if (alsa_thread_is_running()) {
+		tv->tv_sec = timestamp.tv_sec;
+		tv->tv_usec = timestamp.tv_nsec / 1000;
+	} else {
+		tv->tv_sec = 1337;
+		tv->tv_usec = 0;
+	}
+}
diff --git a/utils/qv4l2/alsa_stream.h b/utils/qv4l2/alsa_stream.h
index c68fd6d..b74c3aa 100644
--- a/utils/qv4l2/alsa_stream.h
+++ b/utils/qv4l2/alsa_stream.h
@@ -1,5 +1,12 @@
-int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
-			FILE *__error_fp,
-			int __verbose);
+#ifndef ALSA_STRAM_H
+#define ALSA_STRAM_H
+
+#include <stdio.h>
+#include <sys/time.h>
+
+int alsa_thread_startup(const char *pdevice, const char *cdevice,
+			int latency, FILE *__error_fp, int __verbose);
 void alsa_thread_stop(void);
 int alsa_thread_is_running(void);
+void alsa_thread_timestamp(struct timeval *tv);
+#endif
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/5] qv4l2: fix a bug where the alsa thread never stops
  2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 2/5] qv4l2: new ALSA stream source code Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 3/5] qv4l2: add ALSA stream to qv4l2 Bård Eirik Winther
@ 2013-08-02 12:05   ` Bård Eirik Winther
  2013-08-02 12:05   ` [PATCH 5/5] qv4l2: add ALSA audio playback Bård Eirik Winther
  3 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

If the output audio device never read the buffer then the alsa thread
would continue to fill it up and never stop when the capture stops.

Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
 utils/qv4l2/alsa_stream.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/qv4l2/alsa_stream.c b/utils/qv4l2/alsa_stream.c
index 90d3afb..43ecda3 100644
--- a/utils/qv4l2/alsa_stream.c
+++ b/utils/qv4l2/alsa_stream.c
@@ -436,7 +436,7 @@ static snd_pcm_sframes_t writebuf(snd_pcm_t *handle, char *buf, long len)
 {
     snd_pcm_sframes_t r;
 
-    while (1) {
+    while (!stop_alsa) {
 	r = snd_pcm_writei(handle, buf, len);
 	if (r == len)
 	    return 0;
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/5] qv4l2: add ALSA audio playback
  2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
                     ` (2 preceding siblings ...)
  2013-08-02 12:05   ` [PATCH 4/5] qv4l2: fix a bug where the alsa thread never stops Bård Eirik Winther
@ 2013-08-02 12:05   ` Bård Eirik Winther
  3 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-02 12:05 UTC (permalink / raw)
  To: linux-media

The qv4l2 test utility now supports ALSA playback of audio.
This allows for PCM playback during capture for supported devices.

Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
---
 utils/qv4l2/general-tab.cpp | 296 +++++++++++++++++++++++++++++++++++++++++++-
 utils/qv4l2/general-tab.h   |  36 ++++++
 utils/qv4l2/qv4l2.cpp       | 143 ++++++++++++++++++++-
 utils/qv4l2/qv4l2.h         |   7 ++
 4 files changed, 478 insertions(+), 4 deletions(-)

diff --git a/utils/qv4l2/general-tab.cpp b/utils/qv4l2/general-tab.cpp
index 10b14ca..5996c03 100644
--- a/utils/qv4l2/general-tab.cpp
+++ b/utils/qv4l2/general-tab.cpp
@@ -30,6 +30,16 @@
 
 #include <stdio.h>
 #include <errno.h>
+#include <QRegExp>
+
+bool GeneralTab::m_fullAudioName = false;
+
+enum audioDeviceAdd {
+	AUDIO_ADD_NO,
+	AUDIO_ADD_READ,
+	AUDIO_ADD_WRITE,
+	AUDIO_ADD_READWRITE
+};
 
 GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent) :
 	QGridLayout(parent),
@@ -48,12 +58,16 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent)
 	m_vidCapFormats(NULL),
 	m_frameSize(NULL),
 	m_vidOutFormats(NULL),
-	m_vbiMethods(NULL)
+	m_vbiMethods(NULL),
+	m_audioInDevice(NULL),
+	m_audioOutDevice(NULL)
 {
+	m_device.append(device);
 	setSpacing(3);
 
 	setSizeConstraint(QLayout::SetMinimumSize);
 
+
 	if (querycap(m_querycap)) {
 		addLabel("Device:");
 		addLabel(device + (useWrapper() ? " (wrapped)" : ""), Qt::AlignLeft);
@@ -132,6 +146,42 @@ GeneralTab::GeneralTab(const QString &device, v4l2 &fd, int n, QWidget *parent)
 		updateAudioOutput();
 	}
 
+	if (hasAlsaAudio()) {
+		m_audioInDevice = new QComboBox(parent);
+		m_audioOutDevice = new QComboBox(parent);
+		m_audioInDevice->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+		m_audioOutDevice->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+		if (createAudioDeviceList()) {
+			addLabel("Audio Input Device");
+			connect(m_audioInDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
+			addWidget(m_audioInDevice);
+
+			addLabel("Audio Output Device");
+			connect(m_audioOutDevice, SIGNAL(activated(int)), SLOT(changeAudioDevice()));
+			addWidget(m_audioOutDevice);
+
+			if (isRadio()) {
+				setAudioDeviceBufferSize(75);
+			} else {
+				v4l2_fract fract;
+				if (!v4l2::get_interval(fract)) {
+					// Default values are for 30 FPS
+					fract.numerator = 33;
+					fract.denominator = 1000;
+				}
+				// Standard capacity is two frames
+				setAudioDeviceBufferSize((fract.numerator * 2000) / fract.denominator);
+			}
+		} else {
+			fprintf(stderr, "BANNA\n");
+			delete m_audioInDevice;
+			delete m_audioOutDevice;
+			m_audioInDevice = NULL;
+			m_audioOutDevice = NULL;
+		}
+	}
+
 	if (needsStd) {
 		v4l2_std_id tmp;
 
@@ -370,6 +420,180 @@ done:
 	setRowStretch(rowCount() - 1, 1);
 }
 
+void GeneralTab::showAllAudioDevices(bool use)
+{
+	QString oldIn(m_audioInDevice->currentText());
+	QString oldOut(m_audioOutDevice->currentText());
+
+	m_fullAudioName = use;
+	if (oldIn == NULL || oldOut == NULL || !createAudioDeviceList())
+		return;
+
+	// Select a similar device as before the listings method change
+	// check by comparing old selection with any matching in the new list
+	bool setIn = false, setOut = false;
+	int listSize = std::max(m_audioInDevice->count(), m_audioOutDevice->count());
+
+	for (int i = 0; i < listSize; i++) {
+		QString oldInCmp(oldIn.left(std::min(m_audioInDevice->itemText(i).length(), oldIn.length())));
+		QString oldOutCmp(oldOut.left(std::min(m_audioOutDevice->itemText(i).length(), oldOut.length())));
+
+		if (!setIn && i < m_audioInDevice->count()
+		    && m_audioInDevice->itemText(i).startsWith(oldInCmp)) {
+			setIn = true;
+			m_audioInDevice->setCurrentIndex(i);
+		}
+
+		if (!setOut && i < m_audioOutDevice->count()
+		    && m_audioOutDevice->itemText(i).startsWith(oldOutCmp)) {
+			setOut = true;
+			m_audioOutDevice->setCurrentIndex(i);
+		}
+	}
+}
+
+bool GeneralTab::filterAudioInDevice(QString &deviceName)
+{
+	// Removes S/PDIF, front speakers and surround from input devices
+	// as they are output devices, not input
+	if (deviceName.contains("surround")
+	    || deviceName.contains("front")
+	    || deviceName.contains("iec958"))
+		return false;
+
+	// Removes sysdefault too if not full audio mode listings
+	if (!m_fullAudioName && deviceName.contains("sysdefault"))
+		return false;
+
+	return true;
+}
+
+bool GeneralTab::filterAudioOutDevice(QString &deviceName)
+{
+	// Removes advanced options if not full audio mode listings
+	if (!m_fullAudioName && (deviceName.contains("surround")
+				 || deviceName.contains("front")
+				 || deviceName.contains("iec958")
+				 || deviceName.contains("sysdefault"))) {
+		return false;
+	}
+
+	return true;
+}
+
+int GeneralTab::addAudioDevice(void *hint, int deviceNum)
+{
+	int added = 0;
+#ifdef ENABLE_ALSA
+	char *name;
+	char *iotype;
+	QString deviceName;
+	QString listName;
+	QStringList deviceType;
+	iotype = snd_device_name_get_hint(hint, "IOID");
+	name = snd_device_name_get_hint(hint, "NAME");
+	deviceName.append(name);
+
+	snd_card_get_name(deviceNum, &name);
+	listName.append(name);
+
+	deviceType = deviceName.split(":");
+
+	// Add device io capability to list name
+	if (m_fullAudioName) {
+		listName.append(" ");
+
+		// Makes the surround name more readable
+		if (deviceName.contains("surround"))
+			listName.append(QString("surround %1.%2")
+					.arg(deviceType.value(0)[8]).arg(deviceType.value(0)[9]));
+		else
+			listName.append(deviceType.value(0));
+
+	} else if (!deviceType.value(0).contains("default")) {
+		listName.append(" ").append(deviceType.value(0));
+	}
+
+	// Add device number if it is not 0
+	if (deviceName.contains("DEV=")) {
+		int devNo;
+		QStringList deviceNo = deviceName.split("DEV=");
+		devNo = deviceNo.value(1).toInt();
+		if (devNo)
+			listName.append(QString(" %1").arg(devNo));
+	}
+
+	if ((iotype == NULL || strncmp(iotype, "Input", 5) == 0) && filterAudioInDevice(deviceName)) {
+		m_audioInDevice->addItem(listName);
+		m_audioInDeviceMap[listName] = snd_device_name_get_hint(hint, "NAME");
+		added += AUDIO_ADD_READ;
+	}
+
+	if ((iotype == NULL || strncmp(iotype, "Output", 6) == 0)  && filterAudioOutDevice(deviceName)) {
+		m_audioOutDevice->addItem(listName);
+		m_audioOutDeviceMap[listName] = snd_device_name_get_hint(hint, "NAME");
+		added += AUDIO_ADD_WRITE;
+	}
+#endif
+	return added;
+}
+
+bool GeneralTab::createAudioDeviceList()
+{
+#ifdef ENABLE_ALSA
+	if (m_audioInDevice == NULL || m_audioOutDevice == NULL)
+		return false;
+
+	m_audioInDevice->clear();
+	m_audioOutDevice->clear();
+	m_audioInDeviceMap.clear();
+	m_audioOutDeviceMap.clear();
+
+	m_audioInDevice->addItem("None");
+	m_audioOutDevice->addItem("Default");
+	m_audioInDeviceMap["None"] = "None";
+	m_audioOutDeviceMap["Default"] = "default";
+
+	int deviceNum = -1;
+	int audioDevices = 0;
+	int matchDevice = matchAudioDevice();
+	int indexDevice = -1;
+	int indexCount = 0;
+
+	while (snd_card_next(&deviceNum) >= 0) {
+		if (deviceNum == -1)
+			break;
+
+		audioDevices++;
+		if (deviceNum == matchDevice && indexDevice == -1)
+			indexDevice = indexCount;
+
+		void **hint;
+
+		snd_device_name_hint(deviceNum, "pcm", &hint);
+		for (int i = 0; hint[i] != NULL; i++) {
+			int addAs = addAudioDevice(hint[i], deviceNum);
+			if (addAs == AUDIO_ADD_READ || addAs == AUDIO_ADD_READWRITE)
+				indexCount++;
+		}
+		snd_device_name_free_hint(hint);
+	}
+
+	snd_config_update_free_global();
+	m_audioInDevice->setCurrentIndex(indexDevice + 1);
+	changeAudioDevice();
+	return m_audioInDeviceMap.size() > 1 && m_audioOutDeviceMap.size() > 1 && audioDevices > 1;
+#else
+	return false;
+#endif
+}
+
+void GeneralTab::changeAudioDevice()
+{
+	m_audioOutDevice->setEnabled(getAudioInDevice() != NULL ? getAudioInDevice().compare("None") : false);
+	emit audioDeviceChanged();
+}
+
 void GeneralTab::addWidget(QWidget *w, Qt::Alignment align)
 {
 	QGridLayout::addWidget(w, m_row, m_col, align | Qt::AlignVCenter);
@@ -932,3 +1156,73 @@ bool GeneralTab::get_interval(struct v4l2_fract &interval)
 
 	return m_has_interval;
 }
+
+QString GeneralTab::getAudioInDevice()
+{
+	if (m_audioInDevice == NULL)
+		return NULL;
+
+	return m_audioInDeviceMap[m_audioInDevice->currentText()];
+}
+
+QString GeneralTab::getAudioOutDevice()
+{
+	if (m_audioOutDevice == NULL)
+		return NULL;
+
+	return m_audioOutDeviceMap[m_audioOutDevice->currentText()];
+}
+
+void GeneralTab::setAudioDeviceBufferSize(int size)
+{
+	m_audioDeviceBufferSize = size;
+}
+
+int GeneralTab::getAudioDeviceBufferSize()
+{
+	return m_audioDeviceBufferSize;
+}
+
+#ifdef ENABLE_ALSA
+int GeneralTab::checkMatchAudioDevice(void *md, const char *vid, const enum device_type type)
+{
+	const char *devname = NULL;
+
+	while ((devname = get_associated_device(md, devname, type, vid, MEDIA_V4L_VIDEO)) != NULL) {
+		if (type == MEDIA_SND_CAP) {
+			QStringList devAddr = QString(devname).split(QRegExp("[:,]"));
+			return devAddr.value(1).toInt();
+		}
+	}
+	return -1;
+}
+
+int GeneralTab::matchAudioDevice()
+{
+	QStringList devPath = m_device.split("/");
+	QString curDev = devPath.value(devPath.count() - 1);
+
+	void *media;
+	const char *video = NULL;
+	int match;
+
+	media = discover_media_devices();
+
+	while ((video = get_associated_device(media, video, MEDIA_V4L_VIDEO, NULL, NONE)) != NULL)
+		if (curDev.compare(video) == 0)
+			for (int i = 0; i <= MEDIA_SND_HW; i++)
+				if ((match = checkMatchAudioDevice(media, video, static_cast<device_type>(i))) != -1)
+					return match;
+
+	return -1;
+}
+#endif
+
+bool GeneralTab::hasAlsaAudio()
+{
+#ifdef ENABLE_ALSA
+	return !isVbi();
+#else
+	return false;
+#endif
+}
diff --git a/utils/qv4l2/general-tab.h b/utils/qv4l2/general-tab.h
index 5903ed8..c83368a 100644
--- a/utils/qv4l2/general-tab.h
+++ b/utils/qv4l2/general-tab.h
@@ -24,9 +24,18 @@
 #include <QSpinBox>
 #include <sys/time.h>
 #include <linux/videodev2.h>
+#include <map>
 #include "qv4l2.h"
 #include "v4l2-api.h"
 
+#ifdef ENABLE_ALSA
+extern "C" {
+#include "../libmedia_dev/get_media_devices.h"
+#include "alsa_stream.h"
+}
+#include <alsa/asoundlib.h>
+#endif
+
 class QComboBox;
 class QCheckBox;
 class QSpinBox;
@@ -41,6 +50,11 @@ public:
 	virtual ~GeneralTab() {}
 
 	CapMethod capMethod();
+	QString getAudioInDevice();
+	QString getAudioOutDevice();
+	void setAudioDeviceBufferSize(int size);
+	int getAudioDeviceBufferSize();
+	bool hasAlsaAudio();
 	bool get_interval(struct v4l2_fract &interval);
 	int width() const { return m_width; }
 	int height() const { return m_height; }
@@ -69,6 +83,12 @@ public:
 	inline bool streamon() { return v4l2::streamon(m_buftype); }
 	inline bool streamoff() { return v4l2::streamoff(m_buftype); }
 
+public slots:
+	void showAllAudioDevices(bool use);
+
+signals:
+	void audioDeviceChanged();
+
 private slots:
 	void inputChanged(int);
 	void outputChanged(int);
@@ -92,6 +112,7 @@ private slots:
 	void frameIntervalChanged(int);
 	void vidOutFormatChanged(int);
 	void vbiMethodsChanged(int);
+	void changeAudioDevice();
 
 private:
 	void updateVideoInput();
@@ -108,6 +129,14 @@ private:
 	void updateFrameSize();
 	void updateFrameInterval();
 	void updateVidOutFormat();
+	int addAudioDevice(void *hint, int deviceNum);
+	bool filterAudioInDevice(QString &deviceName);
+	bool filterAudioOutDevice(QString &deviceName);
+	bool createAudioDeviceList();
+#ifdef ENABLE_ALSA
+	int matchAudioDevice();
+	int checkMatchAudioDevice(void *md, const char *vid, const enum device_type type);
+#endif
 
 	void addWidget(QWidget *w, Qt::Alignment align = Qt::AlignLeft);
 	void addLabel(const QString &text, Qt::Alignment align = Qt::AlignRight)
@@ -130,6 +159,7 @@ private:
 	bool m_isVbi;
 	__u32 m_buftype;
 	__u32 m_audioModes[5];
+	QString m_device;
 	struct v4l2_tuner m_tuner;
 	struct v4l2_modulator m_modulator;
 	struct v4l2_capability m_querycap;
@@ -137,6 +167,10 @@ private:
 	__u32 m_width, m_height;
 	struct v4l2_fract m_interval;
 	bool m_has_interval;
+	int m_audioDeviceBufferSize;
+	static bool m_fullAudioName;
+	std::map<QString, QString> m_audioInDeviceMap;
+	std::map<QString, QString> m_audioOutDeviceMap;
 
 	// General tab
 	QComboBox *m_videoInput;
@@ -163,6 +197,8 @@ private:
 	QComboBox *m_vidOutFormats;
 	QComboBox *m_capMethods;
 	QComboBox *m_vbiMethods;
+	QComboBox *m_audioInDevice;
+	QComboBox *m_audioOutDevice;
 };
 
 #endif
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index 275b399..e078e91 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -24,6 +24,12 @@
 #include "capture-win-qt.h"
 #include "capture-win-gl.h"
 
+#ifdef ENABLE_ASLA
+extern "C" {
+#include "alsa_stream.h"
+}
+#endif
+
 #include <QToolBar>
 #include <QToolButton>
 #include <QMenuBar>
@@ -45,15 +51,18 @@
 #include <QWhatsThis>
 #include <QThread>
 #include <QCloseEvent>
+#include <QInputDialog>
 
 #include <assert.h>
 #include <sys/mman.h>
+#include <sys/time.h>
 #include <errno.h>
 #include <dirent.h>
 #include <libv4l2.h>
 
 ApplicationWindow::ApplicationWindow() :
 	m_capture(NULL),
+	m_genTab(NULL),
 	m_sigMapper(NULL)
 {
 	setAttribute(Qt::WA_DeleteOnClose, true);
@@ -76,7 +85,7 @@ ApplicationWindow::ApplicationWindow() :
 	openRawAct->setShortcut(Qt::CTRL+Qt::Key_R);
 	connect(openRawAct, SIGNAL(triggered()), this, SLOT(openrawdev()));
 
-	m_capStartAct = new QAction(QIcon(":/record.png"), "&Start Capturing", this);
+	m_capStartAct = new QAction(QIcon(":/record.png"), "Start &Capturing", this);
 	m_capStartAct->setStatusTip("Start capturing");
 	m_capStartAct->setCheckable(true);
 	m_capStartAct->setDisabled(true);
@@ -145,6 +154,21 @@ ApplicationWindow::ApplicationWindow() :
 		m_renderMethod = QV4L2_RENDER_QT;
 	}
 
+#ifdef ENABLE_ALSA
+	captureMenu->addSeparator();
+
+	m_showAllAudioAct = new QAction("Show All Audio Devices", this);
+	m_showAllAudioAct->setStatusTip("Show all audio input and output devices if set");
+	m_showAllAudioAct->setCheckable(true);
+	m_showAllAudioAct->setChecked(false);
+	captureMenu->addAction(m_showAllAudioAct);
+
+	m_audioBufferAct = new QAction("Set Audio Buffer Size...", this);
+	m_audioBufferAct->setStatusTip("Set audio buffer capacity in amout of ms than can be stored");
+	connect(m_audioBufferAct, SIGNAL(triggered()), this, SLOT(setAudioBufferSize()));
+	captureMenu->addAction(m_audioBufferAct);
+#endif
+
 	QMenu *helpMenu = menuBar()->addMenu("&Help");
 	helpMenu->addAction("&About", this, SLOT(about()), Qt::Key_F1);
 
@@ -172,8 +196,11 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
 	m_sigMapper = new QSignalMapper(this);
 	connect(m_sigMapper, SIGNAL(mapped(int)), this, SLOT(ctrlAction(int)));
 
-	if (!open(device, !rawOpen))
+	if (!open(device, !rawOpen)) {
+		m_showAllAudioAct->setEnabled(false);
+		m_audioBufferAct->setEnabled(false);
 		return;
+	}
 
 	newCaptureWin();
 
@@ -181,6 +208,19 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
 
 	QWidget *w = new QWidget(m_tabs);
 	m_genTab = new GeneralTab(device, *this, 4, w);
+
+#ifdef ENABLE_ALSA
+	if (m_genTab->hasAlsaAudio()) {
+		connect(m_showAllAudioAct, SIGNAL(toggled(bool)), m_genTab, SLOT(showAllAudioDevices(bool)));
+		connect(m_genTab, SIGNAL(audioDeviceChanged()), this, SLOT(changeAudioDevice()));
+		m_showAllAudioAct->setEnabled(true);
+		m_audioBufferAct->setEnabled(true);
+	} else {
+		m_showAllAudioAct->setEnabled(false);
+		m_audioBufferAct->setEnabled(false);
+	}
+#endif
+
 	m_tabs->addTab(w, "General");
 	addTabs();
 	if (caps() & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)) {
@@ -195,7 +235,7 @@ void ApplicationWindow::setDevice(const QString &device, bool rawOpen)
 	m_tabs->show();
 	m_tabs->setFocus();
 	m_convertData = v4lconvert_create(fd());
-	m_capStartAct->setEnabled(fd() >= 0 && !m_genTab->isRadio());
+	m_capStartAct->setEnabled(fd() >= 0);
 	m_ctrlNotifier = new QSocketNotifier(fd(), QSocketNotifier::Exception, m_tabs);
 	connect(m_ctrlNotifier, SIGNAL(activated(int)), this, SLOT(ctrlEvent()));
 }
@@ -235,6 +275,19 @@ void ApplicationWindow::setRenderMethod()
 	newCaptureWin();
 }
 
+void ApplicationWindow::setAudioBufferSize()
+{
+	bool ok;
+	int buffer = QInputDialog::getInt(this, "Audio Device Buffer Size", "Capacity in ms:",
+					   m_genTab->getAudioDeviceBufferSize(), 1, 65535, 1, &ok);
+
+	if (ok) {
+		m_genTab->setAudioDeviceBufferSize(buffer);
+		changeAudioDevice();
+	}
+}
+
+
 void ApplicationWindow::ctrlEvent()
 {
 	v4l2_event ev;
@@ -413,12 +466,18 @@ void ApplicationWindow::capFrame()
 	int s = 0;
 	int err = 0;
 	bool again;
+#ifdef ENABLE_ALSA
+	struct timeval tv_alsa;
+#endif
 
 	unsigned char *displaybuf = NULL;
 
 	switch (m_capMethod) {
 	case methodRead:
 		s = read(m_frameData, m_capSrcFormat.fmt.pix.sizeimage);
+#ifdef ENABLE_ALSA
+		alsa_thread_timestamp(&tv_alsa);
+#endif
 		if (s < 0) {
 			if (errno != EAGAIN) {
 				error("read");
@@ -449,6 +508,9 @@ void ApplicationWindow::capFrame()
 			m_capStartAct->setChecked(false);
 			return;
 		}
+#ifdef ENABLE_ALSA
+		alsa_thread_timestamp(&tv_alsa);
+#endif
 		if (again)
 			return;
 
@@ -475,6 +537,10 @@ void ApplicationWindow::capFrame()
 			m_capStartAct->setChecked(false);
 			return;
 		}
+#ifdef ENABLE_ALSA
+		alsa_thread_timestamp(&tv_alsa);
+#endif
+
 		if (again)
 			return;
 
@@ -511,9 +577,24 @@ void ApplicationWindow::capFrame()
 		m_lastFrame = m_frame;
 		m_tv = tv;
 	}
+
+
 	status = QString("Frame: %1 Fps: %2").arg(++m_frame).arg(m_fps);
+#ifdef ENABLE_ALSA
+	if (alsa_thread_is_running()) {
+		if (tv_alsa.tv_sec || tv_alsa.tv_usec) {
+			m_totalAudioLatency.tv_sec += buf.timestamp.tv_sec - tv_alsa.tv_sec;
+			m_totalAudioLatency.tv_usec += buf.timestamp.tv_usec - tv_alsa.tv_usec;
+		}
+		//m_totalAudioLatency.tv_sec = tv_alsa.tv_sec;
+		//m_totalAudioLatency.tv_usec = tv_alsa.tv_usec;
+		status.append(QString(" Average A-V: %3 ms")
+			      .arg((m_totalAudioLatency.tv_sec * 1000 + m_totalAudioLatency.tv_usec / 1000) / m_frame));
+	}
+#endif
 	if (displaybuf == NULL && m_showFrames)
 		status.append(" Error: Unsupported format.");
+
 	if (m_showFrames)
 		m_capture->setFrame(m_capImage->width(), m_capImage->height(),
 				    m_capDestFormat.fmt.pix.pixelformat, displaybuf, status);
@@ -530,6 +611,11 @@ void ApplicationWindow::capFrame()
 
 bool ApplicationWindow::startCapture(unsigned buffer_size)
 {
+	startAudio();
+
+	if (m_genTab->isRadio())
+		return true;
+
 	__u32 buftype = m_genTab->bufType();
 	v4l2_requestbuffers req;
 	unsigned int i;
@@ -645,6 +731,11 @@ error:
 
 void ApplicationWindow::stopCapture()
 {
+	stopAudio();
+
+	if (m_genTab->isRadio())
+		return;
+
 	__u32 buftype = m_genTab->bufType();
 	v4l2_requestbuffers reqbufs;
 	v4l2_encoder_cmd cmd;
@@ -695,6 +786,42 @@ void ApplicationWindow::stopOutput()
 {
 }
 
+void ApplicationWindow::startAudio()
+{
+#ifdef ENABLE_ALSA
+	m_totalAudioLatency.tv_sec = 0;
+	m_totalAudioLatency.tv_usec = 0;
+
+	QString audIn = m_genTab->getAudioInDevice();
+	QString audOut = m_genTab->getAudioOutDevice();
+
+	if (audIn != NULL && audOut != NULL && audIn.compare("None") && audIn.compare(audOut) != 0) {
+		alsa_thread_startup(audOut.toAscii().data(), audIn.toAscii().data(),
+				    m_genTab->getAudioDeviceBufferSize(), NULL, 0);
+
+		if (m_genTab->isRadio())
+			statusBar()->showMessage("Capturing audio");
+	}
+#endif
+}
+
+void ApplicationWindow::stopAudio()
+{
+#ifdef ENABLE_ALSA
+	if (m_genTab != NULL && m_genTab->isRadio())
+		statusBar()->showMessage("");
+	alsa_thread_stop();
+#endif
+}
+
+void ApplicationWindow::changeAudioDevice()
+{
+	stopAudio();
+	if (m_capStartAct->isChecked()) {
+		startAudio();
+	}
+}
+
 void ApplicationWindow::closeCaptureWin()
 {
 	m_capStartAct->setChecked(false);
@@ -702,6 +829,15 @@ void ApplicationWindow::closeCaptureWin()
 
 void ApplicationWindow::capStart(bool start)
 {
+	if (m_genTab->isRadio()) {
+		if (start)
+			startCapture(0);
+		else
+			stopCapture();
+
+		return;
+	}
+
 	QImage::Format dstFmt = QImage::Format_RGB888;
 	struct v4l2_fract interval;
 	v4l2_pix_format &srcPix = m_capSrcFormat.fmt.pix;
@@ -821,6 +957,7 @@ void ApplicationWindow::capStart(bool start)
 
 void ApplicationWindow::closeDevice()
 {
+	stopAudio();
 	delete m_sigMapper;
 	m_sigMapper = NULL;
 	m_capStartAct->setEnabled(false);
diff --git a/utils/qv4l2/qv4l2.h b/utils/qv4l2/qv4l2.h
index 2921b16..223db75 100644
--- a/utils/qv4l2/qv4l2.h
+++ b/utils/qv4l2/qv4l2.h
@@ -98,6 +98,8 @@ private:
 	void startOutput(unsigned buffer_size);
 	void stopOutput();
 	void newCaptureWin();
+	void startAudio();
+	void stopAudio();
 
 	struct buffer *m_buffers;
 	struct v4l2_format m_capSrcFormat;
@@ -118,6 +120,7 @@ private slots:
 	void capVbiFrame();
 	void saveRaw(bool);
 	void setRenderMethod();
+	void changeAudioDevice();
 
 	// gui
 private slots:
@@ -126,6 +129,7 @@ private slots:
 	void ctrlAction(int);
 	void openRawFile(const QString &s);
 	void rejectedRawFile();
+	void setAudioBufferSize();
 
 	void about();
 
@@ -176,6 +180,8 @@ private:
 	QAction *m_saveRawAct;
 	QAction *m_showFramesAct;
 	QAction *m_useGLAct;
+	QAction *m_showAllAudioAct;
+	QAction *m_audioBufferAct;
 	QString m_filename;
 	QSignalMapper *m_sigMapper;
 	QTabWidget *m_tabs;
@@ -196,6 +202,7 @@ private:
 	unsigned m_lastFrame;
 	unsigned m_fps;
 	struct timeval m_tv;
+	struct timeval m_totalAudioLatency;
 	QFile m_saveRaw;
 };
 
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/5] qv4l2: add ALSA stream to qv4l2
  2013-08-02 12:05   ` [PATCH 3/5] qv4l2: add ALSA stream to qv4l2 Bård Eirik Winther
@ 2013-08-02 12:25     ` Hans Verkuil
  2013-08-02 22:19     ` Gregor Jasny
  1 sibling, 0 replies; 9+ messages in thread
From: Hans Verkuil @ 2013-08-02 12:25 UTC (permalink / raw)
  To: Bård Eirik Winther; +Cc: linux-media

Hi Bård!

Two small comments below...

On 08/02/2013 02:05 PM, Bård Eirik Winther wrote:
> Changes the ALSA streaming code to work with qv4l2 and allows it to
> be compiled in. qv4l2 does not use the streaming function yet.
> 
> Signed-off-by: Bård Eirik Winther <bwinther@cisco.com>
> ---
>  configure.ac              |  6 ++++++
>  utils/qv4l2/Makefile.am   |  9 ++++++++-
>  utils/qv4l2/alsa_stream.c | 21 +++++++++++++++------
>  utils/qv4l2/alsa_stream.h | 13 ++++++++++---
>  4 files changed, 39 insertions(+), 10 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index d74da61..e12507e 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -136,6 +136,11 @@ if test "x$qt_pkgconfig_gl" = "xfalse"; then
>     AC_MSG_WARN(Qt4 OpenGL or higher is not available)
>  fi
>  
> +PKG_CHECK_MODULES(ALSA, [alsa], [alsa_pkgconfig=true], [alsa_pkgconfig=false])
> +if test "x$alsa_pkgconfig" = "xfalse"; then
> +   AC_MSG_WARN(ALSA library not available)
> +fi
> +
>  AC_SUBST([JPEG_LIBS])
>  
>  # The dlopen() function is in the C library for *BSD and in
> @@ -243,6 +248,7 @@ AM_CONDITIONAL([WITH_LIBV4L], [test x$enable_libv4l != xno])
>  AM_CONDITIONAL([WITH_V4LUTILS], [test x$enable_v4lutils != xno])
>  AM_CONDITIONAL([WITH_QV4L2], [test ${qt_pkgconfig} = true -a x$enable_qv4l2 != xno])
>  AM_CONDITIONAL([WITH_QV4L2_GL], [test WITH_QV4L2 -a ${qt_pkgconfig_gl} = true])
> +AM_CONDITIONAL([WITH_QV4L2_ALSA], [test WITH_QV4L2 -a ${alsa_pkgconfig} = true])
>  AM_CONDITIONAL([WITH_V4L_PLUGINS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
>  AM_CONDITIONAL([WITH_V4L_WRAPPERS], [test x$enable_libv4l != xno -a x$enable_shared != xno])
>  
> diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
> index 22d4c17..eed25b0 100644
> --- a/utils/qv4l2/Makefile.am
> +++ b/utils/qv4l2/Makefile.am
> @@ -4,7 +4,8 @@ qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp
>    capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h \
>    raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h v4l2-api.h raw2sliced.h
>  nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
> -qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la
> +qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la \
> +  ../libmedia_dev/libmedia_dev.la
>  
>  if WITH_QV4L2_GL
>  qv4l2_CPPFLAGS = $(QTGL_CFLAGS) -DENABLE_GL
> @@ -14,6 +15,12 @@ qv4l2_CPPFLAGS = $(QT_CFLAGS)
>  qv4l2_LDFLAGS = $(QT_LIBS)
>  endif
>  
> +if WITH_QV4L2_ALSA
> +qv4l2_CPPFLAGS += $(ALSA_CFLAGS) -DENABLE_ALSA
> +qv4l2_LDFLAGS += $(ALSA_LIBS) -pthread
> +qv4l2_SOURCES += alsa_stream.c alsa_stream.h
> +endif
> +
>  EXTRA_DIST = exit.png fileopen.png qv4l2_24x24.png qv4l2_64x64.png qv4l2.png qv4l2.svg snapshot.png \
>    video-television.png fileclose.png qv4l2_16x16.png qv4l2_32x32.png qv4l2.desktop qv4l2.qrc record.png \
>    saveraw.png qv4l2.pro
> diff --git a/utils/qv4l2/alsa_stream.c b/utils/qv4l2/alsa_stream.c
> index 3e33b5e..90d3afb 100644
> --- a/utils/qv4l2/alsa_stream.c
> +++ b/utils/qv4l2/alsa_stream.c
> @@ -26,9 +26,7 @@
>   *
>   */
>  
> -#include "config.h"
> -
> -#ifdef HAVE_ALSA_ASOUNDLIB_H
> +#include "alsa_stream.h"
>  
>  #include <stdio.h>
>  #include <stdlib.h>
> @@ -40,12 +38,12 @@
>  #include <alsa/asoundlib.h>
>  #include <sys/time.h>
>  #include <math.h>
> -#include "alsa_stream.h"
>  
>  #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
>  
>  /* Private vars to control alsa thread status */
>  static int stop_alsa = 0;
> +static snd_htimestamp_t timestamp;
>  
>  /* Error handlers */
>  snd_output_t *output = NULL;
> @@ -422,7 +420,8 @@ static int setparams(snd_pcm_t *phandle, snd_pcm_t *chandle,
>  static snd_pcm_sframes_t readbuf(snd_pcm_t *handle, char *buf, long len)
>  {
>      snd_pcm_sframes_t r;
> -
> +    snd_pcm_uframes_t frames;
> +    snd_pcm_htimestamp(handle, &frames, &timestamp);
>      r = snd_pcm_readi(handle, buf, len);
>      if (r < 0 && r != -EAGAIN) {
>  	r = snd_pcm_recover(handle, r, 0);
> @@ -453,6 +452,7 @@ static snd_pcm_sframes_t writebuf(snd_pcm_t *handle, char *buf, long len)
>  	len -= r;
>  	snd_pcm_wait(handle, 100);
>      }
> +    return -1;
>  }
>  
>  static int alsa_stream(const char *pdevice, const char *cdevice, int latency)
> @@ -642,4 +642,13 @@ int alsa_thread_is_running(void)
>      return alsa_is_running;
>  }
>  
> -#endif
> +void alsa_thread_timestamp(struct timeval *tv)
> +{
> +	if (alsa_thread_is_running()) {
> +		tv->tv_sec = timestamp.tv_sec;
> +		tv->tv_usec = timestamp.tv_nsec / 1000;
> +	} else {
> +		tv->tv_sec = 1337;

Why 1337? I would expect either 0, or a bool return from this function to signify that there
is no valid timestamp.

> +		tv->tv_usec = 0;
> +	}
> +}
> diff --git a/utils/qv4l2/alsa_stream.h b/utils/qv4l2/alsa_stream.h
> index c68fd6d..b74c3aa 100644
> --- a/utils/qv4l2/alsa_stream.h
> +++ b/utils/qv4l2/alsa_stream.h
> @@ -1,5 +1,12 @@
> -int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
> -			FILE *__error_fp,
> -			int __verbose);
> +#ifndef ALSA_STRAM_H
> +#define ALSA_STRAM_H

A small typo: STRAM -> STREAM

> +
> +#include <stdio.h>
> +#include <sys/time.h>
> +
> +int alsa_thread_startup(const char *pdevice, const char *cdevice,
> +			int latency, FILE *__error_fp, int __verbose);
>  void alsa_thread_stop(void);
>  int alsa_thread_is_running(void);
> +void alsa_thread_timestamp(struct timeval *tv);
> +#endif
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/5] qv4l2: add ALSA stream to qv4l2
  2013-08-02 12:05   ` [PATCH 3/5] qv4l2: add ALSA stream to qv4l2 Bård Eirik Winther
  2013-08-02 12:25     ` Hans Verkuil
@ 2013-08-02 22:19     ` Gregor Jasny
  2013-08-05  7:45       ` Bård Eirik Winther
  1 sibling, 1 reply; 9+ messages in thread
From: Gregor Jasny @ 2013-08-02 22:19 UTC (permalink / raw)
  To: Bård Eirik Winther; +Cc: linux-media

Hello,

> diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
> index 22d4c17..eed25b0 100644
> --- a/utils/qv4l2/Makefile.am
> +++ b/utils/qv4l2/Makefile.am
> @@ -4,7 +4,8 @@ qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp
>     capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h \
>     raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h v4l2-api.h raw2sliced.h
>   nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
> -qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la
> +qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la \
> +  ../libmedia_dev/libmedia_dev.la
>
>   if WITH_QV4L2_GL
>   qv4l2_CPPFLAGS = $(QTGL_CFLAGS) -DENABLE_GL
> @@ -14,6 +15,12 @@ qv4l2_CPPFLAGS = $(QT_CFLAGS)
>   qv4l2_LDFLAGS = $(QT_LIBS)
>   endif
>
> +if WITH_QV4L2_ALSA
> +qv4l2_CPPFLAGS += $(ALSA_CFLAGS) -DENABLE_ALSA

I would prefer if you don't add another define to the command line. To 
check for ALSA support please include config.h and use the flag provided 
there.

> diff --git a/utils/qv4l2/alsa_stream.h b/utils/qv4l2/alsa_stream.h
> index c68fd6d..b74c3aa 100644
> --- a/utils/qv4l2/alsa_stream.h
> +++ b/utils/qv4l2/alsa_stream.h
> @@ -1,5 +1,12 @@
> -int alsa_thread_startup(const char *pdevice, const char *cdevice, int latency,
> -			FILE *__error_fp,
> -			int __verbose);
> +#ifndef ALSA_STRAM_H
> +#define ALSA_STRAM_H

unimportant typo here

Thanks,
Gregor


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/5] qv4l2: add ALSA stream to qv4l2
  2013-08-02 22:19     ` Gregor Jasny
@ 2013-08-05  7:45       ` Bård Eirik Winther
  0 siblings, 0 replies; 9+ messages in thread
From: Bård Eirik Winther @ 2013-08-05  7:45 UTC (permalink / raw)
  To: Gregor Jasny; +Cc: linux-media, hverkuil

On Saturday, August 03, 2013 12:19:54 AM you wrote:
> Hello,
> 
> > diff --git a/utils/qv4l2/Makefile.am b/utils/qv4l2/Makefile.am
> > index 22d4c17..eed25b0 100644
> > --- a/utils/qv4l2/Makefile.am
> > +++ b/utils/qv4l2/Makefile.am
> > @@ -4,7 +4,8 @@ qv4l2_SOURCES = qv4l2.cpp general-tab.cpp ctrl-tab.cpp vbi-tab.cpp v4l2-api.cpp
> >     capture-win-qt.cpp capture-win-qt.h capture-win-gl.cpp capture-win-gl.h \
> >     raw2sliced.cpp qv4l2.h capture-win.h general-tab.h vbi-tab.h v4l2-api.h raw2sliced.h
> >   nodist_qv4l2_SOURCES = moc_qv4l2.cpp moc_general-tab.cpp moc_capture-win.cpp moc_vbi-tab.cpp qrc_qv4l2.cpp
> > -qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la
> > +qv4l2_LDADD = ../../lib/libv4l2/libv4l2.la ../../lib/libv4lconvert/libv4lconvert.la ../libv4l2util/libv4l2util.la \
> > +  ../libmedia_dev/libmedia_dev.la
> >
> >   if WITH_QV4L2_GL
> >   qv4l2_CPPFLAGS = $(QTGL_CFLAGS) -DENABLE_GL
> > @@ -14,6 +15,12 @@ qv4l2_CPPFLAGS = $(QT_CFLAGS)
> >   qv4l2_LDFLAGS = $(QT_LIBS)
> >   endif
> >
> > +if WITH_QV4L2_ALSA
> > +qv4l2_CPPFLAGS += $(ALSA_CFLAGS) -DENABLE_ALSA
> 
> I would prefer if you don't add another define to the command line. To 
> check for ALSA support please include config.h and use the flag provided 
> there.

That is fine for me. However, this design was to make the alsa code not compile in when not required.

While I am at it, should I do the same for OpenGL, that is, remove WITH_QV4L2_GL with a config.h define)? The patch series has already been patched in, but I do have another series that adds features and fixes to the first OpenGL patches. It should not be a problem to change OpenGL accordingly as well.

B.

> 
> Thanks,
> Gregor
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2013-08-05  7:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-08-02 12:05 [PATCH 0/5] qv4l2: add ALSA audio playback Bård Eirik Winther
2013-08-02 12:05 ` [PATCH 1/5] qv4l2: alter capture menu Bård Eirik Winther
2013-08-02 12:05   ` [PATCH 2/5] qv4l2: new ALSA stream source code Bård Eirik Winther
2013-08-02 12:05   ` [PATCH 3/5] qv4l2: add ALSA stream to qv4l2 Bård Eirik Winther
2013-08-02 12:25     ` Hans Verkuil
2013-08-02 22:19     ` Gregor Jasny
2013-08-05  7:45       ` Bård Eirik Winther
2013-08-02 12:05   ` [PATCH 4/5] qv4l2: fix a bug where the alsa thread never stops Bård Eirik Winther
2013-08-02 12:05   ` [PATCH 5/5] qv4l2: add ALSA audio playback Bård Eirik Winther

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.