Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Playing a video in tkinter
#1
Hi, I've been trying to simply play a video in python using the python GUI. So far, it has been working as intended and the video has been playing fine with no interuptions. However, there seem to be some issues:

- No sound (no module is installed)
- Tiny window showing the video
- The video is sped up
- If I try to exit the program while the video is playing, it crashes
*Yes, I do know the reason why the sound isnt playing but I have tried my best to install different modules in order to gain sound and none have worked.
Below is the code:

I hope that I manage to fix these problems I am experiencing with your sincere help and advice. I thank you for your time, patience and kindness! Heart Smash
import tkinter as tk
from tkinter import Label
from PIL import Image, ImageTk
import imageio
import threading

class VideoPlayer:
    def __init__(self, root, video_path):
        self.root = root
        self.video_path = video_path
        self.video_label = Label(root)
        self.video_label.pack()
        self.video = imageio.get_reader(r"C:\Users\RPC-K\Downloads\windows 95 maze screensaver 022 speedrun  [WORLD RECORD].mp4")
        self.play_video = True
        self.thread = threading.Thread(target=self.update, args=())
        self.thread.start()

    def update(self):
        for frame in self.video.iter_data():
            if not self.play_video:
                break
            image = Image.fromarray(frame)
            image = ImageTk.PhotoImage(image)
            self.video_label.config(image=image)
            self.video_label.image = image
            self.root.update()
        self.video.close()

    def stop(self):
        self.play_video = False
        self.thread.join()

def on_close():
    player.stop()
    root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    root.title("Video Player")
    video_path = 'path_to_your_video.mp4'
    player = VideoPlayer(root, video_path)
    root.protocol("WM_DELETE_WINDOW", on_close)
    root.mainloop()
Gribouillis write Jul-23-2024, 04:44 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
JUST A FRIENDLY REMINDER, I AM INFACT A YOUNG CHILD SO PLEASE FORGIVE ME IF I LACK OF ANY KNOWLEDGE THAT MAY SEEM OBVIOUS...I AM STILL DEVELOPING MY SKILLS IN PYTHON - YOURS SINCERELY, Moon
😋
Reply
#2
Show what you tried.
Reply
#3
(Jul-23-2024, 04:30 PM)Axel_Erfurt Wrote: Show what you tried.

Hi. Apologies, but I've now edited my OP which includes the code too.

Thanks.
JUST A FRIENDLY REMINDER, I AM INFACT A YOUNG CHILD SO PLEASE FORGIVE ME IF I LACK OF ANY KNOWLEDGE THAT MAY SEEM OBVIOUS...I AM STILL DEVELOPING MY SKILLS IN PYTHON - YOURS SINCERELY, Moon
😋
Reply
#4
Here is a basic example using TkVideoPlayer
There seems to be some issue with the stop at times

from tkVideoPlayer import TkinterVideo
import tkinter as tk
from pathlib import Path

path = Path(__file__).parent


root = tk.Tk()
root.geometry('800x600+200+200')

file = f'{path}/some.mp4'

player = TkinterVideo(root, scaled=True)
player.pack(expand=True, fill='both')
player.load(file)

player.play()

play = tk.Button(root, text='Play', command=player.play)
play.pack(side='left', padx=4, pady=8)

pause = tk.Button(root, text='Pause', command=player.pause)
pause.pack(side='left', padx=4, pady=8)

stop = tk.Button(root, text='Close', command=player.stop)
stop.pack(side='left', padx=4, pady=8)
root.mainloop()
I welcome all feedback.
The only dumb question, is one that doesn't get asked.
My Github
How to post code using bbtags
Download my project scripts


Reply
#5
You would need something to regulate the rate at which frames are read and displayed. In this example from the imageio documentation this is done by pyav.

https://imageio.readthedocs.io/en/stable...in-a-video

As for the image size, that appears to be set by the resolution of the video. I just recorded a video using the camera on my laptop. When I play it back with your program the image size matches the frame size of the video. At 720p the image fills about half the screen. At 240p it is much smaller. The smaller image also plays back at a faster rate because there is nothing to regulate how often a new image is drawn.
Reply
#6
Hiya! Soo while I'm still trying to grasp the concept of what your explanation of this is issue is, I kinda still don't really get it. Im sorry, it's not about the way you've explained it, it's just im still quite young, i guess, so im really confused lol. Anyway, thankyou for this information you've provided and I'll trymy best to see what I can make out of it! See ya! Big Grin Snowman
JUST A FRIENDLY REMINDER, I AM INFACT A YOUNG CHILD SO PLEASE FORGIVE ME IF I LACK OF ANY KNOWLEDGE THAT MAY SEEM OBVIOUS...I AM STILL DEVELOPING MY SKILLS IN PYTHON - YOURS SINCERELY, Moon
😋
Reply
#7
A video has a resolution. A video recorded at 720p resolution is has frames that are 1280 x 720 pixels in size (921,600 pixels). When I play a 720p video using your program, that is the size of the video that I see. Since my screen resolution is 1920 x 1080 pixels (2,073,600 pixels), the image takes up 45% of the screen.

When I set the video to record at 240p resolution it has frames that are 320 x 240 pixels in size (76,800 pixels). This results in a smaller window. In fact, it should make a window that is only 320 x 240 pixels in size (about 4% of my laptop screen).

I modified your code to increase the video size, resizing each image.
    def update(self):
        for frame in self.video.iter_data():
            if not self.play_video:
                break
            image = Image.fromarray(frame).resize((640, 520))  # <- 2x the default size of 320 x 260
            image = ImageTk.PhotoImage(image)
            self.video_label.config(image=image)
            self.video_label.image = image
            self.root.update()
        self.video.close()
You could do something similar to change the of your video images.

In addition to resolution, a video has a frame rate. The only settings I have available for recording video on my laptop is 30 fps. When I play back the 720p video the frame rate looks about right. Your program must be taking about 1/30 of a second to read the frame, convert to an image, and set the image in the label. When I play the 260p video it runs much too fast. It takes less time to read and process the smaller frames, so the program plays more frames per second.

What you need is something that throttles your program so it only reads and displays 30 frames per second (or whatever the frame rate should be for your video). The tkVideoPlayer that menator mentions will do this. If you want to stick with using imageio, you need to either write code to do this, or import a tool that will do it for you (like the pyav package I mentioned in my previous post). Writing code to do this yourself might be fun. Here's a simple and not very good attempt.
import tkinter as tk
from PIL import Image, ImageTk
import imageio


class VideoPlayer:
    def __init__(self, root, video_path):
        self.root = root
        self.video_label = tk.Label(root)
        self.video_label.pack()
        self.video = imageio.get_reader(video_path)
        self.play_video = True
        self.frame_iter = self.video.iter_data()
        self.update()

    def update(self):
        if self.play_video:
            self.root.after(33, self.update)  # call update again after 1/30 of a second.
            try:
                frame = next(self.frame_iter)
                image = Image.fromarray(frame)
                image = ImageTk.PhotoImage(image)
                self.video_label.config(image=image)
                self.video_label.image = image
                self.root.update()
            except StopIteration:
                self.play_video = False
        else:
            self.video.close()
            root.destroy()

    def stop(self):
        self.play_video = False


if __name__ == "__main__":
    root = tk.Tk()
    root.title("Video Player")
    video_path = r"...\video.mp4"
    player = VideoPlayer(root, video_path)
    root.protocol("WM_DELETE_WINDOW", player.stop)
    root.mainloop()
Reply
#8
Hi! Thankyou so so much for you heavily deatailed response, I really appreciate it. The one thing thats preventing me from trying your methods is the fact that even though I have managed in the past, I am really struggling and cannot for whatever reason, install tkvideoplayer. I do all the necessary steps and input "pip install tkvideoplayer" into command prompt and press enter. Unlike intalling other modules however, when it comes to installing tkvideoplayer, many errors are shown in the command prompt and I can't seem to resolve them. It would be of a great help if someone could please find the solution to this. Just to clarify, when displaying the python output when running the program on the tkvideoplay module, it does come up with another error saying that the module is not found. Here is what is shown in command prompt when I try to install tkvideoplayer:
Error:
Defaulting to user installation because normal site-packages is not writeable Collecting tkvideoplayer Using cached tkvideoplayer-2.8-py3-none-any.whl (6.2 kB) Collecting av==9.2.0 (from tkvideoplayer) Using cached av-9.2.0.tar.gz (2.4 MB) Installing build dependencies ... done Getting requirements to build wheel ... error error: subprocess-exited-with-error × Getting requirements to build wheel did not run successfully. │ exit code: 1 ╰─> [74 lines of output] Compiling av\buffer.pyx because it changed. [1/1] Cythonizing av\buffer.pyx Compiling av\bytesource.pyx because it changed. [1/1] Cythonizing av\bytesource.pyx Compiling av\descriptor.pyx because it changed. [1/1] Cythonizing av\descriptor.pyx Compiling av\dictionary.pyx because it changed. [1/1] Cythonizing av\dictionary.pyx Compiling av\enum.pyx because it changed. [1/1] Cythonizing av\enum.pyx Compiling av\error.pyx because it changed. [1/1] Cythonizing av\error.pyx Compiling av\format.pyx because it changed. [1/1] Cythonizing av\format.pyx Compiling av\frame.pyx because it changed. [1/1] Cythonizing av\frame.pyx performance hint: av\logging.pyx:232:5: Exception check on 'log_callback' will always require the GIL to be acquired. Possible solutions: 1. Declare 'log_callback' as 'noexcept' if you control the definition and you're sure you don't want the function to raise exceptions. 2. Use an 'int' return type on 'log_callback' to allow an error code to be returned. Error compiling Cython file: ------------------------------------------------------------ ... cdef const char *log_context_name(void *ptr) nogil: cdef log_context *obj = <log_context*>ptr return obj.name cdef lib.AVClass log_class log_class.item_name = log_context_name ^ ------------------------------------------------------------ av\logging.pyx:216:22: Cannot assign type 'const char *(void *) except? NULL nogil' to 'const char *(*)(void *) noexcept nogil'. Exception values are incompatible. Suggest adding 'noexcept' to the type of 'log_context_name'. Error compiling Cython file: ------------------------------------------------------------ ... # Start the magic! # We allow the user to fully disable the logging system as it will not play # nicely with subinterpreters due to FFmpeg-created threads. if os.environ.get('PYAV_LOGGING') != 'off': lib.av_log_set_callback(log_callback) ^ ------------------------------------------------------------ av\logging.pyx:351:28: Cannot assign type 'void (void *, int, const char *, va_list) except * nogil' to 'av_log_callback' (alias of 'void (*)(void *, int, const char *, va_list) noexcept nogil'). Exception values are incompatible. Suggest adding 'noexcept' to the type of 'log_callback'. Compiling av\logging.pyx because it changed. [1/1] Cythonizing av\logging.pyx Traceback (most recent call last): File "C:\Program Files\Python311\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 353, in <module> main() File "C:\Program Files\Python311\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 335, in main json_out['return_val'] = hook(**hook_input['kwargs']) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Program Files\Python311\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 118, in get_requires_for_build_wheel return hook(config_settings) ^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\setuptools\build_meta.py", line 327, in get_requires_for_build_wheel return self._get_build_requires(config_settings, requirements=[]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\setuptools\build_meta.py", line 297, in _get_build_requires self.run_setup() File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\setuptools\build_meta.py", line 497, in run_setup super().run_setup(setup_script=setup_script) File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\setuptools\build_meta.py", line 313, in run_setup exec(code, locals()) File "<string>", line 157, in <module> File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\Cython\Build\Dependencies.py", line 1154, in cythonize cythonize_one(*args) File "C:\Users\RPC-K\AppData\Local\Temp\pip-build-env-ge1hvfdu\overlay\Lib\site-packages\Cython\Build\Dependencies.py", line 1321, in cythonize_one raise CompileError(None, pyx_file) Cython.Compiler.Errors.CompileError: av\logging.pyx [end of output] note: This error originates from a subprocess, and is likely not a problem with pip. error: subprocess-exited-with-error × Getting requirements to build wheel did not run successfully. │ exit code: 1 ╰─> See above for output. note: This error originates from a subprocess, and is likely not a problem with pip. [notice] A new release of pip is available: 23.1.2 -> 24.1.2 [notice] To update, run: python.exe -m pip install --upgrade pip
JUST A FRIENDLY REMINDER, I AM INFACT A YOUNG CHILD SO PLEASE FORGIVE ME IF I LACK OF ANY KNOWLEDGE THAT MAY SEEM OBVIOUS...I AM STILL DEVELOPING MY SKILLS IN PYTHON - YOURS SINCERELY, Moon
😋
Reply
#9
Hellooo everyonee! Great News! I have managed to play the video using your amazing methods! I didn't even need to use the tkvideoplayer. I can successfully play sound in the backround however the two aren't synced. I've heard of threading which links the two elements together however that hasn't been very helpful so far...Any advice would be more than appriciated! Type
JUST A FRIENDLY REMINDER, I AM INFACT A YOUNG CHILD SO PLEASE FORGIVE ME IF I LACK OF ANY KNOWLEDGE THAT MAY SEEM OBVIOUS...I AM STILL DEVELOPING MY SKILLS IN PYTHON - YOURS SINCERELY, Moon
😋
Reply
#10
Post your new code.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How would I be able to detect a media player app playing a video. phpjunkie 2 1,522 Oct-16-2023, 02:09 PM
Last Post: phpjunkie
  Shapes over video in tkinter finndude 1 2,000 Oct-04-2022, 06:14 PM
Last Post: deanhystad
  Tkinter Drawing/Annotating on live video KDog 4 4,419 Jan-24-2022, 03:18 PM
Last Post: Minimaxer
  run code while playing video almulder 0 2,619 Aug-26-2018, 07:19 PM
Last Post: almulder
  Python + OpenCV + Tkinter playing video help Hanban 1 20,564 May-08-2017, 01:25 AM
Last Post: Joseph_f2

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020