The .mp4 file that gets created by the VideoWriter gets an error when I try to play it that says "This file isn't playable. That might be because the file type is unsupported, the file extension is incorrect, or the file is corrupt"
I tried moving my VideoWriter setup to my other file just to see and I had the same result with that. Im rather new to OpenCV and Numpy so Im not really sure how I should go about this.
This is the code in my main.py file:
import cv2 as cv
import numpy as np
from time import time
import datetime
from windowcapture import WindowCapture
wincap = WindowCapture('Geometry Dash')
time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
fourcc = cv.VideoWriter_fourcc('m', 'p', '4', 'v')
file_name = f'{time_stamp}.mp4'
captured_video = cv.VideoWriter(file_name, fourcc, 60.0, (800, 600))
loop_time = time()
while True:
screenshot = wincap.get_screenshot()
# prints the video fps
print('FPS {:.2f}'.format(1 / (time() - loop_time)))
loop_time = time()
cv.imshow('Screen Capture', screenshot)
captured_video.write(screenshot)
# press 'q' with output window focused to close window
# waits 1ms every loop to process if q is pressed
if cv.waitKey(1) == ord('q'):
cv.destroyAllWindows()
break
print('q pressed // window closing')
This is the code in my windowcapture.py file:
import numpy as np
import win32gui, win32ui, win32con
class WindowCapture:
# properties
w = 0
h = 0
hwnd = None
cropped_x = 0
cropped_y = 0
offset_x = 0
offset_y = 0
# constructor
def __init__(self, window_name):
self.hwnd = win32gui.FindWindow(None, window_name)
if not self.hwnd:
raise Exception('Window not found "{}"'.format(window_name))
# get window size
window_rect = win32gui.GetWindowRect(self.hwnd)
self.w = window_rect[2] - window_rect[0]
self.h = window_rect[3] - window_rect[1]
# account for window border and titlebar and cut them off
border_pixels = 8
titlebar_pixels = 30
self.w = self.w - (border_pixels * 2)
self.h = self.h - titlebar_pixels - border_pixels
self.cropped_x = border_pixels
self.cropped_y = titlebar_pixels
# set the cropped coordinates offset so we can translate screenshot
# images into actual screen positions
self.offset_x = window_rect[0] + self.cropped_x
self.offset_y = window_rect[1] + self.cropped_y
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# saves the screenshot
#dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.frombuffer(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# Free Resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
# make image C_CONTIGUOUS to avoid errors like:
# File ... in draw_rectangles
# TypeError: an integer is required (got type tuple)
# github discussion:
# https://github.com/opencv/opencv/issues/14866#issuecomment-580207109
img = np.array(img)
return img
# find the name of the window you're interested in.
# once you have it, update window_capture()
# https://stackoverflow.com/questions/55547940/how-to-get-a-list-of-the-name-of-every-open-window
def list_window_names(self):
def winEnumHandler(hwnd, ctx):
if win32gui.IsWindowVisible(hwnd):
print(hex(hwnd), win32gui.GetWindowText(hwnd))
win32gui.EnumWindows(winEnumHandler, None)
#list_window_names()
# translate a pixel position on a screenshot image to a pixel position on the screen.
# pos = (x,y)
# WARNING: if you move the window being captured after execution is started, this will
# return incorrect coordinates, because the window position is only calculated in
# the __init__ constructor.
def get_screen_position(self, pos):
return (pos[0] + self.offset_x, pos[1] + self.offset_y)
'''
I have no problem viewing my video when I have my game open it plays perfectly fine in the window that pops up from opencv but when I try to save this video to mp4 the video does not work.
This may be more of a temporary answer to my question because this doesn't really feel like the "correct" way of solving this but this is what I did.
In my main.py I changed
time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
fourcc = cv.VideoWriter_fourcc('m', 'p', '4', 'v')
file_name = f'{time_stamp}.mp4'
captured_video = cv.VideoWriter(file_name, fourcc, 60.0, (800, 600))
Ive changed this code to
time_stamp = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
fourcc = cv.VideoWriter_fourcc(*'mp4v')
file_name = f'{time_stamp}.mp4'
captured_video = cv.VideoWriter(file_name, fourcc, 60.0, (1600,900))
and then in my windowcapture.py file I changed
img = np.array(img)
return img
Ive now changed that to
img_np = np.array(img)
img_bgr = cv.cvtColor(img_np, cv.COLOR_RGB2BGR)
img_rgb = cv.cvtColor(img_bgr, cv.COLOR_BGR2RGB)
return img_rgb
The rest of the code I've left alone and I now get my mp4 files to play. The first change that I noticed from the testing I did was I needed to set my VideoWriter to the correct screensize as I had it in 800, 600 when the window was 1600, 900. The second change was the color conversions If I only convert to BGR the code works but obviously I don't want it in BGR I want it in RGB so I had to convert back, but if I don't do at least one of these conversions the mp4 will not play.