From b48cb0b1bc8277a3a2cc50a161ae4f267b2cd8c3 Mon Sep 17 00:00:00 2001 From: HankChang Date: Thu, 16 May 2024 17:32:54 +0800 Subject: [PATCH] 1.Add a test page 2. 2.Add gstreamer support on rpi5 and simultaneously implement hdmi rx play, pause, and cropping features. 3.Add protection to prevent quick pressing of the preview and sound buttons. --- c_mainwindow.py | 6 +- ext_dev/tc358743.py | 88 +++++++++++++-- media_engine/media_engine.py | 203 +++++++++++++++++++++++++---------- requirements.txt | 1 + ui/ui_hdmi_in_page.py | 63 ++++++++--- ui/ui_media_files_page.py | 7 ++ ui/ui_test_page2.py | 31 ++++++ utils/utils_gst_pipeline.py | 65 +++++++++++ version.py | 2 +- 9 files changed, 383 insertions(+), 83 deletions(-) create mode 100644 ui/ui_test_page2.py create mode 100644 utils/utils_gst_pipeline.py diff --git a/c_mainwindow.py b/c_mainwindow.py index ad94c6a..d678a2d 100644 --- a/c_mainwindow.py +++ b/c_mainwindow.py @@ -21,10 +21,10 @@ from ui.ui_media_files_page import MediaFilesPage from ui.ui_sys_sw_info import UiSystemSoftware from ui.ui_test_page import TestPage - +from ui.ui_test_page2 import TestPage2 '''List of Page Selector Button Name ''' -Page_Select_Btn_Name_List = ["FPGA_List", "Media_Files", "HDMI_In", "Led_Settings", "Test"] -Page_List = [FpgaListPage, MediaFilesPage, HDMIInPage, LedSettingsPage, TestPage] +Page_Select_Btn_Name_List = ["FPGA_List", "Media_Files", "HDMI_In", "Led_Settings", "Test[1]", "Test[2]"] +Page_List = [FpgaListPage, MediaFilesPage, HDMIInPage, LedSettingsPage, TestPage, TestPage2] Page_Map = dict(zip(Page_Select_Btn_Name_List, Page_List)) diff --git a/ext_dev/tc358743.py b/ext_dev/tc358743.py index 736c781..13c9b0e 100644 --- a/ext_dev/tc358743.py +++ b/ext_dev/tc358743.py @@ -1,3 +1,6 @@ +import re +import time + from PyQt5.QtCore import QThread, pyqtSignal, QDateTime, QObject, QTimer, QMutex import utils.log_utils # import utils.net_utils @@ -19,7 +22,8 @@ def __init__(self, **kwargs): self.hdmi_height = None self.hdmi_fps = None self.res_set_dv_bt_timing = False - self.video_device = self.get_video_device(self) + self.video_device, self.sub_device, self.media_device = self.get_video_device() + if platform.machine() in ('arm', 'arm64', 'aarch64'): self.check_hdmi_status_mutex = QMutex() else: @@ -41,7 +45,7 @@ def check_hdmi_status_unlock(self): self.check_hdmi_status_mutex.unlock() def x86_get_video_timing(self): - video_device = self.get_video_device(self) + video_device = self.video_device connected = False width = 0 height = 0 @@ -98,8 +102,16 @@ def get_tc358743_dv_timing(self): else: connected, width, height, fps = self.x86_get_video_timing() return connected, width, height, fps + if "pi5" in platform.node(): + v4l2_query_timing = ( + f"v4l2-ctl -d {self.sub_device} --query-dv-timings" + ) + else: + v4l2_query_timing = ( + f"v4l2-ctl --query-dv-timings" + ) self.check_hdmi_status_lock() - dv_timings = os.popen("v4l2-ctl --query-dv-timings").read() + dv_timings = os.popen(v4l2_query_timing).read() self.check_hdmi_status_unlock() list_dv_timings = dv_timings.split("\n") @@ -131,8 +143,16 @@ def set_tc358743_dv_bt_timing(self): pass else: return True + if "pi5" in platform.node(): + v4l2_bt_timing_set = ( + f"v4l2-ctl -d {self.sub_device} --set-dv-bt-timings query" + ) + else: + v4l2_bt_timing_set = ( + f"v4l2-ctl --set-dv-bt-timings query" + ) self.check_hdmi_status_lock() - res_set_dv_bt_timing = os.popen("v4l2-ctl --set-dv-bt-timings query").read() + res_set_dv_bt_timing = os.popen(v4l2_bt_timing_set).read() self.check_hdmi_status_unlock() # log.debug("res_set_dv_bt_timing = %s", res_set_dv_bt_timing) if 'BT timings set' in res_set_dv_bt_timing: @@ -161,9 +181,59 @@ def get_tc358743_hdmi_info(self): return connected, width, height, fps @staticmethod - def get_video_device(cls): - preferred_video = "/dev/video0" if platform.machine() in ('arm', 'arm64', 'aarch64') else "/dev/video2" - if os.path.exists(preferred_video): - return preferred_video + def set_media_controller(video_device, media_device, width, height): + try: + + media_ctl_csi_dev = [ + f"media-ctl -d {media_device} -r", + f"media-ctl -d {media_device} -l \"'csi2':4 -> 'rp1-cfe-csi2_ch0':0 [1]\"", + f"media-ctl -d {media_device} -V \"'csi2':0 [fmt:UYVY8_1X16/{width}x{height} field:none colorspace:srgb]\"", + f"media-ctl -d {media_device} -V \"'csi2':4 [fmt:UYVY8_1X16/{width}x{height} field:none colorspace:srgb]\"", + f"media-ctl -d {media_device} -V \"'tc358743 4-000f':0 [fmt:UYVY8_1X16/{width}x{height} field:none colorspace:srgb]\"" + ] + + for cmd in media_ctl_csi_dev: + try: + log.debug(f"Executing command: {cmd}") + result = subprocess.run(cmd, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True) + if result.stderr: + log.debug("media error:", result.stderr) + except subprocess.CalledProcessError as e: + log.debug(f"Command failed with error: {e}") + + video_capture_fmt = f"v4l2-ctl -d {video_device} -v width={width},height={height},pixelformat=UYVY" + subprocess.run(video_capture_fmt, shell=True, check=True) + + return True + except subprocess.CalledProcessError as e: + log.debug(f"Failed to execute command: {e.cmd} with exit status {e.returncode}") + return False + + @staticmethod + def get_video_device(): + if "pi5" in platform.node(): + preferred_video = "/dev/video0" + alternative_video = "/dev/v4l-subdev2" + + output = subprocess.check_output(["v4l2-ctl", "--list-devices"], universal_newlines=True) + device_sections = re.findall(r'rp1-cfe \(platform:\w+\.csi\):\n((?:.|\n)+?)(?=\n\n|\Z)', output) + csi_devices = [] + for section in device_sections: + lines = section.strip().split('\n') + for line in lines: + if '/dev/media' in line: + csi_devices.append(line.strip()) + media_video = csi_devices[0] if csi_devices else None + + if os.path.exists(alternative_video): + return preferred_video, alternative_video, media_video else: - return None + preferred_video = "/dev/video0" if platform.machine() in ('arm', 'arm64', 'aarch64') else "/dev/video2" + alternative_video = None + media_video = None + + if os.path.exists(preferred_video): + return preferred_video, alternative_video, media_video + + return None, None, None diff --git a/media_engine/media_engine.py b/media_engine/media_engine.py index bf3aa53..ec5da36 100644 --- a/media_engine/media_engine.py +++ b/media_engine/media_engine.py @@ -12,6 +12,13 @@ import psutil from PyQt5.QtCore import QObject, pyqtSignal, QMutex, QThread +import gi +import sys +from gi.repository import Gst, GObject, GLib + +gi.require_version('Gst', '1.0') +Gst.init(sys.argv) + import utils.utils_file_access from ext_qt_widgets.playing_preview_widget import PlayingPreviewWindow from media_engine.media_engine_def import PlayStatus, RepeatOption @@ -25,6 +32,7 @@ from multiprocessing import shared_memory from ctypes import * from media_engine.linux_ipc_pyapi_sem import * +from utils.utils_gst_pipeline import * class MediaEngine(QObject): @@ -44,6 +52,8 @@ def __init__(self): self.play_single_file_worker = None self.play_hdmi_in_thread = None self.play_hdmi_in_worker = None + self.gst_app_sink = None + self.gst_pipe_line = None log.debug("") self.sync_output_streaming_resolution() self.led_video_params = VideoParams(True, 50, 2.2, @@ -181,10 +191,11 @@ def stop_play(self): if self.play_hdmi_in_worker is not None: self.resume_playing() self.play_hdmi_in_worker.stop() - for i in range(5): - if self.ff_process is not None: + if self.ff_process is not None: + for i in range(5): try: os.kill(self.ff_process.pid, signal.SIGTERM) + time.sleep(1) except ProcessLookupError: log.debug("PID might have changed or process could have exited already") except Exception as e: @@ -192,7 +203,6 @@ def stop_play(self): finally: self.ff_process = None break - time.sleep(1) try: if self.play_hdmi_in_thread is not None: self.play_hdmi_in_thread.quit() @@ -238,6 +248,7 @@ def hdmi_in_play(self, video_src, **kwargs): self.play_hdmi_in_worker = Playing_HDMI_in_worker(self, video_src, with_audio=audio_active, with_preview=preview_visible, + active_width=active_width, active_height=active_height, c_width=c_width, c_height=c_height, c_pos_x=c_pos_x, c_pos_y=c_pos_y, ) @@ -254,6 +265,7 @@ def hdmi_in_play(self, video_src, **kwargs): def pause_playing(self): if self.playing_status != PlayStatus.Stop: log.debug("enter pause_play!\n") + self.playing_status = PlayStatus.Pausing try: if self.ff_process is not None: sub_ff_process = self.find_child("ffmpeg", self.ff_process.pid) @@ -265,6 +277,7 @@ def pause_playing(self): if self.sound_device.audio_process is not None: mute_audio_sinks(True) if self.play_hdmi_in_worker is not None: + self.play_hdmi_in_worker.gst_pipe_pause() self.play_status_changed(self.playing_status, self.play_hdmi_in_worker.video_src) if self.play_single_file_thread is not None: self.play_status_changed(self.playing_status, self.play_single_file_worker.file_uri) @@ -275,6 +288,7 @@ def pause_playing(self): def resume_playing(self): if self.playing_status != PlayStatus.Stop: log.debug("enter resume_play!\n") + self.playing_status = PlayStatus.Playing try: if self.ff_process is not None: sub_ff_process = self.find_child("ffmpeg", self.ff_process.pid) @@ -282,10 +296,10 @@ def resume_playing(self): sub_ff_process.resume() else: os.kill(self.ff_process.pid, signal.SIGCONT) - self.playing_status = PlayStatus.Playing if self.sound_device.audio_process is not None: mute_audio_sinks(False) if self.play_hdmi_in_worker is not None: + self.play_hdmi_in_worker.gst_pipe_resume() self.play_status_changed(self.playing_status, self.play_hdmi_in_worker.video_src) if self.play_single_file_thread is not None: self.play_status_changed(self.playing_status, self.play_single_file_worker.file_uri) @@ -566,10 +580,15 @@ class Playing_HDMI_in_worker(QThread): pysignal_refresh_image_pixmap = pyqtSignal(np.ndarray) def __init__(self, media_engine: MediaEngine, - video_src, c_width: int, c_height: int, + video_src, + active_width: int, active_height: int, + c_width: int, c_height: int, c_pos_x: int, c_pos_y: int, with_audio: bool, with_preview: bool): super().__init__() + self.Glib_Main_loop = None + self.pipeline = None + self.gst_app_sink = None self.ffmpeg_cmd = None self.agent_cmd = None self.agent_process = None @@ -583,6 +602,8 @@ def __init__(self, media_engine: MediaEngine, self.video_src = video_src self.play_with_audio = with_audio self.play_with_preview = with_preview + self.active_area_width = active_width + self.active_area_height = active_height self.crop_visible_area_width = c_width self.crop_visible_area_height = c_height self.crop_position_x = c_pos_x @@ -593,6 +614,7 @@ def __init__(self, media_engine: MediaEngine, self.output_height = self.media_engine.output_streaming_height self.output_fps = self.media_engine.output_streaming_fps self.play_status_change(PlayStatus.Initial, self.video_src) + self.gst_pipe_description = None self.raw_image = None self.image_from_pipe = None self.force_stop = False @@ -759,6 +781,11 @@ def cleanup_resources(self): log.debug(f"An error occurred while stopping the ff_process: {e}") finally: self.ff_process = None + if self.pipeline: + self.pipeline.set_state(Gst.State.NULL) + if self.Glib_Main_loop: + self.Glib_Main_loop.quit() + self.quit() self.play_status_change(PlayStatus.Stop, self.video_src) self.process_release_mutex.unlock() @@ -778,15 +805,30 @@ def run(self): try: target_fps_str = f"{self.output_fps}/1" - self.ffmpeg_cmd = get_ffmpeg_cmd_for_media(self.video_src, - width=self.output_width, - height=self.output_height, - target_fps=target_fps_str, - c_width=self.crop_visible_area_width, - c_height=self.crop_visible_area_height, - c_pos_x=self.crop_position_x, - c_pos_y=self.crop_position_y, - ) + + if "pi5" in platform.node() or "x86_64" in platform.machine(): + self.gst_pipe_description = get_gst_cmd_for_media(self.video_src, + active_width=self.active_area_width, + active_height=self.active_area_height, + output_width=self.output_width, + output_height=self.output_height, + target_fps=target_fps_str, + c_width=self.crop_visible_area_width, + c_height=self.crop_visible_area_height, + c_pos_x=self.crop_position_x, + c_pos_y=self.crop_position_y, + video_sink=self.play_with_preview + ) + else: + self.ffmpeg_cmd = get_ffmpeg_cmd_for_media(self.video_src, + width=self.output_width, + height=self.output_height, + target_fps=target_fps_str, + c_width=self.crop_visible_area_width, + c_height=self.crop_visible_area_height, + c_pos_x=self.crop_position_x, + c_pos_y=self.crop_position_y, + ) if self.play_with_audio is True: if self.media_engine.pulse_audio_status is True: if self.media_engine.hdmi_sound is not None and self.media_engine.headphone_sound is not None: @@ -794,58 +836,85 @@ def run(self): sink_card, sink_device = self.media_engine.headphone_sound self.media_engine.sound_device.start_play(source_card, source_device, sink_card, sink_device) - while True: + if "pi5" in platform.node() or "x86_64" in platform.machine(): - if self.ff_process and self.ff_process.poll() is None: - self.ff_process.terminate() - try: - self.ff_process.wait(timeout=2) - except subprocess.TimeoutExpired: - log.debug("ff_process termination timeout.") - self.ff_process = None + if self.pipeline: + self.pipeline.set_state(Gst.State.NULL) - try: - self.ff_process = subprocess.Popen(self.ffmpeg_cmd.split(), stdout=subprocess.PIPE, bufsize=10 ** 8) - if self.ff_process.pid > 0: - log.debug(f"ff_process started: {self.ff_process.pid}") + self.pipeline = Gst.parse_launch(self.gst_pipe_description) + self.pipeline.set_state(Gst.State.PLAYING) + + self.gst_app_sink = self.pipeline.get_by_name("sink") + self.gst_app_sink.connect("new-sample", self.gst_process_frame) + + if self.gst_app_sink: + self.pipeline.set_state(Gst.State.PLAYING) + self.worker_status = 1 + + self.media_engine.gst_pipe_line = self.pipeline + self.media_engine.gst_app_sink = self.gst_app_sink + self.Glib_Main_loop = GLib.MainLoop() + + def play_status_change(): self.play_status_change(PlayStatus.Playing, self.video_src) - self.worker_status = 1 - except Exception as e: - log.debug(f"ff_process Failed: {e}") + return False + + GObject.timeout_add(200, play_status_change) + self.Glib_Main_loop.run() + else: + while True: + if self.ff_process and self.ff_process.poll() is None: + self.ff_process.terminate() + try: + self.ff_process.wait(timeout=2) + except subprocess.TimeoutExpired: + log.debug("ff_process termination timeout.") + self.ff_process = None - self.media_engine.ff_process = self.ff_process + try: + self.ff_process = subprocess.Popen(self.ffmpeg_cmd.split(), stdout=subprocess.PIPE, + bufsize=10 ** 8) + if self.ff_process.pid > 0: + log.debug(f"ff_process started: {self.ff_process.pid}") + self.play_status_change(PlayStatus.Playing, self.video_src) + self.worker_status = 1 + except Exception as e: + log.debug(f"ff_process Failed: {e}") - while self.worker_status and self.force_stop is False: + self.media_engine.ff_process = self.ff_process - data_ready, _, _ = select.select([self.ff_process.stdout], [], [], 3000) + while self.worker_status and self.force_stop is False: - if data_ready: - self.image_from_pipe = self.ff_process.stdout.read(self.output_width * self.output_height * 3) - if not self.image_from_pipe: - if self.agent_process is not None: - self.agent_process.kill() - log.debug("Pipe read attempt returned no data.") + data_ready, _, _ = select.select([self.ff_process.stdout], [], [], 3000) + + if data_ready: + self.image_from_pipe = self.ff_process.stdout.read( + self.output_width * self.output_height * 3) + if not self.image_from_pipe: + if self.agent_process is not None: + self.agent_process.kill() + log.debug("Pipe read attempt returned no data.") + break + else: + log.debug("No data available from pipe.") break - else: - log.debug("No data available from pipe.") - break - self.raw_image = np.frombuffer(self.image_from_pipe, dtype='uint8') - self.raw_image = self.raw_image.reshape((self.output_height, self.output_width, 3)) + self.raw_image = np.frombuffer(self.image_from_pipe, dtype='uint8') + self.raw_image = self.raw_image.reshape((self.output_height, self.output_width, 3)) - if self.ff_process is not None: - self.ff_process.stdout.flush() + if self.ff_process is not None: + self.ff_process.stdout.flush() - if self.image_from_pipe: - raw_image_array = np.array(self.raw_image) - self.write_to_shm(raw_image_array.tobytes()) + if self.image_from_pipe: + raw_image_array = np.array(self.raw_image) + self.write_to_shm(raw_image_array.tobytes()) - if self.play_with_preview is True: - self.pysignal_refresh_image_pixmap.emit(self.raw_image) + if self.play_with_preview is True: + self.pysignal_refresh_image_pixmap.emit(self.raw_image) - if self.force_stop is True: - log.debug("self.force_stop is True") - break + if self.force_stop is True: + log.debug("self.force_stop is True") + break self.cleanup_resources() self.pysignal_hdmi_play_finished.emit() @@ -856,10 +925,36 @@ def run(self): self.stop() def stop(self): - log.debug("FFmpeg process and pipe closed.") self.force_stop = True self.cleanup_resources() + def gst_process_frame(self, gst_app_sink): + if self.force_stop: + return Gst.FlowReturn.EOS + pipeline = gst_app_sink.emit("pull-sample") + if pipeline: + pipeBuffer = pipeline.get_buffer() + gst_capabilities = pipeline.get_caps().get_structure(0) + width = gst_capabilities.get_value('width') + height = gst_capabilities.get_value('height') + result, mapinfo = pipeBuffer.map(Gst.MapFlags.READ) + if result: + self.raw_image = np.ndarray((height, width, 3), buffer=mapinfo.data, dtype=np.uint8) + pipeBuffer.unmap(mapinfo) + if self.raw_image is not None: + raw_image_array = np.array(self.raw_image) + self.write_to_shm(raw_image_array.tobytes()) + return Gst.FlowReturn.OK + return Gst.FlowReturn.ERROR + + def gst_pipe_pause(self): + if self.pipeline: + self.pipeline.set_state(Gst.State.PAUSED) + + def gst_pipe_resume(self): + if self.pipeline: + self.pipeline.set_state(Gst.State.PLAYING) + def test(self): log.debug("") diff --git a/requirements.txt b/requirements.txt index c4df263..433511e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,3 +20,4 @@ QtPy==2.4.1 shiboken2==5.15.2.1 Werkzeug==2.0.2 WTForms==3.1.2 +PyGObject~=3.48.2 diff --git a/ui/ui_hdmi_in_page.py b/ui/ui_hdmi_in_page.py index 68e818e..83eb44d 100644 --- a/ui/ui_hdmi_in_page.py +++ b/ui/ui_hdmi_in_page.py @@ -67,7 +67,6 @@ def __init__(self, _main_window, _frame: QWidget, _name, media_engine: MediaEngi self.main_windows = _main_window self.frame = _frame self.media_engine = media_engine - self.video_device = TC358743.get_video_device(self) self.widget = QWidget(self.frame) self.name = _name self.prev_hdmi_info = None @@ -78,25 +77,28 @@ def __init__(self, _main_window, _frame: QWidget, _name, media_engine: MediaEngi self.audioActiveToggle = True self.previewVisibleToggle = True self.streamStateMutex = QMutex() + self.tc358743 = TC358743() log.debug("start hdmi-in page") self.init_ui() self.media_engine.install_hdmi_play_status_changed_slot(self.media_play_status_changed) + self.video_device = self.tc358743.video_device + self.sub_device = self.tc358743.sub_device + self.media_device = self.tc358743.media_device self.measurement_tc358743 = True self.check_tc358743_interval = 1000 self.check_tc358743_timer = QTimer(self) self.check_tc358743_timer.timeout.connect(self.check_tc358743_timer_event) if platform.machine() in ('arm', 'arm64', 'aarch64'): # Venom add for script not found - ensure_edid_validity(self) - + self.edid_validator = EDIDValidator(self.sub_device) + self.edid_validator.validate() try: self.check_tc358743_timer.start(self.check_tc358743_interval) except Exception as e: log.debug(e) - self.tc358743 = TC358743() self.tc358743.signal_refresh_tc358743_param.connect(self.refresh_tc358743_param) self.tc358743.get_tc358743_dv_timing() subprocess.Popen("pkill -9 -f ffmpeg", shell=True) @@ -458,6 +460,10 @@ def handleHdmiStreamStart(self): if self.tc358743.set_tc358743_dv_bt_timing() is True: self.tc358743.reinit_tc358743_dv_timing() + if "pi5" in platform.node(): + self.tc358743.set_media_controller(self.video_device,self.media_device, + int(self.tc358743.hdmi_width), + int(self.tc358743.hdmi_height)) self.media_engine.hdmi_in_play(self.video_device, active_width=int(self.tc358743.hdmi_width), active_height=int(self.tc358743.hdmi_height), @@ -466,7 +472,7 @@ def handleHdmiStreamStart(self): self.streamStateMutex.unlock() def sound_btn_clicked(self): - + self.sound_control_btn.setEnabled(False) if self.sound_control_btn.isChecked(): self.audioActiveToggle = False self.sound_control_btn.setIcon(QIcon('materials/soundOffIcon.png')) @@ -477,9 +483,11 @@ def sound_btn_clicked(self): if self.streamingStatus is True: self.stop_streaming(False) self.start_streaming() + time.sleep(2) + self.sound_control_btn.setEnabled(True) def preview_btn_clicked(self): - + self.preview_control_btn.setEnabled(False) if self.preview_control_btn.isChecked(): self.previewVisibleToggle = False self.preview_control_btn.setIcon(QIcon('materials/eyeCloseIcon.png')) @@ -490,6 +498,8 @@ def preview_btn_clicked(self): if self.streamingStatus is True: self.stop_streaming(False) self.start_streaming() + time.sleep(2) + self.preview_control_btn.setEnabled(True) def update_process_info(self): try: @@ -530,16 +540,37 @@ def adj_hdmi_ctrl_param(self): self.start_streaming() -def ensure_edid_validity(self): - p = os.popen("v4l2-ctl --get-edid") - preproc = p.read() - if "failed" in preproc: - p = os.popen("write_tc358743_edid.sh") - time.sleep(5) - p.close() - p.close() - - def parse_pid(line): parts = line.split() return parts[3] if len(parts) > 3 else None + + +class EDIDValidator: + def __init__(self, sub_device): + self.sub_device = sub_device + self.get_edid = None + + if "pi5" in platform.node(): + self.get_edid = ( + f"v4l2-ctl -d {self.sub_device} --get-edid" + ) + self.set_edid = ( + f"v4l2-ctl -d {self.sub_device} --set-edid=file=/home/root/tc358743_edid.txt --fix-edid-checksums" + ) + else: + self.get_edid = ( + "v4l2-ctl --get-edid" + ) + self.set_edid = ( + "write_tc358743_edid.sh" + ) + + def validate(self): + p = os.popen(self.get_edid) + edid_output = p.read() + p.close() + + if "failed" in edid_output: + p = os.popen(self.set_edid) + time.sleep(5) + p.close() diff --git a/ui/ui_media_files_page.py b/ui/ui_media_files_page.py index 9656591..973bc49 100644 --- a/ui/ui_media_files_page.py +++ b/ui/ui_media_files_page.py @@ -2,6 +2,7 @@ import os import platform import shutil +import time import qdarkstyle from PyQt5.QtCore import Qt, QRect, QSize @@ -703,6 +704,7 @@ def pause_btn_clicked(self): def sound_btn_clicked(self): log.debug("") + self.sound_control_btn.setEnabled(False) if self.sound_control_btn.isChecked(): self.audioActiveToggle = False self.sound_control_btn.setIcon(QIcon('materials/soundOffIcon.png')) @@ -719,9 +721,12 @@ def sound_btn_clicked(self): active_height=self.media_active_height, audio_active=self.audioActiveToggle, preview_visible=self.previewVisibleToggle) + time.sleep(2) + self.sound_control_btn.setEnabled(True) def preview_btn_clicked(self): log.debug("") + self.preview_control_btn.setEnabled(False) if self.preview_control_btn.isChecked(): self.previewVisibleToggle = False self.preview_control_btn.setIcon(QIcon('materials/eyeCloseIcon.png')) @@ -738,6 +743,8 @@ def preview_btn_clicked(self): active_height=self.media_active_height, audio_active=self.audioActiveToggle, preview_visible=self.previewVisibleToggle) + time.sleep(2) + self.preview_control_btn.setEnabled(True) def adj_media_ctrl_param(self): self.media_engine.led_video_params.set_media_file_crop_w(int(self.media_crop_w_lineedit.text())) diff --git a/ui/ui_test_page2.py b/ui/ui_test_page2.py new file mode 100644 index 0000000..0207a0e --- /dev/null +++ b/ui/ui_test_page2.py @@ -0,0 +1,31 @@ +import numpy as np +from PyQt5 import QtWidgets +from PyQt5.QtCore import QObject, Qt, QTimer, pyqtSignal +from PyQt5.QtGui import QFont, QImage, QPixmap +from PyQt5.QtWidgets import QTreeWidget, QTableWidget, QWidget, QVBoxLayout, QTableWidgetItem, QLabel, QGridLayout, \ + QPushButton + +from ext_qt_widgets.playing_preview_widget import PlayingPreviewWindow +from global_def import * +from media_engine import media_engine +from media_engine.media_engine import MediaEngine +from qt_ui_style.button_qss import * +from PyQt5.QtCore import QThread, pyqtSignal + + +class TestPage2(QWidget): + pyqt_signal_rw_single_run_completed = pyqtSignal() + + def __init__(self, _main_window, _frame: QWidget, _name: str, **kwargs): + super(TestPage2, self).__init__() + self.main_windows = _main_window + self.frame = _frame + self.widget = QWidget(self.frame) + self.name = _name + self.label_name = None + self.layout = None + self.init_ui() + + def init_ui(self): + self.label_name = QLabel(self.name) + self.layout = QVBoxLayout(self) diff --git a/utils/utils_gst_pipeline.py b/utils/utils_gst_pipeline.py new file mode 100644 index 0000000..48a4d09 --- /dev/null +++ b/utils/utils_gst_pipeline.py @@ -0,0 +1,65 @@ +import platform +from global_def import log + + +def get_gst_cmd_for_media(video_uri: str, **kwargs): + active_width = kwargs.get("active_width") + active_height = kwargs.get("active_height") + output_width = kwargs.get("output_width", 1280) + output_height = kwargs.get("output_height", 720) + target_fps = kwargs.get("target_fps", "24/1") + video_sink = kwargs.get("video_sink") + c_width = kwargs.get("c_width") + c_height = kwargs.get("c_height") + c_pos_x = kwargs.get("c_pos_x") + c_pos_y = kwargs.get("c_pos_y") + + pipeline_description = None + + if "/dev/video" in video_uri: + if "pi5" in platform.node(): + pipeline_description = ( + f"v4l2src device={video_uri} ! " + f"video/x-raw, width={active_width}, height={active_height}, format=UYVY ! " + "videoconvert ! " + "videoscale ! " + f"video/x-raw, width={output_width}, height={output_height}, " + f"format=RGB, framerate={target_fps} ! " + "tee name=t " + ) + elif "x86_64" in platform.machine(): + ''' + pipeline_description = ( + f"v4l2src device={video_uri} ! " + f"video/x-raw, width={active_width}, height={active_height}, format=YUY2 ! " + "videoconvert ! " + "videoscale ! " + f"video/x-raw, width={output_width}, height={output_height}, " + f"format=RGB, framerate=30/1 ! " + "tee name=t " + ) + ''' + pipeline_description = ( + f"v4l2src device={video_uri} ! " + f"image/jpeg, width={active_width}, height={active_height}, framerate=30/1 ! " + "jpegdec ! " + "videoconvert ! " + "videoscale ! " + f"video/x-raw, width={output_width}, height={output_height}, " + f"format=RGB, framerate=30/1 ! " + "tee name=t " + ) + + if video_sink: + pipeline_description += ( + "t. ! queue ! videoconvert ! autovideosink " + ) + + pipeline_description += ( + "t. ! queue ! videoconvert ! appsink name=sink emit-signals=True " + "max-buffers=1 drop=True sync=False" + ) + + log.debug("gst pipeline description: %s", pipeline_description) + return pipeline_description + diff --git a/version.py b/version.py index 0eedb25..fbe3668 100644 --- a/version.py +++ b/version.py @@ -1,6 +1,6 @@ Version_Year = '2024' Version_Month = '05' -Version_Date = '03' +Version_Date = '06' Version_Major = "01" Version_Minor = "00" Version_Patch = "00"