forked from openlp/openlp
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:
commit
690062b56b
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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'
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user