Genymobile/scrcpy
Extension is deliberately table-driven. Client CLI: add an OPT_* enum + options[] row + case in the getopt switch + field in scrcpy_options. Server options: mirror the param as an ADD_PARAM in execute_server() and a case in Options.parse(). New control messages require a coordinated change in enum sc_control_msg_type, sc_control_msg_serialize/is_droppable/destroy/log, ControlMessage type constants, ControlMessageReader parsing and Controller.handleEvent dispatch. New media sinks plug into the packet_source/packet_sink and frame_source/frame_sink trait interfaces; new input modalities implement key/mouse/gamepad processor interfaces (SDK/UHID/AOA are the three existing backends). Server-side work units implement AsyncProcessor; captures implement SurfaceCapture; audio sources implement AudioCapture.
- Adding a CLI option = new OPT_* enum entry + a row in the static options[] table (shortopt/longopt/argdesc/optional_arg/text) + handling in parse_args_with_getopt's switch; undocumented options are supported by leaving .text NULL.
- Client->server option plumbing is a three-step mirror: sc_server_params field -> ADD_PARAM("key=value") in execute_server() -> case "key" in Options.parse() switch.
- New control message types must be added symmetrically: enum sc_control_msg_type + serialize/log/droppable/destroy cases client-side, and ControlMessage.TYPE_* + ControlMessageReader.read() case + Controller.handleEvent dispatch server-side.
- The trait/ directory defines generic producer/consumer interfaces (packet_source/packet_sink for AVPackets, frame_source/frame_sink for AVFrames); recorder, decoders, screen, v4l2_sink and regulators all plug in via *_add_sink without knowing each other.
- Input backends implement narrow processor interfaces (sc_key_processor, sc_mouse_processor, sc_gamepad_processor in trait/); scrcpy.c picks sdk/uhid/aoa implementations into union members and passes them to the screen as kp/mp/gp.
- Server capture sources implement SurfaceCapture — ScreenCapture, NewDisplayCapture, CameraCapture — selected by videoSource/newDisplay in Server.scrcpy() and attached to SurfaceEncoder.
- Audio capture is pluggable via AudioCapture implementations AudioDirectCapture and AudioPlaybackCapture chosen by AudioSource.isDirect().
- Server-side background work implements the AsyncProcessor interface (start/stop/join + TerminationListener); Server keeps a List<AsyncProcessor> it starts and joins uniformly.
- Adding a codec means extending enum sc_codec, sc_server_get_codec_name, the demuxer SC_CODEC_ID_* mapping to AVCodecID, and the server-side VideoCodec/AudioCodec enums with matching wire ids.
- Custom SDL event types (SC_EVENT_*) in events.h + sc_push_event provide the extension mechanism for cross-thread signaling into the main event loop.
- Recording formats extend enum sc_record_format -> sc_recorder_get_format_name() mapping to FFmpeg muxer names (mp4/matroska/opus/flac/wav).
- Feature flags compile out whole subsystems: HAVE_USB gates OTG/AOA code, HAVE_V4L2 gates the webcam sink, SERVER_DEBUGGER adds jdwp flags to the app_process command.