import gi import struct import os gi.require_version('Gst', '1.0') from gi.repository import Gst, GLib class BrightnessManager: def __init__(self): self.backlight_path = "/sys/class/backlight/main_backlight/brightness" self.led_multi_path = "/sys/class/leds/display-indicator/multi_intensity" self.led_trig_path = "/sys/class/leds/display-indicator/trigger" self.led_br_path = "/sys/class/leds/display-indicator/brightness" # Backlight state self.bl_current = 10 self.bl_direction = 1 # 1 for increasing, -1 for decreasing # LED state self.led_val = 0 self.led_dir = 5 # Step size for smooth ramping self.color_index = 0 # 0: Red, 1: Green, 2: Blue self.setup_led() def setup_led(self): try: with open(self.led_trig_path, 'w') as f: f.write("none") with open(self.led_br_path, 'w') as f: f.write("255") except Exception as e: print(f"LED Setup Error: {e}") def update_led_ramp(self): # Calculate next brightness step self.led_val += self.led_dir # If we hit 255 or 0, reverse direction if self.led_val >= 255: self.led_val = 255 self.led_dir = -5 elif self.led_val <= 0: self.led_val = 0 self.led_dir = 5 # Move to the next color once we finish a down-ramp self.color_index = (self.color_index + 1) % 3 # Construct the RGB string rgb = [0, 0, 0] rgb[self.color_index] = self.led_val try: with open(self.led_multi_path, 'w') as f: f.write(f"{rgb[0]} {rgb[1]} {rgb[2]}") except: pass return True # Keep the GLib timer running def cycle_backlight(self): """Standard backlight cycle on touch""" self.bl_current += self.bl_direction if self.bl_current >= 20: self.bl_current = 20 self.bl_direction = -1 elif self.bl_current <= 0: self.bl_current = 0 self.bl_direction = 1 try: with open(self.backlight_path, 'w') as f: f.write(str(self.bl_current)) print(f"Touch! Backlight: {self.bl_current}") except Exception as e: print(f"Backlight Error: {e}") def handle_touch(source, condition, manager): # Linux input_event struct: long, long, unsigned short, unsigned short, unsigned int # We read 24 bytes (standard for 64-bit systems like i.MX8M) EVENT_SIZE = 24 data = source.read(EVENT_SIZE) if len(data) < EVENT_SIZE: return True # Unpack the binary data # type (H), code (H), value (I) are the last 3 fields _, _, ev_type, ev_code, ev_value = struct.unpack('qqHHI', data) # EV_KEY (1) and BTN_TOUCH (330). ev_value 1 is press, 0 is release. # We trigger the change only on the 'press' (1) if ev_type == 1 and ev_code == 330 and ev_value == 1: manager.cycle_backlight() return True def play_kiosk_video(): Gst.init(None) brightness_manager = BrightnessManager() VIDEO_PATH = "file:///root/python/vid.mp4" SINK_STR = ( "videoconvert ! " "video/x-raw,format=BGRx ! " "kmssink driver-name=mxsfb-drm connector-id=37 plane-id=31 can-scale=false" ) # Create the playbin element pipeline = Gst.ElementFactory.make("playbin", "player") pipeline.set_property("uri", VIDEO_PATH) # Set the custom video sink video_sink = Gst.parse_bin_from_description(SINK_STR, True) pipeline.set_property("video-sink", video_sink) # Set audio to fakesink (prevents errors if no audio hardware is found) audio_sink = Gst.ElementFactory.make("fakesink", "audio-fake") pipeline.set_property("audio-sink", audio_sink) # --- INPUT MONITORING --- try: # Open the touch device in binary read mode touch_fd = open("/dev/input/event2", "rb") # Add to GLib loop so it doesn't block the video GLib.io_add_watch(touch_fd, GLib.IO_IN, handle_touch, brightness_manager) except FileNotFoundError: print("Warning: /dev/input/event2 not found. Brightness control disabled.") GLib.timeout_add(20, brightness_manager.update_led_ramp) # --- LOOPING LOGIC --- bus = pipeline.get_bus() bus.add_signal_watch() def on_message(bus, msg): t = msg.type if t == Gst.MessageType.EOS: print("End of video. Looping...") pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0) elif t == Gst.MessageType.ERROR: err, debug = msg.parse_error() print(f"Error: {err}") loop.quit() return True bus.connect("message", on_message) # Start playback print(f"Starting playback: {VIDEO_PATH}") pipeline.set_state(Gst.State.PLAYING) loop = GLib.MainLoop() try: loop.run() except KeyboardInterrupt: print("\nStopping player...") finally: pipeline.set_state(Gst.State.NULL) if __name__ == "__main__": play_kiosk_video()