Headless Browser: Audio Output to Pulseaudio

I'm trying to run a headless browser on an EC2/Ubuntu 20.04 instance and then output the resulting audio stream to the default Pulseaudio sink (which is then picked up by DarkIce/Icecast). I'm only wanting to run a single webpage (mine, hosted on a different server, requiring jQuery & Howler.js), and the resulting browser/stream has to remain open 24/7.

I've managed to get an audio file on the instance to play to Icecast (using ogg123), so ogg123>Pulseaudio>Darkice>Icecast2 works. I've created a default sink as below

pactl load-module module-null-sink sink_name=radio
pacmd update-sink-proplist radio device.description=radio
pacmd set-default-sink radio

and made Pulseaudio the default driver by creating ~/.asoundrc to include

pcm.default pulse
ctl.default pulse

I'm not sure what the correct approach is to get the browser to play nicely. I've tried (both google-chome and chromium) pointing directly to both an audio file and a page with js that plays audio (with the intention of eventually running it in Screen), both of which appear to find the content, neither approach plays any audio. For example,

 google-chrome-stable --headless --disable-gpu --autoplay-policy=no-user-gesture-required --user-data-dir=/home/ubuntu/chromeUser --disable-accelerated-video-decode --disable-software-rasterizer --enable-logging=stderr --v=1  https://domain.name/stream.html 

generates the following

[0608/102421.257217:INFO:cpu_info.cc(53)] Available number of cores: 1
[0608/102421.258656:INFO:cpu_info.cc(53)] Available number of cores: 1
[0608/102421.258861:VERBOSE1:zygote_main_linux.cc(217)] ZygoteMain: initializing 0 fork delegates
[0608/102421.259318:VERBOSE1:zygote_main_linux.cc(217)] ZygoteMain: initializing 0 fork delegates
[0608/102421.271947:VERBOSE1:webrtc_internals.cc(118)] Could not get the download directory.
[0608/102421.280120:VERBOSE1:breakpad_linux.cc(2071)] Non Browser crash dumping enabled for: gpu-process
[0608/102421.283276:ERROR:gpu_init.cc(440)] Passthrough is not supported, GL is disabled
[0608/102421.286107:VERBOSE1:breakpad_linux.cc(2071)] Non Browser crash dumping enabled for: renderer
[0608/102421.288099:VERBOSE1:sandbox_linux.cc(69)] Activated seccomp-bpf sandbox for process type: gpu-process.
[0608/102421.293815:VERBOSE1:sandbox_linux.cc(69)] Activated seccomp-bpf sandbox for process type: renderer.
[0608/102421.303930:VERBOSE1:device_data_manager_x11.cc(216)] X Input extension not available
[0608/102421.356750:VERBOSE1:configured_proxy_resolution_service.cc(852)] PAC support disabled because there is no system implementation
[0608/102421.357514:VERBOSE1:configured_proxy_resolution_service.cc(852)] PAC support disabled because there is no system implementation
[0608/102421.359494:VERBOSE1:network_delegate.cc(32)] NetworkDelegate::NotifyBeforeURLRequest: https://domain.name/stream.html
[0608/102421.411474:VERBOSE1:document.cc(3974)] Document::DispatchUnloadEvents() URL = <null>
[0608/102421.411727:VERBOSE1:document.cc(4054)] Actually dispatching an UnloadEvent: URL = <null>
[0608/102421.421675:VERBOSE1:network_delegate.cc(32)] NetworkDelegate::NotifyBeforeURLRequest: /path/to/jquery.min.js
[0608/102421.424968:VERBOSE1:network_delegate.cc(32)] NetworkDelegate::NotifyBeforeURLRequest: /path/to/jquery-ui.min.js
[0608/102421.429480:VERBOSE1:network_delegate.cc(32)] NetworkDelegate::NotifyBeforeURLRequest: /path/to/howler.min.js

Is this the right approach to get this to work (and, if so, why doesn't it?), or should I be using Selenium/Puppeteer/something else?

Thanks, Chris

[aside: I've tried a number of things to try to remove the "Passthrough is not supported, GL is swiftshader" error, without success, although it doesn't appear to prevent the browser from accessing the page]


Solution 1:

I've managed to get this working by running Puppeteer, rather than trying to wrangle Chromium from the command line. I've used this answer to (hopefully) keep Puppeteer open with the following args to get the audio to start automatically.

 this.browser = await puppeteer.launch({
     headless: true,
     ignoreDefaultArgs: [
         "--mute-audio",
     ],
     args: [
         "--autoplay-policy=no-user-gesture-required",
     ],
  });

After setting up the default sink in PulseAudio and adding ~/.ascoundrc (as above), it all works (sink audio picked up by DarkIce > IceCast > broadcast). I'll likely use pm2 to oversee Puppeteer, but for now it's a working solution.