Skip to content

Commit 0a6ebde

Browse files
Update main.py
Added support for changing monitor resolution during stream.
1 parent f977317 commit 0a6ebde

1 file changed

Lines changed: 164 additions & 91 deletions

File tree

main.py

Lines changed: 164 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,34 @@
1212
from fractions import Fraction
1313
import ctypes
1414
import time
15+
from win32api import GetSystemMetrics
1516
import dxcam
1617
import hashlib
1718
import getpass
19+
import win32api
20+
import win32con
21+
import pywintypes
22+
#import rotatescreen
23+
import signal
24+
import atexit
25+
26+
originalwidth = GetSystemMetrics(0)
27+
originalheight = GetSystemMetrics(1)
28+
29+
FrameRate = 60
30+
31+
devmode = pywintypes.DEVMODEType()
32+
33+
devmode.DisplayFrequency = 60
34+
#rotate_screen = rotatescreen.get_primary_display()
35+
#screen = rotate_screen.current_orientation
36+
#rotate_screen.rotate_to(0)
1837

1938
os.environ['DXGIDebug'] = '1'
2039
ROOT = os.path.dirname(__file__)
2140

2241
class POINT(ctypes.Structure):
23-
_fields_ = [('x', ctypes.c_int),
42+
_fields_ = [('x', ctypes.c_int),
2443
('y', ctypes.c_int)]
2544

2645
class CURSORINFO(ctypes.Structure):
@@ -32,76 +51,111 @@ class CURSORINFO(ctypes.Structure):
3251
GetCursorInfo = ctypes.windll.user32.GetCursorInfo
3352
GetCursorInfo.argtypes = [ctypes.POINTER(CURSORINFO)]
3453

35-
def force_codec(pc, sender, forced_codec):
36-
kind = forced_codec.split("/")[0]
37-
codecs = RTCRtpSender.getCapabilities(kind).codecs
38-
transceiver = next(t for t in pc.getTransceivers() if t.sender == sender)
39-
transceiver.setCodecPreferences(
40-
[codec for codec in codecs if codec.mimeType == forced_codec]
41-
)
54+
def force_codec(pc, sender, forced_codec):
55+
try:
56+
kind = forced_codec.split("/")[0]
57+
codecs = RTCRtpSender.getCapabilities(kind).codecs
58+
transceiver = next(t for t in pc.getTransceivers() if t.sender == sender)
59+
transceiver.setCodecPreferences(
60+
[codec for codec in codecs if codec.mimeType == forced_codec]
61+
)
62+
except Exception as e:
63+
logging.error(f"Error in force_codec: {e}")
64+
65+
def exit_handler():
66+
print("Program Exiting")
67+
#rotate_screen.rotate_to(screen)
4268

4369
def encrypt_string(input_string):
44-
sha256 = hashlib.sha256()
45-
sha256.update(input_string.encode('utf-8'))
46-
encrypted_string = sha256.hexdigest()
47-
return encrypted_string
70+
try:
71+
sha256 = hashlib.sha256()
72+
sha256.update(input_string.encode('utf-8'))
73+
encrypted_string = sha256.hexdigest()
74+
return encrypted_string
75+
except Exception as e:
76+
logging.error(f"Error in encrypt_string: {e}")
4877

4978
def save_to_file(encrypted_string, filename='password.txt'):
50-
with open(filename, 'w') as file:
51-
file.write(encrypted_string)
79+
try:
80+
with open(filename, 'w') as file:
81+
file.write(encrypted_string)
82+
except Exception as e:
83+
logging.error(f"Error in save_to_file: {e}")
5284

5385
def is_cursor_hidden():
54-
info = CURSORINFO()
55-
info.cbSize = ctypes.sizeof(info)
86+
try:
87+
info = CURSORINFO()
88+
info.cbSize = ctypes.sizeof(info)
5689

57-
if GetCursorInfo(ctypes.byref(info)):
58-
return not bool(info.flags & 0x00000001)
59-
else:
90+
if GetCursorInfo(ctypes.byref(info)):
91+
return not bool(info.flags & 0x00000001)
92+
else:
93+
return False
94+
except Exception as e:
95+
logging.error(f"Error in is_cursor_hidden: {e}")
6096
return False
6197

6298
def getsecurity():
63-
global passwordtext
64-
filename = 'password.txt'
65-
if os.path.exists(filename):
66-
with open(filename, 'r') as file:
67-
existing_content = file.read()
68-
passwordtext = existing_content
69-
print(f"{filename} already exists. The encrypted password is:\n{existing_content}")
70-
else:
71-
user_input = getpass.getpass("Enter your password: ")
72-
checkpass = getpass.getpass("Please re-enter your password: ")
73-
if user_input == checkpass:
74-
encrypted_string = encrypt_string(user_input)
75-
save_to_file(encrypted_string)
76-
passwordtext = encrypted_string
77-
print(f"Password encrypted and saved to '{filename}':\n{encrypted_string}")
78-
99+
try:
100+
global passwordtext
101+
filename = 'password.txt'
102+
if os.path.exists(filename):
103+
with open(filename, 'r') as file:
104+
existing_content = file.read()
105+
passwordtext = existing_content
106+
print(f"{filename} already exists. The encrypted password is:\n{existing_content}")
79107
else:
80-
print("The two passwords do not match. Please try again.")
81-
getsecurity()
108+
user_input = getpass.getpass("Enter your password: ")
109+
checkpass = getpass.getpass("Please re-enter your password: ")
110+
if user_input == checkpass:
111+
encrypted_string = encrypt_string(user_input)
112+
save_to_file(encrypted_string)
113+
passwordtext = encrypted_string
114+
print(f"Password encrypted and saved to '{filename}':\n{encrypted_string}")
115+
116+
else:
117+
print("The two passwords do not match. Please try again.")
118+
getsecurity()
119+
except Exception as e:
120+
logging.error(f"Error in getsecurity: {e}")
121+
82122
getsecurity()
83123

84124
class DxCamTrack(MediaStreamTrack):
85125
kind = "video"
86126

87127
camera = None
128+
original_width = GetSystemMetrics(0)
129+
original_height = GetSystemMetrics(1)
88130

89131
def __init__(self):
90132
super().__init__()
91133
self.start_time = time.time()
92-
self.frame_rate = 61
134+
self.frame_rate = FrameRate
93135
self.framedex = 90000
94136
self.previous_state = None
95-
137+
96138
if DxCamTrack.camera is None:
97-
DxCamTrack.camera = dxcam.create(output_idx=0, output_color="BGR")
98-
DxCamTrack.camera.start(target_fps=self.frame_rate, video_mode=True)
99-
print("Created a new DXCamera instance.")
139+
try:
140+
DxCamTrack.camera = dxcam.create(output_idx=0, output_color="BGR")#region=region1
141+
DxCamTrack.camera.start(target_fps=self.frame_rate, video_mode=True)
142+
print("Created a new DXCamera instance.")
143+
except Exception as e:
144+
logging.error(f"Error in DxCamTrack initialization: {e}")
100145

101146
async def recv(self):
102147
try:
148+
if DxCamTrack.original_width != GetSystemMetrics(0) or DxCamTrack.original_height != GetSystemMetrics(1):
149+
DxCamTrack.original_width = GetSystemMetrics(0)
150+
DxCamTrack.original_height = GetSystemMetrics(1)
151+
DxCamTrack.camera.stop()
152+
DxCamTrack.camera = None
153+
DxCamTrack.camera = dxcam.create(output_idx=0, output_color="BGR")#region=region1
154+
DxCamTrack.camera.start(target_fps=self.frame_rate, video_mode=True)
155+
print("DXCamera stopped.")
156+
103157
img = DxCamTrack.camera.get_latest_frame()
104-
158+
105159
cursor_position = pyautogui.position()
106160
cursor_hidden = is_cursor_hidden()
107161
if cursor_hidden != self.previous_state:
@@ -112,65 +166,84 @@ async def recv(self):
112166
frame = VideoFrame.from_ndarray(img, format="rgb24")
113167
elapsed_time = time.time() - self.start_time
114168
frame_index = int(elapsed_time * self.frame_rate)
115-
frame.pts = frame_index * int(self.framedex / self.frame_rate) # Calculate RTP timestamp
169+
frame.pts = frame_index * int(self.framedex / self.frame_rate)
116170
frame.time_base = Fraction(1, self.framedex)
117171

118172
return frame
119173
except Exception as e:
120-
self.camera.stop()
121-
print(f"Error receiving frame: {str(e)}")
174+
logging.error(f"Error receiving frame: {e}")
175+
122176

123177
async def index(request):
124-
content = open(os.path.join(ROOT, "templates/index.html"), "r").read()
125-
return web.Response(content_type="text/html", text=content)
178+
try:
179+
content = open(os.path.join(ROOT, "templates/index.html"), "r").read()
180+
return web.Response(content_type="text/html", text=content)
181+
except Exception as e:
182+
logging.error(f"Error in index request handling: {e}")
126183

127184
async def offer(request):
128-
params = await request.json()
129-
password = params.get("password")
130-
131-
if passwordtext != encrypt_string(password):
132-
return web.Response(status=401, text="Unauthorized")
133-
134-
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
135-
136-
pc = RTCPeerConnection()
137-
pcs.add(pc)
138-
139-
screenshot_track = DxCamTrack()
140-
pc.addTrack(screenshot_track)
141-
142-
video_sender = pc.getSenders()[0]
143-
force_codec(pc, video_sender, "video/VP8")
144-
145-
await pc.setRemoteDescription(offer)
146-
147-
answer = await pc.createAnswer()
148-
await pc.setLocalDescription(answer)
149-
150-
return web.Response(
151-
content_type="application/json",
152-
text=json.dumps(
153-
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
154-
),
155-
)
185+
try:
186+
#devmode.Fields = win32con.DM_DISPLAYFREQUENCY
187+
#win32api.ChangeDisplaySettings(devmode, 0)
188+
189+
params = await request.json()
190+
password = params.get("password")
191+
192+
if passwordtext != encrypt_string(password):
193+
return web.Response(status=401, text="Unauthorized")
194+
195+
offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"])
196+
197+
pc = RTCPeerConnection()
198+
pcs.add(pc)
199+
200+
screenshot_track = DxCamTrack()
201+
pc.addTrack(screenshot_track)
202+
203+
video_sender = pc.getSenders()[0]
204+
force_codec(pc, video_sender, "video/VP8")
205+
206+
await pc.setRemoteDescription(offer)
207+
208+
answer = await pc.createAnswer()
209+
await pc.setLocalDescription(answer)
210+
211+
return web.Response(
212+
content_type="application/json",
213+
text=json.dumps(
214+
{"sdp": pc.localDescription.sdp, "type": pc.localDescription.type}
215+
),
216+
)
217+
except Exception as e:
218+
logging.error(f"Error in offer request handling: {e}")
156219

157220
pcs = set()
158221

159222
async def on_shutdown(app):
160-
coros = [pc.close() for pc in pcs]
161-
await asyncio.gather(*coros)
162-
pcs.clear()
223+
try:
224+
coros = [pc.close() for pc in pcs]
225+
await asyncio.gather(*coros)
226+
pcs.clear()
227+
except Exception as e:
228+
logging.error(f"Error in on_shutdown: {e}")
163229

164230
if __name__ == "__main__":
165-
file_path = "background.exe"
166-
if os.path.exists(file_path):
167-
subprocess.Popen([file_path])
168-
else:
169-
subprocess.Popen(["python", "background.py"])
170-
logging.basicConfig(level=logging.INFO)
171-
172-
app = web.Application()
173-
app.on_shutdown.append(on_shutdown)
174-
app.router.add_get("/", index)
175-
app.router.add_post("/offer", offer)
176-
web.run_app(app, host="0.0.0.0", port=6966)
231+
try:
232+
atexit.register(exit_handler)
233+
signal.signal(signal.SIGTERM, exit_handler)
234+
signal.signal(signal.SIGINT, exit_handler)
235+
file_path = "background.exe"
236+
if os.path.exists(file_path):
237+
subprocess.Popen([file_path])
238+
else:
239+
subprocess.Popen(["python", "background.py"])
240+
logging.basicConfig(level=logging.INFO)
241+
242+
app = web.Application()
243+
app.on_shutdown.append(on_shutdown)
244+
app.router.add_get("/", index)
245+
app.router.add_post("/offer", offer)
246+
web.run_app(app, host="0.0.0.0", port=6966)
247+
248+
except Exception as e:
249+
logging.error(f"Error in main: {e}")

0 commit comments

Comments
 (0)