이번 포스트는 Python을 기반으로 여러 가지 Video를 플레이하는 프로그램을 만들어보도록 하겠습니다.
그리고 이를 Raspberry Pi에서 플레이해보도록 할 것입니다.
우선, 개발하고자 하는 프로그램에 대해 간략히 설명해 보도록 하겠습니다.
프로그램을 실행시키면 0번 영상과 0번 사운드가 플레이됩니다. 그리고 키보드 I 또는 O를 클릭하면 다음 영상 또는 이전 영상이 플레이됩니다. 이때 맨 처음 영상일 때는 O를 눌러도 이전 영상이 아닌 처음 영상이 플레이되어야 하며, 마지막 영상의 경우는 I를 누르면 처음 영상으로 넘어가야 합니다. 또한 사운드의 경우는 0번 영상부터 2번 영상까지는 0번 사운드가, 3번 영상부터 14번 영상까지는 1번 사운드가 이후 영상부터 마지막 영상까지는 2번 사운드가 플레이되어야 합니다. 이러한 방식으로 무산 루프를 돌며 플레이되다가 End 버튼을 누르면 프로그램이 종료됩니다.
핵심은 영상과 사운드의 전환과 제어 기능을 부여하여 개발해야 한다는 것입니다.
그럼 이제 본격적으로 개발을 진행해보겠습니다!
Video Player를 개발하는 방법은 여러 가지가 있을 것입니다. 뭐... OpenCV를 이용해서 개발하는 방법도 있을 것이고 MFC를 통해 개발해도 좋고요. 그러나 저는 Python환경에서 PyQt와 VLC를 이용해 진행해 볼 것입니다.
당연한 이야기지만, Python과 pyqt5, VLC가 설치되어 있어야 합니다. PyQt5와 VLC의 경우 다음과 같은 명령어를 통해 설치할 수 있습니다.
>> pip3 install PyQt5
>> pip install python-vlc
우선, 플레이할 영상과 사운드 데이터는 VideoList와 SoundList Directory에 저장해 놓습니다. 순서대로 a.mp4, b.mp4, ..., 0.wav, 1.wav, 2.wav 이런 식으로 말이죠.
메인 코드는 다음과 같습니다.
def main():
app = QtWidgets.QApplication(sys.argv)
player = VideoPlayer()
player.showFullScreen()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
단순합니다. QT Widget을 생성하고 해당 위젯에서 구동될 VideoPlayer라는 클래스를 Load 한 후 사용하다가 Class에서 종료신호를 주면 Widget을 해제합니다. 참고로 showFullScreen()을 통해 위젯을 전체화면으로 띄웁니다.
다음은 Video Player의 전역변수와 init 부분입니다.
class VideoPlayer(QtWidgets.QMainWindow):
clip_start_sig = False;
video_list = []
video_index = 0
sound_list = []
sound_index = -1
button_pushed_sig = False
button_sound_player = None
def __init__(self, master=None):
QtWidgets.QMainWindow.__init__(self, master)
# Create a basic vlc instance
self.instance = vlc.Instance()
self.media = None
self.instance_sound = vlc.Instance()
self.media_sound = None
# Create an empty vlc media player
self.mediaplayer = self.instance.media_player_new()
self.sound_player = self.instance_sound.media_player_new()
self.create_ui()
self.is_paused = False
Video와 Sound 리스트를 관리하고 플레이 상황을 관리하기 위한 Index 데이터, Button Event가 발생되었는지 판단하기 위한 변수들이 전역변수로 위치합니다. 그리고 Video와 Sound 플레이를 위한 각각의 instance를 생성하고 초기화해 주는 작업을 수행합니다.
위 코드 중에서 UI를 구성해 주는 함수가 있는데, 해당 부분은 다음과 같습니다.
def create_ui(self):
self.widget = QtWidgets.QWidget(self)
self.setCentralWidget(self.widget)
# In this widget, the video will be drawn
if platform.system() == "Darwin": # for MacOS
self.videoframe = QtWidgets.QMacCocoaViewContainer(0)
else: # the others
self.videoframe = QtWidgets.QFrame()
self.palette = self.videoframe.palette()
self.palette.setColor(QtGui.QPalette.Window, Qt.black)
self.videoframe.setPalette(self.palette)
self.videoframe.setAutoFillBackground(True)
self.vboxlayout = QtWidgets.QVBoxLayout()
self.vboxlayout.addWidget(self.videoframe)
self.widget.setLayout(self.vboxlayout)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(100)
self.timer.timeout.connect(self.update_ui)
# Main Program
self.play_video_list()
self.play_video()
def update_ui(self):
# No need to call this function if nothing is played
if not self.mediaplayer.is_playing():
self.timer.stop()
def play_video_list(self):
self.video_list = glob.glob("VideoList/*")
self.video_list = sorted(self.video_list)
print(self.video_list)
self.sound_list = glob.glob("SoundList/*")
self.sound_list = sorted(self.sound_list)
print(self.sound_list)
if not self.is_paused:
self.stop()
위젯을 중앙에 생성하고 영상을 Draw 할 공간을 만들어줍니다. 빈 공간은 자동을 채워주도록 하고 Update를 수행할 때의 Interval 값을 설정해 줍니다. 이때의 Update는 각각의 영상 플레이가 완료되었을 때의 처리를 위한 것입니다.
전반적으로 아래와 같이 영상이 나오게 됩니다.
그리고 플레이할 영상과 사운드 데이터의 파일명을 Read 하고 순서대로 정렬해서 저장합니다. 이는 재생 시 순서를 올바르게 해 주기 위한 작업입니다.
다음은 영상의 전환을 위한 Keyboard Event을 구현합니다.
def keyPressEvent(self, e):
# exit program
if e.key() == Qt.Key_Escape:
self.close()
# next video
if e.key() == Qt.Key_I:
# Button Sound
self.button_sound_player = vlc.MediaPlayer("ButtonSound/button.wav")
self.button_sound_player.play()
self.button_pushed_sig = True
if self.video_index == 0 :
self.clip_start_sig = True
self.stop()
# previous video
elif e.key() == Qt.Key_O:
# Button Sound
self.button_sound_player = vlc.MediaPlayer("ButtonSound/button.wav")
self.button_sound_player.play()
self.button_pushed_sig = True
if self.clip_start_sig == True :
self.video_index -= 2
if self.video_index == -2 :
self.video_index = len(self.video_list) - 2
self.stop()
Keyboard Event가 발생되면 해당 이벤트에 맞게 영상이 플레이되도록 합니다. 기본적으로는 영상과 사운드의 Index를 적절한 값으로 변화시켜 주는 작업을 수행합니다.
위와 같이 버튼이 눌렸을 때 효과음을 주기 위해 단기적으로 MediaPlayer를 구성해 플레이해 줄 수도 있을 것입니다.
다음은 영상과 사운드 데이터를 순서대로, 또는 키보드 이벤트에 맞게 플레이하기 위한 부분입니다.
def play_video(self):
self.media = self.instance.media_new(self.video_list[self.video_index])
self.mediaplayer.set_media(self.media)
self.media.parse()
if platform.system() == "Linux": # for Linux using the X Server
self.mediaplayer.set_xwindow(int(self.videoframe.winId()))
elif platform.system() == "Windows": # for Windows
self.mediaplayer.set_hwnd(int(self.videoframe.winId()))
elif platform.system() == "Darwin": # for MacOS
self.mediaplayer.set_nsobject(int(self.videoframe.winId()))
# Sound - each clip
if self.video_index < 3 :
if self.sound_index != 0:
self.sound_index = 0
self.media_sound = self.instance_sound.media_new(self.sound_list[self.sound_index])
self.sound_player.set_media(self.media_sound)
self.media.parse()
self.sound_player.stop()
self.sound_player.play()
elif self.video_index >= 3 and self.video_index < 15:
if self.sound_index != 1 :
self.sound_index = 1
self.media_sound = self.instance_sound.media_new(self.sound_list[self.sound_index])
self.sound_player.set_media(self.media_sound)
self.media.parse()
self.sound_player.stop()
self.sound_player.play()
elif self.video_index >= 14 :
if self.sound_index != 2 :
self.sound_index = 2
self.media_sound = self.instance_sound.media_new(self.sound_list[self.sound_index])
self.sound_player.set_media(self.media_sound)
self.media.parse()
self.sound_player.stop()
self.sound_player.play()
if not self.sound_player.is_playing():
self.sound_player.stop()
self.sound_player.play()
self.mediaplayer.play()
self.timer.start()
self.is_paused = False
def stop(self):
if self.button_pushed_sig == True or self.video_index == 18 :
self.button_pushed_sig = False
if self.clip_start_sig == True:
# start to next video
self.video_index += 1
if self.video_index >= len(self.video_list):
self.video_index = 0
self.clip_start_sig = False
if self.video_index == 0 :
self.clip_start_sig = False
self.play_video()
솔직히 영상의 경우는 그냥 순서대로 플레이해 주면 됩니다. 그러나 사운드의 경우는 영상의 번호에 종속되어 있으므로 특정 인덱스에 따라 처리해 줄 필요가 있습니다.
뭐... 해당 부분은 구현하고자 하는 필요에 따라 적절히 변경되어야 할 부분일 것입니다.ㅎㅎ
이와 같이 정말 간단하게 구현할 수 있었습니다.
길게 늘여 써서 그렇지 코드를 정리한다면 100줄 정도로 구현할 수 있지 않을까요?
뭐... 위 방식은 자원을 최적화한 코드가 아니기에 코드 정리가 필연적으로 필요하긴 합니다.
참고로 Raspberry Pi에 LCD를 연결해서 플레이시킨다면 꽤 쓸만한 슬라이드 쇼나 전자 액자 또는 홍보 디바이스로 활용해 볼 수 있을 것 같습니다.
따라서 다음 포스트에서는 Raspberry Pi에서 이 프로그램을 구동시켜 활용하는 방법을 다뤄보도록 하겠습니다.
이번 포스트는 여기서 마무리하도록 하겠습니다.
PS. Raspberry Pi에서 Video Player를 적용하는 부분에 대한 내용은 다음 포스트를 참고해주세요.
- Raspberry Pi 초기 Setting
- Raspberry Pi + Button(Shutdown, Reboot, Keyboard Event)
- Raspberry Pi Mouse Curser 제어
'Programming > Raspberry Pi' 카테고리의 다른 글
[Mouse] Raspberry Pi에서 마우스 커서 제어 (0) | 2023.03.23 |
---|---|
[GPIO] Raspberry Pi에 스위치/버튼 연결 후 Shutdown/Reboot, Keyboard Event 수행 (0) | 2023.03.23 |
[Environment] Raspberry Pi 초기 Setting (0) | 2023.03.23 |
[E-Paper] Raspberry Pi에서 E-Paper Display 연동 (1) | 2023.01.05 |
[TensorFlow Lite] TensorFlow를 Raspberry Pi에서 사용 (0) | 2023.01.05 |
댓글