mirror of
https://github.com/cyberboy666/r_e_c_u_r.git
synced 2025-12-16 13:20:13 +01:00
@@ -1,6 +1,8 @@
|
|||||||
# r_e_c_u_r
|
# r_e_c_u_r
|
||||||
an open diy py/pi based video sampler
|
an open diy py/pi based video sampler
|
||||||
|
|
||||||
|
![vectorfront][vectorfront]
|
||||||
|
|
||||||
__r_e_c_u_r__ is an embedded python application on _raspberry pi3_ that uses `input` from the _keypad_ to control omxplayer's `video out` while `displaying` a simple text user interface on the _rpi lcd screen_
|
__r_e_c_u_r__ is an embedded python application on _raspberry pi3_ that uses `input` from the _keypad_ to control omxplayer's `video out` while `displaying` a simple text user interface on the _rpi lcd screen_
|
||||||
|
|
||||||
## features
|
## features
|
||||||
@@ -34,6 +36,7 @@ i started a [board] of features i would like to explore
|
|||||||
|
|
||||||
langolierz@gmail.com
|
langolierz@gmail.com
|
||||||
|
|
||||||
|
[vectorfront]: ./documentation/vectorfront.png
|
||||||
[board]: https://trello.com/b/mmJJFyrp/feature-ideas
|
[board]: https://trello.com/b/mmJJFyrp/feature-ideas
|
||||||
[operating]: documentation/operate_docs.md
|
[operating]: documentation/operate_docs.md
|
||||||
[building]: documentation/build_docs.md
|
[building]: documentation/build_docs.md
|
||||||
|
|||||||
@@ -79,17 +79,20 @@ class Actions(object):
|
|||||||
self.load_this_slot_into_next_player(9)
|
self.load_this_slot_into_next_player(9)
|
||||||
|
|
||||||
def trigger_next_player(self):
|
def trigger_next_player(self):
|
||||||
self.video_driver.manual_next = True
|
self.video_driver.switch_players_and_play_video()
|
||||||
|
|
||||||
def cycle_display_mode(self):
|
def cycle_display_mode(self):
|
||||||
self.display.top_menu_index = 0
|
self.display.top_menu_index = 0
|
||||||
self.display.selected_list_index = self.display.top_menu_index
|
self.display.selected_list_index = self.display.top_menu_index
|
||||||
if self.display.display_mode == "BROWSER":
|
if self.display.display_mode == "BROWSER":
|
||||||
self.display.display_mode = "SETTINGS"
|
self.display.display_mode = "SETTINGS"
|
||||||
|
self.display.control_mode = 'NAV_SETTINGS'
|
||||||
elif self.display.display_mode == "SAMPLER":
|
elif self.display.display_mode == "SAMPLER":
|
||||||
self.display.display_mode = "BROWSER"
|
self.display.display_mode = "BROWSER"
|
||||||
|
self.display.control_mode = 'NAV_BROWSER'
|
||||||
elif self.display.display_mode == "SETTINGS":
|
elif self.display.display_mode == "SETTINGS":
|
||||||
self.display.display_mode = "SAMPLER"
|
self.display.display_mode = "SAMPLER"
|
||||||
|
self.display.control_mode = 'PLAYER'
|
||||||
|
|
||||||
def toggle_pause_on_player(self):
|
def toggle_pause_on_player(self):
|
||||||
self.video_driver.current_player.toggle_pause()
|
self.video_driver.current_player.toggle_pause()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
[{"start": 607.690078, "end": 610.079466, "length": 1132.04, "name": "mushroom-dreams.mp4", "location": "/media/pi/TIM/videos_to_play/mushroom-dreams.mp4"}, {"start": -1, "end": -1, "length": 3.008, "name": "samplerloop3s3.mp4", "location": "/media/pi/TIM/samplerloop3s3.mp4"}, {"start": -1, "end": -1, "length": 804.245, "name": "long_spinning.mp4", "location": "/media/pi/TIM/videos_to_play/long_spinning.mp4"}, {"start": 66.892487, "end": 68.500718, "length": 87.04, "name": "colour_pixel_02.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_02.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}, {"end": -1, "start": -1, "length": 52.757, "name": "colour_pixel_03.mp4", "location": "/media/pi/TIM/videos_to_play/colour_pixel_03.mp4"}]
|
[{"name": "colour_pixel_01.mp4", "length": 49.28, "end": -1, "start": -1, "location": "/media/pi/TIM1/videos_to_play/colour_pixel_01.mp4"}, {"name": "colour_pixel_02.mp4", "start": -1, "location": "/media/pi/TIM1/videos_to_play/colour_pixel_02.mp4", "length": 87.04, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}, {"name": "", "length": -1, "location": "", "start": -1, "end": -1}]
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
{
|
{
|
||||||
"a": {
|
"a": {
|
||||||
"BROWSER": ["move_browser_selection_up"],
|
"NAV_BROWSER": ["move_browser_selection_up"],
|
||||||
"SAMPLER": ["seek_back_on_player"],
|
"PLAYER": ["seek_back_on_player"],
|
||||||
"SETTINGS": ["move_settings_selection_up"]
|
"NAV_SETTINGS": ["move_settings_selection_up"]
|
||||||
},
|
},
|
||||||
"b": {
|
"b": {
|
||||||
"BROWSER": ["move_browser_selection_down"],
|
"NAV_BROWSER": ["move_browser_selection_down"],
|
||||||
"SAMPLER": ["seek_forward_on_player"],
|
"PLAYER": ["seek_forward_on_player"],
|
||||||
"SETTINGS": ["move_settings_selection_down"]
|
"NAV_SETTINGS": ["move_settings_selection_down"]
|
||||||
},
|
},
|
||||||
"c": {
|
"c": {
|
||||||
"BROWSER": ["enter_on_browser_selection"],
|
"NAV_BROWSER": ["enter_on_browser_selection"],
|
||||||
"SAMPLER": ["toggle_pause_on_player"],
|
"PLAYER": ["toggle_pause_on_player"],
|
||||||
"SETTINGS": ["enter_on_settings_selection"]
|
"NAV_SETTINGS": ["enter_on_settings_selection"]
|
||||||
},
|
},
|
||||||
"d": {
|
"d": {
|
||||||
"DEFAULT": ["trigger_next_player"]
|
"DEFAULT": ["trigger_next_player"]
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"DEFAULT": ["load_slot_1_into_next_player"]
|
"DEFAULT": ["load_slot_1_into_next_player"]
|
||||||
},
|
},
|
||||||
"l": {
|
"l": {
|
||||||
"DEFAULT": ["load_slot_2_into_next_player"]
|
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"]
|
||||||
},
|
},
|
||||||
"m": {
|
"m": {
|
||||||
"DEFAULT": ["load_slot_3_into_next_player"]
|
"DEFAULT": ["load_slot_3_into_next_player"]
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
4
|
0
|
||||||
@@ -1 +1 @@
|
|||||||
[{"name": "SCREEN_SIZE", "options": ["dev_mode", "XGA", "composite"]}, {"name": "quit_the_program", "options": ["run_action"]}, {"name": "switch_display_to_hdmi", "options": ["run_action"]}, {"name": "switch_display_to_lcd", "options": ["run_action"]}]
|
[{"options": ["dev_mode", "XGA", "composite"], "name": "SCREEN_SIZE"}, {"options": ["run_action"], "name": "quit_the_program"}, {"options": ["run_action"], "name": "switch_display_to_hdmi"}, {"options": ["run_action"], "name": "switch_display_to_lcd"}]
|
||||||
@@ -17,6 +17,7 @@ class Display(object):
|
|||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
self.display_mode = "SAMPLER"
|
self.display_mode = "SAMPLER"
|
||||||
|
self.control_mode = 'PLAYER'
|
||||||
self.top_menu_index = 0
|
self.top_menu_index = 0
|
||||||
self.selected_list_index = self.top_menu_index
|
self.selected_list_index = self.top_menu_index
|
||||||
self.browser_list = self.data.rewrite_browser_list()
|
self.browser_list = self.data.rewrite_browser_list()
|
||||||
@@ -124,7 +125,7 @@ class Display(object):
|
|||||||
|
|
||||||
def _load_message(self):
|
def _load_message(self):
|
||||||
if self.message_handler.current_message[1]:
|
if self.message_handler.current_message[1]:
|
||||||
self.display_text.insert(END, '{:5}: {:38}'.format(
|
self.display_text.insert(END, '{:5} {:38}'.format(
|
||||||
self.message_handler.current_message[0], self.message_handler.current_message[1][0:38]))
|
self.message_handler.current_message[0], self.message_handler.current_message[1][0:38]))
|
||||||
self.display_text.tag_add('{}_MESSAGE'.format(
|
self.display_text.tag_add('{}_MESSAGE'.format(
|
||||||
self.message_handler.current_message[0]), 16.0,16.0 + self.SELECTOR_WIDTH)
|
self.message_handler.current_message[0]), 16.0,16.0 + self.SELECTOR_WIDTH)
|
||||||
@@ -135,6 +136,9 @@ class Display(object):
|
|||||||
elif self.message_handler.function_on:
|
elif self.message_handler.function_on:
|
||||||
self.display_text.insert(END, '{:^45}'.format('< FUNCTION KEY ON >'))
|
self.display_text.insert(END, '{:^45}'.format('< FUNCTION KEY ON >'))
|
||||||
self.display_text.tag_add('FUNCTION', 16.0,16.0 + self.SELECTOR_WIDTH)
|
self.display_text.tag_add('FUNCTION', 16.0,16.0 + self.SELECTOR_WIDTH)
|
||||||
|
else:
|
||||||
|
self.display_text.insert(END, '{:8} {:<10}'.format('CONTROL:', self.control_mode))
|
||||||
|
self.display_text.tag_add('TITLE', 16.0,16.0 + self.SELECTOR_WIDTH)
|
||||||
|
|
||||||
def _highlight_this_row(self, row):
|
def _highlight_this_row(self, row):
|
||||||
self.display_text.tag_add("SELECT", self.ROW_OFFSET + row,
|
self.display_text.tag_add("SELECT", self.ROW_OFFSET + row,
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class NumpadInput(object):
|
|||||||
|
|
||||||
def run_action_for_mapped_key(self, key):
|
def run_action_for_mapped_key(self, key):
|
||||||
this_mapping = self.key_mappings[key]
|
this_mapping = self.key_mappings[key]
|
||||||
if self.display.display_mode in this_mapping:
|
if self.display.control_mode in this_mapping:
|
||||||
mode = self.display.display_mode
|
mode = self.display.control_mode
|
||||||
elif 'DEFAULT' in this_mapping:
|
elif 'DEFAULT' in this_mapping:
|
||||||
mode = 'DEFAULT'
|
mode = 'DEFAULT'
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ class VideoDriver(object):
|
|||||||
self.data = data
|
self.data = data
|
||||||
self.delay = 50
|
self.delay = 50
|
||||||
self.has_omx = self.data.has_omx
|
self.has_omx = self.data.has_omx
|
||||||
|
self.in_first_load_cycle = False
|
||||||
|
self.in_current_playing_cycle = False
|
||||||
|
self.in_next_load_cycle = False
|
||||||
print(self.has_omx)
|
print(self.has_omx)
|
||||||
if self.has_omx:
|
if self.has_omx:
|
||||||
self.last_player = video_player(self.root, self.message_handler, self.data, 'a.a')
|
self.last_player = video_player(self.root, self.message_handler, self.data, 'a.a')
|
||||||
self.current_player = video_player(self.root,self.message_handler, self.data, 'b.b')
|
self.current_player = video_player(self.root,self.message_handler, self.data, 'b.b')
|
||||||
self.next_player = video_player(self.root, self.message_handler, self.data, 'c.c')
|
self.next_player = video_player(self.root, self.message_handler, self.data, 'c.c')
|
||||||
self.manual_next = False
|
|
||||||
self.print_status()
|
self.print_status()
|
||||||
self.root.after(self.delay, self.begin_playing)
|
self.root.after(self.delay, self.begin_playing)
|
||||||
else:
|
else:
|
||||||
@@ -28,16 +30,27 @@ class VideoDriver(object):
|
|||||||
|
|
||||||
def begin_playing(self):
|
def begin_playing(self):
|
||||||
# TODO: the first clip will be a demo
|
# TODO: the first clip will be a demo
|
||||||
self.current_player.load()
|
if self.current_player.try_load():
|
||||||
|
self.in_first_load_cycle = True
|
||||||
self.wait_for_first_load()
|
self.wait_for_first_load()
|
||||||
|
else:
|
||||||
|
print('load failed')
|
||||||
|
|
||||||
def wait_for_first_load(self):
|
def wait_for_first_load(self):
|
||||||
|
if self.in_first_load_cycle:
|
||||||
if self.current_player.is_loaded():
|
if self.current_player.is_loaded():
|
||||||
|
self.in_first_load_cycle = False
|
||||||
self.play_video()
|
self.play_video()
|
||||||
else:
|
else:
|
||||||
self.root.after(self.delay, self.wait_for_first_load)
|
self.root.after(self.delay, self.wait_for_first_load)
|
||||||
|
|
||||||
|
def switch_players_and_play_video(self):
|
||||||
|
self.in_first_load_cycle = False
|
||||||
|
self.in_current_playing_cycle = False
|
||||||
|
self.in_next_load_cycle = True
|
||||||
|
|
||||||
|
self.switch_if_next_is_loaded()
|
||||||
|
|
||||||
def switch_players(self):
|
def switch_players(self):
|
||||||
temp_player = self.last_player
|
temp_player = self.last_player
|
||||||
self.last_player = self.current_player
|
self.last_player = self.current_player
|
||||||
@@ -47,22 +60,30 @@ class VideoDriver(object):
|
|||||||
|
|
||||||
def play_video(self):
|
def play_video(self):
|
||||||
self.current_player.play()
|
self.current_player.play()
|
||||||
self.next_player.load()
|
self.next_player.try_load()
|
||||||
|
self.in_current_playing_cycle = True
|
||||||
self.wait_for_next_cycle()
|
self.wait_for_next_cycle()
|
||||||
|
|
||||||
def wait_for_next_cycle(self):
|
def wait_for_next_cycle(self):
|
||||||
if self.current_player.is_finished() or self.manual_next:
|
if self.in_current_playing_cycle:
|
||||||
self.manual_next = False
|
if self.current_player.is_finished():
|
||||||
self.wait_for_next_load()
|
self.in_current_playing_cycle = False
|
||||||
|
self.in_next_load_cycle = True
|
||||||
|
self.switch_if_next_is_loaded()
|
||||||
else:
|
else:
|
||||||
self.root.after(self.delay, self.wait_for_next_cycle)
|
self.root.after(self.delay, self.wait_for_next_cycle)
|
||||||
|
|
||||||
def wait_for_next_load(self):
|
def switch_if_next_is_loaded(self):
|
||||||
|
if self.in_next_load_cycle:
|
||||||
if self.next_player.is_loaded():
|
if self.next_player.is_loaded():
|
||||||
|
self.in_next_load_cycle = False
|
||||||
self.switch_players()
|
self.switch_players()
|
||||||
self.play_video()
|
self.play_video()
|
||||||
else:
|
else:
|
||||||
self.root.after(self.delay, self.wait_for_next_load)
|
if self.next_player.status != 'ERROR':
|
||||||
|
self.root.after(self.delay, self.switch_if_next_is_loaded)
|
||||||
|
else:
|
||||||
|
self.in_next_load_cycle = False
|
||||||
|
|
||||||
def get_info_for_player_display(self):
|
def get_info_for_player_display(self):
|
||||||
if self.has_omx:
|
if self.has_omx:
|
||||||
|
|||||||
@@ -19,6 +19,21 @@ class video_player:
|
|||||||
self.end = -1.0
|
self.end = -1.0
|
||||||
self.crop_length = 0.0
|
self.crop_length = 0.0
|
||||||
self.location = ''
|
self.location = ''
|
||||||
|
self.load_attempts = 0
|
||||||
|
|
||||||
|
def try_load(self):
|
||||||
|
load_attempts = 0
|
||||||
|
while(load_attempts < 4):
|
||||||
|
load_attempts = load_attempts + 1
|
||||||
|
if self.load():
|
||||||
|
print('load success')
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print('load failed')
|
||||||
|
self.message_handler.set_message('ERROR', 'failed to load')
|
||||||
|
self.status = 'ERROR'
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
try:
|
try:
|
||||||
@@ -38,9 +53,11 @@ class video_player:
|
|||||||
if self.start > 0.5:
|
if self.start > 0.5:
|
||||||
self.set_position(self.start - 0.5)
|
self.set_position(self.start - 0.5)
|
||||||
self.pause_at_start()
|
self.pause_at_start()
|
||||||
|
self.load_attempts = 0
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
self.message_handler.set_message('ERROR', 'first load error')
|
self.message_handler.set_message('ERROR', 'load attempt fail')
|
||||||
self.root.after(100, self.load)
|
return False
|
||||||
|
|
||||||
def pause_at_start(self):
|
def pause_at_start(self):
|
||||||
position = self.get_position()
|
position = self.get_position()
|
||||||
|
|||||||
Reference in New Issue
Block a user