sterepando commited on
Commit
01b5f3e
·
verified ·
1 Parent(s): 5480a7b

Update Dockerfile

Browse files
Files changed (1) hide show
  1. Dockerfile +21 -56
Dockerfile CHANGED
@@ -4,7 +4,7 @@ FROM golang:1.21
4
  # 1. Установка необходимых инструментов
5
  RUN apt-get update && apt-get install -y wget unzip build-essential
6
 
7
- # 2. Настройка Android NDK (нужен для CGO кросс-компиляции)
8
  ENV ANDROID_NDK_HOME /opt/android-ndk
9
  ENV ANDROID_NDK_VERSION r25c
10
 
@@ -13,7 +13,6 @@ RUN wget -q https://dl.google.com/android/repository/android-ndk-${ANDROID_NDK_V
13
  mv android-ndk-${ANDROID_NDK_VERSION} /opt/android-ndk && \
14
  rm android-ndk-${ANDROID_NDK_VERSION}-linux.zip
15
 
16
- # Добавляем тулчейны в PATH
17
  ENV PATH ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
18
 
19
  WORKDIR /app
@@ -21,7 +20,7 @@ WORKDIR /app
21
  # 3. Инициализация Go модуля
22
  RUN go mod init mandre_server
23
 
24
- # 4. Код сервера на Go (Diff-Streamer)
25
  RUN cat <<EOF > main.go
26
  package main
27
 
@@ -31,11 +30,9 @@ import (
31
  "bytes"
32
  "fmt"
33
  "image"
34
- "image/color"
35
  "image/png"
36
  "net/http"
37
  "sync"
38
- "time"
39
  "unsafe"
40
 
41
  "github.com/gorilla/websocket"
@@ -43,15 +40,15 @@ import (
43
 
44
  // Глобальные переменные
45
  var (
46
- mu sync.RWMutex
47
- prevFrame *image.RGBA
48
- clients = make(map[*websocket.Conn]bool)
49
- broadcast = make(chan []byte)
50
- upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
51
  serverStarted bool
52
  )
53
 
54
- // HTML клиент с Canvas для отрисовки Diff-кадров
55
  const indexHTML = \`
56
  <!DOCTYPE html>
57
  <html>
@@ -68,7 +65,7 @@ const indexHTML = \`
68
  <div id="status">Connecting...</div>
69
  <script>
70
  const canvas = document.getElementById('screen');
71
- const ctx = canvas.getContext('2d', { alpha: false }); // Optimizes compositing
72
  const status = document.getElementById('status');
73
 
74
  let ws = new WebSocket("ws://" + location.host + "/ws");
@@ -83,12 +80,10 @@ const indexHTML = \`
83
  const img = new Image();
84
 
85
  img.onload = () => {
86
- // Первый кадр задает размер
87
  if (canvas.width !== img.width || canvas.height !== img.height) {
88
  canvas.width = img.width;
89
  canvas.height = img.height;
90
  }
91
- // РИСУЕМ ПОВЕРХ СТАРОГО (Прозрачные пиксели не затрут старые)
92
  ctx.drawImage(img, 0, 0);
93
  URL.revokeObjectURL(url);
94
  };
@@ -123,36 +118,24 @@ func StartServer(port int) {
123
  }
124
 
125
  //export StopServer
126
- func StopServer() {
127
- // В Go сложно корректно остановить http.ListenAndServe без контекста,
128
- // но для Android плагина это обычно не критично (процесс убивается).
129
- // Можно добавить логику закрытия сокетов.
130
- }
131
 
132
  //export UpdateFrame
133
  func UpdateFrame(width, height int, rawPtr unsafe.Pointer, length int) {
134
- // Превращаем C-pointer в Go slice без копирования (для чтения)
135
- // Android Bitmap обычно ARGB_8888 (4 байта на пиксель)
136
  rawBytes := unsafe.Slice((*byte)(rawPtr), length)
137
 
138
  img := image.NewRGBA(image.Rect(0, 0, width, height))
139
- // Копируем сырые данные в изображение.
140
- // ВНИМАНИЕ: Android Bitmap.copyPixelsToBuffer дает порядок цветов, который может отличаться (RGBA vs ARGB).
141
- // Обычно это RGBA. Если цвета перепутаны, поменяем тут.
142
  copy(img.Pix, rawBytes)
143
 
144
- // Генерация Delta-кадра
145
  diffImg := generateDelta(img)
146
 
147
- // Кодируем в PNG (Fastest compression)
148
  var buf bytes.Buffer
149
- enc := png.Encoder{CompressionLevel: png.NoCompression} // Скорость > Размер, т.к. мы и так шлем только разницу
150
  if err := enc.Encode(&buf, diffImg); err == nil {
151
- // Отправляем PNG в канал
152
  select {
153
  case broadcast <- buf.Bytes():
154
  default:
155
- // Если канал забит, пропускаем кадр (drop frame)
156
  }
157
  }
158
  }
@@ -161,7 +144,6 @@ func generateDelta(current *image.RGBA) image.Image {
161
  mu.Lock()
162
  defer mu.Unlock()
163
 
164
- // Если это первый кадр или размер изменился - отправляем полный кадр
165
  if prevFrame == nil || prevFrame.Bounds() != current.Bounds() {
166
  prevFrame = current
167
  return current
@@ -170,42 +152,28 @@ func generateDelta(current *image.RGBA) image.Image {
170
  bounds := current.Bounds()
171
  diff := image.NewRGBA(bounds)
172
 
173
- // Сравниваем пиксели
174
- // current.Pix и prevFrame.Pix - это []uint8
175
- // Формат RGBA: 4 байта на пиксель
176
  totalPixels := len(current.Pix)
177
-
178
  changed := false
179
 
180
- // Проходим по байтам. Оптимизация: сравниваем uint32 было бы быстрее, но сложнее с alignment.
181
- // Идем шагом 4 (R, G, B, A)
182
  for i := 0; i < totalPixels; i += 4 {
183
- // Сравниваем 4 байта пикселя
184
- if current.Pix[i] != prevFrame.Pix[i] || // R
185
- current.Pix[i+1] != prevFrame.Pix[i+1] || // G
186
- current.Pix[i+2] != prevFrame.Pix[i+2] || // B
187
- current.Pix[i+3] != prevFrame.Pix[i+3] { // A
188
 
189
- // Пиксель изменился -> копируем его в diff
190
  diff.Pix[i] = current.Pix[i]
191
  diff.Pix[i+1] = current.Pix[i+1]
192
  diff.Pix[i+2] = current.Pix[i+2]
193
- diff.Pix[i+3] = current.Pix[i+3] // Alpha 255 (обычно)
194
 
195
  changed = true
196
- } else {
197
- // Пиксель НЕ изменился -> делаем прозрачным
198
- // В image.NewRGBA по умолчанию всё 0 (Transparent), поэтому ничего писать не надо!
199
- // diff.Pix[i+3] = 0 // Уже 0
200
  }
201
  }
202
 
203
- // Обновляем предыдущий кадр
204
  prevFrame = current
205
 
206
  if !changed {
207
- // Если изменений нет вообще, можно вернуть пустой 1x1 пиксель прозрачный, чтобы не слать лишнее
208
- return image.NewRGBA(image.Rect(0, 0, 1, 1))
209
  }
210
 
211
  return diff
@@ -220,12 +188,10 @@ func handleConnections(w http.ResponseWriter, r *http.Request) {
220
 
221
  mu.Lock()
222
  clients[ws] = true
223
- // При подключении сбрасываем prevFrame, чтобы клиенту пришел полный кадр сразу (force I-frame)
224
  prevFrame = nil
225
  mu.Unlock()
226
 
227
  for {
228
- // Читаем сообщения (нужно для keep-alive/close), но игнорируем их
229
  _, _, err := ws.ReadMessage()
230
  if err != nil {
231
  mu.Lock()
@@ -254,17 +220,16 @@ func handleMessages() {
254
  func main() {}
255
  EOF
256
 
257
- # 5. Установка зависимостей (WebSocket)
258
  RUN go get github.com/gorilla/websocket
259
 
260
- # 6. Компиляция shared library для Android (ARM64)
261
- # buildmode=c-shared создает .so файл, который Python может загрузить через ctypes
262
  RUN CGO_ENABLED=1 \
263
  GOOS=android \
264
  GOARCH=arm64 \
265
  CC=aarch64-linux-android33-clang \
266
  go build -buildmode=c-shared -o libmandre_server.so main.go
267
 
268
- # 7. Выдача файла
269
  CMD ["sh", "-c", "mkdir -p /output && cp libmandre_server.so /output/"]
270
  # --- END OF FILE Dockerfile ---
 
4
  # 1. Установка необходимых инструментов
5
  RUN apt-get update && apt-get install -y wget unzip build-essential
6
 
7
+ # 2. Настройка Android NDK
8
  ENV ANDROID_NDK_HOME /opt/android-ndk
9
  ENV ANDROID_NDK_VERSION r25c
10
 
 
13
  mv android-ndk-${ANDROID_NDK_VERSION} /opt/android-ndk && \
14
  rm android-ndk-${ANDROID_NDK_VERSION}-linux.zip
15
 
 
16
  ENV PATH ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH
17
 
18
  WORKDIR /app
 
20
  # 3. Инициализация Go модуля
21
  RUN go mod init mandre_server
22
 
23
+ # 4. Исправленный код сервера (удалены лишние импорты)
24
  RUN cat <<EOF > main.go
25
  package main
26
 
 
30
  "bytes"
31
  "fmt"
32
  "image"
 
33
  "image/png"
34
  "net/http"
35
  "sync"
 
36
  "unsafe"
37
 
38
  "github.com/gorilla/websocket"
 
40
 
41
  // Глобальные переменные
42
  var (
43
+ mu sync.RWMutex
44
+ prevFrame *image.RGBA
45
+ clients = make(map[*websocket.Conn]bool)
46
+ broadcast = make(chan []byte)
47
+ upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
48
  serverStarted bool
49
  )
50
 
51
+ // HTML клиент с Canvas
52
  const indexHTML = \`
53
  <!DOCTYPE html>
54
  <html>
 
65
  <div id="status">Connecting...</div>
66
  <script>
67
  const canvas = document.getElementById('screen');
68
+ const ctx = canvas.getContext('2d', { alpha: false });
69
  const status = document.getElementById('status');
70
 
71
  let ws = new WebSocket("ws://" + location.host + "/ws");
 
80
  const img = new Image();
81
 
82
  img.onload = () => {
 
83
  if (canvas.width !== img.width || canvas.height !== img.height) {
84
  canvas.width = img.width;
85
  canvas.height = img.height;
86
  }
 
87
  ctx.drawImage(img, 0, 0);
88
  URL.revokeObjectURL(url);
89
  };
 
118
  }
119
 
120
  //export StopServer
121
+ func StopServer() {}
 
 
 
 
122
 
123
  //export UpdateFrame
124
  func UpdateFrame(width, height int, rawPtr unsafe.Pointer, length int) {
 
 
125
  rawBytes := unsafe.Slice((*byte)(rawPtr), length)
126
 
127
  img := image.NewRGBA(image.Rect(0, 0, width, height))
128
+ // Копируем данные. Предполагается формат RGBA или совместимый.
 
 
129
  copy(img.Pix, rawBytes)
130
 
 
131
  diffImg := generateDelta(img)
132
 
 
133
  var buf bytes.Buffer
134
+ enc := png.Encoder{CompressionLevel: png.NoCompression}
135
  if err := enc.Encode(&buf, diffImg); err == nil {
 
136
  select {
137
  case broadcast <- buf.Bytes():
138
  default:
 
139
  }
140
  }
141
  }
 
144
  mu.Lock()
145
  defer mu.Unlock()
146
 
 
147
  if prevFrame == nil || prevFrame.Bounds() != current.Bounds() {
148
  prevFrame = current
149
  return current
 
152
  bounds := current.Bounds()
153
  diff := image.NewRGBA(bounds)
154
 
 
 
 
155
  totalPixels := len(current.Pix)
 
156
  changed := false
157
 
 
 
158
  for i := 0; i < totalPixels; i += 4 {
159
+ if current.Pix[i] != prevFrame.Pix[i] ||
160
+ current.Pix[i+1] != prevFrame.Pix[i+1] ||
161
+ current.Pix[i+2] != prevFrame.Pix[i+2] ||
162
+ current.Pix[i+3] != prevFrame.Pix[i+3] {
 
163
 
 
164
  diff.Pix[i] = current.Pix[i]
165
  diff.Pix[i+1] = current.Pix[i+1]
166
  diff.Pix[i+2] = current.Pix[i+2]
167
+ diff.Pix[i+3] = current.Pix[i+3]
168
 
169
  changed = true
 
 
 
 
170
  }
171
  }
172
 
 
173
  prevFrame = current
174
 
175
  if !changed {
176
+ return image.NewRGBA(image.Rect(0, 0, 1, 1))
 
177
  }
178
 
179
  return diff
 
188
 
189
  mu.Lock()
190
  clients[ws] = true
 
191
  prevFrame = nil
192
  mu.Unlock()
193
 
194
  for {
 
195
  _, _, err := ws.ReadMessage()
196
  if err != nil {
197
  mu.Lock()
 
220
  func main() {}
221
  EOF
222
 
223
+ # 5. Зависимости
224
  RUN go get github.com/gorilla/websocket
225
 
226
+ # 6. Компиляция
 
227
  RUN CGO_ENABLED=1 \
228
  GOOS=android \
229
  GOARCH=arm64 \
230
  CC=aarch64-linux-android33-clang \
231
  go build -buildmode=c-shared -o libmandre_server.so main.go
232
 
233
+ # 7. Выдача
234
  CMD ["sh", "-c", "mkdir -p /output && cp libmandre_server.so /output/"]
235
  # --- END OF FILE Dockerfile ---