|
1 | 1 | import logging
|
2 |
| -from io import StringIO |
3 | 2 |
|
4 | 3 | import gevent.event
|
5 |
| -import PIL |
6 |
| -from flask import Response |
7 | 4 | from mxcubecore import HardwareRepository as HWR
|
8 | 5 | from mxcubecore.queue_entry.base_queue_entry import CENTRING_METHOD
|
9 | 6 |
|
|
17 | 14 | SNAPSHOT = None
|
18 | 15 |
|
19 | 16 |
|
20 |
| -class HttpStreamer: |
21 |
| - """ |
22 |
| - Implements 'MJPEG' streaming from the sample view camera. |
23 |
| -
|
24 |
| - Provides get_response() method, that creates a Response object, |
25 |
| - that will stream JPEG images from the sample view camera, |
26 |
| - in 'multipart' HTTP response format. |
27 |
| - """ |
28 |
| - |
29 |
| - def __init__(self): |
30 |
| - self._new_frame = gevent.event.Event() |
31 |
| - self._sample_image = None |
32 |
| - self._clients = 0 |
33 |
| - |
34 |
| - def _client_connected(self): |
35 |
| - if self._clients == 0: |
36 |
| - # first client connected, |
37 |
| - # start listening to frames from sample camera |
38 |
| - HWR.beamline.sample_view.camera.connect( |
39 |
| - "imageReceived", self._new_frame_received |
40 |
| - ) |
41 |
| - |
42 |
| - self._clients += 1 |
43 |
| - |
44 |
| - def _client_disconnected(self): |
45 |
| - self._clients -= 1 |
46 |
| - if self._clients == 0: |
47 |
| - # last client disconnected, |
48 |
| - # disconnect from the sample camera |
49 |
| - HWR.beamline.sample_view.camera.disconnect( |
50 |
| - "imageReceived", self._new_frame_received |
51 |
| - ) |
52 |
| - |
53 |
| - def _new_frame_received(self, img, width, height, *args, **kwargs): |
54 |
| - if not isinstance(img, str | bytes): |
55 |
| - rawdata = img.bits().asstring(img.numBytes()) |
56 |
| - strbuf = StringIO() |
57 |
| - image = PIL.Image.frombytes("RGBA", (width, height), rawdata) |
58 |
| - (r, g, b, a) = image.split() |
59 |
| - image = PIL.Image.merge("RGB", (b, g, r)) |
60 |
| - image.save(strbuf, "JPEG") |
61 |
| - img = strbuf.get_value() |
62 |
| - |
63 |
| - self._sample_image = img |
64 |
| - |
65 |
| - # signal clients that there is a new frame available |
66 |
| - self._new_frame.set() |
67 |
| - self._new_frame.clear() |
68 |
| - |
69 |
| - def get_response(self) -> Response: |
70 |
| - """ |
71 |
| - build new Response object, that will send frames to the client |
72 |
| - """ |
73 |
| - |
74 |
| - def frames(): |
75 |
| - while True: |
76 |
| - self._new_frame.wait() |
77 |
| - yield ( |
78 |
| - b"--frame\r\n--!>\nContent-type: image/jpeg\n\n" |
79 |
| - + self._sample_image |
80 |
| - + b"\r\n" |
81 |
| - ) |
82 |
| - |
83 |
| - self._client_connected() |
84 |
| - |
85 |
| - response = Response( |
86 |
| - frames(), |
87 |
| - mimetype='multipart/x-mixed-replace; boundary="!>"', |
88 |
| - ) |
89 |
| - # keep track of when client stops reading the stream |
90 |
| - response.call_on_close(self._client_disconnected) |
91 |
| - |
92 |
| - return response |
93 |
| - |
94 |
| - |
95 | 17 | class SampleView(ComponentBase):
|
96 | 18 | def __init__(self, app, config):
|
97 | 19 | super().__init__(app, config)
|
98 | 20 | self._click_count = 0
|
99 | 21 | self._click_limit = 3
|
100 | 22 | self._centring_point_id = None
|
101 |
| - self.http_streamer = HttpStreamer() |
102 | 23 |
|
103 | 24 | HWR.beamline.sample_view.connect("shapesChanged", self._emit_shapes_updated)
|
104 | 25 |
|
|
0 commit comments