Skip to content

Commit

Permalink
GStreamer 1.0 capture driver, continuation
Browse files Browse the repository at this point in the history
  • Loading branch information
sarxos committed Dec 4, 2016
1 parent 301f50a commit 4750d89
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 241 deletions.
2 changes: 1 addition & 1 deletion webcam-capture-drivers/driver-gst1/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.1.0</version>
<version>4.2.2</version>
</dependency>
<dependency>
<groupId>org.freedesktop.gstreamer</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamViewer;
import com.github.sarxos.webcam.ds.gst1.Gst1Driver;


public class Gst1Example {

static {
Webcam.setDriver(new Gst1Driver());
}

public static void main(String[] args) throws InterruptedException {
new WebcamViewer();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,10 @@
import java.awt.image.BufferedImage;
import java.io.File;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import org.bridj.Platform;
import org.freedesktop.gstreamer.Bin;
import org.freedesktop.gstreamer.Caps;
import org.freedesktop.gstreamer.Element;
Expand All @@ -28,6 +23,7 @@
import com.github.sarxos.webcam.WebcamDevice;
import com.github.sarxos.webcam.WebcamException;
import com.github.sarxos.webcam.ds.gst1.impl.AppSinkNewSampleListener;
import com.github.sarxos.webcam.ds.gst1.impl.GsUtils;
import com.github.sarxos.webcam.util.Initializable;
import com.github.sarxos.webcam.util.WebcamInitializer;

Expand Down Expand Up @@ -65,7 +61,7 @@ public class Gst1Device implements WebcamDevice, Initializable {
"RGB"
};

private final AtomicReference<BufferedImage> ref = new AtomicReference<>();
private final Exchanger<BufferedImage> exchanger = new Exchanger<>();
private final WebcamInitializer initializer = new WebcamInitializer(this);
private final String name;

Expand All @@ -89,6 +85,18 @@ public Gst1Device(String name) {
this.name = name;
}

@Override
public void initialize() {
pipeLink();
pipeReady();
}

@Override
public void teardown() {
pipeStop();
pipeUnlink();
}

@Override
public void open() {

Expand All @@ -110,117 +118,75 @@ public void open() {
.toString();
final Caps caps = Caps.fromString(str);

// LOG.debug("Opening device {} with caps {}", name, caps);
LOG.debug("Opening device {} with caps {}", name, caps);

filter.setCaps(caps);

// pipeLink();
pipePlay();

open = true;
}

@Override
public void initialize() {
pipeLink();
pipeReady();
}

private Dimension[] findResolutions() {

// final boolean isLinux = Platform.isLinux();
initializer.initialize();

// if (isLinux) {
// pipeLink();
// pipeReady();
// }
pipeReady();

try {
return findResolutions0();
} finally {
// if (isLinux) {
// pipeStop();
// pipeUnlink();
// }
pipeStop();
}
}

private Dimension[] findResolutions0() {

final Element source = getSource();
final List<Pad> pads = source.getPads();

if (pads.isEmpty()) {
throw new WebcamException("Cannot find supported resolutions because pads list is empty!");
}

final Map<String, Dimension> map = new LinkedHashMap<>();

final Pad pad = pads.get(0);
final Pad pad = getSourcePad();
final Caps caps = pad.getCaps();
final String name = getName();
final String format = getFormat();

for (int i = 0; i < caps.size(); i++) {

final Structure structure = caps.getStructure(i);
final String f = structure.getString("format");

LOG.trace("Found device {} caps {}", name, structure);

if (!Objects.equals(f, format)) {
continue;
}

final Dimension resolution = capsStructureToResolution(structure);
return GsUtils.getResolutionsFromCaps(caps, format);
}

if (resolution != null) {
map.put(resolution.width + "x" + resolution.height, resolution);
}
}
private Pad getSourcePad() {

Dimension[] resolutions = new ArrayList<Dimension>(map.values()).toArray(new Dimension[map.size()]);
final Element source = getSource();
final List<Pad> pads = source.getPads();

if (LOG.isDebugEnabled()) {
for (Dimension d : resolutions) {
LOG.debug("Resolution detected {}", d);
}
if (pads.isEmpty()) {
throw new WebcamException("Cannot find supported resolutions because pads list is empty!");
}

return resolutions;
return pads.get(0);
}

private void pipeLink() {

// Bin source = Bin.launch("v4l2src device=/dev/video0", true); // ! videoconvert
final Bin pipe = getPipeline();
final Element source = getSource();
final Element convert = getConvert();
final Element filter = getFilter();
final Element sink = getSink();
final Bin pipe = getPipeline();

pipe.addMany(source, convert, filter, sink);
// pipe.addMany(bin, filter, sink);

if (!Element.linkMany(source, convert, filter, sink)) {
// if (!Element.linkMany(bin, filter, sink)) {
throw new IllegalStateException("Unable to link elements to pipeline bin");
}
}

private void pipeUnlink() {

final Bin pipe = getPipeline();
final Element source = getSource();
final Element convert = getConvert();
final Element filter = getFilter();
final Element sink = getSink();
final Bin pipe = getPipeline();

Element.unlinkMany(source, convert, filter, sink);
// Element.unlinkMany(bin, filter, sink);

pipe.removeMany(source, convert, filter, sink);
// pipe.removeMany(bin, filter, sink);
}

private void pipeReady() {
Expand Down Expand Up @@ -264,7 +230,11 @@ public Element getConvert() {
}

private Element createConvert() {
return ElementFactory.make("videoconvert", getName() + "-videoconvert");

final String name = getName();
final String id = name + "-videoconvert";

return ElementFactory.make("videoconvert", id);
}

public Element getFilter() {
Expand All @@ -275,7 +245,11 @@ public Element getFilter() {
}

private Element createFilter() {
return ElementFactory.make("capsfilter", getName() + "-filter");

final String name = getName();
final String id = name + "-capsfilter";

return ElementFactory.make("capsfilter", id);
}

public AppSink getSink() {
Expand All @@ -288,23 +262,22 @@ public AppSink getSink() {
private AppSink createSink() {

final String format = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "BGRx" : "xRGB";
final String clazz = getClass().getSimpleName();
final String name = getName();
final String identifier = "GstVideoSink-" + name;
final AppSink videosink = new AppSink(identifier);
final String id = name + "-sink";
final AppSink sink = new AppSink(id);
final Caps caps = new Caps("video/x-raw,pixel-aspect-ratio=1/1,format=" + format);

LOG.debug("Creating video sink with caps {}", caps);

videosink.set("emit-signals", true);
videosink.connect(new AppSinkNewSampleListener(this));
videosink.setCaps(caps);
videosink.setMaximumLateness(LATENESS, TimeUnit.MILLISECONDS);
videosink.setQOSEnabled(true);
sink.set("emit-signals", true);
sink.connect(new AppSinkNewSampleListener(exchanger));
sink.setCaps(caps);
sink.setMaximumLateness(LATENESS, TimeUnit.MILLISECONDS);
sink.setQOSEnabled(true);

LOG.debug("Device {} videosing {} has been created", name, videosink);
LOG.debug("Device {} videosing {} has been created", name, sink);

return videosink;
return sink;
}

public Element getSource() {
Expand Down Expand Up @@ -341,8 +314,7 @@ public String getFormat() {

private String findBestFormat() {

final Element source = getSource();
final Pad pad = source.getPads().get(0);
final Pad pad = getSourcePad();
final Caps caps = pad.getCaps();

if (LOG.isDebugEnabled()) {
Expand Down Expand Up @@ -377,28 +349,6 @@ private String findBestFormat() {
throw new WebcamException("Cannot find best format");
}

private Dimension capsStructureToResolution(Structure structure) {

LOG.debug("Device {}, read resolution from caps strcuture {}", getName(), structure);

int w = -1;
int h = -1;

if (Platform.isWindows()) {
w = structure.getRange("width").getMinInt();
h = structure.getRange("height").getMinInt();
} else if (Platform.isLinux()) {
w = structure.getInteger("width");
h = structure.getInteger("height");
}

if (w > 0 && h > 0) {
return new Dimension(w, h);
} else {
return null;
}
}

@Override
public String getName() {
return name;
Expand Down Expand Up @@ -436,33 +386,30 @@ public void setResolution(Dimension resolution) {
@Override
public BufferedImage getImage() {

LOG.debug("Device {} get image", getName());

BufferedImage bi = ref.get();

while (bi == null) {
initializer.initialize();

try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new WebcamException("Thread has been interrupted", e);
}
LOG.debug("Device {} get image", getName());

bi = ref.get();
try {
return exchanger.exchange(null);
} catch (InterruptedException e) {
throw new WebcamException("Image exchange has been interrupted", e);
}

return bi;
}

@Override
public void close() {

// not initialized = do nothing, no need to close

if (!initializer.isInitialized()) {
return;
}
if (!open) {
return;
}

pipeStop();
pipeUnlink();

open = false;
}
Expand All @@ -472,18 +419,13 @@ public void dispose() {

close();

if (source != null) {
source.dispose();
}
if (filter != null) {
filter.dispose();
}
if (sink != null) {
sink.dispose();
}
if (pipe != null) {
pipe.dispose();
}
initializer.teardown();

GsUtils.dispose(source);
GsUtils.dispose(filter);
GsUtils.dispose(convert);
GsUtils.dispose(sink);
GsUtils.dispose(pipe);

disposed = true;
}
Expand All @@ -497,8 +439,4 @@ public boolean isOpen() {
public String toString() {
return getClass().getSimpleName() + " " + getName();
}

public AtomicReference<BufferedImage> getRef() {
return ref;
}
}
Loading

0 comments on commit 4750d89

Please sign in to comment.