I have a Raspberry Pi Zero 2W wired to an Adafruit MEMS microphone with a camera attached. I tested audio recording in a separate Python program and that works perfectly:But when I tried implementing that into my main program that records audio and video, then muxes them, video works fine but there is no audio in either the audio.wav file or the video itself. I know the mic works, the muxxing works. I am fairly certain it's an issue with how I thread and start the audio recording (some parts of the code below are removed for readability):
Code:
import subprocessimport threadingimport osaudio_process = Nonestop_recording_flag = Falseaudio_filename = "test.wav"def record_audio(): global audio_process, stop_recording_flag env = os.environ.copy() env["HOME"] = os.path.expanduser("~") print("Recording... Type 'stop' and press Enter to stop.") audio_process = subprocess.Popen([ "arecord", "-D", "dmic_sv", "-c", "2", "-r", "48000", "-f", "S32_LE", "-t", "wav", audio_filename ], env=env) # Wait until user types 'stop' while not stop_recording_flag and audio_process.poll() is None: pass if audio_process.poll() is None: print("Stopping recording...") audio_process.terminate() try: audio_process.wait(timeout=5) except subprocess.TimeoutExpired: audio_process.kill() print(f"Recording saved to {audio_filename}")def monitor_input(): global stop_recording_flag while True: command = input().strip().lower() if command == "stop": stop_recording_flag = True breakif __name__ == "__main__": t1 = threading.Thread(target=record_audio) t2 = threading.Thread(target=monitor_input) t1.start() t2.start() t1.join() t2.join()Code:
video_selected_index = 0is_recording = Falseframerate = 30video_filename = "/home/pi/video.h264"audio_wav_filename = "/home/pi/audio.wav"final_filename = ""picam = Noneaudio_process = Noneaudio_thread = Nonevideo_thread = Nonedef record_audio(): global audio_process, is_recording try: print(" [record_audio] Starting audio capture, Sir.") env = os.environ.copy() env["HOME"] = os.path.expanduser("~") audio_process = subprocess.Popen([ "arecord", "-D", "dmic_sv", "-c", "2", "-r", "48000", "-f", "S32_LE", "-t", "wav", audio_wav_filename ], env=env) # Wait for is_recording to turn False while is_recording: time.sleep(0.1) if audio_process.poll() is None: print(" [record_audio] Terminating arecord…") audio_process.terminate() try: audio_process.wait(timeout=5) except subprocess.TimeoutExpired: audio_process.kill() print(f" [record_audio] Audio saved to {audio_wav_filename}, Sir.") except Exception as e: print(f" [record_audio] Exception, Sir: {e}")def start_recording(config): """ Launch audio + video threads (non-daemon), with a slight stagger. """ global is_recording, audio_thread, video_thread is_recording = True # Start UI updater thread (could remain daemon) threading.Thread(target=recording_ui_updater, daemon=True).start() # Create *non-daemon* threads for audio & video so they run fully audio_thread = threading.Thread(target=record_audio, daemon=False) video_thread = threading.Thread(target=record_video, args=(config,), daemon=False) # Start audio first, give it time to initialize DMIC audio_thread.start() time.sleep(0.1) # Now start video capture video_thread.start()def stop_recording(): """ Signal both threads to finish, then join. """ global is_recording is_recording = False print(" [stop_recording] Signaled threads to stop.") recording_status("Muxing...") if audio_thread: audio_thread.join() # Give time for OS to flush file buffers time.sleep(0.5) # Crucial if video_thread: print(" [stop_recording] Joining video thread…") video_thread.join() print(" [stop_recording] Threads joined, proceeding to mux.") mux_audio_video()def show_take_video_menu(): global video_selected_index, is_recording, picam try: picam = Picamera2() picam.configure(picam.create_still_configuration()) picam.start() time.sleep(2) # Camera warm-up except Exception as e: print(f"Camera initialization failed: {e}") return while True: if not is_recording: # Draw resolution menu img = Image.new("RGB", (160, 80), "black") draw = ImageDraw.Draw(img) title = "Take Video" title_width = draw.textlength(title, font=font) draw.text(((160 - title_width) // 2, 2), title, font=font, fill="red") for i, (label, _) in enumerate(video_resolutions): prefix = "> " if i == video_selected_index else " " draw.text((10, 18 + i * 14), prefix + label, font=font, fill="red") display.display(img) # Button controls if GPIO.input(21) == GPIO.LOW: # UP wait_for_button_release(21) video_selected_index = (video_selected_index - 1) % len(video_resolutions) elif GPIO.input(12) == GPIO.LOW: # DOWN wait_for_button_release(12) video_selected_index = (video_selected_index + 1) % len(video_resolutions) elif GPIO.input(16) == GPIO.LOW: # SELECT wait_for_button_release(16) label, config = video_resolutions[video_selected_index] if is_recording: stop_recording() continue if label.lower() == "return": if is_recording: stop_recording() print("Exiting video menu.") picam.close() return if config: start_recording(config) time.sleep(0.1) # Input debounce delaydef mux_audio_video(): global final_filename, framerate timestamp = time.strftime("%Y%m%d_%H%M%S") final_filename = f"/home/pi/Videos/recording_{timestamp}.mp4" subprocess.run([ "ffmpeg", "-y", "-framerate", str(framerate), "-i", video_filename, "-i", audio_wav_filename, "-metadata:s:v", "rotate=270", "-c:v", "copy", "-c:a", "aac", "-shortest", final_filename ], check=True) print(f"Muxed to {final_filename}")Statistics: Posted by PythoErgo — Sun Jun 01, 2025 3:56 pm