Ticket #837 (closed defect: fixed)

Opened 20 months ago

Last modified 19 months ago

A52 (ac3/Dolby Digital) encoding for SPDIF / Pulseaudio

Reported by: mcarans Owned by: lennart
Milestone: 0.9.22 Component: module-alsa-*
Keywords: Cc: rans@…

Description

I am using Lucid 10.04 LTS.

I have managed to get A52 encoding through SPDIF to work with Alsa following broadly this guide: http://www.johannes-bauer.com/linux/dolby/?menuid=3

I have struggled to get this to work with Pulseaudio. I have tried a custom default.pa without the hardware autodetection and adding lines like: load-module module-alsa-source device=WebCamMic? source_name=WebCamMic? load-module module-alsa-sink device=HDMI sink_name=HDMI load-module module-alsa-sink device=Filter_RateConvert sink_name=SPDIF

But the Pulseaudio server complains because of the Filter_RateConvert line with: E: alsa-sink.c: Assertion 'err != -11' failed at modules/alsa/alsa-sink.c:395, function try_recover(). Aborting.

Is this a bug?

Please can someone tell me how to get Dolby Digital encoding through SPDIF working with Pulseaudio on Lucid.

Attachments

pulseaudiooutput.txt (8.7 kB) - added by mcarans 20 months ago.
pulseaudiooutput_rateconverter.txt (21.3 kB) - added by mcarans 19 months ago.

Change History

Changed 20 months ago by mcarans

The Alsa developer said:

"Did PA server has any audio data to play since there is no PA client connected to the server ?

Why PA server start the playback device when there is no data to play ?

this lead to underrun when PA server did not write to the driver

you have to ask PA developer why try_recover() occur , it seem underrun since there is no PA client provide audio data to the server" https://bugtrack.alsa-project.org/alsa-bug/view.php?id=5054

Changed 20 months ago by mcarans

I tried using the ppa here: https://launchpad.net/~ricotz/+archive/unstable

This upgraded Alsa to 1.0.23, Pulseaudio to 1:0.9.22 and kernel to 2.6.35-6.7.

The problem is still the same with these newer versions.

Changed 20 months ago by mcarans

  • cc rans@… added

The Alsa developer gave the following detailed analysis. Lennart what do you think? Do you think there is a bug in Pulseaudio or Alsa?

Handshake between application and library The ALSA PCM API design uses the states to determine the communication phase between application and library. The actual state can be determined using snd_pcm_state() call. There are these states:

SND_PCM_STATE_OPEN

The PCM device is in the open state. After the snd_pcm_open() open call, the device is in this state. Also, when snd_pcm_hw_params() call fails, then this state is entered to force application calling snd_pcm_hw_params() function to set right communication parameters.

SND_PCM_STATE_SETUP

The PCM device has accepted communication parameters and it is waiting for snd_pcm_prepare() call to prepare the hardware for selected operation (playback or capture).

SND_PCM_STATE_PREPARE

The PCM device is prepared for operation. Application can use snd_pcm_start() call, write or read data to start the operation.

SND_PCM_STATE_RUNNING

The PCM device has been started and is running. It processes the samples. The stream can be stopped using the snd_pcm_drop() or snd_pcm_drain() calls.

whenever PA server call pa_alsa_sink_new(), it start a thread after snd_pcm_dump() to start the playback

it call process_rewind() on certain condition , can PA rewind a52 plugin since it is compressing data to ac3 ???

In theory, snd_pcm_rewindable() should return zero for a52 plugin

why did alsa-lib allow the application to rewind iec958 device when no-audio-bit is set since the digital receiver already decoding the received compressed data ?

and then mmap_write() but is there any data for PA server to write ???

if first time in the loop , PA server call snd_pcm_start() ,but if PA server has written data using mmap_write() before, why it call snd_pcm_start() ???.

D: alsa-sink.c: Thread starting up

Why there is no "Thread shutting down" message in the log ?

D: main.c: Got org.pulseaudio.Server! I: main.c: Daemon startup complete.

do it mean that the thread is waiting for PA client to connect to provide data ?

This won't work because the digital receiver expect iec958 device providing valid AC3 data to decode as PA server has no data to write iec958 until PA client connected to provide data

static void thread_func(void *userdata) {

struct userdata *u = userdata; unsigned short revents = 0;

pa_assert(u);

pa_log_debug("Thread starting up");

if (u->core->realtime_scheduling)

pa_make_realtime(u->core->realtime_priority);

pa_thread_mq_install(&u->thread_mq);

for (;;) {

int ret;

#ifdef DEBUG_TIMING

pa_log_debug("Loop");

#endif

/* Render some data and write it to the dsp */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {

int work_done; pa_usec_t sleep_usec = 0; pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll);

if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))

if (process_rewind(u) < 0)

goto fail;

if (u->use_mmap)

work_done = mmap_write(u, &sleep_usec, revents & POLLOUT, on_timeout);

else

work_done = unix_write(u, &sleep_usec, revents & POLLOUT, on_timeout);

if (work_done < 0)

goto fail;

/* pa_log_debug("work_done = %i", work_done); */

if (work_done) {

if (u->first) {

pa_log_info("Starting playback."); snd_pcm_start(u->pcm_handle);

pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);

}

...

finish:

pa_log_debug("Thread shutting down");

}

Changed 20 months ago by mcarans

Changed 20 months ago by coling

Hi,

I'm afraid Raymond is often quite hard to work with. He has a good grasp of some of the low level things but manages to confuse just about every conversation he is part off by reply in a with poorly formatted emails and fragmenting threads, with small pockets of valid points in amongst swatches of irrelevant copy/paste from specs and other such like. Very hard to work with.

Anyway, I've posted a patch to the mailing list a couple weeks ago that fixes the assert problem for me. I'm not 100% sure that it is the right fix, but it prevents things crashing.

I was never able to get the alsa system working like this anyway and haven't looked too closely at why it fails. It's very likely that the a52 alsa plugin has totally broken timing information and thus we cannot work with it.

Here is the patch: http://thread.gmane.org/gmane.comp.audio.pulseaudio.general/6950

This will probably solve your crash problem, but I suspect that you'll run into the same timing problems as myself.

Changed 20 months ago by coling

(I should also add that people are working on a more integrated version that may or may not bypass the alsa plugin, so check out the various posts on the mailing list about this for some more information).

Changed 20 months ago by mcarans

Hi, thanks for your reply.

Using Alsa on its own, the AC3 plugin seemed to work fine - I tried with music and the game Oolite. Did the timing problems for you happen straight away or did they take a while to start?

I had trouble following what Raymond was saying here: https://bugtrack.alsa-project.org/alsa-bug/view.php?id=5054 but I think the main points were: 1. Pulseaudio ignores the error "SNDRV_PCM_IOCTL_START failed" 2. Pulseaudio cannot start the device when there is no data and must keep sending data at the sampling rate to prevent any underrun occurring

Does that help? Is there anything I can go back to Raymond et al with for them to look at/fix?

Changed 20 months ago by coling

Due to the way PA drives the audio hardware (which is quite unlike almost every other alsa client) it needs the timing information returned from alsa to be accurate. This is one of the reasons PA adoption was generally problematic for many people as their drivers needed to be fixed.

When I ran under plain alsa, speaker-test played each speaker's sample much quicker than it did under analog. With PA this was compressed much further to an almost inaudible chirp with underruns reported by PA.

I didn't have time to look further than this. It could be that setting tsched=0 when loading the device would work fine, so you could try that but YMMV.

Trying to discuss things with Raymond is not my favourite hobby as you'll see from our exchanges on the alsa mailing lists. It's very annoying as he does have valid points but cannot seem to express them in a constructive and logical manner.

I think he's probably correct about the SNDRV_PCM_IOCTL_START failed error tho'. Not sure what to do other than ignore it tho' (I'm no alsa guru here). I'm not sure about the second point, but I would think if this is a limitation of the a52 plugin, then it should be fixed there. It could be that he's just describing what an "underrun" is generally, which is fine, but if the a52 plugin works different from other PCM plugins in this regard then I'd say it's probably the one to blame, but I really have no idea. The tsched=0 argument when loading the alsa-sink may work around this problem but I doubt it.

Changed 19 months ago by mcarans

Changed 19 months ago by mcarans

I have found something that works for a while.

I add to .asoundrc: # Rate Converter to 48kHz, needed for Pulseaudio it seems pcm.Filter_RateConvert {

type rate slave {

pcm "Filter_A52Encode" rate 48000

}

}

and in default.pa: load-module module-alsa-sink device=Filter_RateConvert rate=48000 channels=6 tsched=0 sink_properties=device.description=SPDIF sink_name=SPDIF

Music plays through AC3 via SPDIF through Pulseaudio and Alsa, but when I close the application (eg. Rhythmbox music player), the SPDIF output "disappears".

I have attached a new back trace: pulseaudiooutput_rateconverter.txt

I think it's here where the Filter_RateConvert sink gets unloaded for some reason: I: client.c: Freed 1 "Rhythmbox" I: protocol-native.c: Connection died. I: module.c: Unloading "module-alsa-sink" (index: #4). D: module-always-sink.c: Autoloading null-sink as no other sinks detected.

How can I prevent this unloading?

Changed 19 months ago by mcarans

As the above is a different issue to the one I originally reported here, I have created a separate bug for it here: http://pulseaudio.org/ticket/839

Changed 19 months ago by mcarans

I added to default.pa: load-module module-suspend-on-idle timeout=0

and it seemed to prevent the sink unloading with Rhythmbox. SO I could open and close Rhythmbox and playing through SPDIF in Dolby Digital would work,

However, when I load Oolite that uses libsdl then the unloading still seems to occur (so Oolite produces no sound).

Changed 19 months ago by mcarans

I forced Pulseaudio not to rewind using:

pa_sink_set_max_rewind(u->sink, 0);

This seemed to fix it.

timeout=0 is no longer needed in default.pa: default.pa: load-module module-suspend-on-idle # timeout=0

However, there is still a need for a rate converter in asoundrc: # Rate Converter to 48kHz, needed for Pulseaudio it seems pcm.Filter_RateConvert {

type rate slave {

pcm "Filter_A52Encode" rate 48000

}

}

Changed 19 months ago by mcarans

Finally I got it all working including a52 Pulseaudio profiles!

These are the steps on Ubuntu Lucid 10.04 LTS:

1. Open from Pulse source code: src/modules/alsa/alsa-sink.c
2. Replace: pa_sink_set_max_rewind(u->sink, u->hwbuf_size);      with:
    if(strcmp(u->device_name, "a52") == 0) {
        pa_sink_set_max_rewind(u->sink, 0);
    } else {
        pa_sink_set_max_rewind(u->sink, u->hwbuf_size);
    }
3. Add Colin's patch:
             if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {

+                if (!after_avail && (int) sframes == -EAGAIN)
+                    break;
+
                 if ((r = try_recover(u, "snd_pcm_mmap_commit", (int) sframes)) == 0)
                     continue;
4. Compile and install Pulseaudio with udev support: make and sudo make install 
5. Edit /usr/local/share/pulseaudio/alsa-mixer/profile-sets/default.conf: change a52:%f to a52
6. Copy /etc/pulse/daemon.conf to ~/.pulse/daemon.conf and ensure it has uncommented: default-sample-rate = 48000
7.  Edit ~/.asoundrc
8.  Put in it:
# Encode AC3 -> Directly on hardware
pcm.Filter_A52Encode {
    type a52
    bitrate 448
    channels 6
    card NVidia
}

# Rate Converter to 48kHz, needed for some applications
pcm.a52 {
    type rate
    slave {
        pcm "Filter_A52Encode"
        rate 48000
    }
}
9. Change NVidia to your card's name from: cat /proc/asound/cards
10. sudo alsa reload
11. Make sure you do not have a ~/.pulse/default.pa or if you do, that it is using load-module module-udev-detect to load Alsa sinks not load-module module-alsa-sink ... 
  
12. pulseaudio -k
13. pulseaudio -D

Changed 19 months ago by coling

[3ede7e4] and [cb55b00] along with a suitable alsa config seem to solve this.

Alsa config is:

pcm.a52 {
   @args [CARD]
   @args.CARD {
       type string
   }
   type rate
   slave {
       pcm {
           type a52
           bitrate 448
           channels 6
           card $CARD
       }
       rate 48000 #required somehow, otherwise nothing happens in PulseAudio
   }
}

Changed 19 months ago by coling

  • status changed from new to closed
  • resolution set to fixed
  • milestone set to 0.9.22

Changed 19 months ago by mcarans

New versions of Pulseaudio will be patched, but in the meantime, here are the revised steps for Ubuntu Lucid 10.04 LTS:

1. Open from Pulse source code: src/modules/alsa/alsa-sink.c

2. Replace: pa_sink_set_max_rewind(u->sink, u->hwbuf_size); with:
if (pa_alsa_pcm_is_hw(u->pcm_handle)) {
    pa_sink_set_max_rewind(u->sink, u->hwbuf_size);
} else {
    pa_log("Disabling rewind for device %s", u->device_name);
    pa_sink_set_max_rewind(u->sink, 0);
}

3. Replace: pa_sink_set_max_rewind_within_thread(u->sink, u->hwbuf_size); with:
if (pa_alsa_pcm_is_hw(u->pcm_handle)) {
    pa_sink_set_max_rewind_within_thread(u->sink, u->hwbuf_size);
} else {
    pa_log("Disabling rewind_within_thread for device %s", u->device_name);
    pa_sink_set_max_rewind_within_thread(u->sink, 0);
}

4. After line: if (PA_UNLIKELY((sframes = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { add:
if (!after_avail && (int) sframes == -EAGAIN)
    break;

5. Compile and install Pulseaudio with udev support

6. Copy /etc/pulse/daemon.conf to ~/.pulse/daemon.conf and ensure it has uncommented: default-sample-rate = 48000

7. Edit ~/.asoundrc and put in it:
pcm.a52 {
   @args [CARD]
   @args.CARD {
       type string
   }
   type rate
   slave {
       pcm {
           type a52
           bitrate 448
           channels 6
           card $CARD
       }
       rate 48000 #required somehow, otherwise nothing happens in PulseAudio
   }

8. sudo alsa reload

9. Make sure you do not have a ~/.pulse/default.pa or if you do, that it is using load-module module-udev-detect to load Alsa sinks not load-module module-alsa-sink ...

10. pulseaudio -k and pulseaudio -D
Note: See TracTickets for help on using tickets.