Merge branch 'fix-cd-media-crash' into 'master'

Fix cd media crash

Closes #838

See merge request openlp/openlp!338
This commit is contained in:
Tim Bentley 2021-08-28 07:04:16 +00:00
commit 690062b56b
4 changed files with 57 additions and 44 deletions

View File

@ -260,8 +260,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
# if this is an optical device use special handling
if service_item.is_capable(ItemCapabilities.IsOptical):
self.log_debug('video is optical and live')
path = service_item.get_frame_path()
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path)
path_string = path_to_str(service_item.get_frame_path())
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path_string)
is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display,
controller)
elif service_item.is_capable(ItemCapabilities.CanStream):
@ -281,8 +281,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
elif controller.preview_display:
if service_item.is_capable(ItemCapabilities.IsOptical):
self.log_debug('video is optical and preview')
path = service_item.get_frame_path()
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path)
path_string = path_to_str(service_item.get_frame_path())
(name, title, audio_track, subtitle_track, start, end, clip_name) = parse_optical_path(path_string)
is_valid = self.media_setup_optical(name, title, audio_track, subtitle_track, start, end, display,
controller)
elif service_item.is_capable(ItemCapabilities.CanStream):
@ -482,7 +482,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
start_again = False
stopped = False
if controller.media_info.is_playing and controller.media_info.length > 0:
if controller.media_info.timer + TICK_TIME >= controller.media_info.length:
if controller.media_info.timer - controller.media_info.start_time + TICK_TIME >= \
controller.media_info.length:
if controller.media_info.is_looping_playback:
start_again = True
else:
@ -494,12 +495,12 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
stopped = True
if start_again:
controller.media_info.timer = 0
controller.media_info.timer = controller.media_info.start_time
self._update_seek_ui(controller)
return not stopped
def _update_seek_ui(self, controller):
seconds = controller.media_info.timer // 1000
seconds = (controller.media_info.timer - controller.media_info.start_time) // 1000
minutes = seconds // 60
seconds %= 60
total_seconds = controller.media_info.length // 1000
@ -592,7 +593,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.set_hide_mode(display.hide_mode or HideMode.Blank)
else:
self._media_set_visibility(controller, False)
controller.seek_slider.setSliderPosition(0)
controller.seek_slider.setSliderPosition(controller.media_info.start_time)
total_seconds = controller.media_info.length // 1000
total_minutes = total_seconds // 60
total_seconds %= 60
@ -601,7 +602,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False)
controller.media_info.is_playing = False
controller.media_info.timer = 0
controller.media_info.timer = controller.media_info.start_time
controller.output_has_changed()
return True
return False

View File

@ -48,12 +48,12 @@ class MediaPlayer(RegistryProperties):
"""
return False
def setup(self, controller, display, live_display):
def setup(self, controller, display):
"""
Create the related widgets for the current display
:param controller: Which Controller is running the show.
:param display: The display to be updated.
:param live_display: Is the display a live one.
"""
pass
@ -66,11 +66,11 @@ class MediaPlayer(RegistryProperties):
"""
return True
def resize(self, display):
def resize(self, controller):
"""
If the main display size or position is changed, the media widgets
should also resized
:param display: The display to be updated.
:param controller: Which Controller is running the show.
"""
pass
@ -83,53 +83,53 @@ class MediaPlayer(RegistryProperties):
"""
pass
def pause(self, display):
def pause(self, controller):
"""
Pause of current Media File
:param display: The display to be updated.
:param controller: Which Controller is running the show.
"""
pass
def stop(self, display):
def stop(self, controller):
"""
Stop playing of current Media File
:param display: The display to be updated.
:param controller: Which Controller is running the show.
"""
pass
def volume(self, display, volume):
def volume(self, controller, volume):
"""
Change volume of current Media File
:param display: The display to be updated.
:param controller: Which Controller is running the show.
:param volume: The volume to set.
"""
pass
def seek(self, display, seek_value):
def seek(self, controller, seek_value):
"""
Change playing position of current Media File
:param display: The display to be updated.
:param controller: Which Controller is running the show.
:param seek_value: The where to seek to.
"""
pass
def reset(self, display):
def reset(self, controller):
"""
Remove the current loaded video
:param display: The display to be updated.
:param controller: Which Controller is running the show.
"""
pass
def set_visible(self, display, status):
def set_visible(self, controller, status):
"""
Show/Hide the media widgets
:param display: The display to be updated.
:param controller: Which Controller is running the show.
:param status: The status to be set.
"""
pass

View File

@ -101,7 +101,8 @@ class VlcPlayer(MediaPlayer):
"""
Set up the media player
:param controller: The display where the media is.
:param controller: The controller where the media is
:param display: The display where the media is.
:return:
"""
vlc = get_vlc()
@ -182,12 +183,15 @@ class VlcPlayer(MediaPlayer):
controller.vlc_media_player.set_media(controller.vlc_media)
controller.vlc_media_player.play()
# Wait for media to start playing. In this case VLC actually returns an error.
self.media_state_wait(output_display, vlc.State.Playing)
self.media_state_wait(controller, vlc.State.Playing)
# If subitems exists, this is a CD
audio_cd_tracks = controller.vlc_media.subitems()
if not audio_cd_tracks or audio_cd_tracks.count() < 1:
return False
controller.vlc_media_player = audio_cd_tracks.item_at_index(controller.media_info.title_track)
controller.vlc_media = audio_cd_tracks.item_at_index(int(controller.media_info.title_track))
# VLC's start and stop time options work on seconds
controller.vlc_media.add_option(f"start-time={int(controller.media_info.start_time // 1000)}")
controller.vlc_media.add_option(f"stop-time={int(controller.media_info.end_time // 1000)}")
elif controller.media_info.media_type == MediaType.Stream:
controller.vlc_media = controller.vlc_instance.media_new_location(file[0])
controller.vlc_media.add_options(file[1])
@ -241,7 +245,7 @@ class VlcPlayer(MediaPlayer):
:return:
"""
vlc = get_vlc()
start_time = 0
start_time = controller.media_info.start_time
log.debug('vlc play')
if controller.is_live:
if self.get_live_state() != MediaState.Paused and controller.media_info.timer > 0:
@ -284,7 +288,8 @@ class VlcPlayer(MediaPlayer):
self.volume(controller, controller.media_info.volume)
if start_time > 0 and controller.vlc_media_player.is_seekable():
controller.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(controller.media_info.length)
controller.seek_slider.setMaximum(controller.media_info.start_time + controller.media_info.length)
controller.seek_slider.setMinimum(controller.media_info.start_time)
self.set_state(MediaState.Playing, controller)
return True
@ -362,8 +367,7 @@ class VlcPlayer(MediaPlayer):
"""
if not controller.seek_slider.isSliderDown():
controller.seek_slider.blockSignals(True)
if controller.media_info.media_type == MediaType.CD \
or controller.media_info.media_type == MediaType.DVD:
if controller.media_info.media_type == MediaType.DVD:
controller.seek_slider.setSliderPosition(
controller.vlc_media_player.get_time() - int(controller.media_info.start_time))
else:

View File

@ -26,7 +26,7 @@ import sys
import pytest
from datetime import timedelta
from unittest import skipIf
from unittest.mock import MagicMock, call, patch
from unittest.mock import MagicMock, call, patch, ANY
from openlp.core.common import is_macosx
from openlp.core.common.registry import Registry
@ -367,7 +367,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
# WHEN: An audio CD is loaded into VLC
with patch.object(vlc_player, 'volume') as mocked_volume, \
patch.object(vlc_player, 'media_state_wait'):
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
result = vlc_player.load(mocked_controller, mocked_display, media_path)
# THEN: The video should be loaded
@ -377,6 +377,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
mocked_vlc_media.parse.assert_called_with()
mocked_volume.assert_called_with(mocked_controller, 100)
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
assert result is True
@ -408,11 +409,12 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
mocked_subitems.count.return_value = 1
mocked_subitems.item_at_index.return_value = mocked_vlc_media
mocked_vlc_media.subitems.return_value = mocked_subitems
vlc_player = VlcPlayer(None)
# WHEN: An audio CD is loaded into VLC
with patch.object(vlc_player, 'volume') as mocked_volume, \
patch.object(vlc_player, 'media_state_wait'):
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
result = vlc_player.load(mocked_controller, mocked_display, media_path)
# THEN: The video should be loaded
@ -422,6 +424,7 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
mocked_vlc_media.parse.assert_called_with()
mocked_volume.assert_called_with(mocked_controller, 100)
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
assert result is True
@ -482,15 +485,15 @@ def test_media_state_wait(mocked_get_vlc):
mocked_vlc = MagicMock()
mocked_vlc.State.Error = 1
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.vlc_media.get_state.return_value = 2
mocked_controller = MagicMock()
mocked_controller.vlc_media.get_state.return_value = 2
Registry.create()
mocked_application = MagicMock()
Registry().register('application', mocked_application)
vlc_player = VlcPlayer(None)
# WHEN: media_state_wait() is called
result = vlc_player.media_state_wait(mocked_display, 2)
result = vlc_player.media_state_wait(mocked_controller, 2)
# THEN: The results should be True
assert result is True
@ -506,15 +509,15 @@ def test_media_state_wait_error(mocked_get_vlc, vlc_env):
mocked_vlc = MagicMock()
mocked_vlc.State.Error = 1
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.vlc_media.get_state.return_value = 1
mocked_controller = MagicMock()
mocked_controller.vlc_media.get_state.return_value = 1
Registry.create()
mocked_application = MagicMock()
Registry().register('application', mocked_application)
vlc_player = VlcPlayer(None)
# WHEN: media_state_wait() is called
result = vlc_player.media_state_wait(mocked_display, 2)
result = vlc_player.media_state_wait(mocked_controller, 2)
# THEN: The results should be True
assert result is False
@ -532,15 +535,15 @@ def test_media_state_wait_times_out(mocked_get_vlc, vlc_env):
mocked_vlc = MagicMock()
mocked_vlc.State.Error = 1
mocked_get_vlc.return_value = mocked_vlc
mocked_display = MagicMock()
mocked_display.vlc_media.get_state.return_value = 2
mocked_controller = MagicMock()
mocked_controller.vlc_media.get_state.return_value = 2
Registry.create()
mocked_application = MagicMock()
Registry().register('application', mocked_application)
vlc_player = VlcPlayer(None)
# WHEN: media_state_wait() is called
result = vlc_player.media_state_wait(mocked_display, 3)
result = vlc_player.media_state_wait(mocked_controller, 3)
# THEN: The results should be True
assert result is False
@ -582,6 +585,8 @@ def test_play(mocked_get_vlc, mocked_threading):
mocked_controller.media_info.start_time = 0
mocked_controller.media_info.media_type = MediaType.Video
mocked_controller.media_info.volume = 100
mocked_controller.media_info.start_time = 20000
mocked_controller.media_info.length = 10000
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
vlc_player = VlcPlayer(None)
vlc_player.set_state(MediaState.Paused, mocked_controller)
@ -595,6 +600,9 @@ def test_play(mocked_get_vlc, mocked_threading):
# THEN: A bunch of things should happen to play the media
mocked_thread.start.assert_called_with()
mocked_volume.assert_called_with(mocked_controller, 100)
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
assert MediaState.Playing == vlc_player.get_live_state()
assert result is True, 'The value returned from play() should be True'