picamera2: [BUG]BrokenPipeError: [Errno 32] Broken pipe

I have a simple python script for motion detection on Raspberry Pi 4B:

motion.py

import time
from datetime import datetime
import RPi.GPIO as GPIO
from picamera2 import Picamera2
from picamera2.encoders import H264Encoder
from picamera2.outputs import FfmpegOutput

PIR_SENSOR = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(PIR_SENSOR, GPIO.IN)

picam2 = Picamera2()
video_config = picam2.create_video_configuration()
picam2.configure(video_config)
encoder = H264Encoder(10000000)


def detectMotion(x):
    print("Motion was detect!\n")
    picam2.start()
    timestamp = datetime.now().strftime('%H-%M-%S-%d-%m-%Y')
    picam2.capture_file('/home/admin/camera/images/image-%s.jpg' % timestamp)
    picam2.stop()
    video_file = FfmpegOutput('/home/admin/camera/videos/video-%s.mp4' % timestamp, audio=True)
    picam2.start_recording(encoder, video_file)
    time.sleep(10)
    picam2.stop_recording()


try:
    GPIO.add_event_detect(PIR_SENSOR, GPIO.RISING, callback=detectMotion)
    print("Started detect...\n")
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    print("exited")
finally:
    GPIO.cleanup()

I run the script as a service

[Unit]
Description=Motion Detector
After=multi-user.target

[Service]
User=admin
Group=admin
WorkingDirectory=/home/admin/camera
ExecStart=/usr/bin/python3 -u /home/admin/camera/motion.py
StandardOutput=/home/admin/camera/motion.log
StandardError=/home/admin/camera/motion.log
Restart=on-failure


[Install]
WantedBy=multi-user.target

The service raise an error break pipe:

sudo systemctl status motion.service
● motion.service - Motion Detector
     Loaded: loaded (/lib/systemd/system/motion.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2022-12-06 18:07:36 +07; 15h ago
   Main PID: 1147 (python3)
      Tasks: 9 (limit: 4915)
        CPU: 10.529s
     CGroup: /system.slice/motion.service
             └─1147 /usr/bin/python3 -u /home/admin/camera/motion.py

Dec 07 09:13:44 raspberrypi python3[1147]:     self.run()
Dec 07 09:13:44 raspberrypi python3[1147]:   File "/usr/lib/python3.9/threading.py", line 892, in run
Dec 07 09:13:44 raspberrypi python3[1147]:     self._target(*self._args, **self._kwargs)
Dec 07 09:13:44 raspberrypi python3[1147]:   File "/usr/lib/python3/dist-packages/picamera2/encoders/v4l2_encoder.py", line 188, in thread_poll
Dec 07 09:13:44 raspberrypi python3[1147]:     self.outputframe(b, keyframe, (buf.timestamp.secs * 1000000) + buf.timestamp.usecs)
Dec 07 09:13:44 raspberrypi python3[1147]:   File "/usr/lib/python3/dist-packages/picamera2/encoders/encoder.py", line 209, in outputframe
Dec 07 09:13:44 raspberrypi python3[1147]:     out.outputframe(frame, keyframe, timestamp)
Dec 07 09:13:44 raspberrypi python3[1147]:   File "/usr/lib/python3/dist-packages/picamera2/outputs/ffmpegoutput.py", line 85, in outputframe
Dec 07 09:13:44 raspberrypi python3[1147]:     self.ffmpeg.stdin.write(frame)
Dec 07 09:13:44 raspberrypi python3[1147]: BrokenPipeError: [Errno 32] Broken pipe

Strangely, if I run the python script directly without running it in the systemd service it works properly with no errors

python motion.py
[0:30:41.464106200] [1733]  INFO Camera camera_manager.cpp:293 libcamera v0.0.1+54-d528119f
[0:30:41.496729242] [1734]  INFO RPI raspberrypi.cpp:1414 Registered camera /base/soc/i2c0mux/i2c@1/ov5647@36 to Unicam device /dev/media2 and ISP device /dev/media0
[0:30:41.501582991] [1733]  INFO Camera camera.cpp:1026 configuring streams: (0) 1280x720-XBGR8888
[0:30:41.501995300] [1734]  INFO RPI raspberrypi.cpp:800 Sensor: /base/soc/i2c0mux/i2c@1/ov5647@36 - Selected sensor format: 1920x1080-SGBRG10_1X10 - Selected unicam format: 1920x1080-pGAA
Started detect...

Motion was detect!

Guessed Channel Layout for Input Stream #0.0 : stereo

Thanks.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 16

Most upvoted comments

Managed to get this working. The issue is that for some reason, pulseaudio is not reachable/working/whatever while being accessed from systemd-ran processes. You can check this easily by creating a dead-simple systemd service which starts ffmpeg -sources pulse - the output of this will not have any audio devices, while running the very same command by hand, you can see the devices. The other giveaway to investigate pulseaudio was that if you disable the audio, the service works as it should.

The solution (or rather workaround…) is to run pulseaudio as a system-wide daemon instead of a per-user one. I’ve done the following steps:

  1. sudo systemctl --global disable pulseaudio.service pulseaudio.socket - first we disable pulseaudio services
  2. sudo usermod -a -G pulse-access root - we prepare for pulseaudio to be run as a system-wide “daemon” by the root user. For that to work, all users need to be added to the pulse-access group.
  3. sudo usermod -a -G pulse-access <your_username> - replace your user here which you want to run your service under systemd which needs to access pulseaudio.
  4. Edit (as root) /etc/pulse/client.conf and set autospawn = no
  5. Create a systemd service to run pulseaudio: 4.1. Create a file named pulseaudio-system.service and copy the content below. 4.2. Copy this to systemd: sudo cp pulseaudio-system.service /lib/systemd/system/
  6. sudo systemctl daemon-reload
  7. sudo systemctl enable pulseaudio-system.service - enable the service
  8. sudo reboot - reboot the PI
  9. (optional) ffmpeg -sources pulse - After reboot, test if ffmpeg detects audio devices. You should see some detected devices at the end of the output.

Here’s the content for the pulseaudio-system.service file:

[Unit]
Description=PulseAudio workaround
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/usr/bin/pulseaudio --system --disallow-exit --disallow-module-loading --daemonize=no
WorkingDirectory=/root/
StandardOutput=append:/root/pulseaudio.log
StandardError=append:/root/pulseaudio.err

[Install]
WantedBy=multi-user.target

Also worth noting, that using pulseaudio this way is not recommended even by pulseaudio itself; security reasons and whatnot. So keep that in mind too if you want to use this workaround.

Found another workaround. If you you create a user service instead of a system service then it works fine.

alsa should always be available after stopping pulse (or uninstalling it). e.g. kodi in RPiOS uses this in launcher:

 systemctl --user stop pulseaudio.socket
 systemctl --user stop pulseaudio.service
 < run kodi >
 systemctl --user start pulseaudio.service
 systemctl --user start pulseaudio.socket

as running without pulse allows access to passthrough audio.

Also if an alsa device is in use (by pulse) then an application should return an error rather than seg fault.