reachy-mini 1.2.5rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- reachy_mini/__init__.py +4 -0
- reachy_mini/apps/__init__.py +25 -0
- reachy_mini/apps/app.py +258 -0
- reachy_mini/apps/assistant.py +787 -0
- reachy_mini/apps/manager.py +312 -0
- reachy_mini/apps/sources/__init__.py +4 -0
- reachy_mini/apps/sources/hf_space.py +106 -0
- reachy_mini/apps/sources/local_common_venv.py +567 -0
- reachy_mini/apps/templates/README.md.j2 +12 -0
- reachy_mini/apps/templates/gitignore.j2 +3 -0
- reachy_mini/apps/templates/index.html.j2 +40 -0
- reachy_mini/apps/templates/main.py.j2 +74 -0
- reachy_mini/apps/templates/pyproject.toml.j2 +28 -0
- reachy_mini/apps/templates/static/index.html.j2 +27 -0
- reachy_mini/apps/templates/static/main.js.j2 +47 -0
- reachy_mini/apps/templates/static/style.css.j2 +25 -0
- reachy_mini/apps/templates/style.css.j2 +411 -0
- reachy_mini/apps/utils.py +30 -0
- reachy_mini/assets/config/hardware_config.yaml +164 -0
- reachy_mini/assets/confused1.wav +0 -0
- reachy_mini/assets/count.wav +0 -0
- reachy_mini/assets/dance1.wav +0 -0
- reachy_mini/assets/firmware/CHANGELOG.md +15 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.2.bin +0 -0
- reachy_mini/assets/firmware/reachymini_ua_io16_lin_v2.1.3.bin +0 -0
- reachy_mini/assets/firmware/update.sh +9 -0
- reachy_mini/assets/go_sleep.wav +0 -0
- reachy_mini/assets/impatient1.wav +0 -0
- reachy_mini/assets/kinematics_data.json +253 -0
- reachy_mini/assets/models/fknetwork.dynamic.onnx +0 -0
- reachy_mini/assets/models/fknetwork.onnx +0 -0
- reachy_mini/assets/models/fknetwork_int8.onnx +0 -0
- reachy_mini/assets/models/iknetwork.onnx +0 -0
- reachy_mini/assets/wake_up.wav +0 -0
- reachy_mini/daemon/__init__.py +1 -0
- reachy_mini/daemon/app/__init__.py +1 -0
- reachy_mini/daemon/app/bg_job_register.py +142 -0
- reachy_mini/daemon/app/dashboard/static/assets/KO-cartoon-static.svg +40 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon-static.svg +42 -0
- reachy_mini/daemon/app/dashboard/static/assets/awake-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/go-to-sleep-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon-static.svg +50 -0
- reachy_mini/daemon/app/dashboard/static/assets/no-wifi-cartoon.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-awake.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-connection-lost-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-go-to-sleep-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-ko.svg +22 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping-static.svg +41 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-sleeping.svg +18 -0
- reachy_mini/daemon/app/dashboard/static/assets/reachy-mini-wake-up-animation.svg +6 -0
- reachy_mini/daemon/app/dashboard/static/css/app.css +1433 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Archivo-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-Italic-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/fonts/Asap-VariableFont_wdth,wght.ttf +0 -0
- reachy_mini/daemon/app/dashboard/static/js/3rdparty/gstwebrtc-api-2.0.0.min.js +5 -0
- reachy_mini/daemon/app/dashboard/static/js/apps.js +323 -0
- reachy_mini/daemon/app/dashboard/static/js/appstore.js +167 -0
- reachy_mini/daemon/app/dashboard/static/js/daemon.js +169 -0
- reachy_mini/daemon/app/dashboard/static/js/health_check.js +9 -0
- reachy_mini/daemon/app/dashboard/static/js/move_player.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/notification.js +41 -0
- reachy_mini/daemon/app/dashboard/static/js/update.js +132 -0
- reachy_mini/daemon/app/dashboard/static/js/volume_control.js +252 -0
- reachy_mini/daemon/app/dashboard/static/js/wifi.js +176 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/README.md +15 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package-lock.json +1127 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/package.json +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/pnpm-lock.yaml +608 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/postcss.config.js +6 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/styles/app.css +185 -0
- reachy_mini/daemon/app/dashboard/tailwindcss/tailwind.config.js +11 -0
- reachy_mini/daemon/app/dashboard/templates/base.html +21 -0
- reachy_mini/daemon/app/dashboard/templates/index.html +20 -0
- reachy_mini/daemon/app/dashboard/templates/sections/apps.html +8 -0
- reachy_mini/daemon/app/dashboard/templates/sections/appstore.html +35 -0
- reachy_mini/daemon/app/dashboard/templates/sections/daemon.html +45 -0
- reachy_mini/daemon/app/dashboard/templates/sections/move_player.html +121 -0
- reachy_mini/daemon/app/dashboard/templates/sections/notification.html +19 -0
- reachy_mini/daemon/app/dashboard/templates/sections/update.html +54 -0
- reachy_mini/daemon/app/dashboard/templates/sections/wifi.html +46 -0
- reachy_mini/daemon/app/dashboard/templates/settings.html +14 -0
- reachy_mini/daemon/app/dependencies.py +41 -0
- reachy_mini/daemon/app/main.py +469 -0
- reachy_mini/daemon/app/models.py +149 -0
- reachy_mini/daemon/app/routers/apps.py +114 -0
- reachy_mini/daemon/app/routers/daemon.py +85 -0
- reachy_mini/daemon/app/routers/kinematics.py +57 -0
- reachy_mini/daemon/app/routers/motors.py +41 -0
- reachy_mini/daemon/app/routers/move.py +265 -0
- reachy_mini/daemon/app/routers/state.py +146 -0
- reachy_mini/daemon/app/routers/update.py +86 -0
- reachy_mini/daemon/app/routers/volume.py +423 -0
- reachy_mini/daemon/app/routers/wifi_config.py +219 -0
- reachy_mini/daemon/app/services/__init__.py +1 -0
- reachy_mini/daemon/app/services/bluetooth/bluetooth_service.py +796 -0
- reachy_mini/daemon/app/services/bluetooth/commands/HOTSPOT.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/commands/RESTART_DAEMON.sh +4 -0
- reachy_mini/daemon/app/services/bluetooth/commands/SOFTWARE_RESET.sh +7 -0
- reachy_mini/daemon/app/services/bluetooth/install_service_bluetooth.sh +39 -0
- reachy_mini/daemon/app/services/gpio_shutdown/__init__.py +1 -0
- reachy_mini/daemon/app/services/gpio_shutdown/install_service.sh +33 -0
- reachy_mini/daemon/app/services/gpio_shutdown/launcher.sh +3 -0
- reachy_mini/daemon/app/services/gpio_shutdown/shutdown_monitor.py +28 -0
- reachy_mini/daemon/app/services/wireless/generate_asoundrc.sh +70 -0
- reachy_mini/daemon/app/services/wireless/install_service.sh +33 -0
- reachy_mini/daemon/app/services/wireless/launcher.sh +5 -0
- reachy_mini/daemon/backend/__init__.py +1 -0
- reachy_mini/daemon/backend/abstract.py +815 -0
- reachy_mini/daemon/backend/mujoco/__init__.py +36 -0
- reachy_mini/daemon/backend/mujoco/backend.py +395 -0
- reachy_mini/daemon/backend/mujoco/utils.py +59 -0
- reachy_mini/daemon/backend/mujoco/video_udp.py +57 -0
- reachy_mini/daemon/backend/robot/__init__.py +8 -0
- reachy_mini/daemon/backend/robot/backend.py +597 -0
- reachy_mini/daemon/daemon.py +651 -0
- reachy_mini/daemon/utils.py +116 -0
- reachy_mini/descriptions/reachy_mini/mjcf/additional.xml +6 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/croissant_1k.obj +5021 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/croissant_1k.blend/textures/croissant_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/food_apple_01_1k.obj +18045 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/food_apple_01_1k.blend/textures/food_apple_01_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/rubber_duck_toy_1k.obj +8940 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/rubber_duck_toy_1k.blend/textures/rubber_duck_toy_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/textures/wooden_table_02_diff_1k.png +0 -0
- reachy_mini/descriptions/reachy_mini/mjcf/assets/wooden_table_02_1k.blend/wooden_table_02_1k.obj +485 -0
- reachy_mini/descriptions/reachy_mini/mjcf/config.json +21 -0
- reachy_mini/descriptions/reachy_mini/mjcf/joints_properties.xml +29 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml +642 -0
- reachy_mini/descriptions/reachy_mini/mjcf/reachy_mini.xml.bak +442 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scene.xml +24 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/empty.xml +27 -0
- reachy_mini/descriptions/reachy_mini/mjcf/scenes/minimal.xml +132 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/5w_speaker.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_body_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_l_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_holder_r_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/antenna_interface_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arducam.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/b3b_eh_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bearing_85x110x13.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/big_lens_d40.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_down_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_foot_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_top_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/body_turning_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/bts2_m2_6x8.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/README.m +1 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/coarse/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_back_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/body_top_3dprint_collider_front_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/collision/fine/head_one_3dprint_collider_0.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_b_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_f_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_case_m_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_horn_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/dc15_a01_led_cap2_dummy.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/drive_palonier__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/eye_support.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/foot.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/glasses_dolder_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_back_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_front_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_interface.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_mic_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d30_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/lens_cap_d40_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_fisheye_lens_1_8mm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/m12_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/mp01062_stewart_arm_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/neck_reference_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_1.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/phs_1_7x20_5_dc10_3.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp00xxx_stewart_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01062_stewart_arm.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01063_stewart_plateform.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01064_stewart_main_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01065_stewart_side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01066_stewart_mid_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01067_bottom_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01068_top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01069_head_shell_front.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01070_head_head_back.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01071_turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01072_turning_end.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01078_glasses.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01079_back_big_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01080_back_small_eye.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/pp01102_arducam_carter.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/shape.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/side_plate.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/small_lens_d30.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_ball__2.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_link_rod.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_main_plate_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/stewart_tricap_3dprint.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/test_antenna_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/top_body.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/turning_bowl.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.part +13 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/uc_a37_rev_a_step.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0122topcabinetcase_95__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0123middlecase_56__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_default.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.part +14 -0
- reachy_mini/descriptions/reachy_mini/urdf/assets/wj_wk00_0124bottomcase_45__configuration_simple_axe.stl +0 -0
- reachy_mini/descriptions/reachy_mini/urdf/config.json +18 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf +3281 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot.urdf.bak +3282 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_no_collision.urdf +2316 -0
- reachy_mini/descriptions/reachy_mini/urdf/robot_simple_collision.urdf +2399 -0
- reachy_mini/io/__init__.py +15 -0
- reachy_mini/io/abstract.py +70 -0
- reachy_mini/io/audio_ws.py +242 -0
- reachy_mini/io/protocol.py +44 -0
- reachy_mini/io/video_ws.py +135 -0
- reachy_mini/io/ws_controller.py +120 -0
- reachy_mini/io/zenoh_client.py +273 -0
- reachy_mini/io/zenoh_server.py +230 -0
- reachy_mini/kinematics/__init__.py +68 -0
- reachy_mini/kinematics/analytical_kinematics.py +151 -0
- reachy_mini/kinematics/nn_kinematics.py +109 -0
- reachy_mini/kinematics/placo_kinematics.py +656 -0
- reachy_mini/media/__init__.py +1 -0
- reachy_mini/media/audio_base.py +111 -0
- reachy_mini/media/audio_control_utils.py +455 -0
- reachy_mini/media/audio_gstreamer.py +272 -0
- reachy_mini/media/audio_sounddevice.py +321 -0
- reachy_mini/media/audio_utils.py +114 -0
- reachy_mini/media/camera_base.py +100 -0
- reachy_mini/media/camera_constants.py +152 -0
- reachy_mini/media/camera_gstreamer.py +231 -0
- reachy_mini/media/camera_opencv.py +89 -0
- reachy_mini/media/camera_utils.py +111 -0
- reachy_mini/media/media_manager.py +281 -0
- reachy_mini/media/webrtc_client_gstreamer.py +288 -0
- reachy_mini/media/webrtc_daemon.py +334 -0
- reachy_mini/motion/__init__.py +4 -0
- reachy_mini/motion/goto.py +71 -0
- reachy_mini/motion/move.py +43 -0
- reachy_mini/motion/recorded_move.py +146 -0
- reachy_mini/reachy_mini.py +753 -0
- reachy_mini/tools/reflash_motors.py +116 -0
- reachy_mini/tools/setup_motor.py +422 -0
- reachy_mini/tools/setup_motor_rpi.py +108 -0
- reachy_mini/utils/__init__.py +46 -0
- reachy_mini/utils/constants.py +9 -0
- reachy_mini/utils/hardware_config/__init__.py +1 -0
- reachy_mini/utils/hardware_config/parser.py +65 -0
- reachy_mini/utils/interpolation.py +227 -0
- reachy_mini/utils/parse_urdf_for_kinematics.py +110 -0
- reachy_mini/utils/rerun.py +320 -0
- reachy_mini/utils/wireless_version/__init__.py +1 -0
- reachy_mini/utils/wireless_version/startup_check.py +144 -0
- reachy_mini/utils/wireless_version/update.py +20 -0
- reachy_mini/utils/wireless_version/update_available.py +61 -0
- reachy_mini/utils/wireless_version/utils.py +39 -0
- reachy_mini-1.2.5rc1.dist-info/METADATA +161 -0
- reachy_mini-1.2.5rc1.dist-info/RECORD +479 -0
- reachy_mini-1.2.5rc1.dist-info/WHEEL +5 -0
- reachy_mini-1.2.5rc1.dist-info/entry_points.txt +4 -0
- reachy_mini-1.2.5rc1.dist-info/licenses/LICENSE +201 -0
- reachy_mini-1.2.5rc1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Camera utility for Reachy Mini."""
|
|
2
|
+
|
|
3
|
+
import platform
|
|
4
|
+
from typing import Optional, Tuple, cast
|
|
5
|
+
|
|
6
|
+
import cv2
|
|
7
|
+
from cv2_enumerate_cameras import enumerate_cameras
|
|
8
|
+
|
|
9
|
+
from reachy_mini.media.camera_constants import (
|
|
10
|
+
ArducamSpecs,
|
|
11
|
+
CameraSpecs,
|
|
12
|
+
OlderRPiCamSpecs,
|
|
13
|
+
ReachyMiniLiteCamSpecs,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def find_camera(
|
|
18
|
+
apiPreference: int = cv2.CAP_ANY, no_cap: bool = False
|
|
19
|
+
) -> Tuple[Optional[cv2.VideoCapture], Optional[CameraSpecs]]:
|
|
20
|
+
"""Find and return the Reachy Mini camera.
|
|
21
|
+
|
|
22
|
+
Looks for the Reachy Mini camera first, then Arducam, then older Raspberry Pi Camera. Returns None if no camera is found.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
apiPreference (int): Preferred API backend for the camera. Default is cv2.CAP_ANY.
|
|
26
|
+
no_cap (bool): If True, close the camera after finding it. Default is False.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
cv2.VideoCapture | None: A VideoCapture object if the camera is found and opened successfully, otherwise None.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
cap = find_camera_by_vid_pid(
|
|
33
|
+
ReachyMiniLiteCamSpecs.vid, ReachyMiniLiteCamSpecs.pid, apiPreference
|
|
34
|
+
)
|
|
35
|
+
if cap is not None:
|
|
36
|
+
fourcc = cv2.VideoWriter_fourcc("M", "J", "P", "G") # type: ignore
|
|
37
|
+
cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
|
38
|
+
if no_cap:
|
|
39
|
+
cap.release()
|
|
40
|
+
return cap, cast(CameraSpecs, ReachyMiniLiteCamSpecs)
|
|
41
|
+
|
|
42
|
+
cap = find_camera_by_vid_pid(
|
|
43
|
+
OlderRPiCamSpecs.vid, OlderRPiCamSpecs.pid, apiPreference
|
|
44
|
+
)
|
|
45
|
+
if cap is not None:
|
|
46
|
+
fourcc = cv2.VideoWriter_fourcc("M", "J", "P", "G") # type: ignore
|
|
47
|
+
cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
|
48
|
+
if no_cap:
|
|
49
|
+
cap.release()
|
|
50
|
+
return cap, cast(CameraSpecs, OlderRPiCamSpecs)
|
|
51
|
+
|
|
52
|
+
cap = find_camera_by_vid_pid(ArducamSpecs.vid, ArducamSpecs.pid, apiPreference)
|
|
53
|
+
if cap is not None:
|
|
54
|
+
if no_cap:
|
|
55
|
+
cap.release()
|
|
56
|
+
return cap, cast(CameraSpecs, ArducamSpecs)
|
|
57
|
+
|
|
58
|
+
return None, None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def find_camera_by_vid_pid(
|
|
62
|
+
vid: int = ReachyMiniLiteCamSpecs.vid,
|
|
63
|
+
pid: int = ReachyMiniLiteCamSpecs.pid,
|
|
64
|
+
apiPreference: int = cv2.CAP_ANY,
|
|
65
|
+
) -> cv2.VideoCapture | None:
|
|
66
|
+
"""Find and return a camera with the specified VID and PID.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
vid (int): Vendor ID of the camera. Default is ReachyMiniCamera
|
|
70
|
+
pid (int): Product ID of the camera. Default is ReachyMiniCamera
|
|
71
|
+
apiPreference (int): Preferred API backend for the camera. Default is cv2.CAP_ANY.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
cv2.VideoCapture | None: A VideoCapture object if the camera is found and opened successfully, otherwise None.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
if platform.system() == "Linux":
|
|
78
|
+
apiPreference = cv2.CAP_V4L2
|
|
79
|
+
|
|
80
|
+
selected_cap = None
|
|
81
|
+
for c in enumerate_cameras(apiPreference):
|
|
82
|
+
if c.vid == vid and c.pid == pid:
|
|
83
|
+
# the Arducam camera creates two /dev/videoX devices
|
|
84
|
+
# that enumerate_cameras cannot differentiate
|
|
85
|
+
try:
|
|
86
|
+
cap = cv2.VideoCapture(c.index, c.backend)
|
|
87
|
+
if cap.isOpened():
|
|
88
|
+
selected_cap = cap
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"Error opening camera {c.index}: {e}")
|
|
91
|
+
return selected_cap
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
from reachy_mini.media.camera_constants import CameraResolution
|
|
96
|
+
|
|
97
|
+
cam, _ = find_camera()
|
|
98
|
+
if cam is None:
|
|
99
|
+
exit("Camera not found")
|
|
100
|
+
|
|
101
|
+
cam.set(cv2.CAP_PROP_FRAME_WIDTH, CameraResolution.R1280x720at30fps.value[0])
|
|
102
|
+
cam.set(cv2.CAP_PROP_FRAME_HEIGHT, CameraResolution.R1280x720at30fps.value[1])
|
|
103
|
+
|
|
104
|
+
while True:
|
|
105
|
+
ret, frame = cam.read()
|
|
106
|
+
if not ret:
|
|
107
|
+
print("Failed to grab frame")
|
|
108
|
+
break
|
|
109
|
+
cv2.imshow("Camera Feed", frame)
|
|
110
|
+
if cv2.waitKey(1) & 0xFF == ord("q"):
|
|
111
|
+
break
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Media Manager.
|
|
2
|
+
|
|
3
|
+
Provides camera and audio access based on the selected backedn
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
from reachy_mini.media.audio_base import AudioBase
|
|
14
|
+
from reachy_mini.media.camera_base import CameraBase
|
|
15
|
+
|
|
16
|
+
# actual backends are dynamically imported
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MediaBackend(Enum):
|
|
20
|
+
"""Media backends."""
|
|
21
|
+
|
|
22
|
+
NO_MEDIA = "no_media"
|
|
23
|
+
DEFAULT = "default"
|
|
24
|
+
DEFAULT_NO_VIDEO = "default_no_video"
|
|
25
|
+
GSTREAMER = "gstreamer"
|
|
26
|
+
GSTREAMER_NO_VIDEO = "gstreamer_no_video"
|
|
27
|
+
WEBRTC = "webrtc"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MediaManager:
|
|
31
|
+
"""Abstract class for opening and managing audio devices."""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
backend: MediaBackend = MediaBackend.DEFAULT,
|
|
36
|
+
log_level: str = "INFO",
|
|
37
|
+
use_sim: bool = False,
|
|
38
|
+
signalling_host: str = "localhost",
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Initialize the audio device."""
|
|
41
|
+
self.logger = logging.getLogger(__name__)
|
|
42
|
+
self.logger.setLevel(log_level)
|
|
43
|
+
self.backend = backend
|
|
44
|
+
self.camera: Optional[CameraBase] = None
|
|
45
|
+
self.audio: Optional[AudioBase] = None
|
|
46
|
+
|
|
47
|
+
match backend:
|
|
48
|
+
case MediaBackend.NO_MEDIA:
|
|
49
|
+
self.logger.info("No media backend selected.")
|
|
50
|
+
case MediaBackend.DEFAULT:
|
|
51
|
+
self.logger.info("Using default media backend (OpenCV + SoundDevice).")
|
|
52
|
+
self._init_camera(use_sim, log_level)
|
|
53
|
+
self._init_audio(log_level)
|
|
54
|
+
case MediaBackend.DEFAULT_NO_VIDEO:
|
|
55
|
+
self.logger.info("Using default media backend (SoundDevice only).")
|
|
56
|
+
self._init_audio(log_level)
|
|
57
|
+
case MediaBackend.GSTREAMER:
|
|
58
|
+
self.logger.info("Using GStreamer media backend.")
|
|
59
|
+
self._init_camera(use_sim, log_level)
|
|
60
|
+
self._init_audio(log_level)
|
|
61
|
+
case MediaBackend.GSTREAMER_NO_VIDEO:
|
|
62
|
+
self.logger.info("Using GStreamer audio backend.")
|
|
63
|
+
self._init_audio(log_level)
|
|
64
|
+
case MediaBackend.WEBRTC:
|
|
65
|
+
self.logger.info("Using WebRTC GStreamer backend.")
|
|
66
|
+
self._init_webrtc(log_level, signalling_host, 8443)
|
|
67
|
+
# self._init_audio(log_level)
|
|
68
|
+
case _:
|
|
69
|
+
raise NotImplementedError(f"Media backend {backend} not implemented.")
|
|
70
|
+
|
|
71
|
+
def close(self) -> None:
|
|
72
|
+
"""Close the media manager and release resources."""
|
|
73
|
+
if self.camera is not None:
|
|
74
|
+
self.camera.close()
|
|
75
|
+
if self.audio is not None:
|
|
76
|
+
self.audio.stop_recording()
|
|
77
|
+
self.audio.stop_playing()
|
|
78
|
+
|
|
79
|
+
def __del__(self) -> None:
|
|
80
|
+
"""Destructor to ensure resources are released."""
|
|
81
|
+
self.close()
|
|
82
|
+
|
|
83
|
+
def _init_camera(
|
|
84
|
+
self,
|
|
85
|
+
use_sim: bool,
|
|
86
|
+
log_level: str,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Initialize the camera."""
|
|
89
|
+
self.logger.debug("Initializing camera...")
|
|
90
|
+
if self.backend == MediaBackend.DEFAULT:
|
|
91
|
+
self.logger.info("Using OpenCV camera backend.")
|
|
92
|
+
from reachy_mini.media.camera_opencv import OpenCVCamera
|
|
93
|
+
|
|
94
|
+
self.camera = OpenCVCamera(log_level=log_level)
|
|
95
|
+
if use_sim:
|
|
96
|
+
self.camera.open(udp_camera="udp://@127.0.0.1:5005")
|
|
97
|
+
else:
|
|
98
|
+
self.camera.open()
|
|
99
|
+
elif self.backend == MediaBackend.GSTREAMER:
|
|
100
|
+
self.logger.info("Using GStreamer camera backend.")
|
|
101
|
+
from reachy_mini.media.camera_gstreamer import GStreamerCamera
|
|
102
|
+
|
|
103
|
+
self.camera = GStreamerCamera(log_level=log_level)
|
|
104
|
+
self.camera.open()
|
|
105
|
+
# Todo: use simulation with gstreamer?
|
|
106
|
+
|
|
107
|
+
else:
|
|
108
|
+
raise NotImplementedError(f"Camera backend {self.backend} not implemented.")
|
|
109
|
+
|
|
110
|
+
def get_frame(self) -> Optional[npt.NDArray[np.uint8]]:
|
|
111
|
+
"""Get a frame from the camera.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Optional[npt.NDArray[np.uint8]]: The captured BGR frame, or None if the camera is not available.
|
|
115
|
+
|
|
116
|
+
"""
|
|
117
|
+
if self.camera is None:
|
|
118
|
+
self.logger.warning("Camera is not initialized.")
|
|
119
|
+
return None
|
|
120
|
+
return self.camera.read()
|
|
121
|
+
|
|
122
|
+
def _init_audio(self, log_level: str) -> None:
|
|
123
|
+
"""Initialize the audio system."""
|
|
124
|
+
self.logger.debug("Initializing audio...")
|
|
125
|
+
if (
|
|
126
|
+
self.backend == MediaBackend.DEFAULT
|
|
127
|
+
or self.backend == MediaBackend.DEFAULT_NO_VIDEO
|
|
128
|
+
):
|
|
129
|
+
self.logger.info("Using SoundDevice audio backend.")
|
|
130
|
+
from reachy_mini.media.audio_sounddevice import SoundDeviceAudio
|
|
131
|
+
|
|
132
|
+
self.audio = SoundDeviceAudio(log_level=log_level)
|
|
133
|
+
elif (
|
|
134
|
+
self.backend == MediaBackend.GSTREAMER
|
|
135
|
+
or self.backend == MediaBackend.GSTREAMER_NO_VIDEO
|
|
136
|
+
):
|
|
137
|
+
self.logger.info("Using GStreamer audio backend.")
|
|
138
|
+
from reachy_mini.media.audio_gstreamer import GStreamerAudio
|
|
139
|
+
|
|
140
|
+
self.audio = GStreamerAudio(log_level=log_level)
|
|
141
|
+
else:
|
|
142
|
+
raise NotImplementedError(f"Audio backend {self.backend} not implemented.")
|
|
143
|
+
|
|
144
|
+
def _init_webrtc(
|
|
145
|
+
self, log_level: str, signalling_host: str, signalling_port: int
|
|
146
|
+
) -> None:
|
|
147
|
+
"""Initialize the WebRTC system (not implemented yet)."""
|
|
148
|
+
from gst_signalling.utils import find_producer_peer_id_by_name
|
|
149
|
+
|
|
150
|
+
from reachy_mini.media.webrtc_client_gstreamer import GstWebRTCClient
|
|
151
|
+
|
|
152
|
+
peer_id = find_producer_peer_id_by_name(
|
|
153
|
+
signalling_host, signalling_port, "reachymini"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
webrtc_media: GstWebRTCClient = GstWebRTCClient(
|
|
157
|
+
log_level=log_level,
|
|
158
|
+
peer_id=peer_id,
|
|
159
|
+
signaling_host=signalling_host,
|
|
160
|
+
signaling_port=signalling_port,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
self.camera = webrtc_media
|
|
164
|
+
self.audio = webrtc_media # GstWebRTCClient handles both audio and video
|
|
165
|
+
self.camera.open()
|
|
166
|
+
|
|
167
|
+
def play_sound(self, sound_file: str) -> None:
|
|
168
|
+
"""Play a sound file.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
sound_file (str): Path to the sound file to play.
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
|
+
if self.audio is None:
|
|
175
|
+
self.logger.warning("Audio system is not initialized.")
|
|
176
|
+
return
|
|
177
|
+
self.audio.play_sound(sound_file)
|
|
178
|
+
|
|
179
|
+
def start_recording(self) -> None:
|
|
180
|
+
"""Start recording audio."""
|
|
181
|
+
if self.audio is None:
|
|
182
|
+
self.logger.warning("Audio system is not initialized.")
|
|
183
|
+
return
|
|
184
|
+
self.audio.start_recording()
|
|
185
|
+
|
|
186
|
+
def get_audio_sample(self) -> Optional[bytes | npt.NDArray[np.float32]]:
|
|
187
|
+
"""Get an audio sample from the audio device.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Optional[np.ndarray]: The recorded audio sample, or None if no data is available.
|
|
191
|
+
|
|
192
|
+
"""
|
|
193
|
+
if self.audio is None:
|
|
194
|
+
self.logger.warning("Audio system is not initialized.")
|
|
195
|
+
return None
|
|
196
|
+
return self.audio.get_audio_sample()
|
|
197
|
+
|
|
198
|
+
def get_input_audio_samplerate(self) -> int:
|
|
199
|
+
"""Get the input samplerate of the audio device."""
|
|
200
|
+
if self.audio is None:
|
|
201
|
+
self.logger.warning("Audio system is not initialized.")
|
|
202
|
+
return -1
|
|
203
|
+
return self.audio.get_input_audio_samplerate()
|
|
204
|
+
|
|
205
|
+
def get_output_audio_samplerate(self) -> int:
|
|
206
|
+
"""Get the output samplerate of the audio device."""
|
|
207
|
+
if self.audio is None:
|
|
208
|
+
self.logger.warning("Audio system is not initialized.")
|
|
209
|
+
return -1
|
|
210
|
+
return self.audio.get_output_audio_samplerate()
|
|
211
|
+
|
|
212
|
+
def get_input_channels(self) -> int:
|
|
213
|
+
"""Get the number of input channels of the audio device."""
|
|
214
|
+
if self.audio is None:
|
|
215
|
+
self.logger.warning("Audio system is not initialized.")
|
|
216
|
+
return -1
|
|
217
|
+
return self.audio.get_input_channels()
|
|
218
|
+
|
|
219
|
+
def get_output_channels(self) -> int:
|
|
220
|
+
"""Get the number of output channels of the audio device."""
|
|
221
|
+
if self.audio is None:
|
|
222
|
+
self.logger.warning("Audio system is not initialized.")
|
|
223
|
+
return -1
|
|
224
|
+
return self.audio.get_output_channels()
|
|
225
|
+
|
|
226
|
+
def stop_recording(self) -> None:
|
|
227
|
+
"""Stop recording audio."""
|
|
228
|
+
if self.audio is None:
|
|
229
|
+
self.logger.warning("Audio system is not initialized.")
|
|
230
|
+
return
|
|
231
|
+
self.audio.stop_recording()
|
|
232
|
+
|
|
233
|
+
def start_playing(self) -> None:
|
|
234
|
+
"""Start playing audio."""
|
|
235
|
+
if self.audio is None:
|
|
236
|
+
self.logger.warning("Audio system is not initialized.")
|
|
237
|
+
return
|
|
238
|
+
self.audio.start_playing()
|
|
239
|
+
|
|
240
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
241
|
+
"""Push audio data to the output device.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
data (npt.NDArray[np.float32]): The audio data to push to the output device (mono format).
|
|
245
|
+
|
|
246
|
+
"""
|
|
247
|
+
if self.audio is None:
|
|
248
|
+
self.logger.warning("Audio system is not initialized.")
|
|
249
|
+
return
|
|
250
|
+
|
|
251
|
+
if data.ndim > 2 or data.ndim == 0:
|
|
252
|
+
self.logger.warning(
|
|
253
|
+
f"Audio samples arrays must have at most 2 dimensions and at least 1 dimension, got {data.ndim}"
|
|
254
|
+
)
|
|
255
|
+
return
|
|
256
|
+
|
|
257
|
+
# Transpose data to match sounddevice channels last convention
|
|
258
|
+
if data.ndim == 2 and data.shape[1] > data.shape[0]:
|
|
259
|
+
data = data.T
|
|
260
|
+
|
|
261
|
+
# Fit data to match output stream channels
|
|
262
|
+
output_channels = self.get_output_channels()
|
|
263
|
+
|
|
264
|
+
# Mono input to multiple channels output : duplicate to fit
|
|
265
|
+
if data.ndim == 1 and output_channels > 1:
|
|
266
|
+
data = np.column_stack((data,) * output_channels)
|
|
267
|
+
# Lower channels input to higher channels output : reduce to mono and duplicate to fit
|
|
268
|
+
elif data.ndim == 2 and data.shape[1] < output_channels:
|
|
269
|
+
data = np.column_stack((data[:, 0],) * output_channels)
|
|
270
|
+
# Higher channels input to lower channels output : crop to fit
|
|
271
|
+
elif data.ndim == 2 and data.shape[1] > output_channels:
|
|
272
|
+
data = data[:, :output_channels]
|
|
273
|
+
|
|
274
|
+
self.audio.push_audio_sample(data)
|
|
275
|
+
|
|
276
|
+
def stop_playing(self) -> None:
|
|
277
|
+
"""Stop playing audio."""
|
|
278
|
+
if self.audio is None:
|
|
279
|
+
self.logger.warning("Audio system is not initialized.")
|
|
280
|
+
return
|
|
281
|
+
self.audio.stop_playing()
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
"""GStreamer WebRTC client implementation.
|
|
2
|
+
|
|
3
|
+
The class is a client for the webrtc server hosted on the Reachy Mini Wireless robot.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from threading import Thread
|
|
7
|
+
from typing import Optional, cast
|
|
8
|
+
|
|
9
|
+
import gi
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
|
|
13
|
+
from reachy_mini.media.audio_base import AudioBase
|
|
14
|
+
from reachy_mini.media.camera_base import CameraBase
|
|
15
|
+
from reachy_mini.media.camera_constants import (
|
|
16
|
+
CameraResolution,
|
|
17
|
+
CameraSpecs,
|
|
18
|
+
ReachyMiniWirelessCamSpecs,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
gi.require_version("Gst", "1.0")
|
|
22
|
+
gi.require_version("GstApp", "1.0")
|
|
23
|
+
from gi.repository import GLib, Gst, GstApp # noqa: E402, F401
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GstWebRTCClient(CameraBase, AudioBase):
|
|
27
|
+
"""GStreamer WebRTC client implementation."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
log_level: str = "INFO",
|
|
32
|
+
peer_id: str = "",
|
|
33
|
+
signaling_host: str = "",
|
|
34
|
+
signaling_port: int = 8443,
|
|
35
|
+
):
|
|
36
|
+
"""Initialize the GStreamer WebRTC client."""
|
|
37
|
+
super().__init__(log_level=log_level)
|
|
38
|
+
Gst.init(None)
|
|
39
|
+
self._loop = GLib.MainLoop()
|
|
40
|
+
self._thread_bus_calls = Thread(target=lambda: self._loop.run(), daemon=True)
|
|
41
|
+
self._thread_bus_calls.start()
|
|
42
|
+
|
|
43
|
+
self._pipeline_record = Gst.Pipeline.new("audio_recorder")
|
|
44
|
+
self._bus_record = self._pipeline_record.get_bus()
|
|
45
|
+
self._bus_record.add_watch(
|
|
46
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self._appsink_audio = Gst.ElementFactory.make("appsink")
|
|
50
|
+
caps = Gst.Caps.from_string(
|
|
51
|
+
f"audio/x-raw,rate={self.SAMPLE_RATE},channels={self.CHANNELS},format=F32LE,layout=interleaved"
|
|
52
|
+
)
|
|
53
|
+
self._appsink_audio.set_property("caps", caps)
|
|
54
|
+
self._appsink_audio.set_property("drop", True) # avoid overflow
|
|
55
|
+
self._appsink_audio.set_property("max-buffers", 500)
|
|
56
|
+
self._pipeline_record.add(self._appsink_audio)
|
|
57
|
+
|
|
58
|
+
self.camera_specs = cast(CameraSpecs, ReachyMiniWirelessCamSpecs)
|
|
59
|
+
self.set_resolution(CameraResolution.R1920x1080at30fps)
|
|
60
|
+
|
|
61
|
+
self._appsink_video = Gst.ElementFactory.make("appsink")
|
|
62
|
+
caps_video = Gst.Caps.from_string("video/x-raw,format=BGR")
|
|
63
|
+
self._appsink_video.set_property("caps", caps_video)
|
|
64
|
+
self._appsink_video.set_property("drop", True) # avoid overflow
|
|
65
|
+
self._appsink_video.set_property("max-buffers", 1) # keep last image only
|
|
66
|
+
self._pipeline_record.add(self._appsink_video)
|
|
67
|
+
|
|
68
|
+
webrtcsrc = self._configure_webrtcsrc(signaling_host, signaling_port, peer_id)
|
|
69
|
+
self._pipeline_record.add(webrtcsrc)
|
|
70
|
+
|
|
71
|
+
self._pipeline_playback = Gst.Pipeline.new("audio_player")
|
|
72
|
+
self._init_pipeline_playback(
|
|
73
|
+
self._pipeline_playback, signaling_host, signaling_port
|
|
74
|
+
)
|
|
75
|
+
self._bus_playback = self._pipeline_playback.get_bus()
|
|
76
|
+
self._bus_playback.add_watch(
|
|
77
|
+
GLib.PRIORITY_DEFAULT, self._on_bus_message, self._loop
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def __del__(self) -> None:
|
|
81
|
+
"""Destructor to ensure gstreamer resources are released."""
|
|
82
|
+
super().__del__()
|
|
83
|
+
self._loop.quit()
|
|
84
|
+
self._bus_record.remove_watch()
|
|
85
|
+
self._bus_playback.remove_watch()
|
|
86
|
+
|
|
87
|
+
def set_resolution(self, resolution: CameraResolution) -> None:
|
|
88
|
+
"""Set the camera resolution."""
|
|
89
|
+
super().set_resolution(resolution)
|
|
90
|
+
self._resolution = resolution
|
|
91
|
+
|
|
92
|
+
def _configure_webrtcsrc(
|
|
93
|
+
self, signaling_host: str, signaling_port: int, peer_id: str
|
|
94
|
+
) -> Gst.Element:
|
|
95
|
+
source = Gst.ElementFactory.make("webrtcsrc")
|
|
96
|
+
if not source:
|
|
97
|
+
raise RuntimeError(
|
|
98
|
+
"Failed to create webrtcsrc element. Is the GStreamer webrtc rust plugin installed?"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
source.connect("pad-added", self._webrtcsrc_pad_added_cb)
|
|
102
|
+
signaller = source.get_property("signaller")
|
|
103
|
+
signaller.set_property("producer-peer-id", peer_id)
|
|
104
|
+
signaller.set_property("uri", f"ws://{signaling_host}:{signaling_port}")
|
|
105
|
+
return source
|
|
106
|
+
|
|
107
|
+
def _configure_webrtcbin(self, webrtcsrc: Gst.Element) -> None:
|
|
108
|
+
if isinstance(webrtcsrc, Gst.Bin):
|
|
109
|
+
webrtcbin_name = "webrtcbin0"
|
|
110
|
+
webrtcbin = webrtcsrc.get_by_name(webrtcbin_name)
|
|
111
|
+
assert webrtcbin is not None
|
|
112
|
+
# jitterbuffer has a default 200 ms buffer. Should be ok to lower this in localnetwork config
|
|
113
|
+
webrtcbin.set_property("latency", 100)
|
|
114
|
+
|
|
115
|
+
def _webrtcsrc_pad_added_cb(self, webrtcsrc: Gst.Element, pad: Gst.Pad) -> None:
|
|
116
|
+
self._configure_webrtcbin(webrtcsrc)
|
|
117
|
+
if pad.get_name().startswith("video"):
|
|
118
|
+
queue = Gst.ElementFactory.make("queue")
|
|
119
|
+
|
|
120
|
+
videoconvert = Gst.ElementFactory.make("videoconvert")
|
|
121
|
+
videoscale = Gst.ElementFactory.make("videoscale")
|
|
122
|
+
videorate = Gst.ElementFactory.make("videorate")
|
|
123
|
+
|
|
124
|
+
self._pipeline_record.add(queue)
|
|
125
|
+
self._pipeline_record.add(videoconvert)
|
|
126
|
+
self._pipeline_record.add(videoscale)
|
|
127
|
+
self._pipeline_record.add(videorate)
|
|
128
|
+
pad.link(queue.get_static_pad("sink"))
|
|
129
|
+
|
|
130
|
+
queue.link(videoconvert)
|
|
131
|
+
videoconvert.link(videoscale)
|
|
132
|
+
videoscale.link(videorate)
|
|
133
|
+
videorate.link(self._appsink_video)
|
|
134
|
+
|
|
135
|
+
queue.sync_state_with_parent()
|
|
136
|
+
videoconvert.sync_state_with_parent()
|
|
137
|
+
videoscale.sync_state_with_parent()
|
|
138
|
+
videorate.sync_state_with_parent()
|
|
139
|
+
self._appsink_video.sync_state_with_parent()
|
|
140
|
+
|
|
141
|
+
elif pad.get_name().startswith("audio"):
|
|
142
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
143
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
144
|
+
self._pipeline_record.add(audioconvert)
|
|
145
|
+
self._pipeline_record.add(audioresample)
|
|
146
|
+
|
|
147
|
+
pad.link(audioconvert.get_static_pad("sink"))
|
|
148
|
+
audioconvert.link(audioresample)
|
|
149
|
+
audioresample.link(self._appsink_audio)
|
|
150
|
+
|
|
151
|
+
self._appsink_audio.sync_state_with_parent()
|
|
152
|
+
audioconvert.sync_state_with_parent()
|
|
153
|
+
audioresample.sync_state_with_parent()
|
|
154
|
+
|
|
155
|
+
def _on_bus_message(self, bus: Gst.Bus, msg: Gst.Message, loop) -> bool: # type: ignore[no-untyped-def]
|
|
156
|
+
t = msg.type
|
|
157
|
+
if t == Gst.MessageType.EOS:
|
|
158
|
+
self.logger.warning("End-of-stream")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
elif t == Gst.MessageType.ERROR:
|
|
162
|
+
err, debug = msg.parse_error()
|
|
163
|
+
self.logger.error(f"Error: {err} {debug}")
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
def open(self) -> None:
|
|
169
|
+
"""Open the video stream."""
|
|
170
|
+
self._pipeline_record.set_state(Gst.State.PLAYING)
|
|
171
|
+
|
|
172
|
+
def _get_sample(self, appsink: GstApp.AppSink) -> Optional[bytes]:
|
|
173
|
+
sample = appsink.try_pull_sample(20_000_000)
|
|
174
|
+
if sample is None:
|
|
175
|
+
return None
|
|
176
|
+
data = None
|
|
177
|
+
if isinstance(sample, Gst.Sample):
|
|
178
|
+
buf = sample.get_buffer()
|
|
179
|
+
if buf is None:
|
|
180
|
+
self.logger.warning("Buffer is None")
|
|
181
|
+
|
|
182
|
+
data = buf.extract_dup(0, buf.get_size())
|
|
183
|
+
return data
|
|
184
|
+
|
|
185
|
+
def get_audio_sample(self) -> Optional[npt.NDArray[np.float32]]:
|
|
186
|
+
"""Read a sample from the audio card. Returns the sample or None if error.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
Optional[npt.NDArray[np.float32]]: The captured sample in raw format, or None if error.
|
|
190
|
+
|
|
191
|
+
"""
|
|
192
|
+
sample = self._get_sample(self._appsink_audio)
|
|
193
|
+
if sample is None:
|
|
194
|
+
return None
|
|
195
|
+
return np.frombuffer(sample, dtype=np.float32).reshape(-1, 2)
|
|
196
|
+
|
|
197
|
+
def read(self) -> Optional[npt.NDArray[np.uint8]]:
|
|
198
|
+
"""Read a frame from the camera. Returns the frame or None if error.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Optional[npt.NDArray[np.uint8]]: The captured frame in BGR format, or None if error.
|
|
202
|
+
|
|
203
|
+
"""
|
|
204
|
+
data = self._get_sample(self._appsink_video)
|
|
205
|
+
if data is None:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
arr = np.frombuffer(data, dtype=np.uint8).reshape(
|
|
209
|
+
(self.resolution[1], self.resolution[0], 3)
|
|
210
|
+
)
|
|
211
|
+
return arr
|
|
212
|
+
|
|
213
|
+
def close(self) -> None:
|
|
214
|
+
"""Stop the pipeline."""
|
|
215
|
+
# self._loop.quit()
|
|
216
|
+
self._pipeline_record.set_state(Gst.State.NULL)
|
|
217
|
+
|
|
218
|
+
def start_recording(self) -> None:
|
|
219
|
+
"""Open the audio card using GStreamer."""
|
|
220
|
+
pass # already started in open()
|
|
221
|
+
|
|
222
|
+
def stop_recording(self) -> None:
|
|
223
|
+
"""Release the camera resource."""
|
|
224
|
+
pass # managed in close()
|
|
225
|
+
|
|
226
|
+
def _init_pipeline_playback(
|
|
227
|
+
self, pipeline: Gst.Pipeline, signaling_host: str, signaling_port: int
|
|
228
|
+
) -> None:
|
|
229
|
+
"""Initialize the audio playback pipeline."""
|
|
230
|
+
self._appsrc = Gst.ElementFactory.make("appsrc")
|
|
231
|
+
self._appsrc.set_property("format", Gst.Format.TIME)
|
|
232
|
+
self._appsrc.set_property("is-live", True)
|
|
233
|
+
caps = Gst.Caps.from_string(
|
|
234
|
+
f"audio/x-raw,format=F32LE,channels={self.CHANNELS},rate={self.SAMPLE_RATE},layout=interleaved"
|
|
235
|
+
)
|
|
236
|
+
self._appsrc.set_property("caps", caps)
|
|
237
|
+
audioconvert = Gst.ElementFactory.make("audioconvert")
|
|
238
|
+
audioresample = Gst.ElementFactory.make("audioresample")
|
|
239
|
+
opusenc = Gst.ElementFactory.make("opusenc")
|
|
240
|
+
queue = Gst.ElementFactory.make("queue")
|
|
241
|
+
rtpopuspay = Gst.ElementFactory.make("rtpopuspay")
|
|
242
|
+
udpsink = Gst.ElementFactory.make("udpsink")
|
|
243
|
+
|
|
244
|
+
udpsink.set_property("host", signaling_host)
|
|
245
|
+
udpsink.set_property("port", 5000)
|
|
246
|
+
|
|
247
|
+
pipeline.add(self._appsrc)
|
|
248
|
+
pipeline.add(audioconvert)
|
|
249
|
+
pipeline.add(audioresample)
|
|
250
|
+
pipeline.add(opusenc)
|
|
251
|
+
pipeline.add(queue)
|
|
252
|
+
pipeline.add(rtpopuspay)
|
|
253
|
+
pipeline.add(udpsink)
|
|
254
|
+
|
|
255
|
+
self._appsrc.link(audioconvert)
|
|
256
|
+
audioconvert.link(audioresample)
|
|
257
|
+
audioresample.link(opusenc)
|
|
258
|
+
opusenc.link(queue)
|
|
259
|
+
queue.link(rtpopuspay)
|
|
260
|
+
rtpopuspay.link(udpsink)
|
|
261
|
+
|
|
262
|
+
def start_playing(self) -> None:
|
|
263
|
+
"""Open the audio output using GStreamer."""
|
|
264
|
+
self._pipeline_playback.set_state(Gst.State.PLAYING)
|
|
265
|
+
|
|
266
|
+
def stop_playing(self) -> None:
|
|
267
|
+
"""Stop playing audio and release resources."""
|
|
268
|
+
self._pipeline_playback.set_state(Gst.State.NULL)
|
|
269
|
+
|
|
270
|
+
def push_audio_sample(self, data: npt.NDArray[np.float32]) -> None:
|
|
271
|
+
"""Push audio data to the output device."""
|
|
272
|
+
if self._appsrc is not None:
|
|
273
|
+
buf = Gst.Buffer.new_wrapped(data.tobytes())
|
|
274
|
+
self._appsrc.push_buffer(buf)
|
|
275
|
+
|
|
276
|
+
else:
|
|
277
|
+
self.logger.warning(
|
|
278
|
+
"AppSrc is not initialized. Call start_playing() first."
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
def play_sound(self, sound_file: str) -> None:
|
|
282
|
+
"""Play a sound file.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
sound_file (str): Path to the sound file to play.
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
self.logger.warning("Audio playback not implemented in WebRTC client.")
|