No audio saved in camera.record service file (mp4)

Hi,

I have been looking for information about this subject but none of the topics I have read deal with this issue.

I have configured my wifi cameras using generic camera integration and all of them are working fine, including audio.

In my automations I take a snapshot or a video which is stored in my media folder without any issue, problem is that those videos do not have audio. I have tried different media players and devices and it seems that there is no audio track in file generated (.mp4).

However, if I get these videos from webrtc and send them via telegram, audio is fine.

Any suggestion??

Thanks a lot

Hi,

I’m sorry to refloat this post but I wasn’t able to solve the audio track problem in my recordings for months :disappointed_relieved:

Does anyone have any opinion or news?

Thanks a lot

Same here. Audio also works on my onvif, go2rtc, offical and unoffical tp-link integrations, generic camera, you name it but no audio on recorded mp4 videos. It’s not that it’s silent, there is no audio in the file. Can’t find any information on this.

I have this problem too. Is there any solution?

I finally solved! @franlmc @shane0802
With this shell command, the video output has audio in it.
You will need to paste the shell_command in your configuration.yaml, if you are beginner ask help from AI for your situation.
You need to create the folder if it doesn’t exists, otherwise ffmpeg command will fail. That’s why the code creates the folder first.
If you want to record more than 60 seconds you need to install long_shell_command from hacs because default shell command will timeout after 60 seconds. This one is 600 seconds.
I will share my setup which creates folders for each week for the year. ( 2025-06-26 )
And the file name is from an input text. Which you will see in my automation.
Only downside compared to camera.record is there seems no way for lookback feature. If you find a way, let me know.

long_shell_command:
  camera_record: >
    bash -c 'mkdir -p /media/Camera/Recordings/{{ states("sensor.date").split("-")[0] }}-{{ now().strftime("%m") }}-{{ now().isocalendar().week }} &&
    ffmpeg -i rtsp://C220_1C3B:[email protected]:554/stream1 -c:v copy -c:a aac -t 60 -y
    /media/Camera/Recordings/{{ states("sensor.date").split("-")[0] }}-{{ now().strftime("%m") }}-{{ now().isocalendar().week }}/{{ states("input_text.snapshot_filename") }}.mp4'
alias: Camera Control
description: ""
triggers:
  - trigger: state
    entity_id:
      - binary_sensor.c220_1c3b_motion_detection
      - binary_sensor.c220_1c3b_motion_alarm
      - binary_sensor.c220_1c3b_person_detection
    from: "off"
    to: "on"
    enabled: true
conditions:
  - condition: state
    entity_id: input_boolean.camera_recording
    state: "off"
  - condition: template
    value_template: |-
      {% set last = state_attr('automation.camera_control', 'last_triggered') %}
      {{ last is none or (now() - last).total_seconds() > 60 }}
actions:
  - parallel:
      - action: input_boolean.turn_on
        metadata: {}
        data: {}
        target:
          entity_id: input_boolean.camera_recording
      - action: switch.turn_on
        metadata: {}
        data: {}
        target:
          entity_id: switch.c220_1c3b_led
  - repeat:
      sequence:
        - action: input_text.set_value
          metadata: {}
          data:
            value: "{{ now().strftime('%Y%m%d_%H%M%S') }}"
          target:
            entity_id: input_text.snapshot_filename
        - action: long_shell_command.camera_record
          metadata: {}
          data: {}
      until:
        - condition: and
          conditions:
            - condition: or
              conditions:
                - condition: state
                  entity_id: binary_sensor.c220_1c3b_motion_detection
                  state: "off"
                - condition: template
                  value_template: |2-
                          {{ not (
                            is_state('binary_sensor.c220_1c3b_motion_detection', 'on') and
                            (now() - states.binary_sensor.c220_1c3b_motion_detection.last_changed).total_seconds() <= 60
                          ) }}
            - condition: or
              conditions:
                - condition: state
                  entity_id: binary_sensor.c220_1c3b_motion_alarm
                  state: "off"
                - condition: template
                  value_template: |2-
                          {{ not (
                            is_state('binary_sensor.c220_1c3b_motion_alarm', 'on') and
                            (now() - states.binary_sensor.c220_1c3b_motion_alarm.last_changed).total_seconds() <= 60
                          ) }}
            - condition: or
              conditions:
                - condition: state
                  entity_id: binary_sensor.c220_1c3b_person_detection
                  state: "off"
                - condition: template
                  value_template: |2-
                          {{ not (
                            is_state('binary_sensor.c220_1c3b_person_detection', 'on') and
                            (now() - states.binary_sensor.c220_1c3b_person_detection.last_changed).total_seconds() <= 60
                          ) }}
    enabled: true
  - parallel:
      - action: input_boolean.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: input_boolean.camera_recording
      - action: switch.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: switch.c220_1c3b_led
mode: single

1 Like

Hey @xertux

Your message really made me happy. I tried to implement it in my setup, but… it didn’t work :sob:

Following your advice, I asked ChatGPT what the issue might be, and after lots of tests and technical explanations, we (well, it :sweat_smile:) came to this conclusion:

“Your Home Assistant setup (probably using Home Assistant OS or running in a Docker container) doesn’t have ffmpeg available in the environment where shell_commands run. That’s why any command trying to call ffmpeg from a shell_command will fail automatically — the system just can’t find it.”

So then I asked if there was any alternative way to get audio in my recordings, and it suggested pulling both, video and audio, directly from go2rtc instead of using camera.record.

The workaround was to create a new shell_command that fetches the stream straight from go2rtc, and right there it converts the audio to a more “universal” codec like AAC or FLAC. Here’s the command in case it helps someone else:

yaml

CopiarEditar

go2rtc_record_garaje: 'curl -o /media/Cam_garaje/{{ filename }}.mp4 "http://localhost:1984/api/stream.mp4?src=Cam.garaje&mp4=aac&duration=15"'

Just remember to change the folder path to wherever you want to save the file. And in my case, the 15 at the end means I’m recording 15 seconds of video.

Lastly, in your automation or script, instead of calling camera.record, just call shell_command.go2rtc_record_garaje.

Hope this helps someone out there — and huge thanks again @xertux for pointing me in the right direction to solve the issue! :raised_hands:

This may or may not work for you, but I had the same issue as the OP where live audio worked in Home Assistant, but camera.record wouldn’t record it. I found that changing the audio codec in the camera from G711 to AAC worked.

Here is my new solution for camera record with sound and dynamic record duration. The code must be revised for your own use. Get help from AI if you are not sure what to do.

To get rid of long_shell_command extension we now create the folder that will contain the video beforehand.Fixed 1 minute record was timing out the shell command module that’s why we were using long_shell_command but with this build, the code gets the job done immediately and ffmpeg works in background.
These are shell_commands that will be put into the configuration.yaml

  create_camera_folder: >
    bash -c "mkdir -p /media/Camera/Recordings/{{ states('sensor.date').split('-')[0] }}-{{ now().strftime('%m') }}-{{ now().isocalendar().week }}"

This command start recording with no end, to infinity and beyond.

  camera_record_regular_nonstop: >
    bash -c "nohup ffmpeg -i rtsp://C220_1C3B:[email protected]:554/stream1 -c:v copy -c:a aac -movflags +faststart -y /media/Camera/Recordings/{{ states('sensor.date').split('-')[0] }}-{{ now().strftime('%m') }}-{{ now().isocalendar().week }}/{{ states('input_text.record_filename') }}.mp4 > /dev/null 2>&1 &"

This command will stop the recording that is ongoing to named folder.

  camera_record_stop: >
    bash -c "pkill -f 'ffmpeg.*Recordings/'"

Bonus. This command will trim your video from the end. It’s usefull if your movement sensor has delayed clear duration. For me it’s sonoff snzb-03 which sends cleared after 1 min. of no motion. last 1 minute of my videos were always empty but no more.

  camera_record_trim_regular: >
    bash -c "FILE_PATH=/media/Camera/Recordings/{{ states('sensor.date').split('-')[0] }}-{{ now().strftime('%m') }}-{{ now().isocalendar().week }}/{{ states('input_text.record_filename') }}.mp4;
    DURATION=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 $FILE_PATH);
    NEW_DURATION=$(echo \"$DURATION - 45\" | bc);
    ffmpeg -i $FILE_PATH -t $NEW_DURATION -c copy -y ${FILE_PATH%.mp4}_trimmed.mp4 && mv ${FILE_PATH%.mp4}_trimmed.mp4 $FILE_PATH"