Skip to content

Commit

Permalink
Merge pull request #6 from shravanasati/stream
Browse files Browse the repository at this point in the history
write directly to video stream
  • Loading branch information
shravanasati authored Nov 8, 2024
2 parents e47d170 + 39f8b19 commit 646b2ef
Show file tree
Hide file tree
Showing 10 changed files with 476 additions and 199 deletions.
6 changes: 1 addition & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,10 @@ jobs:
run: |
python -m pip install --upgrade pip
python -m pip install flake8
python -m pip install mypy
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Type checking with mypy
run: |
mypy --ignore-missing-imports pyscreenrec/__init__.py
11 changes: 11 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,14 @@ Change Log
- Temporary screenshots are now stored in `~/.pyscreenrec_data` folder.
- Internal refactors.
--------------


0.6 (09/11/24)
--------------
- Write screenshots directly to the video stream instead of the disk.
- Delegate image writing to a separate thread.
- Use mss library instead of pyscreeze for capturing screenshots.
- Capture a part of the screen.
- Performance improvements.
- Internal refactors.
--------------
72 changes: 44 additions & 28 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,54 @@

The following is a set of guidelines for contributing to *pyscreenrec*, which is hosted on GitHub. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.

## Repository structure
```
.
├── .github # issue templates and workflows
│ ├── ISSUE_TEMPLATE # issue templates
├── bug_report.md
├── custom.md
├── feature_request.md
├── workflows # CI/CD workflows
├── .python-package.yml
└── pyscreenrec
├── __init__.py # main code
├── .gitignore
├── CHANGELOG
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── requirements.txt
└── setup.py # setup config for pypi
```

## Setup environment for contribution
## Setup local development environment

This section shows how you can setup your development environment to contribute to *pyscreenrec*.

1. Fork the repository.

2. Clone it using Git (`git clone https://github.com/<YOUR USERNAME>/pyscreenrec.git`).
3. Install requirements using `pip install -r requirements.txt`.
4. Make changes.
5. Stage and commit (`git add .` and `git commit -m "COMMIT MESSAGE"`).
6. Push it your remote repository (`git push`).
7. Open a pull request by clicking [here](https://github.com/shravanasati/pyscreenrec/compare).

3. Create a virtual environment.

```
python -m venv venv
```

Activate it using

```
./venv/Scripts/Activate.ps1
```
on Windows

```
source ./venv/bin/activate
```

on unix-based systems (make sure to choose the activation script according to your shell)

4. Install dependencies.

We recommend using [poetry](https://python-poetry.org) for dependency management.

```
poetry install --no-root
```

Otherwise, using pip:

```
pip install .
```

5. Make your changes.

6. Stage and commit (`git add .` and `git commit -m "COMMIT MESSAGE"`).

7. Push it your remote repository (`git push`).

8. Open a pull request by clicking [here](https://github.com/shravanasati/pyscreenrec/compare).


## Reporting Issues
Expand Down
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,26 @@ Install on Linux/macOS:
<br>

## Example usage


``` python
>>> import pyscreenrec
>>> recorder = pyscreenrec.ScreenRecorder()

>>> # to start recording
>>> recorder.start_recording("recording.mp4", 10)
>>> recorder.start_recording("recording.mp4", 30, {
"mon": 1,
"left": 100,
"top": 100,
"width": 1000,
"height": 1000
})
>>> # 'recording.mp4' is the name of the output video file, may also contain full path like 'C:/Users/<user>/Videos/video.mp4'
>>> # the second parameter(10) is the FPS. You can specify the FPS for the screen recording using the second parameter.
>>> # the second parameter is the FPS for the recording
>>> # the third parameter (optional) is the monitor and the dimensions that needs to be recorded,
# here we're capturing the first monitor, 100px from left, 100px from right, and then 1000px each in resp. axes
# refer https://python-mss.readthedocs.io/examples.html#part-of-the-screen-of-the-2nd-monitor for more information


>>> # to pause recording
>>> recorder.pause_recording()
Expand All @@ -36,6 +48,8 @@ Install on Linux/macOS:
>>> recorder.stop_recording()
```

> Take a look at the GUI screen recorder [here](examples/gui_recorder.py) for more information.
Keep in mind that the `start_recording` method is non-blocking, it will start a thread in the background to capture the screenshots.

The `stop_recording` saves the video and deletes all screenshots used in the session.
Expand All @@ -45,29 +59,35 @@ If a screen recording session is already running, calling the `start_recording`

Similarly, if a screen recording session is not running, calling the `stop_recording` and `pause_recording` methods raises a `NoScreenRecodingInProgress` warning.


<br>

## Known limitations

*pyscreenrec* is not able to:
- capture the system sound during screen recording
- capture only a certain part of the screen

<br>

## Change Log
Changes made in the latest version (*v0.5*) are:
- Remove the `HighFPSWarning` and `InvalidFPS` exception classes.
- Raise frame count by almost 2 times.
- Calling start and resume recording methods on an already running recorder instance raises a warning instead of printing, and vice versa.
- Temporary screenshots are now stored in `~/.pyscreenrec_data` folder.

Changes made in the latest version (*v0.6*) are:

- Write screenshots directly to the video stream instead of the disk.
- Delegate image writing to a separate thread.
- Use mss library instead of pyscreeze for capturing screenshots.
- Capture a part of the screen.
- Performance improvements.
- Internal refactors.



View [CHANGELOG](https://github.com/shravanasati/pyscreenrec/blob/master/CHANGELOG) for more details.

<br>

## Contribution

Pull requests are welcome. If you want to make a major change, open an issue first to discuss about the change.

For further details, view [CONTRIBUTING.md](https://github.com/shravanasati/pyscreenrec/blob/master/CONTRIBUTING.md).
140 changes: 140 additions & 0 deletions examples/gui_recorder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import tkinter as tk
from tkinter import messagebox
from warnings import filterwarnings

from pyscreenrec import ScreenRecorder, ScreenRecordingInProgress, NoScreenRecordingInProgress

filterwarnings("error")

COORDINATES = None

class RegionSelector(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("Select Recording Region")
self.geometry("800x600")
self.attributes('-alpha', 0.3)
self.attributes('-fullscreen', True)
self.configure(background='grey')

self.start_x = None
self.start_y = None
self.end_x = None
self.end_y = None

self.canvas = tk.Canvas(self, highlightthickness=0)
self.canvas.pack(fill='both', expand=True)

self.canvas.bind('<Button-1>', self.start_selection)
self.canvas.bind('<B1-Motion>', self.update_selection)
self.canvas.bind('<ButtonRelease-1>', self.end_selection)

self.selection_rect = None

def start_selection(self, event):
self.start_x = event.x
self.start_y = event.y
if self.selection_rect:
self.canvas.delete(self.selection_rect)

def update_selection(self, event):
if self.selection_rect:
self.canvas.delete(self.selection_rect)
self.selection_rect = self.canvas.create_rectangle(
self.start_x, self.start_y, event.x, event.y, outline='red'
)

def end_selection(self, event):
self.end_x = event.x
self.end_y = event.y
global COORDINATES
COORDINATES = self.get_coordinates()
self.destroy()

def get_coordinates(self):
# Ensure coordinates are in the correct order
left = min(self.start_x, self.end_x)
top = min(self.start_y, self.end_y)
x2 = max(self.start_x, self.end_x)
y2 = max(self.start_y, self.end_y)
width = x2 - left
height = y2 - top
return {
"top": top,
"left": left,
"height": height,
"width": width,
}


class GUIScreenRecorder(tk.Tk):
def __init__(self):
super().__init__()
self.title("Screen Recorder")
self.geometry("500x400")

self.recorder = ScreenRecorder()

self.start_button = tk.Button(self, text="Start Recording", command=self.start_recording)
self.start_button.pack(pady=10)

self.select_region_button = tk.Button(self, text="Select Region", command=self.select_recording_region)
self.select_region_button.pack(pady=10)

self.pause_button = tk.Button(self, text="Pause Recording", command=self.pause_recording)
self.pause_button.pack(pady=10)

self.resume_button = tk.Button(self, text="Resume Recording", command=self.resume_recording)
self.resume_button.pack(pady=10)

self.stop_button = tk.Button(self, text="Stop Recording", command=self.stop_recording)
self.stop_button.pack(pady=10)

self.filename_entry = tk.Entry(self, width=40)
self.filename_entry.pack(pady=10)
self.filename_entry.insert(0, "Recording.mp4")

self.fps_entry = tk.Entry(self, width=10)
self.fps_entry.pack(pady=10)
self.fps_entry.insert(0, "30")

def select_recording_region(self):
selector = RegionSelector(self)
selector.mainloop()

def start_recording(self):
try:
filename = self.filename_entry.get()
fps = int(self.fps_entry.get())
self.recorder.start_recording(filename, fps, COORDINATES)
messagebox.showinfo("Recording Started", "Screen recording has started.")
except (ValueError, SyntaxError):
messagebox.showerror("Invalid Input", "Please enter valid values for the filename and FPS.")
except ScreenRecordingInProgress:
messagebox.showerror("Recording in Progress", "Screen recording is already in progress.")

def pause_recording(self):
try:
self.recorder.pause_recording()
messagebox.showinfo("Recording Paused", "Screen recording has been paused.")
except NoScreenRecordingInProgress:
messagebox.showerror("No Recording", "No screen recording is in progress.")

def resume_recording(self):
try:
self.recorder.resume_recording()
messagebox.showinfo("Recording Resumed", "Screen recording has been resumed.")
except ScreenRecordingInProgress:
messagebox.showerror("Recording in Progress", "Screen recording is already in progress.")

def stop_recording(self):
try:
self.recorder.stop_recording()
messagebox.showinfo("Recording Stopped", "Screen recording has stopped.")
except NoScreenRecordingInProgress:
messagebox.showerror("No Recording", "No screen recording is in progress.")


if __name__ == "__main__":
app = GUIScreenRecorder()
app.mainloop()
Loading

0 comments on commit 646b2ef

Please sign in to comment.