11package com .github .sarxos .webcam .ds .gstreamer ;
22
3+ import static com .github .sarxos .webcam .ds .gstreamer .GStreamerDriver .FORMAT_MJPEG ;
4+
35import java .awt .Dimension ;
46import java .awt .image .BufferedImage ;
57import java .awt .image .DataBufferInt ;
@@ -39,16 +41,6 @@ public class GStreamerDevice implements WebcamDevice, RGBDataSink.Listener, Webc
3941 */
4042 private static final long LATENESS = 20 ; // ms
4143
42- /**
43- * First formats are better. For example video/x-raw-rgb gives 30 FPS on HD720p where
44- * video/x-raw-yuv only 10 FPS on the same resolution. The goal is to use these "better" formats
45- * first, and then fallback to less efficient when not available.
46- */
47- private static final String [] BEST_FORMATS = {
48- "video/x-raw-rgb" ,
49- "video/x-raw-yuv" ,
50- };
51-
5244 /**
5345 * Video format to capture.
5446 */
@@ -68,11 +60,16 @@ public class GStreamerDevice implements WebcamDevice, RGBDataSink.Listener, Webc
6860 */
6961 private final File vfile ;
7062
63+ private final GStreamerDriver driver ;
64+
7165 /* gstreamer stuff */
7266
7367 private Pipeline pipe = null ;
7468 private Element source = null ;
7569 private Element filter = null ;
70+ private Element jpegpar = null ;
71+ private Element jpegdec = null ;
72+ private Element [] elements = null ;
7673 private RGBDataSink sink = null ;
7774
7875 private Caps caps = null ;
@@ -98,12 +95,14 @@ public class GStreamerDevice implements WebcamDevice, RGBDataSink.Listener, Webc
9895 *
9996 * @param name the name of webcam device
10097 */
101- protected GStreamerDevice (String name ) {
98+ protected GStreamerDevice (GStreamerDriver driver , String name ) {
99+ this .driver = driver ;
102100 this .name = name ;
103101 this .vfile = null ;
104102 }
105103
106- protected GStreamerDevice (File vfile ) {
104+ protected GStreamerDevice (GStreamerDriver driver , File vfile ) {
105+ this .driver = driver ;
107106 this .name = null ;
108107 this .vfile = vfile ;
109108 }
@@ -122,10 +121,10 @@ private synchronized void init() {
122121 pipe = new Pipeline (name );
123122
124123 if (Platform .isWindows ()) {
125- source = ElementFactory .make ("dshowvideosrc" , "source " );
124+ source = ElementFactory .make ("dshowvideosrc" , "dshowvideosrc " );
126125 source .set ("device-name" , name );
127126 } else if (Platform .isLinux ()) {
128- source = ElementFactory .make ("v4l2src" , "source " );
127+ source = ElementFactory .make ("v4l2src" , "v4l2src " );
129128 source .set ("device" , vfile .getAbsolutePath ());
130129 }
131130
@@ -134,21 +133,20 @@ private synchronized void init() {
134133 sink .getSinkElement ().setMaximumLateness (LATENESS , TimeUnit .MILLISECONDS );
135134 sink .getSinkElement ().setQOSEnabled (true );
136135
137- filter = ElementFactory .make ("capsfilter" , "filter " );
136+ filter = ElementFactory .make ("capsfilter" , "capsfilter " );
138137
139- if (Platform .isLinux ()) {
140- pipe .addMany (source , filter , sink );
141- Element .linkMany (source , filter , sink );
142- pipe .setState (State .READY );
143- }
138+ jpegpar = ElementFactory .make ("jpegparse" , "jpegparse" );
139+ jpegdec = ElementFactory .make ("jpegdec" , "jpegdec" );
140+
141+ // if (Platform.isLinux()) {
142+ pipelineReady ();
143+ // }
144144
145145 resolutions = parseResolutions (source .getPads ().get (0 ));
146146
147- if (Platform .isLinux ()) {
148- pipe .setState (State .NULL );
149- Element .unlinkMany (source , filter , sink );
150- pipe .removeMany (source , filter , sink );
151- }
147+ // if (Platform.isLinux()) {
148+ pipelineStop ();
149+ // }
152150 }
153151
154152 /**
@@ -161,15 +159,15 @@ private Dimension[] parseResolutions(Pad pad) {
161159
162160 Caps caps = pad .getCaps ();
163161
164- format = findBestFormat (caps );
162+ format = findPreferredFormat (caps );
165163
166164 LOG .debug ("Best format is {}" , format );
167165
168166 Dimension r = null ;
169167 Structure s = null ;
170168 String mime = null ;
171169
172- int n = caps .size ();
170+ final int n = caps .size ();
173171 int i = 0 ;
174172
175173 Map <String , Dimension > map = new HashMap <String , Dimension >();
@@ -190,19 +188,19 @@ private Dimension[] parseResolutions(Pad pad) {
190188
191189 } while (i < n );
192190
193- Dimension [] resolutions = new ArrayList <Dimension >(map .values ()).toArray (new Dimension [map . size () ]);
191+ final Dimension [] resolutions = new ArrayList <Dimension >(map .values ()).toArray (new Dimension [0 ]);
194192
195193 if (LOG .isDebugEnabled ()) {
196194 for (Dimension d : resolutions ) {
197- LOG .debug ("Resolution detected {}" , d );
195+ LOG .debug ("Resolution detected {} with format {} " , d , format );
198196 }
199197 }
200198
201199 return resolutions ;
202200 }
203201
204- private static String findBestFormat (Caps caps ) {
205- for (String f : BEST_FORMATS ) {
202+ private String findPreferredFormat (Caps caps ) {
203+ for (String f : driver . getPreferredFormats () ) {
206204 for (int i = 0 , n = caps .size (); i < n ; i ++) {
207205 if (f .equals (caps .getStructure (i ).getName ())) {
208206 return f ;
@@ -287,19 +285,17 @@ public void open() {
287285 caps .dispose ();
288286 }
289287
290- caps = Caps .fromString (String .format ("%s,width=%d,height=%d" , format , size .width , size .height ));
291-
288+ caps = Caps .fromString (String .format ("%s,framerate=30/1,width=%d,height=%d" , format , size .width , size .height ));
292289 filter .setCaps (caps );
293290
294- LOG .debug ("Link elements" );
291+ LOG .debug ("Using filter caps: {}" , caps );
295292
296- pipe . addMany ( source , filter , sink );
297- Element . linkMany ( source , filter , sink );
298- pipe . setState ( State . PLAYING );
293+ pipelinePlay ( );
294+
295+ LOG . debug ( "Wait for device to be ready" );
299296
300297 // wait max 20s for image to appear
301298 synchronized (this ) {
302- LOG .debug ("Wait for device to be ready" );
303299 try {
304300 this .wait (20000 );
305301 } catch (InterruptedException e ) {
@@ -308,6 +304,51 @@ public void open() {
308304 }
309305 }
310306
307+ private void pipelineElementsReset () {
308+ elements = null ;
309+ }
310+
311+ private Element [] pipelineElementsPrepare () {
312+ if (elements == null ) {
313+ if (FORMAT_MJPEG .equals (format )) {
314+ elements = new Element [] { source , filter , jpegpar , jpegdec , sink };
315+ } else {
316+ elements = new Element [] { source , filter , sink };
317+ }
318+ }
319+ return elements ;
320+ }
321+
322+ private void pipelineElementsLink () {
323+ final Element [] elements = pipelineElementsPrepare ();
324+ pipe .addMany (elements );
325+ if (!Element .linkMany (elements )) {
326+ LOG .warn ("Some elements were not successfully linked!" );
327+ }
328+ }
329+
330+ private void pipelineElementsUnlink () {
331+ final Element [] elements = pipelineElementsPrepare ();
332+ Element .unlinkMany (elements );
333+ pipe .removeMany (elements );
334+ }
335+
336+ private void pipelineReady () {
337+ pipelineElementsLink ();
338+ pipe .setState (State .READY );
339+ }
340+
341+ private void pipelinePlay () {
342+ pipelineElementsReset ();
343+ pipelineElementsLink ();
344+ pipe .setState (State .PLAYING );
345+ }
346+
347+ private void pipelineStop () {
348+ pipe .setState (State .NULL );
349+ pipelineElementsUnlink ();
350+ }
351+
311352 @ Override
312353 public void close () {
313354
@@ -317,13 +358,9 @@ public void close() {
317358
318359 LOG .debug ("Closing GStreamer device" );
319360
320- image = null ;
321-
322- LOG .debug ("Unlink elements" );
361+ pipelineStop ();
323362
324- pipe .setState (State .NULL );
325- Element .unlinkMany (source , filter , sink );
326- pipe .removeMany (source , filter , sink );
363+ image = null ;
327364 }
328365
329366 @ Override
@@ -337,11 +374,13 @@ public void dispose() {
337374
338375 close ();
339376
340- filter .dispose ();
341377 source .dispose ();
378+ filter .dispose ();
379+ jpegpar .dispose ();
380+ jpegdec .dispose ();
381+ caps .dispose ();
342382 sink .dispose ();
343383 pipe .dispose ();
344- caps .dispose ();
345384 }
346385
347386 @ Override
0 commit comments