Python Forum

Full Version: Playing a video in tkinter
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
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()
Show what you tried.
(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.
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()
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.
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
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()
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
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
Post your new code.
Pages: 1 2