From d81d59a36f55809aeefd94341c5fd4f732f123c5 Mon Sep 17 00:00:00 2001 From: Waseem AL Tamer <107763849+WaseemALTamer@users.noreply.github.com> Date: Tue, 3 Sep 2024 07:40:02 -0700 Subject: [PATCH 1/5] Add files via upload --- .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 262 bytes .../__pycache__/tkvideoplayer.cpython-312.pyc | Bin 0 -> 13136 bytes tkVideoPlayer/tkvideoplayer.py | 189 ++++++++---------- 3 files changed, 88 insertions(+), 101 deletions(-) create mode 100644 tkVideoPlayer/__pycache__/__init__.cpython-312.pyc create mode 100644 tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc diff --git a/tkVideoPlayer/__pycache__/__init__.cpython-312.pyc b/tkVideoPlayer/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d0fb591c651baef334a953e123ff862828c950a GIT binary patch literal 262 zcmX@j%ge<81RC$JrI`Zh#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r8OGnvA!2 zLb5aSN>Yo$GE-9X{WO_wNta}U*#S9;m8nH~CD~;lnF27ch#9D9CBtWs-d{=1RxzQ) zsYS&x<%z|qsWFZP1ulsti7`I;$%#2J0Y&-gMTxn^F#(k&8Tolo%Gl5-#wRl=rZ}@C zRkt89IXf{uwKxXFM*aBs%)HE!_;|g7%3mBdx%nxjIjMF<96&1>fw&mN_`uA_$as&z Jq=*g30RS{nOicg) literal 0 HcmV?d00001 diff --git a/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc b/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dd2338a4cb8c9d51ac80fb3911010cce98f5255 GIT binary patch literal 13136 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 literal 0 HcmV?d00001 diff --git a/tkVideoPlayer/tkvideoplayer.py b/tkVideoPlayer/tkvideoplayer.py index f2c8a9f..b8f8c7c 100644 --- a/tkVideoPlayer/tkvideoplayer.py +++ b/tkVideoPlayer/tkvideoplayer.py @@ -1,4 +1,3 @@ -import gc import av import time import threading @@ -39,7 +38,6 @@ def __init__(self, master, scaled: bool = True, consistant_frame_rate: bool = Tr "duration": 0, # duration of the video "framerate": 0, # frame rate of the video "framesize": (0, 0) # tuple containing frame height and width of the video - } self.set_scaled(scaled) @@ -104,126 +102,101 @@ def _load(self, path): current_thread = threading.current_thread() - try: - with av.open(path) as self._container: + with av.open(path) as self._container: - self._container.streams.video[0].thread_type = "AUTO" - - self._container.fast_seek = True - self._container.discard_corrupt = True + self._container.streams.video[0].thread_type = "AUTO" + + self._container.fast_seek = True + self._container.discard_corrupt = True - stream = self._container.streams.video[0] + stream = self._container.streams.video[0] - try: - self._video_info["framerate"] = int(stream.average_rate) + try: + self._video_info["framerate"] = int(stream.average_rate / 2) # For some reason the frame rate was double for each video i tried to play + # this will ensure that it will play at the frame rate the video is intended + # Edited by https://github.com/WaseemALTamer or waseem8630 for Discord reach + # out for clarfcation you want to - except TypeError: - raise TypeError("Not a video file") - - try: + except TypeError: + raise TypeError("Not a video file") + + try: - self._video_info["duration"] = float(stream.duration * stream.time_base) - self.event_generate("<>") # duration has been found + self._video_info["duration"] = float(stream.duration * stream.time_base) + self.event_generate("<>") # duration has been found - except (TypeError, tk.TclError): # the video duration cannot be found, this can happen for mkv files - pass + except (TypeError, tk.TclError): # the video duration cannot be found, this can happen for mkv files + pass - self._frame_number = 0 + self._frame_number = 0 - self._set_frame_size() + self._set_frame_size() - self.stream_base = stream.time_base + self.stream_base = stream.time_base - try: - self.event_generate("<>") # generated when the video file is opened - - except tk.TclError: - pass + try: + self.event_generate("<>") # generated when the video file is opened + + except tk.TclError: + pass + + now = time.time_ns() // 1_000_000 # time in milliseconds + then = now + + time_in_frame = (1/self._video_info["framerate"])*1000 # second it should play each frame + + + while self._load_thread == current_thread and not self._stop: + if self._seek: # seek to specific second + self._container.seek(self._seek_sec*1000000 , whence='time', backward=True, any_frame=False) # the seek time is given in av.time_base, the multiplication is to correct the frame + self._seek = False + self._frame_number = self._video_info["framerate"] * self._seek_sec + + self._seek_sec = 0 + + if self._paused: + time.sleep(0.0001) # to allow other threads to function better when its paused + continue now = time.time_ns() // 1_000_000 # time in milliseconds + delta = now - then # time difference between current frame and previous frame then = now + + # print("Frame: ", frame.time, frame.index, self._video_info["framerate"]) + try: + frame = next(self._container.decode(video=0)) - time_in_frame = (1/self._video_info["framerate"])*1000 # second it should play each frame + self._time_stamp = float(frame.pts * stream.time_base) + self._current_img = frame.to_image() - while self._load_thread == current_thread and not self._stop: - if self._seek: # seek to specific second - self._container.seek(self._seek_sec*1000000 , whence='time', backward=True, any_frame=False) # the seek time is given in av.time_base, the multiplication is to correct the frame - self._seek = False - self._frame_number = self._video_info["framerate"] * self._seek_sec + self._frame_number += 1 + + self.event_generate("<>") - self._seek_sec = 0 + if self._frame_number % self._video_info["framerate"] == 0: + self.event_generate("<>") - if self._paused: - time.sleep(0.0001) # to allow other threads to function better when its paused - continue + if self.consistant_frame_rate: + time.sleep(max((time_in_frame - delta)/1000, 0)) - now = time.time_ns() // 1_000_000 # time in milliseconds - delta = now - then # time difference between current frame and previous frame - then = now - - # print("Frame: ", frame.time, frame.index, self._video_info["framerate"]) - try: - frame = next(self._container.decode(video=0)) - - self._time_stamp = float(frame.pts * stream.time_base) - - width = self._current_frame_size[0] - height = self._current_frame_size[1] - if self._keep_aspect_ratio: - im_ratio = frame.width / frame.height - dest_ratio = width / height - if im_ratio != dest_ratio: - if im_ratio > dest_ratio: - new_height = round(frame.height / frame.width * width) - height = new_height - else: - new_width = round(frame.width / frame.height * height) - width = new_width - - self._current_img = frame.to_image(width=width, height=height, interpolation="FAST_BILINEAR") - - self._frame_number += 1 - - self.event_generate("<>") - - if self._frame_number % self._video_info["framerate"] == 0: - self.event_generate("<>") - - if self.consistant_frame_rate: - time.sleep(max((time_in_frame - delta)/1000, 0)) - - # time.sleep(abs((1 / self._video_info["framerate"]) - (delta / 1000))) - - except (StopIteration, av.error.EOFError, tk.TclError): - break - - self._container.close() - - # print("Container: ", self._container.c) - if self._container: - self._container.close() - self._container = None - - finally: - self._cleanup() - gc.collect() + # time.sleep(abs((1 / self._video_info["framerate"]) - (delta / 1000))) + + except (StopIteration, av.error.EOFError, tk.TclError): + break - def _cleanup(self): self._frame_number = 0 self._paused = True - self._stop = True - if self._load_thread: - self._load_thread = None - if self._container: - self._container.close() - self._container = None + self._load_thread = None + + self._container = None + try: - self.event_generate("<>") + self.event_generate("<>") # this is generated when the video ends + except tk.TclError: pass - def load(self, path: str): """ loads the file from the given path """ self.stop() @@ -233,7 +206,6 @@ def stop(self): """ stops reading the file """ self._paused = True self._stop = True - self._cleanup() def pause(self): """ pauses the video file """ @@ -279,10 +251,25 @@ def current_img(self) -> Image: def _display_frame(self, event): """ displays the frame on the label """ - if self.current_imgtk.width() == self._current_img.width and self.current_imgtk.height() == self._current_img.height: - self.current_imgtk.paste(self._current_img) - else: - self.current_imgtk = ImageTk.PhotoImage(self._current_img) + if self.scaled or (len(self._current_frame_size) == 2 and all(self._current_frame_size)): + + if self._keep_aspect_ratio: + self._current_img = ImageOps.contain(self._current_img, self._current_frame_size, self._resampling_method) + + else: + self._current_img = self._current_img.resize(self._current_frame_size, self._resampling_method) + + else: + self._current_frame_size = self.video_info()["framesize"] if all(self.video_info()["framesize"]) else (1, 1) + + if self._keep_aspect_ratio: + self._current_img = ImageOps.contain(self._current_img, self._current_frame_size, self._resampling_method) + + else: + self._current_img = self._current_img.resize(self._current_frame_size, self._resampling_method) + + + self.current_imgtk = ImageTk.PhotoImage(self._current_img) self.config(image=self.current_imgtk) def seek(self, sec: int): From cb26d669ab76385a35bc45257c904f3cbd66a322 Mon Sep 17 00:00:00 2001 From: Waseem AL Tamer <107763849+WaseemALTamer@users.noreply.github.com> Date: Tue, 3 Sep 2024 13:37:32 -0700 Subject: [PATCH 2/5] Add files via upload --- .../__pycache__/tkvideoplayer.cpython-312.pyc | Bin 13136 -> 13980 bytes tkVideoPlayer/tkvideoplayer.py | 29 +++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc b/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc index 1dd2338a4cb8c9d51ac80fb3911010cce98f5255..f813084067d037230b0eb83cad9002d219aeb220 100644 GIT binary patch delta 4177 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 Ke8wS8aqa(1DgI#q diff --git a/tkVideoPlayer/tkvideoplayer.py b/tkVideoPlayer/tkvideoplayer.py index b8f8c7c..638b458 100644 --- a/tkVideoPlayer/tkvideoplayer.py +++ b/tkVideoPlayer/tkvideoplayer.py @@ -17,6 +17,8 @@ def __init__(self, master, scaled: bool = True, consistant_frame_rate: bool = Tr self.path = "" self._load_thread = None + self.Frame_Rate_Scaler = 1 # this var is to contral how slow or fast the frame rate is for speeding or slowing down video + self._paused = True self._stop = True @@ -112,14 +114,11 @@ def _load(self, path): stream = self._container.streams.video[0] try: - self._video_info["framerate"] = int(stream.average_rate / 2) # For some reason the frame rate was double for each video i tried to play - # this will ensure that it will play at the frame rate the video is intended - # Edited by https://github.com/WaseemALTamer or waseem8630 for Discord reach - # out for clarfcation you want to + self._video_info["framerate"] = int(stream.average_rate * self.Frame_Rate_Scaler) # this file has been edited the the var can speed or slow down the video except TypeError: raise TypeError("Not a video file") - + try: self._video_info["duration"] = float(stream.duration * stream.time_base) @@ -148,8 +147,24 @@ def _load(self, path): while self._load_thread == current_thread and not self._stop: if self._seek: # seek to specific second - self._container.seek(self._seek_sec*1000000 , whence='time', backward=True, any_frame=False) # the seek time is given in av.time_base, the multiplication is to correct the frame - self._seek = False + self._container.seek(self._seek_sec*1000000) # the seek time is given in av.time_base, the multiplication is to correct the frame + frame = next(self._container.decode(video=0)) # grab the next frame + CurrentFrame = float(frame.pts * stream.time_base) # calclate the time of the frame + + if CurrentFrame >= self._seek_sec and not self._stop: # check if the frame time is before + self._container.seek((self._seek_sec - 5)*1000000) # if it is then seek the before 5 sec + + self._seek = False # we set the seek to false so it dose not correct where we are + # as we are seeking it from a seek bar this is mostly for gui + + while CurrentFrame <= self._seek_sec and not self._seek and not self._stop: # we can loop through the frames until we get to the desired frame + frame = next(self._container.decode(video=0)) # keep getting the next frame + CurrentFrame = float(frame.pts * stream.time_base) # update the current frame time so we know where we are at so far + + + + + self._frame_number = self._video_info["framerate"] * self._seek_sec self._seek_sec = 0 From 70feb10e23b463c4b97303483449b2d5683fc039 Mon Sep 17 00:00:00 2001 From: Waseem AL Tamer <107763849+WaseemALTamer@users.noreply.github.com> Date: Wed, 4 Sep 2024 11:34:58 +0100 Subject: [PATCH 3/5] Delete tkVideoPlayer/__pycache__ directory --- .../__pycache__/__init__.cpython-312.pyc | Bin 262 -> 0 bytes .../__pycache__/tkvideoplayer.cpython-312.pyc | Bin 13980 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tkVideoPlayer/__pycache__/__init__.cpython-312.pyc delete mode 100644 tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc diff --git a/tkVideoPlayer/__pycache__/__init__.cpython-312.pyc b/tkVideoPlayer/__pycache__/__init__.cpython-312.pyc deleted file mode 100644 index 2d0fb591c651baef334a953e123ff862828c950a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 262 zcmX@j%ge<81RC$JrI`Zh#~=<2FhLog1%Qm{3@HpLj5!Rsj8Tk?43$ip%r8OGnvA!2 zLb5aSN>Yo$GE-9X{WO_wNta}U*#S9;m8nH~CD~;lnF27ch#9D9CBtWs-d{=1RxzQ) zsYS&x<%z|qsWFZP1ulsti7`I;$%#2J0Y&-gMTxn^F#(k&8Tolo%Gl5-#wRl=rZ}@C zRkt89IXf{uwKxXFM*aBs%)HE!_;|g7%3mBdx%nxjIjMF<96&1>fw&mN_`uA_$as&z Jq=*g30RS{nOicg) diff --git a/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc b/tkVideoPlayer/__pycache__/tkvideoplayer.cpython-312.pyc deleted file mode 100644 index f813084067d037230b0eb83cad9002d219aeb220..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13980 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 From d9f7dce6fee1e3bd23c2eaa42edb40275ee84ea5 Mon Sep 17 00:00:00 2001 From: Waseem AL Tamer <107763849+WaseemALTamer@users.noreply.github.com> Date: Wed, 4 Sep 2024 03:56:29 -0700 Subject: [PATCH 4/5] Add files via upload --- tkVideoPlayer/tkvideoplayer.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tkVideoPlayer/tkvideoplayer.py b/tkVideoPlayer/tkvideoplayer.py index 638b458..d2c189f 100644 --- a/tkVideoPlayer/tkvideoplayer.py +++ b/tkVideoPlayer/tkvideoplayer.py @@ -146,20 +146,26 @@ def _load(self, path): while self._load_thread == current_thread and not self._stop: + if self._seek: # seek to specific second - self._container.seek(self._seek_sec*1000000) # the seek time is given in av.time_base, the multiplication is to correct the frame - frame = next(self._container.decode(video=0)) # grab the next frame + # the seek time is given in av.time_base, the multiplication is to correct the frame + self._container.seek(self._seek_sec*1000000) + frame = next(self._container.decode(video=0)) # grab the next frame CurrentFrame = float(frame.pts * stream.time_base) # calclate the time of the frame + if CurrentFrame >= self._seek_sec and not self._stop: # check if the frame time is before self._container.seek((self._seek_sec - 5)*1000000) # if it is then seek the before 5 sec self._seek = False # we set the seek to false so it dose not correct where we are # as we are seeking it from a seek bar this is mostly for gui - while CurrentFrame <= self._seek_sec and not self._seek and not self._stop: # we can loop through the frames until we get to the desired frame + # we can loop through the frames until we get to the desired frame + while CurrentFrame <= self._seek_sec and not self._seek and not self._stop: + frame = next(self._container.decode(video=0)) # keep getting the next frame CurrentFrame = float(frame.pts * stream.time_base) # update the current frame time so we know where we are at so far + From 3fa6e0e3c4802ea3524b0c4b1c3c1f699fcc66f0 Mon Sep 17 00:00:00 2001 From: Waseem AL Tamer <107763849+WaseemALTamer@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:05:15 -0700 Subject: [PATCH 5/5] Add files via upload fixed the time between each frame as it was using sleep.time() which gave me trouble as the videos i was playing was not in the correct frame rate i used a simple module to correct the frame time which takes count of the time code take to run. i also put the module into the project it is a simple module i wrote --- tkVideoPlayer/TickSystem.py | 58 ++++++++++++++++++++++++++++++++++ tkVideoPlayer/__init__.py | 1 + tkVideoPlayer/tkvideoplayer.py | 48 +++++++++++++++++++++------- 3 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 tkVideoPlayer/TickSystem.py diff --git a/tkVideoPlayer/TickSystem.py b/tkVideoPlayer/TickSystem.py new file mode 100644 index 0000000..cddb49d --- /dev/null +++ b/tkVideoPlayer/TickSystem.py @@ -0,0 +1,58 @@ +import time + + + +# this is a small Module that allowes us to run our any while loop +# exactly set amount of times we want it without worring about other +# lines of the code effecting how many times we run it like in the +# the only limmiting factor becomes if your cpu can handle all the +# proccessing in the while loop that you put in it + +class FpsController: # this is a tick system that will help you block the + # the main loop up until a certain time + + def __init__(self, DesiredFps:int = 60): # we take the DesierdFps + self.fpsCount:int = 0 # this is for us to keep track of fps + # if we want to view it at real time + + self.Tick:float = 1/DesiredFps # intialise the time for the tick + # we are aiming for + + + #private Variables + self.FpsTimer:float = time.time() + 1 # we create a timer if we want + # this will help us view the + # images our selfs + + self.TickTimer:float = time.time() # this help us keep track of how + # much time has passed by adding + # the tick we made + + + def BlockUntilNextFrame(self): + while True: # this blockes the functions when you run it through a while loop + + if self.Tick <= (time.time() - self.TickTimer): # this will check if more + # time has passed than our + # tick if that is the case + # add the tick to the + # ticktimer and unblock the + # function we blocked by + # running this function + self.TickTimer += self.Tick + return + + time.sleep(1/1000) # sleep for one milliseconds + # we do this so we dont over + # stress the cpu by the while + # loop + + + def ShowFps(self): # this function will simply show the fps + self.fpsCount += 1 # this will keep count of the fps + if time.time() >= self.FpsTimer: # check if it hase been + # 1 sec + print(self.fpsCount) # print out the fps + + self.fpsCount = 0 # reset the fps + self.FpsTimer = time.time() + 1 # reset the timer \ No newline at end of file diff --git a/tkVideoPlayer/__init__.py b/tkVideoPlayer/__init__.py index 55c72e8..6b7eb2f 100644 --- a/tkVideoPlayer/__init__.py +++ b/tkVideoPlayer/__init__.py @@ -1 +1,2 @@ from tkVideoPlayer.tkvideoplayer import TkinterVideo +from tkVideoPlayer.TickSystem import FpsController diff --git a/tkVideoPlayer/tkvideoplayer.py b/tkVideoPlayer/tkvideoplayer.py index d2c189f..ff692fb 100644 --- a/tkVideoPlayer/tkvideoplayer.py +++ b/tkVideoPlayer/tkvideoplayer.py @@ -5,6 +5,10 @@ import tkinter as tk from PIL import ImageTk, Image, ImageOps from typing import Tuple, Dict +from TickSystem import FpsController # this library is going to be used to controll the fps + # this takes count of the time that the other lines is + # going to take + logging.getLogger('libav').setLevel(logging.ERROR) # removes warning: deprecated pixel format used @@ -15,14 +19,17 @@ def __init__(self, master, scaled: bool = True, consistant_frame_rate: bool = Tr super(TkinterVideo, self).__init__(master, *args, **kwargs) self.path = "" - self._load_thread = None - - self.Frame_Rate_Scaler = 1 # this var is to contral how slow or fast the frame rate is for speeding or slowing down video - + self._load_thread:threading.Thread = None self._paused = True self._stop = True - self.consistant_frame_rate = consistant_frame_rate # tries to keep the frame rate consistant by skipping over a few frames + self.consistant_frame_rate:bool = consistant_frame_rate # tries to keep the frame rate consistant by skipping over a few frames + + self.Frame_Rate_Controller:FpsController = None # this will be used to block the thread until the next frame it utlise a + # tick system so the time it takes to run your code is accounted for + # refare to the file TickSystem.py for further explnation + + self.Frame_Rate_Scaler:float = 1.0 # this var is to contral how slow or fast the frame rate is for speeding or slowing down video self._container = None @@ -115,6 +122,7 @@ def _load(self, path): try: self._video_info["framerate"] = int(stream.average_rate * self.Frame_Rate_Scaler) # this file has been edited the the var can speed or slow down the video + self.Frame_Rate_Controller = FpsController(DesiredFps=(stream.average_rate * self.Frame_Rate_Scaler)) except TypeError: raise TypeError("Not a video file") @@ -145,9 +153,12 @@ def _load(self, path): time_in_frame = (1/self._video_info["framerate"])*1000 # second it should play each frame + + + while self._load_thread == current_thread and not self._stop: - if self._seek: # seek to specific second + if self._seek and not self._stop: # seek to specific second # the seek time is given in av.time_base, the multiplication is to correct the frame self._container.seek(self._seek_sec*1000000) frame = next(self._container.decode(video=0)) # grab the next frame @@ -155,7 +166,10 @@ def _load(self, path): if CurrentFrame >= self._seek_sec and not self._stop: # check if the frame time is before - self._container.seek((self._seek_sec - 5)*1000000) # if it is then seek the before 5 sec + try: # we have to try because this is running on a thread + self._container.seek((self._seek_sec - 5)*1000000) # if it is then seek the before 5 sec + except: + pass self._seek = False # we set the seek to false so it dose not correct where we are # as we are seeking it from a seek bar this is mostly for gui @@ -163,9 +177,11 @@ def _load(self, path): # we can loop through the frames until we get to the desired frame while CurrentFrame <= self._seek_sec and not self._seek and not self._stop: - frame = next(self._container.decode(video=0)) # keep getting the next frame - CurrentFrame = float(frame.pts * stream.time_base) # update the current frame time so we know where we are at so far - + try: + frame = next(self._container.decode(video=0)) # keep getting the next frame + CurrentFrame = float(frame.pts * stream.time_base) # update the current frame time so we know where we are at so far + except: + pass @@ -177,6 +193,10 @@ def _load(self, path): if self._paused: time.sleep(0.0001) # to allow other threads to function better when its paused + + self.Frame_Rate_Controller.TickTimer = time.time() # reset the TickTimer this will help + # tell our code that we are not behind on + # fps continue now = time.time_ns() // 1_000_000 # time in milliseconds @@ -199,7 +219,13 @@ def _load(self, path): self.event_generate("<>") if self.consistant_frame_rate: - time.sleep(max((time_in_frame - delta)/1000, 0)) + #time.sleep(max((time_in_frame - delta)/1000, 0)) + self.Frame_Rate_Controller.BlockUntilNextFrame() # this is much better methode than the time.sleep() + # this utlise the simple mousle that will achieve + # the Desired Frame rate through keeping track of a tick + # system i still left the code for the previous methode + # so you can use that if you dont care about playing the + # video faster or slower # time.sleep(abs((1 / self._video_info["framerate"]) - (delta / 1000)))