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
  • 24,905
  • 3
  • 35
  • 79
  • 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/[email protected]/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
  • 24,905
  • 3
  • 35
  • 79
  • Thank you for all that info, makes much more sense right now. actually, it was solved by adding a few more lines to my .sh script. based on: [ffmpeg returns "no such process" error when called by cron](https://stackoverflow.com/questions/68069727/ffmpeg-returns-no-such-process-error-when-called-by-cron) – Elias Tommeh Apr 25 '23 at 13:48
  • @EliasTommeh You’re welcome … Glad that solved it … However, you might need to pay attention that [PULSE_RUNTIME_PATH](https://pulseaudio-discuss.freedesktop.narkive.com/LE8XtC9t/patch-don-t-let-user-set-pulse-runtime-path-values-affect-behaviour) might be deprecated soon … In case it stops working in the future. – Raffa Apr 25 '23 at 14:16