Commit
This commit is contained in:
@@ -1,18 +1,21 @@
|
||||
import argparse
|
||||
import gi
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
import os
|
||||
import sys
|
||||
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import Gst, GLib
|
||||
|
||||
SWITCH_UDP_PORT = 5001
|
||||
|
||||
class KioskManager:
|
||||
def __init__(self, pipeline):
|
||||
self.pipeline = pipeline
|
||||
self.videos = [
|
||||
"file:///root/vid.mp4",
|
||||
"file:///root/vid2.mp4"
|
||||
"file:///root/python/vid.mp4",
|
||||
"file:///root/python/vid2.mp4"
|
||||
]
|
||||
self.current_video_index = 0
|
||||
|
||||
@@ -117,7 +120,32 @@ def handle_button(source, condition, manager):
|
||||
|
||||
return True
|
||||
|
||||
def play_kiosk():
|
||||
|
||||
def handle_udp_switch(sock, condition, manager):
|
||||
"""Receives 'switch' datagrams from device_server.py and cycles the video."""
|
||||
try:
|
||||
data, _ = sock.recvfrom(64)
|
||||
except BlockingIOError:
|
||||
return True
|
||||
if data.strip() == b"switch":
|
||||
print("UDP Trigger: switch")
|
||||
manager.switch_video()
|
||||
return True
|
||||
|
||||
|
||||
def _resolve_start_index(start_name: str, videos: list) -> int:
|
||||
"""Map a basename like 'vid.mp4' to its index in the videos list."""
|
||||
target = os.path.basename(start_name).lower()
|
||||
for i, uri in enumerate(videos):
|
||||
if os.path.basename(uri).lower() == target:
|
||||
return i
|
||||
raise SystemExit(
|
||||
f"--start {start_name!r} not in kiosk video list: "
|
||||
+ ", ".join(os.path.basename(u) for u in videos)
|
||||
)
|
||||
|
||||
|
||||
def play_kiosk(start_index: int = 0):
|
||||
Gst.init(None)
|
||||
|
||||
pipeline = Gst.ElementFactory.make("playbin", "player")
|
||||
@@ -129,17 +157,19 @@ def play_kiosk():
|
||||
pipeline.set_property("audio-sink", Gst.ElementFactory.make("fakesink"))
|
||||
|
||||
manager = KioskManager(pipeline)
|
||||
pipeline.set_property("uri", manager.videos[0])
|
||||
manager.current_video_index = start_index
|
||||
pipeline.set_property("uri", manager.videos[start_index])
|
||||
print(f"Starting on: {manager.videos[start_index]}")
|
||||
|
||||
# UDP trigger → switch video (device_server sends a packet to port 5001)
|
||||
def _udp_listener():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', 5001))
|
||||
while True:
|
||||
sock.recv(64)
|
||||
GLib.idle_add(manager.switch_video)
|
||||
|
||||
threading.Thread(target=_udp_listener, daemon=True).start()
|
||||
# --- UDP SWITCH LISTENER ---
|
||||
# device_server.py sends b'switch' to 127.0.0.1:5001 to cycle videos remotely.
|
||||
try:
|
||||
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
udp_sock.setblocking(False)
|
||||
udp_sock.bind(("127.0.0.1", SWITCH_UDP_PORT))
|
||||
GLib.io_add_watch(udp_sock, GLib.IO_IN, handle_udp_switch, manager)
|
||||
except Exception as e:
|
||||
print(f"UDP Listener Error: {e}")
|
||||
|
||||
# --- INPUT MONITORING ---
|
||||
try:
|
||||
@@ -159,16 +189,13 @@ def play_kiosk():
|
||||
|
||||
def on_message(bus, msg, manager_instance):
|
||||
if msg.type == Gst.MessageType.EOS:
|
||||
# Video ended. Cycle LED and advance to the next video in the list.
|
||||
manager_instance.change_led_colour()
|
||||
pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0)
|
||||
manager_instance.switch_video()
|
||||
elif msg.type == Gst.MessageType.ERROR:
|
||||
err, debug = msg.parse_error()
|
||||
print(f"GStreamer Error: {err}\nDebug: {debug}", flush=True)
|
||||
loop.quit()
|
||||
elif msg.type == Gst.MessageType.STATE_CHANGED:
|
||||
if msg.src == pipeline:
|
||||
old, new, _ = msg.parse_state_changed()
|
||||
print(f"Pipeline: {old.value_nick} -> {new.value_nick}", flush=True)
|
||||
print(f"GStreamer Error: {err}")
|
||||
loop.quit
|
||||
|
||||
bus.connect("message", on_message, manager)
|
||||
|
||||
@@ -180,73 +207,18 @@ def play_kiosk():
|
||||
except KeyboardInterrupt:
|
||||
pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
def play_static_color(r: int, g: int, b: int):
|
||||
"""Display a solid colour using GStreamer videotestsrc (no video file required).
|
||||
|
||||
Uses videotestsrc pattern=solid-color so every DSI line carries the same
|
||||
repeating RGB triplet — any deviation in the proto_decoder output is a DSI fault.
|
||||
|
||||
Listens on UDP port 5001 for a trigger packet (same as play_kiosk), which
|
||||
briefly cycles the pipeline through READY→PLAYING to generate the LP→HS
|
||||
startup sequence that the scope captures on Pass 1.
|
||||
"""
|
||||
Gst.init(None)
|
||||
|
||||
argb = (0xFF << 24) | (r << 16) | (g << 8) | b
|
||||
|
||||
SINK_STR = ("videoconvert ! video/x-raw,format=BGRx ! "
|
||||
"kmssink driver-name=mxsfb-drm connector-id=37 plane-id=31 can-scale=false")
|
||||
pipeline_str = (
|
||||
f"videotestsrc pattern=solid-color foreground-color={argb} ! "
|
||||
f"video/x-raw,width=1280,height=800,framerate=60/1 ! "
|
||||
f"{SINK_STR}"
|
||||
)
|
||||
|
||||
pipeline = Gst.parse_launch(pipeline_str)
|
||||
bus = pipeline.get_bus()
|
||||
bus.add_signal_watch()
|
||||
|
||||
loop = GLib.MainLoop()
|
||||
|
||||
def on_message(bus, msg):
|
||||
if msg.type == Gst.MessageType.ERROR:
|
||||
err, debug = msg.parse_error()
|
||||
print(f"GStreamer Error: {err}\nDebug: {debug}", flush=True)
|
||||
loop.quit()
|
||||
elif msg.type == Gst.MessageType.STATE_CHANGED:
|
||||
if msg.src == pipeline:
|
||||
old, new, _ = msg.parse_state_changed()
|
||||
print(f"Pipeline: {old.value_nick} -> {new.value_nick}", flush=True)
|
||||
|
||||
def _restart_pipeline():
|
||||
"""Cycle READY→PLAYING to produce the LP→HS startup the scope triggers on."""
|
||||
print("UDP trigger: restarting pipeline (READY → PLAYING)", flush=True)
|
||||
pipeline.set_state(Gst.State.READY)
|
||||
pipeline.set_state(Gst.State.PLAYING)
|
||||
return False # GLib.idle_add one-shot
|
||||
|
||||
def _udp_listener():
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.bind(('127.0.0.1', 5001))
|
||||
while True:
|
||||
sock.recv(64)
|
||||
GLib.idle_add(_restart_pipeline)
|
||||
|
||||
threading.Thread(target=_udp_listener, daemon=True).start()
|
||||
|
||||
bus.connect("message", on_message)
|
||||
pipeline.set_state(Gst.State.PLAYING)
|
||||
print(f"Static colour R:{r} G:{g} B:{b} (0x{argb:08X}) — running", flush=True)
|
||||
|
||||
try:
|
||||
loop.run()
|
||||
except KeyboardInterrupt:
|
||||
pipeline.set_state(Gst.State.NULL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if "--static-pink" in sys.argv:
|
||||
play_static_color(255, 51, 187) # R:255 G:51 B:187
|
||||
else:
|
||||
play_kiosk()
|
||||
p = argparse.ArgumentParser(description="Kiosk video player")
|
||||
p.add_argument("--start", default="vid.mp4",
|
||||
help="Initial video filename (basename match against kiosk list)")
|
||||
# parse_known_args so legacy flags like --static-pink don't crash the kiosk
|
||||
args, _unknown = p.parse_known_args()
|
||||
|
||||
# We need the video list to resolve --start, so we recreate it here (must
|
||||
# stay in sync with KioskManager.videos).
|
||||
_videos = [
|
||||
"file:///root/python/vid.mp4",
|
||||
"file:///root/python/vid2.mp4",
|
||||
]
|
||||
start_index = _resolve_start_index(args.start, _videos)
|
||||
play_kiosk(start_index=start_index)
|
||||
|
||||
Reference in New Issue
Block a user