File size: 6,522 Bytes
6cbbb50
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
import logging
import flet as ft
from js_execution import set_camera_frame_callback
from flet_extensions import eval_js
import asyncio
from typing import List, Dict

logger = logging.getLogger(__name__)

class Frame:
    def __init__(self, data: str, timestamp: float):
        self.data = data
        self.timestamp = timestamp

class CameraImage(ft.Container):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.stream_active = False
        self.video_image1 = ft.Image(
            src="",
            width=self.width,
            height=self.height,
            fit=ft.ImageFit.CONTAIN,
            visible=False,
        )
        self.video_image2 = ft.Image(
            src="",
            width=self.width,
            height=self.height,
            fit=ft.ImageFit.CONTAIN,
            visible=False,
        )
        self.current_video_image = self.video_image1
        self.video_stack = ft.Stack([self.video_image1, self.video_image2])
        self.content = self.video_stack
        set_camera_frame_callback(self.handle_camera_frame)
        self.captured_frames: Dict[int, Frame] = {}
        self.stream_start_time = None

    async def start_camera_stream_async(self) -> str:
        try:
            result = await eval_js(self.page, """
            window.cameraStreamActive = false;
            window.cameraWebSocket = null;

            async function setupCameraStream() {
                console.log("setupCameraStream called");
                try {
                    const stream = await navigator.mediaDevices.getUserMedia({ video: true });
                    console.log("Camera access granted");
                    const video = document.createElement('video');
                    video.srcObject = stream;
                    await video.play();

                    const canvas = document.createElement('canvas');
                    canvas.width = video.videoWidth / 2;
                    canvas.height = video.videoHeight / 2;
                    const ctx = canvas.getContext('2d');

                    window.cameraWebSocket = new WebSocket(`ws://${window.location.host}/ws/camera`);
                    
                    function sendFrame() {
                        if (window.cameraWebSocket && window.cameraWebSocket.readyState === WebSocket.OPEN) {
                            ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, canvas.width, canvas.height);
                            window.cameraWebSocket.send(canvas.toDataURL('image/jpeg', 0.2)); // Added quality parameter
                            window.cameraStreamActive && setTimeout(sendFrame, 1000 / 60);
                        }
                    }

                    window.cameraWebSocket.onopen = () => {
                        console.log("WebSocket connection opened");
                        window.cameraStreamActive = true;
                        sendFrame();
                    };

                    window.cameraWebSocket.onclose = () => {
                        console.log("WebSocket connection closed");
                        window.cameraStreamActive = false;
                        stream.getTracks().forEach(track => track.stop());
                    };
                    
                    return "Camera stream setup complete";
                } catch (error) {
                    console.error("Error accessing camera:", error);
                    throw error;
                }
            }

            return await setupCameraStream();
            """)
            logger.info(f"start_camera_stream_async result: {result}")
            self.stream_active = True
            self.stream_start_time = asyncio.get_event_loop().time()
            self.captured_frames.clear()
            return result
        except Exception as e:
            logger.error(f"Error in start_camera_stream_async: {str(e)}")
            return f"Error: {str(e)}"

    async def stop_camera_stream_async(self) -> str:
        try:
            result = await eval_js(self.page, """
            if (window.cameraWebSocket) {
                window.cameraStreamActive = false;
                window.cameraWebSocket.close();
                window.cameraWebSocket = null;
            }
            return 'Camera stream stopped';
            """)
            logger.info(f"stop_camera_stream_async result: {result}")
            self.stream_active = False
            self.stream_start_time = None
            return result
        except Exception as e:
            logger.error(f"Error in stop_camera_stream_async: {str(e)}")
            return f"Error: {str(e)}"

    async def handle_camera_frame(self, frame_data: str):
        if self.stream_active:
            await self.update_webcam_image(frame_data)
            self.capture_timed_frames(frame_data)

    def capture_timed_frames(self, frame_data: str):
        current_time = asyncio.get_event_loop().time()
        if self.stream_start_time is None:
            self.stream_start_time = current_time

        elapsed_time = current_time - self.stream_start_time

        for seconds in [1, 2, 3]:
            if elapsed_time >= seconds and seconds not in self.captured_frames:
                self.captured_frames[seconds] = Frame(frame_data, current_time)

    def get_captured_frames(self) -> List[Frame]:
        return list(self.captured_frames.values())

    async def update_webcam_image(self, frame_data: str) -> None:
        if not frame_data:
            print("Empty data!")
            return
        
        # Frame data is already in Data URI format
        new_src = frame_data 

        self.video_image1.visible = True
        self.video_image2.visible = True

        # Determine which image to update
        if self.current_video_image == self.video_image1:
            image_to_update = self.video_image2
            image_to_hide = self.video_image1
        else:
            image_to_update = self.video_image1
            image_to_hide = self.video_image2

        # Update the hidden image
        image_to_update.src = new_src
        image_to_update.update()
        
        # Wait for the browser to load the new image
        await asyncio.sleep(0.02)  # Adjust this delay if needed
        
        # Update opacity
        image_to_update.opacity = 1
        image_to_hide.opacity = 0
        self.current_video_image = image_to_update

        # Final update to apply opacity changes
        self.video_stack.update()