desktop/tvtime/tvtime-1.0.2-alsa-r1.patch

553 lines
17 KiB
Diff

Add support for ALSA in combination with USB audio devices, bug #323797
Patch from Ubuntu, rebased to our upstream
diff -Naur tvtime-111b28cca42d.orig/src/audiolib.c tvtime-111b28cca42d/src/audiolib.c
--- tvtime-111b28cca42d.orig/src/audiolib.c 1970-01-01 01:00:00.000000000 +0100
+++ tvtime-111b28cca42d/src/audiolib.c 2011-08-14 11:53:47.726915264 +0200
@@ -0,0 +1,342 @@
+/*
+ Copyright 2008 Empia Technology Inc.
+ Copyright 2008 Markus Rechberger <mrechberger@empiatech.com>
+
+ alsa_set_params is derived from aplay.c
+
+ TODO:
+ more sysfs hacking for determining the empia audio device
+
+*/
+
+#include <stdio.h>
+#include "audiolib.h"
+
+static int alsa_set_params(snd_pcm_t *handle, struct alsa_stream_format *fmt, int start_delay);
+
+char *get_empia_device() {
+ int err;
+ int card = -1;
+ char dev[64];
+ int pcm_device = -1;
+ snd_ctl_t *ctl;
+ snd_pcm_info_t *pcm_info;
+ char *card_name;
+ do {
+ err = snd_card_next(&card);
+ if (card > -1) {
+ sprintf(dev, "hw:%i", card);
+ snd_ctl_open(&ctl, dev, 0);
+ snd_card_get_name(card, &card_name);
+ snd_pcm_info_alloca(&pcm_info);
+
+ for (;;)
+ {
+ char device[500], descr[1024];
+
+ if ((err = snd_ctl_pcm_next_device(ctl, &pcm_device)) < 0)
+ pcm_device = -1;
+
+ if (pcm_device < 0)
+ break;
+
+ snd_pcm_info_set_subdevice(pcm_info, 0);
+ snd_pcm_info_set_device(pcm_info, pcm_device);
+ snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE);
+ if ((err = snd_ctl_pcm_info(ctl, pcm_info))==0) {
+ sprintf(device,"hw:%d,%d", card, pcm_device);
+ sprintf(descr,"%s : %s (%s)", card_name, snd_pcm_info_get_name(pcm_info),device);
+ if(strcmp(snd_pcm_info_get_name(pcm_info), "USB Audio") == 0) {
+ printf("Found \"%s\"\n", descr);
+ snd_ctl_close(ctl);
+ return strdup(device);
+
+ }
+ }
+ }
+ snd_ctl_close(ctl);
+ }
+
+ } while(card > -1);
+ return NULL;
+}
+
+int alsa_get_state(struct alsa_device *dev)
+{
+ return dev->state;
+}
+
+void alsa_set_state(struct alsa_device *dev, enum alsa_state state)
+{
+ dev->state = state;
+}
+
+struct alsa_device *alsa_open(char *input, char *output, unsigned long format, int channels, unsigned int rate)
+{
+ int result;
+ struct alsa_device *dev = malloc(sizeof(struct alsa_device));
+
+ dev->fmt.format = format;
+ dev->fmt.channels = channels;
+ dev->fmt.rate = rate;
+ dev->state = ALSA_STATE_STOPPED;
+
+ if (dev == NULL)
+ return NULL;
+
+
+ if ((result = snd_pcm_open( &dev->playback, output, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK))<0)
+ {
+ free(dev);
+ return NULL;
+ }
+
+// snd_pcm_nonblock( dev->playback, 1);
+
+ alsa_set_params(dev->playback, &dev->fmt, 0);
+
+ if ((result = snd_pcm_open( &dev->capture, input, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK))<0)
+ {
+ free(dev);
+ return NULL;
+ }
+
+// snd_pcm_nonblock(dev->playback, 1);
+
+ alsa_set_params(dev->capture, &dev->fmt, 1);
+
+ return dev;
+}
+
+void alsa_close(struct alsa_device **dev)
+{
+ struct alsa_device *cdev = *dev;
+ snd_pcm_drain(cdev->playback);
+ snd_pcm_close(cdev->playback);
+ snd_pcm_drain(cdev->capture);
+ snd_pcm_close(cdev->capture);
+ free(*dev);
+ snd_config_update_free_global();
+ *dev = NULL;
+}
+
+#define TRANSFER_BUFSIZE 256
+
+void *alsa_start_thread(void *data)
+{
+ struct alsa_device *dev = data;
+ alsa_loop(dev);
+ return NULL;
+}
+
+int alsa_start_threaded_loop(struct alsa_device *dev)
+{
+ int pret = 0;
+ if (dev->state == ALSA_STATE_STOPPED)
+ {
+ dev->state = ALSA_STATE_RUNNING;
+ pthread_create(&dev->athread, NULL, alsa_start_thread, dev);
+ }
+ return pret;
+}
+
+int alsa_join_threaded_loop(struct alsa_device *dev)
+{
+ if (dev->state == ALSA_STATE_RUNNING)
+ {
+ dev->state = ALSA_STATE_STOPPED;
+ pthread_join(dev->athread, NULL);
+ }
+ return 0;
+}
+
+int alsa_loop(struct alsa_device *dev)
+{
+ int length, ret;
+ unsigned char buf[TRANSFER_BUFSIZE];
+ snd_pcm_uframes_t frames = TRANSFER_BUFSIZE/dev->fmt.bits_per_sample;
+ dev->state = ALSA_STATE_RUNNING;
+ while (dev->state == ALSA_STATE_RUNNING) {
+ length = snd_pcm_readi(dev->capture, buf, frames);
+ switch(length) {
+ case -EAGAIN:
+ snd_pcm_wait(dev->capture, 5);
+ continue;
+ case -EPIPE:
+ snd_pcm_drain(dev->capture);
+ snd_pcm_prepare(dev->capture);
+ continue;
+ default:
+ break;
+ }
+ if (length>0) {
+ ret = snd_pcm_writei(dev->playback, buf, length);
+ switch (ret) {
+ case -EPIPE:
+ snd_pcm_drain(dev->playback);
+ snd_pcm_prepare(dev->playback);
+ continue;
+ default:
+ break;
+ }
+ }
+ }
+ snd_pcm_drain(dev->capture);
+ snd_pcm_drain(dev->playback);
+ return 0;
+}
+
+/* set start_delay to 1 for capture */
+/* taken from aplay.c */
+static int alsa_set_params(snd_pcm_t *handle, struct alsa_stream_format *fmt, int start_delay)
+{
+ snd_pcm_uframes_t chunk_size = 0;
+ snd_pcm_hw_params_t *params;
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_uframes_t buffer_size;
+
+ unsigned buffer_time = 0;
+ unsigned period_time = 0;
+ snd_pcm_uframes_t buffer_frames = 0;
+ snd_pcm_uframes_t period_frames = 0;
+
+ int err;
+ size_t n;
+ snd_pcm_uframes_t xfer_align;
+ unsigned int rate;
+ int avail_min = -1;
+ int stop_delay = 0;
+
+ snd_pcm_uframes_t start_threshold, stop_threshold;
+ snd_pcm_hw_params_alloca(&params);
+ snd_pcm_sw_params_alloca(&swparams);
+ err = snd_pcm_hw_params_any(handle, params);
+ if (err < 0) {
+ printf("Broken configuration for this PCM: no configurations available");
+ exit(EXIT_FAILURE);
+ }
+
+
+ snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof());
+ snd_pcm_access_mask_none(mask);
+ snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_RW_INTERLEAVED);
+ err = snd_pcm_hw_params_set_access_mask(handle, params, mask);
+
+ if (err < 0) {
+ printf("Access type not available");
+ exit(EXIT_FAILURE);
+ }
+ err = snd_pcm_hw_params_set_format(handle, params, fmt->format);
+ if (err < 0) {
+ printf("Sample format non available");
+ exit(EXIT_FAILURE);
+ }
+ err = snd_pcm_hw_params_set_channels(handle, params, fmt->channels);
+ if (err < 0) {
+ printf("Channels count non available");
+ exit(EXIT_FAILURE);
+ }
+
+ rate = fmt->rate;
+ err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0);
+
+ assert(err >= 0);
+
+ rate = fmt->rate;
+
+ if (buffer_time == 0 && buffer_frames == 0) {
+ err = snd_pcm_hw_params_get_buffer_time_max(params,
+ &buffer_time, 0);
+ assert(err >= 0);
+ if (buffer_time > 500000)
+ buffer_time = 500000;
+ }
+
+ if (period_time == 0 && period_frames == 0) {
+ if (buffer_time > 0)
+ period_time = buffer_time / 4;
+ else
+ period_frames = buffer_frames / 4;
+ }
+
+ if (period_time > 0)
+ err = snd_pcm_hw_params_set_period_time_near(handle, params,
+ &period_time, 0);
+ else
+ err = snd_pcm_hw_params_set_period_size_near(handle, params,
+ &period_frames, 0);
+
+ assert(err >= 0);
+ if (buffer_time > 0)
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, params,
+ &buffer_time, 0);
+ else
+ err = snd_pcm_hw_params_set_buffer_size_near(handle, params,
+ &buffer_frames);
+
+ assert(err >= 0);
+ err = snd_pcm_hw_params(handle, params);
+ if (err < 0) {
+ printf("Unable to install hw params");
+ return -EINVAL;
+ }
+ snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
+ snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
+
+ if (chunk_size == buffer_size) {
+ printf("Can't use period equal to buffer size (%lu == %lu)", chunk_size, buffer_size);
+ return -EINVAL;
+ }
+
+ snd_pcm_sw_params_current(handle, swparams);
+ err = snd_pcm_sw_params_get_xfer_align(swparams, &xfer_align);
+ if (err < 0) {
+ printf("Unable to obtain xfer align\n");
+ exit(EXIT_FAILURE);
+ }
+
+ err = snd_pcm_sw_params_set_sleep_min(handle, swparams, 0);
+
+ assert(err >= 0);
+ if (avail_min < 0)
+ n = chunk_size;
+ else
+ n = (double) rate * avail_min / 1000000;
+
+ err = snd_pcm_sw_params_set_avail_min(handle, swparams, n);
+
+ /* round up to closest transfer boundary */
+ n = (buffer_size / xfer_align) * xfer_align;
+ if (start_delay <= 0) {
+ start_threshold = n + (double) rate * start_delay / 1000000;
+ } else
+ start_threshold = (double) rate * start_delay / 1000000;
+ if (start_threshold < 1)
+ start_threshold = 1;
+ if (start_threshold > n)
+ start_threshold = n;
+
+ err = snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold);
+ assert(err >= 0);
+
+ if (stop_delay <= 0)
+ stop_threshold = buffer_size + (double) rate * stop_delay / 1000000;
+ else
+ stop_threshold = (double) rate * stop_delay / 1000000;
+
+ err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold);
+ assert(err >= 0);
+
+ err = snd_pcm_sw_params_set_xfer_align(handle, swparams, xfer_align);
+ assert(err >= 0);
+
+ if (snd_pcm_sw_params(handle, swparams) < 0) {
+ printf("unable to install sw params");
+ return -EINVAL;
+ }
+
+ fmt->bits_per_sample = snd_pcm_format_physical_width(fmt->format);
+ fmt->bits_per_frame = fmt->bits_per_sample * fmt->channels;
+ fmt->chunk_bytes = chunk_size * fmt->bits_per_frame / 8;
+ return 0;
+}
diff -Naur tvtime-111b28cca42d.orig/src/audiolib.h tvtime-111b28cca42d/src/audiolib.h
--- tvtime-111b28cca42d.orig/src/audiolib.h 1970-01-01 01:00:00.000000000 +0100
+++ tvtime-111b28cca42d/src/audiolib.h 2011-08-14 11:53:47.771915261 +0200
@@ -0,0 +1,38 @@
+#ifndef _AUDIO_LIB_H
+#define _AUDIO_LIB_H
+#include <alsa/asoundlib.h>
+#include <pthread.h>
+
+struct alsa_stream_format {
+ unsigned long format;
+ int channels;
+ unsigned int rate;
+ size_t bits_per_sample;
+ size_t bits_per_frame;
+ size_t chunk_bytes;
+};
+
+enum alsa_state {
+ ALSA_STATE_STOPPED,
+ ALSA_STATE_RUNNING
+};
+
+struct alsa_device {
+ snd_pcm_t *capture;
+ snd_pcm_t *playback;
+ enum alsa_state state;
+ struct alsa_stream_format fmt;
+ pthread_t athread;
+};
+
+struct alsa_device *alsa_open(char *input, char *output, unsigned long format, int channels, unsigned int rate);
+char *get_empia_device();
+int alsa_loop(struct alsa_device *dev);
+int alsa_get_state(struct alsa_device *dev);
+void alsa_close(struct alsa_device **dev);
+int alsa_get_state(struct alsa_device *dev);
+void alsa_set_state(struct alsa_device *dev, enum alsa_state state);
+int alsa_start_threaded_loop(struct alsa_device *dev);
+int alsa_join_threaded_loop(struct alsa_device *dev);
+
+#endif // _AUDIO_LIB_H
diff -Naur tvtime-111b28cca42d.orig/src/Makefile.am tvtime-111b28cca42d/src/Makefile.am
--- tvtime-111b28cca42d.orig/src/Makefile.am 2011-08-14 11:52:43.404917552 +0200
+++ tvtime-111b28cca42d/src/Makefile.am 2011-08-14 11:56:09.243910228 +0200
@@ -37,7 +37,7 @@
utils.h utils.c pulldown.h pulldown.c hashtable.h hashtable.c \
cpuinfo.h cpuinfo.c videodev2.h menu.c menu.h \
outputfilter.h outputfilter.c xmltv.h xmltv.c gettext.h tvtimeglyphs.h \
- copyfunctions.h copyfunctions.c alsa_stream.c
+ copyfunctions.h copyfunctions.c alsa_stream.c audiolib.h audiolib.c
if ARCH_X86
DSCALER_SRCS = $(top_srcdir)/plugins/dscalerapi.h \
@@ -74,7 +74,7 @@
$(PLUGIN_CFLAGS) $(X11_CFLAGS) $(XML2_FLAG) \
$(FONT_CFLAGS) $(AM_CFLAGS)
tvtime_LDADD = $(TTF_LIBS) $(ZLIB_LIBS) $(PNG_LIBS) \
- $(X11_LIBS) $(XML2_LIBS) $(ASOUND_LIBS) -lm -lsupc++ -lpthread
+ $(X11_LIBS) $(XML2_LIBS) $(ASOUND_LIBS) -lm -lsupc++ -lpthread -lasound
tvtime_command_SOURCES = utils.h utils.c tvtimeconf.h tvtimeconf.c \
tvtime-command.c
diff -Naur tvtime-111b28cca42d.orig/src/tvtime.c tvtime-111b28cca42d/src/tvtime.c
--- tvtime-111b28cca42d.orig/src/tvtime.c 2011-02-01 02:35:26.000000000 +0100
+++ tvtime-111b28cca42d/src/tvtime.c 2011-08-14 11:56:35.443909297 +0200
@@ -78,6 +78,7 @@
#include "menu.h"
#include "tvtimeglyphs.h"
#include "alsa_stream.h"
+#include "audiolib.h"
/**
* This is how many frames to wait until deciding if the pulldown phase
@@ -1210,6 +1211,7 @@
int last_current_id = -1;
int quiet_screenshots = 0;
char prevloc[ 256 ];
+ char *empia_device;
int i;
ct = config_new();
@@ -1581,6 +1583,8 @@
build_fspos_menu( commands_get_menu( commands, "fspos" ),
config_get_fullscreen_position( ct ) );
+ empia_device = get_empia_device();
+
/* Initialize our timestamps. */
for(;;) {
const char *fifo_args = 0;
@@ -2056,9 +2060,30 @@
if( tuner_state == TUNER_STATE_HAS_SIGNAL ) {
has_signal = 1;
+ /* reopen the device for now, there are several issues which don't allow pausing the
+ audio data transfer.
+
+ TODO:
+ The driver(?) seems to crash when setting up the alsa parameters again during the same
+ session.
+ */
+
+ if (empia_device && videoinput_get_audio(vidin) == NULL) {
+ videoinput_set_audio(vidin, alsa_open(empia_device, "default", SND_PCM_FORMAT_S16_LE, 2 /* 2 channels */, 48000 /* rate */));
+ if (videoinput_get_audio(vidin)) {
+ alsa_start_threaded_loop(videoinput_get_audio(vidin));
+ }
+ }
+
if( osd ) tvtime_osd_signal_present( osd, 1 );
} else if( tuner_state == TUNER_STATE_NO_SIGNAL ) {
if( osd ) tvtime_osd_signal_present( osd, 0 );
+ if (videoinput_get_audio(vidin)) {
+ alsa_join_threaded_loop(videoinput_get_audio(vidin));
+ alsa_close(videoinput_get_audio_p(vidin));
+ videoinput_set_audio(vidin, NULL);
+ }
+
if( fadepos < 256 ) {
crossfade_frame( fadeframe, saveframe, blueframe, width,
height, width*2, width*2, width*2, fadepos );
@@ -2499,6 +2524,11 @@
/* Return to normal scheduling. */
set_default_priority();
+ if (videoinput_get_audio(vidin)) {
+ alsa_join_threaded_loop(videoinput_get_audio(vidin));
+ alsa_close(videoinput_get_audio_p(vidin));
+ }
+
/* Remember to save our settings if we were scanning. */
if( scanning ) {
station_writeconfig( stationmgr );
diff -Naur tvtime-111b28cca42d.orig/src/videoinput.c tvtime-111b28cca42d/src/videoinput.c
--- tvtime-111b28cca42d.orig/src/videoinput.c 2011-02-01 02:35:26.000000000 +0100
+++ tvtime-111b28cca42d/src/videoinput.c 2011-08-14 12:03:39.783894207 +0200
@@ -38,6 +38,7 @@
#include <linux/videodev2.h>
#include "videoinput.h"
#include "mixer.h"
+#include "audiolib.h"
/**
* How long to wait when we lose a signal, or acquire a signal.
@@ -198,8 +199,25 @@
/* V4L2 capture state. */
capture_buffer_t capbuffers[ MAX_CAPTURE_BUFFERS ];
int is_streaming;
+
+ struct alsa_device *adev;
};
+struct alsa_device *videoinput_get_audio( videoinput_t *vidin )
+{
+ return vidin->adev;
+}
+
+struct alsa_device **videoinput_get_audio_p( videoinput_t *vidin )
+{
+ return &vidin->adev;
+}
+
+void videoinput_set_audio( videoinput_t *vidin, struct alsa_device *dev )
+{
+ vidin->adev = dev;
+}
+
static void videoinput_start_capture_v4l2( videoinput_t *vidin )
{
if( !vidin->is_streaming ) {
@@ -347,6 +365,7 @@
vidin->norm = norm;
vidin->volume = volume;
vidin->amode = 0;
+ vidin->adev = NULL;
vidin->height = videoinput_get_norm_height( norm );
vidin->cur_tuner_state = TUNER_STATE_NO_SIGNAL;
diff -Naur tvtime-111b28cca42d.orig/src/videoinput.h tvtime-111b28cca42d/src/videoinput.h
--- tvtime-111b28cca42d.orig/src/videoinput.h 2011-02-01 02:35:26.000000000 +0100
+++ tvtime-111b28cca42d/src/videoinput.h 2011-08-14 11:53:47.775915262 +0200
@@ -278,6 +278,20 @@
const char *videoinput_get_driver_name( videoinput_t *vidin );
/**
+ * Returns the audio handle of the device struct.
+ */
+struct alsa_device *videoinput_get_audio( videoinput_t *vidin );
+
+/**
+ * Returns a pointer to the alsa_device pointer, used for cleaning up
+ */
+struct alsa_device **videoinput_get_audio_p (videoinput_t *vidin );
+
+/**
+ * Set adev for vidin
+ */
+void videoinput_set_audio( videoinput_t *vidin, struct alsa_device *dev);
+/**
* Sets the capture card volume to use as a percentage from 0-100.
* If the value is negative, the capture card volume will remain unset.
*/