1

I have a Python script to record audio/video using the "subprocess" module to execute ffmpeg commands, as follows:

def kill_ffmpeg_process(args):
    for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
        if proc.info['name'] == 'ffmpeg' and args in proc.info['cmdline']:
            print("found: " + str(proc))
            proc.kill()

kill_ffmpeg_process("ffmpeg")

rand_num = str(random.randint(0,99)) record_internal_cmd = f"ffmpeg -f pulse -ac 2 -i 'alsa_input.pci-0000_00_05.0.analog-stereo' -f v4l2 -i /dev/video4 -vcodec libx264 -crf 28 Desktop/recordings/{get_today_timestamp()}{str(rand_num)}_internal_feed.mkv" record_external_cmd = f"ffmpeg -f v4l2 -i /dev/video5 Desktop/recordings/{get_today_timestamp()}{str(rand_num)}_external_feed.mkv"

print("Recording /dev/video4 with audio") subprocess.Popen(record_internal_cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE) time.sleep(1) print("Recording /dev/video5") subprocess.Popen(record_external_cmd,shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIPE) time.sleep(3)

I want to schedule this script to be executed every 1 hour (automatically) with crontab, so I created a .sh file that calls the Python script #!/bin/bash python3 script.py. when executing it manually in terminal ./shell_script.sh it works fine, but when adding it to a crontab it doesn't work because it cannot find the audio source "personal thoughts" because when I remove audio recording from it, it works. But pulse audio recording doesn't work; it only executes the second command and creates the file that includes video recording.

Raffa
  • 32,237
  • I don't think the problem is with paths, because it's working as expected, except for the audio record part that can be found in "record_internal_cmd" it ignores it and executes "record_external_cmd" – Elias Tommeh Apr 25 '23 at 07:36
  • Still, ffmpeg may not be found in a cronjob because conjobs have a very limited environment, so even PATH may not be set. – vanadium Apr 25 '23 at 07:49

1 Answers1

1

Why is this happening?

Your script obviously needs Pulse Audio running ... Pulse audio is run(automatically) as a user service ... You can see the service's status with:

$ systemctl --user status pulseaudio
● pulseaudio.service - Sound Service
     Loaded: loaded (/usr/lib/systemd/user/pulseaudio.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2023-04-25 14:26:50 +03; 13min ago
TriggeredBy: ● pulseaudio.socket
   Main PID: 120691 (pulseaudio)
      Tasks: 4 (limit: 23594)
     Memory: 7.3M
        CPU: 389ms
     CGroup: /user.slice/user-1000.slice/user@1000.service/session.slice/pulseaudio.service
             └─120691 /usr/bin/pulseaudio --daemonize=no --log-target=journal

Apr 25 14:26:50 Lenovo systemd[5211]: Starting Sound Service... Apr 25 14:26:50 Lenovo systemd[5211]: Started Sound Service.

The service unit itself can be inspected with:

$ cat /usr/lib/systemd/user/pulseaudio.service
[Unit]
Description=Sound Service

We require pulseaudio.socket to be active before starting the daemon, because

while it is possible to use the service without the socket, it is not clear

why it would be desirable.

A user installing pulseaudio and doing systemctl --user start pulseaudio

will not get the socket started, which might be confusing and problematic if

the server is to be restarted later on, as the client autospawn feature

might kick in. Also, a start of the socket unit will fail, adding to the

confusion.

After=pulseaudio.socket is not needed, as it is already implicit in the

socket-service relationship, see systemd.socket(5).

Requires=pulseaudio.socket ConditionUser=!root

[Service] ExecStart=/usr/bin/pulseaudio --daemonize=no --log-target=journal LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes Restart=on-failure RestrictNamespaces=yes SystemCallArchitectures=native SystemCallFilter=@system-service

Note that notify will only work if --daemonize=no

Type=notify UMask=0077 Slice=session.slice

[Install] Also=pulseaudio.socket WantedBy=default.target

To see three important settings:

  1. WantedBy=default.target which means the service won't necessarily be available until just before default.target(the last multi-user enabled interface in the boot process).

  2. Requires=pulseaudio.socket which is only available after a user session has been started (i.e. after login)

  3. ConditionUser=!root which means the user cannot be root.

Both the above 1 and 2 conditions work against the availability of a running Pulse Audio daemon for a crontab job during boot and before login.

Condition 3, however, will not allow running a Pulse Audio daemon under root even after login.

What to do about it?

  • You can add your cron job in your user's crontab(Not root's crontab) and ensure your user is logged in when the cron job runs.

  • You can manually run a Pulse Audio daemon in your script running your script as a certain user that you explicitly specify and in this case, even root can be used ... compare for example:

    $ sudo su root -c 'pacmd list-sources'
    No PulseAudio daemon running, or not running as session daemon.
    

    Versus:

    $ sudo su root -c 'pulseaudio --start; pacmd list-sources'
    W: [pulseaudio] main.c: This program is not intended to be run as root (unless --system is specified).
    2 source(s) available.
         index: 0
         name: <alsa_output.pci-0000_00_1f.3.analog-stereo.monitor>
         driver: <module-alsa-card.c>
         flags: DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
         state: IDLE
         suspend cause: (none)
         priority: 1030
         volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                 balance 0.00
         base volume: 65536 / 100% / 0.00 dB
         volume steps: 65537
         muted: no
         current latency: 0.00 ms
         max rewind: 344 KiB
         sample spec: s16le 2ch 44100Hz
         channel map: front-left,front-right
                      Stereo
         used by: 0
         linked by: 0
         configured latency: 2000.00 ms; range is 0.50 .. 2000.00 ms
         monitor_of: 0
         card: 0 <alsa_card.pci-0000_00_1f.3>
         module: 7
         properties:
             device.description = "Monitor of Built-in Audio Analogue Stereo"
             device.class = "monitor"
             alsa.card = "0"
             alsa.card_name = "HDA Intel PCH"
             alsa.long_card_name = "HDA Intel PCH at 0xa12a8000 irq 136"
             alsa.driver_name = "snd_hda_intel"
             device.bus_path = "pci-0000:00:1f.3"
             sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
             device.bus = "pci"
             device.vendor.id = "8086"
             device.vendor.name = "Intel Corporation"
             device.product.id = "9d71"
             device.product.name = "Sunrise Point-LP HD Audio"
             device.form_factor = "internal"
             device.string = "0"
             module-udev-detect.discovered = "1"
             device.icon_name = "audio-card-pci"
       * index: 1
         name: <alsa_input.pci-0000_00_1f.3.analog-stereo>
         driver: <module-alsa-card.c>
         flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY DYNAMIC_LATENCY
         state: IDLE
         suspend cause: (none)
         priority: 9039
         volume: front-left: 3381 /   5% / -77.25 dB,   front-right: 3381 /   5% / -77.25 dB
                 balance 0.00
         base volume: 6554 /  10% / -60.00 dB
         volume steps: 65537
         muted: yes
         current latency: 353.71 ms
         max rewind: 0 KiB
         sample spec: s16le 2ch 44100Hz
         channel map: front-left,front-right
                      Stereo
         used by: 0
         linked by: 0
         configured latency: 2000.00 ms; range is 0.50 .. 2000.00 ms
         card: 0 <alsa_card.pci-0000_00_1f.3>
         module: 7
         properties:
             alsa.resolution_bits = "16"
             device.api = "alsa"
             device.class = "sound"
             alsa.class = "generic"
             alsa.subclass = "generic-mix"
             alsa.name = "ALC236 Analog"
             alsa.id = "ALC236 Analog"
             alsa.subdevice = "0"
             alsa.subdevice_name = "subdevice #0"
             alsa.device = "0"
             alsa.card = "0"
             alsa.card_name = "HDA Intel PCH"
             alsa.long_card_name = "HDA Intel PCH at 0xa12a8000 irq 136"
             alsa.driver_name = "snd_hda_intel"
             device.bus_path = "pci-0000:00:1f.3"
             sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
             device.bus = "pci"
             device.vendor.id = "8086"
             device.vendor.name = "Intel Corporation"
             device.product.id = "9d71"
             device.product.name = "Sunrise Point-LP HD Audio"
             device.form_factor = "internal"
             device.string = "front:0"
             device.buffering.buffer_size = "352800"
             device.buffering.fragment_size = "176400"
             device.access_mode = "mmap+timer"
             device.profile.name = "analog-stereo"
             device.profile.description = "Analogue Stereo"
             device.description = "Built-in Audio Analogue Stereo"
             module-udev-detect.discovered = "1"
             device.icon_name = "audio-card-pci"
         ports:
             analog-input-internal-mic: Internal Microphone (priority 8900, latency offset 0 usec, available: unknown)
                 properties:
                     device.icon_name = "audio-input-microphone"
             analog-input-mic: Microphone (priority 8700, latency offset 0 usec, available: no)
                 properties:
                     device.icon_name = "audio-input-microphone"
         active port: <analog-input-internal-mic>
    
  • You can run Pulse Audio as system-wide daemon.

Raffa
  • 32,237