Hi all,
I've bolted together some ESD support, and it appears to work to a
first-order on my setup.
The patch is based around the artsd support, and the libao2 ESD module
from mplayer's CVS. It's under GPL v2.
Features:
- Does output sound via ESD
- wall-clock based timing for buffer fill levels for sync estimation
- Attempted compensation for esd latency.
Issues:
- General bodginess of the code
- Synchronisation issues
- Lack of buffer usage data in the ESD api
- Seems to need a -200ms delta on output on my setup.
- Small ESD buffer size means lots of work for the sound dispatch loop
- Sync issues after jumping forward/back
- No 'flush output' in ESD api
- Jump, pause, unpause usually fixes things
- Volume code appears to have a bad interaction with output and is
disabled.
- Sometimes no sound when starting a playback.
- Restarting playback appears to fix it most times.
I've since reconfigured my setup so I don't need ESD any longer[*], but
I wanted to throw this patch out as a starting point for anyone to
continue to work on if it helps their situation.
Cheers,
Mark
[*] I'm using alsa's dmix setup for simultaneous output of multiple
sound streams instead of esd.
--
Mark Cooke <[EMAIL PROTECTED]>
Index: configure
===================================================================
--- configure (revision 7025)
+++ configure (working copy)
@@ -39,6 +39,7 @@
audio_oss="default"
audio_alsa="default"
audio_arts="default"
+audio_esd="default"
audio_jack="default"
ivtv="yes"
ivtv_header="no"
@@ -138,6 +139,7 @@
echo " --disable-audio-oss disable OSS audio support"
echo " --disable-audio-alsa disable ALSA audio support"
echo " --disable-audio-arts disable aRts audio support"
+echo " --disable-audio-esd disable ESD audio support"
echo " --disable-audio-jack disable JACK audio support"
echo " --disable-dvd disable native DVD playback"
echo " --enable-valgrind disables timeouts for valgrind memory debugging"
@@ -443,6 +445,7 @@
audio_oss="no"
audio_alsa="no"
audio_arts="no"
+audio_esd="no"
audio_jack="no"
dv1394="no"
ffserver="no"
@@ -467,6 +470,7 @@
audio_oss="no"
audio_alsa="no"
audio_arts="no"
+audio_esd="no"
audio_jack="no"
dv1394="no"
extralibs=""
@@ -623,6 +627,8 @@
;;
--disable-audio-arts) audio_arts="no"
;;
+ --disable-audio-esd) audio_esd="no"
+ ;;
--disable-audio-jack) audio_jack="no"
;;
--disable-audio-beos) audio_beos="no"
@@ -1292,6 +1298,7 @@
audio_oss="no"
audio_alsa="no"
audio_arts="no"
+ audio_esd="no"
audio_jack="no"
dv1394="no"
dc1394="no"
@@ -1729,6 +1736,27 @@
fi
##########################################
+# ESD probe
+
+if test x"$audio_esd" = x"default" -o x"$audio_esd" = x"yes" ; then
+
+cat > $TMPC << EOF
+#include <esd.h>
+int main(void) { return (int) ESD_BUF_SIZE; }
+EOF
+
+audio_esd=no
+if (esd-config --version) > /dev/null 2>&1; then
+audio_esd_libs=`esd-config --libs`
+audio_esd_cflags=`esd-config --cflags`
+if $cc -o $TMPE $TMPC $audio_esd_cflags $audio_esd_libs > /dev/null 2>&1; then
+audio_esd=yes
+fi
+fi
+
+fi
+
+##########################################
# JACK probe
if test x"$audio_jack" = x"default" -o x"$audio_jack" = x"yes" ; then
@@ -2032,6 +2060,7 @@
echo "OSS support $audio_oss"
echo "ALSA support $audio_alsa"
echo "aRts support $audio_arts"
+ echo "ESD support $audio_esd"
echo "JACK support $audio_jack"
echo
echo "# Video Output Support"
@@ -2315,6 +2344,13 @@
echo "CONFIG_AUDIO_ARTS_CFLAGS=$audio_arts_cflags" >> $MYTH_CONFIG_MAK
fi
+if test "$audio_esd" = "yes" ; then
+ CCONFIG="$CCONFIG using_esd"
+ echo "#define CONFIG_AUDIO_ESD 1" >> $TMPH
+ echo "CONFIG_AUDIO_ESD_LIBS=$audio_esd_libs" >> $MYTH_CONFIG_MAK
+ echo "CONFIG_AUDIO_ESD_CFLAGS=$audio_esd_cflags" >> $MYTH_CONFIG_MAK
+fi
+
if test "$audio_jack" = "yes" ; then
CCONFIG="$CCONFIG using_jack"
echo "#define CONFIG_AUDIO_JACK 1" >> $TMPH
Index: libs/libmyth/audiooutputesd.cpp
===================================================================
--- libs/libmyth/audiooutputesd.cpp (revision 0)
+++ libs/libmyth/audiooutputesd.cpp (revision 0)
@@ -0,0 +1,253 @@
+//
+// Based on the oudiooutputartsd.cpp and the esd output module of mplayer
+//
+// License: GNU GENERAL PUBLIC LICENSE VERSION 2
+//
+
+#include <cstdio>
+#include <cstdlib>
+
+
+using namespace std;
+
+#include "mythcontext.h"
+#include "audiooutputesd.h"
+
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+AudioOutputESD::AudioOutputESD(QString audiodevice, int audio_bits,
+ int audio_channels, int audio_samplerate,
+ AudioOutputSource source, bool set_initial_vol)
+ : AudioOutputBase(audiodevice, audio_bits, audio_channels,
+ audio_samplerate, source, set_initial_vol)
+{
+ // our initalisation
+ handle = -1;
+ esd_fmt = 0;
+
+ // Set everything up
+ Reconfigure(audio_bits, audio_channels, audio_samplerate);
+}
+
+AudioOutputESD::~AudioOutputESD()
+{
+ // Close down all audio stuff
+ KillAudio();
+}
+
+void AudioOutputESD::CloseDevice()
+{
+ if (handle != -1)
+ esd_close(handle);
+
+ handle = -1;
+}
+
+bool AudioOutputESD::OpenDevice()
+{
+ int err;
+ int fl;
+
+ if (handle != -1)
+ {
+ esd_close(handle);
+ handle = -1;
+ }
+
+ esd_fmt = 0;
+ if (audio_bits == 8) {
+ esd_fmt |= ESD_BITS8;
+ bytes_per_sample = 1;
+ }
+ if (audio_bits == 16) {
+ esd_fmt |= ESD_BITS16;
+ bytes_per_sample = 2;
+ }
+
+ if (audio_channels == 1) {
+ esd_fmt |= ESD_MONO;
+ }
+
+ if (audio_channels == 2) {
+ esd_fmt |= ESD_STEREO;
+ bytes_per_sample *= 2;
+ }
+
+ esd_fmt |= ESD_STREAM | ESD_PLAY;
+
+ VERBOSE(VB_GENERAL, QString("Opening ESD audio device '%1' %2 %3 .")
+ .arg(audiodevice)
+ .arg(audio_samplerate)
+ .arg(esd_fmt));
+ err = esd_play_stream(esd_fmt, audio_samplerate, NULL, "mythtv");
+ if (err < 0)
+ {
+ Error(QString("Opening ESD sound device failed"));
+ return false;
+ }
+
+ if ((fl = fcntl(err, F_GETFL)) >= 0)
+ fcntl(err, F_SETFL, O_NDELAY|fl);
+
+ // Use the ESD buffer size. This is pretty small!
+ buff_size = ESD_BUF_SIZE;
+
+ // Latency is in bytes. This is used as a pre-load of buffered data in
+ // the soundcard to get sound and video more in sync.
+ buff_lat = esd_get_latency(err) * 44100 * 2;
+
+ // Data tracking things
+ esd_bytes_written = 0;
+ esd_play_start.tv_sec = 0;
+
+ // Parent class things
+ soundcard_buffer_size = buff_size;
+ fragment_size = buff_size / 2;
+
+ // Device opened successfully
+ handle = err;
+ return true;
+}
+
+void AudioOutputESD::WriteAudio(unsigned char *aubuf, int size)
+{
+ int err;
+
+ if (handle == -1)
+ return;
+
+ retry:
+ err = write(handle, aubuf, size);
+ if (err < 0) {
+ if (errno == EAGAIN)
+ goto retry;
+
+ // FIXME: Fatal?
+ VERBOSE(VB_IMPORTANT, QString("ESD Write error %1")
+ .arg(strerror(errno)));
+ return;
+ }
+
+ // Wrote some samples... so setup some tracking data
+ if (err > 0) {
+ esd_bytes_written += err;
+ if (esd_play_start.tv_sec == 0) {
+ gettimeofday(&esd_play_start, NULL);
+ }
+
+ // Uh-oh!
+ if (err != size) {
+ VERBOSE(VB_IMPORTANT, QString("ESD short write %1 vs %2")
+ .arg(size)
+ .arg(err));
+ }
+ }
+}
+
+// Supposed to return bytes actually on the soundcard right now
+//
+// It is used during latency calculations for syncing up
+inline int AudioOutputESD::getBufferedOnSoundcard(void)
+{
+ return buff_lat + (buff_size - getSpaceOnSoundcard());
+}
+
+// Bytes of space in out soundcard buffer (buff_size bytes)
+inline int AudioOutputESD::getSpaceOnSoundcard(void)
+{
+ struct timeval now;
+ double play_time;
+ int bytes_free;
+ unsigned long played_bytes;
+ double divisor = 10;
+
+ // No bytes in buffer yet...
+ if (esd_play_start.tv_sec == 0)
+ return buff_size;
+
+ // Calculate time since the sample was queued up (using divisor)
+ gettimeofday(&now, NULL);
+ play_time = (now.tv_sec - esd_play_start.tv_sec) * divisor;
+ play_time += (now.tv_usec - esd_play_start.tv_usec) / divisor;
+
+ // Calculate the bytes played so far
+ played_bytes = (unsigned long) ((play_time * audio_samplerate * bytes_per_sample) / divisor);
+
+ // Underflow. Oopsie.
+ if (played_bytes > esd_bytes_written) {
+
+ VERBOSE(VB_IMPORTANT, QString("ESD buffer underflow detected"));
+
+ // Reset the in-progress counter
+ esd_play_start.tv_sec = 0;
+ esd_bytes_written = 0;
+
+ return buff_size;
+ }
+
+ // Clamp to the buff_size limit.
+ bytes_free = buff_size - (esd_bytes_written - played_bytes);
+ if (bytes_free > buff_size)
+ bytes_free = buff_size;
+
+ return bytes_free;
+}
+
+int AudioOutputESD::GetVolumeChannel(int channel)
+{
+ esd_player_info_t *esd_pi;
+ esd_info_t *esd_i;
+ int left, right;
+
+ return 100;
+
+ // Borrowed from mplayer
+ if ((esd_i = esd_get_all_info(handle)) == NULL) {
+ VERBOSE(VB_IMPORTANT, QString("ESD couldn't get server information"));
+ return 100;
+ }
+
+ for (esd_pi = esd_i->player_list; esd_pi != NULL; esd_pi = esd_pi->next)
+ if (strcmp(esd_pi->name, "mythtv") == 0)
+ break;
+
+ if (esd_pi != NULL) {
+ left = esd_pi->left_vol_scale * 100 / ESD_VOLUME_BASE;
+ right = esd_pi->right_vol_scale * 100 / ESD_VOLUME_BASE;
+ } else {
+ VERBOSE(VB_IMPORTANT, QString("ESD couldn't find this player's information"));
+ left = right = 100;
+ }
+
+ esd_free_all_info(esd_i);
+
+ if (channel == 0)
+ return left;
+ else
+ return right;
+}
+
+void AudioOutputESD::SetVolumeChannel(int channel, int volume)
+{
+ int vol;
+
+ return;
+
+ vol = GetVolumeChannel(1-channel);
+
+ VERBOSE(VB_IMPORTANT, QString("ESD setting volume %1 to %2")
+ .arg(channel)
+ .arg(volume));
+
+ if (channel == 0)
+ esd_set_stream_pan(handle, 0,
+ volume * ESD_VOLUME_BASE / 100,
+ vol * ESD_VOLUME_BASE / 100);
+ else
+ esd_set_stream_pan(handle, 0,
+ vol * ESD_VOLUME_BASE / 100,
+ volume * ESD_VOLUME_BASE / 100);
+}
Index: libs/libmyth/audiooutput.cpp
===================================================================
--- libs/libmyth/audiooutput.cpp (revision 7025)
+++ libs/libmyth/audiooutput.cpp (working copy)
@@ -19,6 +19,9 @@
#ifdef USE_ARTS
#include "audiooutputarts.h"
#endif
+#ifdef USE_ESD
+#include "audiooutputesd.h"
+#endif
#ifdef CONFIG_DARWIN
#include "audiooutputca.h"
#endif
@@ -57,6 +60,17 @@
return NULL;
#endif
}
+ else if (audiodevice.startsWith("ESD:"))
+ {
+#ifdef USE_ESD
+ return new AudioOutputESD(audiodevice.remove(0, 4), audio_bits,
+ audio_channels, audio_samplerate, source, set_initial_vol);
+#else
+ VERBOSE(VB_IMPORTANT, "Audio output device is set to an ESD device "
+ "but ESD support is not compiled in!");
+ return NULL;
+#endif
+ }
else if (audiodevice.startsWith("JACK:"))
{
#ifdef USE_JACK
Index: libs/libmyth/libmyth.pro
===================================================================
--- libs/libmyth/libmyth.pro (revision 7025)
+++ libs/libmyth/libmyth.pro (working copy)
@@ -128,6 +128,13 @@
LIBS += $$ARTS_LIBS
}
+using_esd {
+ DEFINES += USE_ESD
+ HEADERS += audiooutputesd.h
+ SOURCES += audiooutputesd.cpp
+ LIBS += $$ESD_LIBS
+}
+
using_jack {
DEFINES += USE_JACK
HEADERS += bio2jack.h audiooutputjack.h
Index: libs/libmyth/audiooutputesd.h
===================================================================
--- libs/libmyth/audiooutputesd.h (revision 0)
+++ libs/libmyth/audiooutputesd.h (revision 0)
@@ -0,0 +1,49 @@
+#ifndef AUDIOOUTPUTESD
+#define AUDIOOUTPUTESD
+
+#include <qstring.h>
+
+#include <esd.h>
+
+using namespace std;
+
+#include "audiooutputbase.h"
+
+class AudioOutputESD : public AudioOutputBase
+{
+ public:
+ AudioOutputESD(QString audiodevice, int audio_bits,
+ int audio_channels, int audio_samplerate,
+ AudioOutputSource source, bool set_initial_vol);
+ virtual ~AudioOutputESD();
+
+ // Volume control
+ virtual int GetVolumeChannel(int channel); // Returns 0-100
+ virtual void SetVolumeChannel(int channel, int volume); // range 0-100 for vol
+
+ protected:
+
+ // You need to implement the following functions
+ virtual bool OpenDevice(void);
+ virtual void CloseDevice(void);
+ virtual void WriteAudio(unsigned char *aubuf, int size);
+ virtual inline int getSpaceOnSoundcard(void);
+ virtual inline int getBufferedOnSoundcard(void);
+
+
+
+ private:
+ QString audiodevice;
+ int handle;
+ int play_handle;
+ int esd_fmt;
+ int buff_size; // Size of output buffer in bytes
+ int buff_lat; // Latency of output to sound in bytes
+ bool can_hw_pause;
+ bool paused;
+ struct timeval esd_play_start;
+ unsigned long esd_bytes_written;
+ unsigned int bytes_per_sample;
+};
+
+#endif // AUDIOOUTPUTESD
Index: settings.pro
===================================================================
--- settings.pro (revision 7025)
+++ settings.pro (working copy)
@@ -24,6 +24,7 @@
QMAKE_CXXFLAGS += $$ARCHFLAGS
QMAKE_CXXFLAGS += $$CONFIG_AUDIO_ARTS_CFLAGS
+QMAKE_CXXFLAGS += $$CONFIG_AUDIO_ESD_CFLAGS
QMAKE_CXXFLAGS += $$CONFIG_DIRECTFB_CXXFLAGS
QMAKE_CXXFLAGS_SHLIB = -DPIC -fPIC
QMAKE_CXXFLAGS += $$ECXXFLAGS
@@ -67,6 +68,7 @@
EXTRA_LIBS = -lfreetype -lmp3lame
EXTRA_LIBS += $$CONFIG_AUDIO_ALSA_LIBS
EXTRA_LIBS += $$CONFIG_AUDIO_ARTS_LIBS
+EXTRA_LIBS += $$CONFIG_AUDIO_ESD_LIBS
EXTRA_LIBS += $$CONFIG_AUDIO_JACK_LIBS
EXTRA_LIBS += $$CONFIG_FIREWIRE_LIBS
EXTRA_LIBS += $$CONFIG_DIRECTFB_LIBS
_______________________________________________
mythtv-users mailing list
[email protected]
http://mythtv.org/cgi-bin/mailman/listinfo/mythtv-users