App zur Steuerung des mpv Mediaplayers auf einem Raspberry Pi über HTTP
ulrich
2022-02-11 b388a5decf5957cbb119b8c020baef200760d6e1
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package org.tw.pi.framebuffer;
 
/*
*        This file is the JNI Java part of a Raspberry Pi FrameBuffer project.
*
*        Created 2013 by Thomas Welsch (ttww@gmx.de).
*
*        Do whatever you want to do with it :-)
*
**/
 
 
 
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
 
import javax.swing.JPanel;
 
 
/**
* This class is the Java front end for a simple to use FrameBuffer driver.
* Simple draw in the BufferedImage and all changes are transfered to the FrameBuffer device.<p>
* For testing purpose a dummy device is supported (via the devicename "dummy_160x128" instead of "/dev/fb1").<p<
* The Java process needs write access to the frame buffer device file.
* <p>
* It's used to drive small bit mapped screens connected via SPI, see
* http://www.sainsmart.com/blog/ada/
* <p>
* <p>
* My Linux kernel config for SPI display was:
* <pre>
* CONFIG_FB_ST7735=y
* CONFIG_FB_ST7735_PANEL_TYPE_RED_TAB=y
* CONFIG_FB_ST7735_RGB_ORDER_REVERSED=y
* CONFIG_FB_ST7735_MAP=y
* CONFIG_FB_ST7735_MAP_RST_GPIO=25
* CONFIG_FB_ST7735_MAP_DC_GPIO=24
* CONFIG_FB_ST7735_MAP_SPI_BUS_NUM=0
* CONFIG_FB_ST7735_MAP_SPI_BUS_CS=0
* CONFIG_FB_ST7735_MAP_SPI_BUS_SPEED=16000000
* CONFIG_FB_ST7735_MAP_SPI_BUS_MODE=0
* </pre>
* CONFIG_FB_ST7735_MAP_SPI_BUS_SPEED gives faster updates :-)
* <p>
* If you get the wrong colors, try the CONFIG_FB_ST7735_RGB_ORDER_REVERSED option !
*/
public class FrameBuffer {
 
        private static final int FPS = 60;                // Max. update rate
 
        private        String                        deviceName;
 
        private long                        deviceInfo;                // Private data from JNI C
 
        private        int                                width,height;
        private        int                                bits;
 
        private BufferedImage        img;
        private int[]                        imgBuffer;
 
        // -----------------------------------------------------------------------------------------------------------------
 
        private native long                openDevice(String device);
        private native void                closeDevice(long di);
        private native int                getDeviceWidth(long di);
        private native int                getDeviceHeight(long di);
        private native int                getDeviceBitsPerPixel(long di);
        private native boolean        updateDeviceBuffer(long di,int[] buffer);
 
        static {
                System.loadLibrary("FrameBufferJNI"); // FrameBufferJNI.dll (Windows) or FrameBufferJNI.so (Unixes)
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Open the named frame buffer device and starts the automatic update thread between the internal
         * BufferedImage and the device.
         *
         * @param deviceName        e.g. /dev/fb1 or dummy_320x200
         */
        public FrameBuffer(String deviceName) {
                this(deviceName,true);
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Open the named frame buffer device.
         *
         * @param deviceName        e.g. /dev/fb1 or dummy_320x200
         * @param autoUpdate        if true, starts the automatic update thread between the internal
         *                                                BufferedImage and the device.
         */
        public FrameBuffer(String deviceName, boolean autoUpdate) {
 
                this.deviceName = deviceName;
 
                deviceInfo = openDevice(deviceName);
 
                if (deviceInfo < 10) {
                        throw new IllegalArgumentException("Init. for frame buffer "+deviceName+" failed with error code "+deviceInfo);
                }
 
                this.width        = getDeviceWidth(deviceInfo);
                this.height        = getDeviceHeight(deviceInfo);
 
                System.err.println("Open with "+deviceName+" ("+deviceInfo+")");
                System.err.println(" width "+getDeviceWidth(deviceInfo));
                System.err.println(" height "+getDeviceHeight(deviceInfo));
                System.err.println(" bpp "+getDeviceBitsPerPixel(deviceInfo));
 
                // We always use ARGB image type.
                img                        = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                imgBuffer        = ((DataBufferInt) img.getRaster().getDataBuffer()).getBankData()[0];
 
                if (autoUpdate) new UpdateThread().start();
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        private ScreenPanel        screenPanel;
 
        /**
         * Returns a JPanel which represents the actual frame buffer device.
         *
         * @return        JPanel...
         */
        public JPanel getScreenPanel() {
                synchronized (deviceName) {
                        if (screenPanel != null) throw new IllegalStateException("Only one screen panel supported");
 
                        screenPanel = new ScreenPanel();
 
                        return screenPanel;
                }
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Internal helper class for displaying the current frame buffer image via a JPanel.
         */
        @SuppressWarnings("serial")
        private class ScreenPanel extends JPanel {
                public ScreenPanel() {
                        setPreferredSize(new Dimension(FrameBuffer.this.width,FrameBuffer.this.height));
                }
 
                @Override
                protected void paintComponent(Graphics g) {
                        super.paintComponent(g);
                        g.drawImage(img, 0, 0, null);
                }
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Internal helper class for refreshing the frame buffer display and/or JPanel.
         */
        private class UpdateThread extends Thread {
 
                UpdateThread() {
                        setDaemon(true);
                        setName("FB "+deviceName+ " update");
                }
 
                @Override
                public void run() {
                        final int SLEEP_TIME = 1000 / FPS;
 
                        while (deviceInfo != 0) {
 
                                if (updateScreen()) {
                                        if (screenPanel != null) {
                                                screenPanel.repaint();
                                        }
                                }
 
                                try {
                                        sleep(SLEEP_TIME);
                                } catch (InterruptedException e) {
                                        break;
                                }
 
                        }        // while
 
                }
 
        }        // class UpdateThread
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Returns the BufferedImage for drawing. Anything your draw here is synchronizet to the frame buffer.
         *
         * @return        BufferedImage of type ARGB.
         */
        public BufferedImage getScreen() {
                return img;
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Close the device.
         */
        public void close() {
                synchronized (deviceName) {
                        closeDevice(deviceInfo);
                        deviceInfo = 0;
                        img        = null;
                        imgBuffer = null;
                }
        }
 
        // -----------------------------------------------------------------------------------------------------------------
 
        /**
         * Update the screen if no automatic sync is used (see constructor autoUpdate flag).
         * This method is normally called by the autoUpdate thread.
         *
         * @return        true if the BufferedImage was changed since the last call.
         */
        public boolean updateScreen() {
                synchronized (deviceName) {
                        if (deviceInfo == 0) return false;
                        return updateDeviceBuffer(deviceInfo,imgBuffer);
                }
        }
 
 
}        // of class