Skip to content

Commit 6afb7c5

Browse files
authored
Merge pull request #83 from Simple-Robotics/fix-optional-video-recording
Fix FFmpeg not being actually optional when building, add warnings when calling `Visualizer` recording settings in this case
2 parents 846e206 + 5ff9b3d commit 6afb7c5

File tree

16 files changed

+1307
-795
lines changed

16 files changed

+1307
-795
lines changed

.github/workflows/macos-linux-windows-pixi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
fail-fast: false
4040
matrix:
4141
os: [ubuntu-latest, macos-latest]
42-
environment: [all-test]
42+
environment: [test-no-ffmpeg, test-all]
4343
build_type: [Release]
4444

4545
steps:

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- bindings/python : expose video recorder settings
13+
- pixi : separate `pinocchio` and `ffmpeg` features, add to CI build matrix
1314

1415
### Changed
1516

1617
- multibody/Visualizer : expose video settings directly instead of passing fps parameter
18+
- cmake : allow user to turn off FFmpeg support/video recording features even if FFmpeg was detected
1719

1820
### Fixed
1921

20-
- support for Coal's current `devel` branch (remove incompatible fwd-declarations, just include `<coal/shape/geometric_shapes.h>`)
22+
- support for Coal's current `devel` branch (remove incompatible fwd-declarations, just include `<coal/shape/geometric_shapes.h>`) (https://github.com/Simple-Robotics/candlewick/pull/81)
23+
- Make FFmpeg actually optional: add option to turn off support even with FFmpeg installed, fix building with support turned off
2124

2225
## [0.4.0] - 2025-06-10
2326

CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,24 @@ ADD_PROJECT_DEPENDENCY(SDL3 3.2.4 REQUIRED)
8686
ADD_PROJECT_DEPENDENCY(assimp REQUIRED)
8787
ADD_PROJECT_DEPENDENCY(Eigen3 REQUIRED)
8888
ADD_PROJECT_DEPENDENCY(coal REQUIRED PKG_CONFIG_REQUIRES "coal >= 3.0.0")
89+
ADD_PROJECT_DEPENDENCY(nlohmann_json 3.11.3 REQUIRED)
90+
ADD_PROJECT_DEPENDENCY(EnTT REQUIRED)
91+
ADD_PROJECT_DEPENDENCY(magic_enum 0.9.7 CONFIG REQUIRED)
8992
if(BUILD_PINOCCHIO_VISUALIZER)
9093
ADD_PROJECT_DEPENDENCY(
9194
pinocchio
9295
REQUIRED
9396
PKG_CONFIG_REQUIRES "pinocchio >= 3.7.0"
9497
)
9598
endif()
99+
ADD_PROJECT_DEPENDENCY(
100+
FFmpeg
101+
7.0.0
102+
COMPONENTS
103+
avformat
104+
avcodec
105+
swscale
106+
)
96107
set(CANDLEWICK_ASSETS_DIR ${PROJECT_SOURCE_DIR}/assets)
97108
set(CANDLEWICK_SHADER_SRC_DIR ${PROJECT_SOURCE_DIR}/shaders)
98109
set(
@@ -101,6 +112,15 @@ set(
101112
)
102113
message(STATUS "Shader install dir: ${CANDLEWICK_SHADER_INSTALL_DIR}")
103114

115+
if(FFmpeg_FOUND)
116+
message(STATUS "FFmpeg found (version ${FFmpeg_VERSION}).")
117+
option(
118+
BUILD_WITH_FFMPEG_SUPPORT
119+
"Build Candlewick with support for FFmpeg and video recording features."
120+
ON
121+
)
122+
endif()
123+
104124
add_subdirectory(external)
105125
add_subdirectory(src)
106126

bindings/python/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ set(
99
pycandlewick_SOURCES
1010
src/expose-mesh-data.cpp
1111
src/expose-renderer.cpp
12-
src/expose-recording.cpp
1312
src/module.cpp
1413
)
1514

@@ -29,6 +28,9 @@ if(BUILD_PINOCCHIO_VISUALIZER)
2928
PRIVATE CANDLEWICK_PYTHON_PINOCCHIO_SUPPORT
3029
)
3130
endif()
31+
if(BUILD_WITH_FFMPEG_SUPPORT)
32+
target_sources(pycandlewick PRIVATE src/expose-recording.cpp)
33+
endif()
3234

3335
set_target_properties(
3436
pycandlewick

bindings/python/candlewick/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# ruff: noqa: F401, F403, F405
22
from .pycandlewick import * # noqa
33
from .pycandlewick import __version__
4-
from . import video_context
4+
from .video_context import *
55

66

77
def _process():

bindings/python/candlewick/video_context.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,37 @@
1-
from . import Visualizer
2-
from . import VideoRecorderSettings
31
from contextlib import contextmanager
2+
from . import Visualizer
3+
import warnings
4+
5+
try:
6+
import importlib
7+
8+
importlib.import_module("VideoRecorderSettings", package=".")
9+
CANDLEWICK_HAS_VIDEO_RECORDER = True
10+
except ImportError:
11+
CANDLEWICK_HAS_VIDEO_RECORDER = False
412

13+
_DEFAULT_VIDEO_SETTINGS = {"fps": 30, "bitRate": 3_000_000}
514

6-
_DEFAULT_VIDEO_SETTINGS = VideoRecorderSettings()
15+
__all__ = ["create_recorder_context"]
716

817

918
@contextmanager
1019
def create_recorder_context(
1120
viz: Visualizer,
1221
filename: str,
1322
/,
14-
fps: int = _DEFAULT_VIDEO_SETTINGS.fps,
15-
bitRate: int = _DEFAULT_VIDEO_SETTINGS.bitRate,
23+
fps: int = _DEFAULT_VIDEO_SETTINGS["fps"],
24+
bitRate: int = _DEFAULT_VIDEO_SETTINGS["bitRate"],
1625
):
17-
viz.videoSettings().fps = fps
18-
viz.videoSettings().bitRate = bitRate
19-
viz.startRecording(filename)
26+
if not CANDLEWICK_HAS_VIDEO_RECORDER:
27+
warnings.warn(
28+
"This context will do nothing, as Candlewick was built without video recording support."
29+
)
30+
else:
31+
viz.videoSettings().fps = fps
32+
viz.videoSettings().bitRate = bitRate
33+
viz.startRecording(filename)
2034
try:
2135
yield
2236
finally:
2337
viz.stopRecording()
24-
25-
26-
__all__ = ["create_recorder_context"]

bindings/python/src/expose-visualizer.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,24 @@ void exposeVisualizer() {
4242
("self"_a, "filename"), "Save a screenshot to the specified file.")
4343
.def(
4444
"startRecording",
45-
+[](Visualizer &viz, const std::string &filename) {
45+
+[]([[maybe_unused]] Visualizer &viz,
46+
[[maybe_unused]] const std::string &filename) {
47+
#ifndef CANDLEWICK_WITH_FFMPEG_SUPPORT
48+
PyErr_WarnEx(PyExc_UserWarning,
49+
"Recording videos is not available because Candlewick "
50+
"was built without FFmpeg support.",
51+
1);
52+
bp::throw_error_already_set();
53+
#else
4654
viz.startRecording(filename);
55+
#endif
4756
},
4857
("self"_a, "filename"_a))
4958
.def("stopRecording", &Visualizer::stopRecording, "self"_a)
59+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
5060
.def("videoSettings", &Visualizer::videoSettings, "self"_a,
5161
bp::return_internal_reference<>())
62+
#endif
5263
.def("addFrameViz", &Visualizer::addFrameViz,
5364
("self"_a, "frame_id", "show_velocity"_a = true),
5465
"Add visualization (triad and frame velocity) for the given frame "

bindings/python/src/module.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ using namespace candlewick;
88

99
void exposeMeshData();
1010
void exposeRenderer();
11+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
1112
void exposeVideoRecorder();
13+
#endif
1214
#ifdef CANDLEWICK_PYTHON_PINOCCHIO_SUPPORT
1315
void exposeVisualizer();
1416
#endif
@@ -30,7 +32,9 @@ BOOST_PYTHON_MODULE(pycandlewick) {
3032

3133
exposeMeshData();
3234
exposeRenderer();
35+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
3336
exposeVideoRecorder();
37+
#endif
3438
#ifdef CANDLEWICK_PYTHON_PINOCCHIO_SUPPORT
3539
exposeVisualizer();
3640
#endif

cmake

Submodule cmake updated from 6055ba6 to 8833b06

examples/Ur5WithSystems.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
#include "candlewick/multibody/RobotScene.h"
1515
#include "candlewick/multibody/RobotDebug.h"
1616
#include "candlewick/primitives/Primitives.h"
17+
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
1718
#include "candlewick/utils/VideoRecorder.h"
19+
#endif
1820
#include "candlewick/utils/WriteTextureToImage.h"
1921

2022
#include <imgui.h>
@@ -452,9 +454,9 @@ int main(int argc, char **argv) {
452454
Eigen::VectorXd q0 = pin::neutral(model);
453455
Eigen::VectorXd q1 = pin::randomConfiguration(model);
454456

457+
media::TransferBufferPool transfer_buffer_pool{renderer.device};
455458
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
456459
media::VideoRecorder recorder{NoInit};
457-
media::TransferBufferPool transfer_buffer_pool{renderer.device};
458460
if (performRecording) {
459461
media::VideoRecorder::Settings settings;
460462
settings.fps = 50;
@@ -552,9 +554,9 @@ int main(int argc, char **argv) {
552554
robot_scene.release();
553555
debug_scene.release();
554556
gui_system.release();
557+
transfer_buffer_pool.release();
555558
#ifdef CANDLEWICK_WITH_FFMPEG_SUPPORT
556559
recorder.close();
557-
transfer_buffer_pool.release();
558560
#endif
559561
renderer.destroy();
560562
SDL_Quit();

0 commit comments

Comments
 (0)