-
-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #39 from owulveryck/websocket
Streaming v2
- Loading branch information
Showing
55 changed files
with
815 additions
and
4,360 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,183 +1,93 @@ | ||
[![Go](https://github.com/owulveryck/goMarkableStream/actions/workflows/go.yml/badge.svg)](https://github.com/owulveryck/goMarkableStream/actions/workflows/go.yml) | ||
|
||
# goMarkableStream | ||
## Overview | ||
|
||
I use this project to stream my remarkable 2 (firmware 2.5+) on my laptop using the local wifi. | ||
This project does not need any hack installed on the remarkable (only the server which should not void the warranty). | ||
This project does not rely on any exterrnal dependencies and should run on Linux, Windows and. MacOS. | ||
|
||
[video that shows some features](https://www.youtube.com/watch?v=PzlQ2hEIdCc) | ||
|
||
And [another one](https://youtu.be/0PCyUn_-x6Y) that shows the experimental colorize featur | ||
|
||
## Quick start | ||
|
||
You need ssh access to your remarkable | ||
|
||
Download two files from the [release page](https://github.com/owulveryck/goMarkableStream/releases): | ||
|
||
- the server "`Linux/Armv7`" for your remarkable | ||
- the client for your laptop according to the couple `OS/arch` | ||
|
||
or build it yourself if you have the go toolchain installed on your machine. | ||
|
||
### The server | ||
|
||
Copy the server on the remarkable and start it. | ||
|
||
```shell | ||
scp goMarkableStreamServer.arm remarkable: | ||
ssh remarkable 'chmod +x goMarkableStreamServer.arm ; ./goMarkableStreamServer.arm' | ||
``` | ||
|
||
Note: The processus is fault tolerant and should resume automatically on sleep/wakup or network failure; therefore, you can, normally, launch the processus in background (with `nohup`) | ||
|
||
|
||
### The client | ||
|
||
- Start the client: `RK_SERVER_ADDR=ip.of.remarkable:2000 ./goMarkableClient` | ||
|
||
- Point your browser to [`http://localhost:8080/`](http://localhost:8080/) | ||
|
||
The client exposes those endpoints: | ||
|
||
- `/screenshot` takes a screenshot in png | ||
- `/orientation?orientation=[landscape|portrait]` to change the orientation | ||
- `/conf` ugly and incomplete configuration panel | ||
- `/gob` to get a gob encoded picture (for development purpose) | ||
- `/raw` to get a raw picture (a gray bitmap image) | ||
|
||
_Note_: click on the video to take a screenshot. The screenshot is a png file with transparent background. | ||
|
||
### Configuration | ||
|
||
It is possible to tweak the configuration via environment variables: | ||
|
||
#### Server | ||
|
||
| Env var | Default | Descri[ption | ||
|---------------------|-----------|--------------- | ||
| RK_SERVER_BIND_ADDR | :2000 | the TCP listen address | ||
|
||
#### Client | ||
|
||
| Env var | Default | Descri[ption | ||
|---------------------------|-----------------|--------------- | ||
| RK_CLIENT_BIND_ADDR | :8080 | the TCP listen address | ||
| RK_SERVER_ADDR | remarkabke:2000 | the address of the remarkable | ||
| RK_CLIENT_AUTOROTATE | true | activate autorotate (see below) | ||
| RK_CLIENT_PAPER_TEXTURE | null | a path to a texture | ||
| RK_CLIENT_COLORIZE | true | colorize function | ||
| RK_CLIENT_HIGHLIGHT | false | highlight function (cannot work with colorize) | ||
The goMarkableStream is a lightweight and user-friendly application designed specifically for the reMarkable tablet. | ||
Its primary goal is to enable users to stream their reMarkable tablet screen to a web browser without the need for any hacks or modifications that could void the warranty. | ||
|
||
## Features | ||
|
||
### Auto-rotate | ||
|
||
The client tries to locate the location of the top level switch on the picture (the round one) and rotate the picture accordingly. | ||
This experimental behavior can be disabled by env variables in the client. | ||
|
||
Note: the browser does not like the switch of the rotation; the reload of the page solves the problem | ||
|
||
### Texture | ||
|
||
There is an experimental texture feature that reads a texture file and use is as a background in the output. The texture does | ||
not apply to the screenshot. | ||
The texture must have this format: | ||
|
||
```shell | ||
> identify textures/oldpaper.png | ||
textures/oldpaper.png PNG 1872x1404 1872x1404+0+0 8-bit Gray 256c 886691B 0.010u 0:00.001 | ||
``` | ||
- No hacks or warranty voiding: The tool operates within the boundaries of the reMarkable tablet's intended functionality and does not require any unauthorized modifications. | ||
- No subscription required: Unlike other screen streaming solutions, this tool does not impose any subscription fees or recurring charges. It is completely free to use. | ||
- No client-side installation: Users can access the screen streaming feature directly through their web browser without the need to install any additional software or plugins. | ||
- Color support | ||
|
||
Example: | ||
## Quick Start | ||
|
||
```shell | ||
RK_CLIENT_PAPER_TEXTURE=./textures/oldpaper.png goMarkableClient | ||
```text | ||
ssh root#remarkable | ||
curl -O https://github.com/owulveryck/goMarkableStream/releases/download/v0.8.0/goMarkableStream | ||
chmod+x goMarkableStream | ||
./goMarkableStream | ||
``` | ||
|
||
![exemple](docs/textures.png) | ||
then go to [https://remarkable:2001](https://remarkable:2001) and login with `admin`/`password` (can be changed through environment variables) | ||
|
||
_note_: replace _remarkable_ by the IP address if needed. | ||
|
||
### Colorize | ||
## Technical Details | ||
|
||
This option can be activated with the `RK_CLIENT_COLORIZE=true` env variable. This tries to set the highlighter and colors in yellow in the video stream. | ||
### Data Retrieval from reMarkable Memory | ||
|
||
**Caution** This is CPU intensive. | ||
The reMarkable Screen Streaming Tool leverages a combination of techniques to capture the screen data from the reMarkable tablet's memory. | ||
It utilizes low-level access provided by the device's operating system to retrieve the necessary data. | ||
This approach ensures that the tool does not require any unauthorized modifications to the reMarkable tablet. | ||
|
||
![exemple](docs/colorize.png) | ||
### Data Transmission via WebSocket | ||
|
||
### Screenshot | ||
Once the screen data is obtained from the reMarkable tablet's memory, the tool serves it to clients via a WebSocket connection. | ||
A WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection, making it ideal for real-time data streaming. | ||
The WebSocket connection ensures that the screen data is transmitted efficiently and promptly to connected clients. | ||
|
||
The server is exposing a screenshot endpoint. You can grab a screenshot by clicking on the video stream, or by using tools such as curl: | ||
### Client-Side Rendering with HTML Canvas | ||
|
||
ex: | ||
```shell | ||
❯ curl -o /tmp/screenshot.png http://localhost:8080/screenshot | ||
❯ file /tmp/screenshot.png | ||
/tmp/screenshot.png: PNG image data, 1404 x 1872, 8-bit/color RGBA, non-interlaced | ||
``` | ||
|
||
### Raw picture | ||
|
||
the `/raw` endpoind exposes the raw picture without any treatment. It is possible to pipe the result into a thrid party tool such as imageMagick for example. | ||
|
||
This generates a pdf from the screen | ||
```shell | ||
curl http://localhost:8080/raw | convert -depth 8 -size 1404x1872+0 gray:- shot.pdf | ||
``` | ||
On the client side, the reMarkable Screen Streaming Tool fetches the transmitted screen data and renders it using an HTML canvas element. | ||
The HTML canvas provides a powerful and flexible platform for displaying graphics and images on web pages. | ||
By leveraging the capabilities of the HTML canvas, the tool ensures a performant and lossless streaming experience for users. | ||
|
||
## How it works? | ||
## Getting Started | ||
|
||
### Full explanation | ||
To use the reMarkable Screen Streaming Tool, follow these steps: | ||
|
||
I wrote a [blog post](https://blog.owulveryck.info/2021/03/30/streaming-the-remarkable-2.html) that explains all the wiring. | ||
Otherwise a summary is written here. | ||
### Installation | ||
|
||
### The server loop | ||
1. Ensure that you have a reMarkable tablet and a computer or device with an ssh client. | ||
2. Get a compiled version from the release or compile it yourself | ||
3. copy the utility on the tablet | ||
|
||
- The server gets the address of the framebuffer in the memory space of the `xochitl` | ||
- The server launches a "ticketing system" to avoid congestion. The ticketing system is a channel that gets an event every 200ms. | ||
- Then it exposes a gRPC function (with TLS and mutual authentication). | ||
- The gRPC function waits for a "ticket" on the channel, and then grabs the data from the framebuffer. | ||
- It packs the data into an `image` message encoded in protobuf and sends it to the consumer | ||
### Run | ||
|
||
### The client loop | ||
1. launch the utility by conencting via ssh and launch `./goMarkableStream &` | ||
2. go to https://IP-OF-REMARKABLE:2001 (you need to accept the self-signed certificate) | ||
|
||
- The client creates an `MJPEG` stream and serves it over HTTP on the provided address | ||
- The client dial server and sends its certificate, and add the compression header. | ||
- Then it triggers a goroutine to get the `image` in a for loop. | ||
- The image is then encoded into JPEG format and added to the MJPEG stream. | ||
The application is configured via environment variables: | ||
|
||
```text | ||
This application is configured via the environment. The following environment | ||
variables can be used: | ||
## Security | ||
KEY TYPE DEFAULT REQUIRED DESCRIPTION | ||
RK_SERVER_BIND_ADDR String :2001 true | ||
RK_SERVER_DEV True or False false | ||
RK_SERVER_USERNAME String admin | ||
RK_SERVER_PASSWORD String password | ||
RK_HTTPS True or False true | ||
``` | ||
|
||
The communication is using TLS. The client and the server owns an embedded certificate chain (with the CA). There are performing mutual authentication. | ||
A new certificate chain is generated per build. Therefore, if you want restrict the access to your server to your client only, you must rebuild the tool yourself. | ||
### Compilation | ||
|
||
### Manual build | ||
`GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build .` | ||
|
||
_Note_: you need go > 1.16beta to build the tool because of the embedding mechanism for the certificate. | ||
## Contributing | ||
|
||
To build the tool manually, the easiest way is to use `goreleaser`: | ||
I welcome contributions from the community to improve and enhance the reMarkable Screen Streaming Tool. If you have any ideas, bug reports, or feature requests, please submit them through the GitHub repository's issue tracker. | ||
|
||
```shell | ||
goreleaser --snapshot --skip-publish --rm-dist | ||
``` | ||
## License | ||
|
||
To build the services manually: | ||
The reMarkable Screen Streaming Tool is released under the [MIT License](https://opensource.org/licenses/MIT) . Feel free to modify, distribute, and use the tool in accordance with the terms of the license. | ||
|
||
```shell | ||
go generate ./... # This generates the certificates | ||
cd server && GOOS=linux GOARCH=arm GOARM=7 go build -o goStreamServer.arm | ||
cd client && go build -o goStreamClient | ||
``` | ||
|
||
# Tipping | ||
## Tipping | ||
|
||
If you plan to buy a reMarkable 2, you can use my [referal program link](https://remarkable.com/referral/PY5B-PH8U). It will provide a discount for you and also for me. | ||
|
||
## Acknowledgement | ||
|
||
All the people in the reStream projet and specially | ||
[@ddvk](https://github.com/ddvk) and [@raisjn](https://github.com/raisjn) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
) | ||
|
||
// BasicAuthMiddleware is a middleware function that adds basic authentication to a handler | ||
func BasicAuthMiddleware(next http.Handler) http.Handler { | ||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
// Check if the request is authenticated | ||
user, pass, ok := r.BasicAuth() | ||
if !ok || !checkCredentials(user, pass) { | ||
// Authentication failed, send a 401 Unauthorized response | ||
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) | ||
w.WriteHeader(http.StatusUnauthorized) | ||
fmt.Fprintln(w, "Unauthorized") | ||
return | ||
} | ||
|
||
// Authentication succeeded, call the next handler | ||
next.ServeHTTP(w, r) | ||
}) | ||
} | ||
|
||
// checkCredentials is a dummy function to validate the username and password | ||
func checkCredentials(username, password string) bool { | ||
// Add your custom logic here to validate the credentials against your storage (e.g., database, file) | ||
// This is a basic example, so we're using hard-coded credentials for demonstration purposes. | ||
return username == c.Username && password == c.Password | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIDazCCAlOgAwIBAgIUBAVfurBI2ze6o814cYTubdIgV/gwDQYJKoZIhvcNAQEL | ||
BQAwRTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM | ||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA2MjMwNzM1MzlaFw0zMzA2 | ||
MjAwNzM1MzlaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw | ||
HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB | ||
AQUAA4IBDwAwggEKAoIBAQD2CZ/+7qzKTRk02kaPT+U5yu3oTuggkkwkcUUE5gIk | ||
CbhvsLQ4aIx2QHmol1rkfO1LgA7mfyVhO5D4QqwANBieEdO6x+B2KUCo5NQTblBY | ||
17e+OpN6w4s2eNSoB1/BaO8zICqj3GaIcyKCIrXB5zxKXjRbAgiZ7nEsN3R1HKC9 | ||
aL2l/j++X6kd4ET2LSsu5JHqYlYMipZ2TVK/FieBEoKdtb0Fm1es8P3j36uByY2v | ||
p0wvh4a6eGxmxFCrFDSUhk8mLteT+1doUrGj4LdmCw8MKdVmK+BbZSntdNP6/2Rd | ||
gB0MleQdLzB/YjgrXo4Au47YXAI8FNXkWADpJqWD/mnxAgMBAAGjUzBRMB0GA1Ud | ||
DgQWBBSMCi3QGHRzKz7CEsD2O0zX+LgnTzAfBgNVHSMEGDAWgBSMCi3QGHRzKz7C | ||
EsD2O0zX+LgnTzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA4 | ||
LYSX4UhGfHkCldTV0Ug0Fn6rDyBMOfEIYL1WJVvBjEB9CP8aQ8ooRuwzKUaQihbq | ||
KP13r27/uTQ9GrJ9+/Oroqv2iJJPfrSiZsPeKoymjaAT2cvi3qlMYtPVMjIJpSdE | ||
WapJ5VOG6rnCWJfuF2KWR7cxmJ2DOlZu/unOzr5nHfLRxCZaFJhubrCgMlZ8MkqO | ||
aE532ai+E9QRlDs9PS9D9Un7iUw3auvytDtlcxh0UkVHlELF5miJMwNFyMtI7xgm | ||
RSqRM43aEgC4sos413lMNnXqB7bbJsoKOqGTo/j4d+hfPRXi1jntccdtma6NyX7T | ||
vuMr9+DhDxVR/ZD+oci0 | ||
-----END CERTIFICATE----- |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.