Skip to content

Commit

Permalink
Merge pull request #9 from mad-lab-fau/adapted_video
Browse files Browse the repository at this point in the history
Adapted video
  • Loading branch information
MalteOlle authored Jul 18, 2022
2 parents 9cf0ce2 + c15a95d commit 7feee45
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 19 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test_and_lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
sudo apt-get install -y libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-pulseaudio
sudo apt-get -y install git
sudo apt-get -y install libpulse-mainloop-glib0
sudo apt-get -y install vlc
python -m pip install --no-cache-dir --upgrade pip
# sudo pip install git+https://github.com/PyCQA/astroid.git@astroid-2.5.1
poetry run doit test
Expand Down
25 changes: 18 additions & 7 deletions mad_gui/plugins/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ def get_start_time(self, *args, **kwargs) -> datetime.time: # noqa

@staticmethod
def get_sync_file(video_file: str) -> str:
"""Searches for an excel file that has `sync` in its name and returns the file name.
Attributes
----------
video_file
The path of the video file that is being shown.
Returns
-------
sync_file
The path of the excel file that has `sync` in its name and returns the sync indices from there.
"""
files = list(Path(video_file).parent.glob("*sync*.xlsx"))
if len(files) == 0:
UserInformation.inform(
Expand All @@ -161,24 +173,23 @@ def get_sync_file(video_file: str) -> str:
return ""

@classmethod
def get_video_signal_synchronization(cls, video_file: str) -> pd.DataFrame: # noqa
"""Searches for an excel file that has `sync` in its name and returns the sync indices from there.
def get_video_signal_synchronization(cls, sync_file: str) -> pd.DataFrame: # noqa
"""Get information regarding sync for video and signal."
The Excel file should have as first column (index) "start_sample" and "end_sample" and the columns should be
"video_frame" and "signal_sample."
The Excel file should have as first column (index) "start" and "end" and the columns should be
"PLOTNAME_sample" video_ms."
Attributes
----------
video_file
The path of the video that is being shown.
sync_file
The path of the sync file for the video that is being shown.
Returns
-------
sync_indices
A dataframe which tells the gui which frames of the video correspond to which samples in the signal.
Currently only implemented to accept two synchronized events. Between those, the GUI interpolates linearly.
"""
sync_file = cls.get_sync_file(video_file)
try:
sync = pd.read_excel(sync_file, index_col=0, engine="openpyxl")
return sync
Expand Down
7 changes: 4 additions & 3 deletions mad_gui/windows/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@
uic_path = Path(os.sep.join(sys.executable.split(os.sep)[:-1])) / "Scripts"
sys.path.append(str(uic_path))
Window, _ = loadUiType(ui_path)
except TypeError as e:
except TypeError as error:
raise FileNotFoundError(
"Probably python did not find `pyside2-uic`. See "
'"https://mad-gui.readthedocs.io/en/latest/troubleshooting.html#pyside2-uic-not-found" for more '
"information"
) from e
) from error
elif ".py" in ui_path:
from mad_gui.qt_designer.build.main import Ui_MainWindow as Window # noqa

Expand Down Expand Up @@ -511,7 +511,8 @@ def _set_sync(self, sync_file: str):

try:
plots = [*self.sensor_plots.items(), ("video", self.video_plot)]
except AttributeError:
except AttributeError as e:
print(e)
plots = self.sensor_plots.items()

suffixes = {"video": "_ms"}
Expand Down
50 changes: 41 additions & 9 deletions mad_gui/windows/video_window.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import warnings

import pandas as pd
from PySide2.QtCore import QObject, Qt, QUrl
from PySide2.QtMultimedia import QMediaContent, QMediaPlayer, QMediaPlaylist

from mad_gui.components.dialogs import UserInformation
from mad_gui.qt_designer.ui_video import UiVideoWindow
from mad_gui.state_keeper import StateKeeper

Expand All @@ -19,6 +22,8 @@ def __init__(self, parent=None): # MainWindow
self.setPalette(self.parent.palette())
self.fps = None
self.sync_info = None
self.duration = None
self.video_file = None
self.slider.sliderPressed.connect(self.slider_pressed)
self.slider.sliderReleased.connect(self.slider_released)
self.slider.sliderMoved.connect(self.slider_moved)
Expand All @@ -28,6 +33,8 @@ def __init__(self, parent=None): # MainWindow
self.btn_play_pause.clicked.connect(self.toggle_play)
self.setStyleSheet(parent.styleSheet())
self.setWindowFlag(Qt.WindowStaysOnTopHint)
self.user_informed_about_error = False
self._times_set_rate_called = 0

def _init_position(self):
"""Move the window to the center of the parent window."""
Expand All @@ -43,6 +50,7 @@ def toggle_play(self):
self.player.play()

def start_video(self, video_file: str):
self.video_file = video_file
self.playlist.clear()
self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(video_file)))
self.playlist.setCurrentIndex(0)
Expand All @@ -60,20 +68,46 @@ def start_video(self, video_file: str):

def set_rate(self):
if self.fps:
# the signal that calls this will occasionally be called during playing the video but we simply assume
# the signal that calls this will occasionally be called during playing the video, but we simply assume
# that fps is constant
return
if "VideoFrameRate" not in self.player.availableMetaData():
self._obtain_frame_rate_using_vlc()
else:
self.fps = self.player.metaData("VideoFrameRate")
self.duration = self.player.metaData("Duration")

if self.fps is None or self.duration is None:
warnings.warn("Video duration or fps unknown.")
return
self.fps = self.player.metaData("VideoFrameRate")
self.player.duration()

StateKeeper.video_duration_available.emit(self.player.metaData("Duration") / 1000, self.fps)
# get video duration in ms
StateKeeper.video_duration_available.emit(self.duration / 1000, self.fps)

# Not sure yet why this is, but we need the following commands to make sure switching to sync mode directly
# after loading the video works
self.player.play()
self.player.pause()

def _obtain_frame_rate_using_vlc(self):
try:
import vlc # pylint: disable=import-outside-toplevel
except ModuleNotFoundError:
self.user_informed_about_error = True
UserInformation.inform(
"Cannot obtain the framerate of the video, which is necessary for synchronizing. "
"Possibly this can be fixed by installing VLC Media Player.\n"
"Click the Learn More link below to get to the website.",
help_link="https://www.videolan.org/vlc/index.de.html",
)
return
player_vlc = vlc.MediaPlayer()
player_vlc.set_mrl(self.video_file)
vlc.libvlc_media_parse(player_vlc.get_media())
self.fps = player_vlc.get_fps()
self.duration = self.player.duration()
del player_vlc

def set_sync(self, start_frame: float, end_frame: float):
self.sync_info = pd.Series(data=[start_frame, end_frame], index=[["start", "end"]])

Expand Down Expand Up @@ -108,10 +142,8 @@ def set_video_position(self):
self.player.setPosition(self.slider.value())

def frame_changed(self):
if (
self.player.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia
or self.player.metaData("Duration") is None
):
if self.player.mediaStatus() == QMediaPlayer.MediaStatus.LoadedMedia or self.duration is None:
warnings.warn("Video is not playing or duration unknown.")
return
if not self.player.state() == QMediaPlayer.PausedState:
self.set_slider_position()
Expand All @@ -121,7 +153,7 @@ def frame_changed(self):
# else:
if self.sync_info is None:
start = 0
end = self.player.metaData("Duration")
end = self.duration
else:
start = self.sync_info["start"] / self.fps * 1000
end = self.sync_info["end"] / self.fps * 1000
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pyqtgraph = "0.11.0"
PySide2 = "5.15.1"
typing-extensions = "^3.10.0"
sphinx-qt-documentation = "^0.3"
python-vlc = "^3.0.16120"

[tool.poetry.dev-dependencies]
openpyxl = "^3.0.6"
Expand Down

0 comments on commit 7feee45

Please sign in to comment.