Sunday, 15 March 2015

Capturing Screenshots from the Command Line using Java

A few weeks ago I spent most of a day trying to work out how to capture a screen shot from the command line on Windows.  MS don't provide anything to do this.  You can manually [Prtscr], open Paint, Ctrl-V, Save As, but you can't automate it.  And you can't seem to automate the SnippingTool - or even run it from a script.  There are tools you can download from dodgy websites, but I don't want to do that.

It was a bit annoying to realize that I should have started with Java.  The follow captures all attached screens with JDK 1.7:
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;

import javax.imageio.ImageIO;

public class Snapshot {

    public static void main(String[] args) throws Exception {
        Robot robot = new Robot();
        GraphicsEnvironment ge =
                GraphicsEnvironment.getLocalGraphicsEnvironment();
        int screenNumber = 1;
        for(GraphicsDevice gd : ge.getScreenDevices()){
            DisplayMode dm = gd.getDisplayMode();
            Rectangle rect = new Rectangle(dm.getWidth(),
                                              dm.getHeight());

            BufferedImage image = robot.createScreenCapture(rect);
            File f = new File("screen" + screenNumber + ".png");
            ImageIO.write(image, "PNG", f);
        }
    }
}

Using Maven and Eclipse with Native Shared Libraries

Looking around the web I found that Maven can manage native shared libraries to an extent. It can allow you to package up .dll or .so files as dependencies and put them in jars in your repo. 

However, to be useful shared libraries have to be unpacked to disk and have their original name, before they can be loaded when your java program or tests run. Some clever people have ignored the maven "dll" classifier and the "so" type, and written code to unpack their libraries from the applications own jars into temporary directories on disk on start up.

I have yet to see a way to consistently arrange for shared libraries to be unpacked when my code is deployed in different target environments.  I might want to use it from within a WAR deployed into a Tomcat server running in Linux within the cloud.

When you come to deploy on unix like systems, the platform will have its own system for managing natives, and trying to do this in your maven pom files will be a wasted effort.

So my approach is to assume that the shared natives will be available in the target environment, and I will arrange for them to be there separately.  I will also do the same for my maven build, and provide an environment variable to tell the build where to find them.  Maven will also set up my Eclipse workspace.

I am using OpenCV 3.0.0-beta which has a number of shared native libraries and a JNI wrapper jar to allow access to them.  If you don't have a JNI jar, then you may need to create an empty one.

In my project, I have a parent pom and a sub-project that wraps up my dependency on OpenCV.  The parent pom provides a check that the required set-up has been done, and sub-projects will use this as appropriate.  If the required environment variable is missing - it outputs a message telling the user what to do.
<project ...>

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.yourcompany.product</groupId>
  <artifactId>parent</artifactId>
  <packaging>pom</packaging>
  <version>${product-version}</version>

  <modules>
    <module>opencv</module>
    ...
  </modules>

  <properties>
    <product-version>0.1-SNAPSHOT</product-version>
    ...
    <opencv-version>3.0.0-beta</opencv-version>
    <opencv-libraries>${env.OPENCV_LIBRARIES}</opencv-libraries>
    <eclipse-opencv-libname>OPENCV-3.0.0-BETA</eclipse-opencv-libname>
  </properties>

  <build>

    <plugins>
      ...
    </plugins>

    <pluginManagement>
      ...
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-enforcer-plugin</artifactId>
          <executions>
            <execution>
              <id>check-opencv-libraries</id>
              <goals>
                <goal>enforce</goal>
              </goals>
              <configuration>
                <rules>
                  <requireEnvironmentVariable>
                    <variableName>OPENCV_LIBRARIES</variableName>
                    <message><![CDATA[
OPENCV_LIBRARIES
================
The OPENCV_LIBRARIES environment variable needs to be set to the directory
containing the OpenCV dynamic shared libraries required for your platform.

You can download these from http://opencv.org/downloads.html.
These may need to be built for some platforms.

For example on 64bit Windows you might create the directory:
  C:\tools\opencv\3.0.0-beta\x64
and copy the following DLL files from the distribution to it:
  opencv\build\java\x64\opencv_java300.dll
  opencv\build\x64\vc12\bin\opencv_world300.dll
  opencv\build\x64\vc12\bin\opencv_ffmpeg300_64.dll

On Debian Linux you may install the libraries with:
  sudo apt-get install opencv
and set OPENCV_LIBRARIES to /usr/shared/lib or wherever.

OpenCV JNI jar
==============
The prebuilt OpenCV JNI jar for these dynamic libraries can be found in the
Windows distribution and put into your local maven repository with the command:

  mvn install:install-file -Dfile=opencv/build/java/opencv-300.jar \
     -DgroupId=org.opencv -DartifactId=opencv-java-api  \
     -Dversion=3.0.0-beta -Dpackaging=jar

Eclipse Setup
=============
Go to Window -> Preferences -> Java -> Build Path -> User Libraries
Create a new library called ${eclipse-opencv-libname}
Add the external jar from your local repo (opencv-java-api-3.0.0-beta.jar).
Expand the library and jar, highlight "Native library location", then "Edit"
and set it to the location of the OPENCV_LIBRARIES directory.
]]>
                    </message>
                  </requireEnvironmentVariable>
                </rules>
              </configuration>
            </execution>
          </executions>
        </plugin>

        <!-- Makes the natives available to unit tests -->
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <configuration>
            <argLine>-Djava.library.path=${opencv-libraries}</argLine>
          </configuration>
        </plugin>

    </pluginManagement>
  </build>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.yourcompany.product</groupId>
        <artifactId>opencv</artifactId>
        <version>${product-version}</version>
      </dependency>

      <!-- See note above concerning OPENCV_LIBRARIES -->
      <dependency>
        <groupId>org.opencv</groupId>
        <artifactId>opencv-java-api</artifactId>
        <version>${opencv-version}</version>
      </dependency>

      ...
    </dependencies>
  </dependencyManagement>

</project>
Then in the opencv sub-project you need to use the rule enforcer, and supplement the Eclipse classpath generated by "mvn eclipse:eclipse".
<project ...>

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>com.yourcompany.product</groupId>
    <artifactId>parent</artifactId>
    <version>${product-version}</version>
  </parent>

  <artifactId>opencv</artifactId>
  <packaging>jar</packaging>

  <build>
    <plugins>
      <!-- Enforce parent rules about libraries being available -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-eclipse-plugin</artifactId>
        <configuration>
          <!--
             In eclipse we use the opencv JNI jar via a user library,
             which will include the native shared libraries.
          -->
          <excludes>
            <exclude>org.opencv:opencv-java-api</exclude>
          </excludes>
          <classpathContainers>
            <classpathContainer>
              org.eclipse.jdt.launching.JRE_CONTAINER
            </classpathContainer>
            <classpathContainer>
              org.eclipse.jdt.USER_LIBRARY/${eclipse-opencv-libname}
            </classpathContainer>
          </classpathContainers>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.opencv</groupId>
      <artifactId>opencv-java-api</artifactId>
    </dependency>
    ...
  </dependencies>

</project>
Now when you build the sub-project, it will fail and give you instructions on how to set up the native libraries.  You should be able to follow these instructions yourself to do the one-off install of the native shared libraries and Eclipse workspace set up.

Monday, 24 November 2014

Using Code::Blocks as your Rust IDE on Windows

Warnings:
  • Code::Blocks does not support Rust formatting, code completion or syntax colouring - or at least not yet.
  • I haven't yet done much development with Rust, or Code::Blocks, so your mileage may be disappointing.  I'm just happy to be able to edit, build and debug with it.
  • You will need a certain level of understanding of configuring Windows, as I have skipped such things as setting environment variables.

 

Installing Rust

Download and install Rust from http://www.rust-lang.org/install.html.  I am using the 32 bit windows version (rust-nightly-i686-pc-windows-gnu.exe), because it matches the version of Code Blocks I want to use.

During the install uncheck all the plugins - they add a lot of menus, tabs and options that just confuse things.  Do check the box that adds Code::Blocks to your PATH.

Don't try to use it yet.  For Windows you still need MinGW version of the linker that comes with Code::Blocks.

 

Installing Code::Blocks

Download and install Code::Blocks from http://www.codeblocks.org/downloads/binaries. I am using codeblocks-13.12mingw-setup.exe

During the install check the box to add the MinGW bin directory to you PATH.  This will probably be "C:\Program Files (x86)\CodeBlocks\MinGW\bin".

Open a command prompt and type:
ld --version
You should see: "GNU ld (GNU Binutils)..."

Create a file called hello.rs that contains:
fn main() {
    println!("Hello world");
}
At the command prompt type
rustc hello.rs
If this works type
hello.exe
and it should print "Hello world".

If you have got this far, you have a working Rust compiler.

 

Installing Cargo

Next we really need Cargo (the Rust build tool).  We will use it to do all the compilation.  You could configure Code::Blocks to drive the compiler directly, but this way is simpler for now.

Go to https://crates.io/install, get the cargo-nightly-i686-pc-windows-gnu.tar.gz distribution and unpack the cargo.exe in it.  Normally you would expect Windows executable to be packed in a ZIP file, and this format might present you with problems.  However, this can be unpacked with 7-zip which you can install from http://www.7-zip.org. When you unpack the distribution, you will find another file to unpack, but eventually you find cargo.exe in a bin folder.

Put cargo.exe into a directory on your PATH.  I copied it into the Rust bin directory - but you will have to do so as an administrator.

Test cargo out in the command prompt by typing
cargo new hello_world --bin
This will create a new project directory.  Change to the new directory, build and run the result by typing:
cargo run

Configuring the project in Code::Blocks

Start up Code::Blocks.

Go to Settings -> Compler..., find "No Compiler" in the drop down, click Copy and name it "Rust Compiler".

On the Toolchain tab, select Debugger "GDB/CDB debugger", set the Make program to "cargo".

Click the button next to "C compiler" and find the rustc.exe within the installation.  We won't be using it but CodeBlocks gets upset if this is not set.

Go to File -> New -> Project -> Empty Project and Go

Under "Folder to create project in" find the directory we ran cargo in, and give the project the title hello_word.

On the next dialog, select the "Rust Compiler" compiler, and set the debug output dir to "target\".
Uncheck the Release config, and finish.

Right click the "hello_world" project and "add files ...", and select "Cargo.toml" and "src/main.rs".

Right click the "hello_world" project and "properties", set Makefile to "Cargo.toml" and check "This is a custom Makefile.  Then click "Project's build options...", on the "Make" commands tab, select "Debug" and set the clean project option to "$make clean" and the others to "$make build".  Then click "OK".

On the "Build targets" tab set the Type to "Console application", and "Output filename" to "target\hello_world.exe".  Then "OK".

Find and open main.rs. Right click on line 2  and add a break point.  Click the Debug button, the program should build, run and stop at line 2.

 

Syntax Colouring Fudge

We can now build and debug, but we don't have any syntax highlighting.  The underlying editor supports Rust, but Code::Blocks doesn't yet configure it.  So for now we will piggyback on the C syntax colouring.

Go to Setting -> Editor... -> Syntax highlighting -> C/C++ and Filemasks...and add "*.rs".

Click keywords and add "let" and "fn" (and any other keywords you can think of) to the first set.

The syntax colouring and formatting needs more work - this is just a fudge.  The underlying editor (Scintilla) has a Rust lexer but there is no config for it in "share/CodeBlocks/lexers". I presume that config could be derived from one already here, and giving it an index of 111.  See SciLexer.h in the Scintilla 3.51 sources. (also see: Creating a custom lexer for Code::Blocks editor and the Scintilla sources.)

Monday, 5 November 2012

Using Flazr as a Streaming Media Server

I'm no expert on streaming video, but I've been experimenting with Flazr on Windows.  Flazr can stream .flv and .f4v streams.  Here is my adventures with FLV.

I've used Tomcat and JWPlayer to create a web client to consume the flash video stream.  FFMpeg also gets used at various points.

Streaming Video-on-Demand (VOD)

  • Download Flazr from flazr.com, unzip into a folder and run server-start.bat.
  • Download a binary distribution of Tomcat from tomcat.apache.org, unzip into a folder and run bin\startup.bat.
  • Download JWPlayer from longtailvideo.com and unzip it.
  • Within the Tomcat's webapps\ROOT directory create a folder called streaming, and copy jwplayer.js, player.swf and preview.jpg from the JWPlayer bundle into it.
  • Copy a sample .flv file into the home\apps\vod folder.  Call it video.flv or change the following HTML.
    Example flv files are a bit hard to find so you may need to convert a .mp4 with FFMpeg.  Try converting the video.mp4 file that came with JWPlayer:
    ffmpeg -i video.mp4 -f flv video.flv
  • Create the following HTML file as webapps\ROOT\streaming\vodtest.html within the Tomcat directory.  (See the JWPlayer setup wizard for other configuration options)
<html>
  <head>
    <script type='text/javascript' src='jwplayer.js'>
    </script>

  </head>
  <body>
    <div id='mediaspace'>This text will be replaced</div>
    <script type='text/javascript'>
      jwplayer('mediaspace').setup({
        'flashplayer': 'player.swf',
        'image': 'preview.jpg',
        'file': 'video.flv',
        'streamer': 'rtmp://localhost/vod',
        'controlbar': 'over',
        'duration': '34',
        'fullscreen': 'true',
        'stretching': 'fill',
        'width': '470',
        'height': '320'
      });
    </script>
  </body>
</html>

Streaming a Live Video Stream

Here we use a client to stream a video to the Flazr server which it then relays to connected clients.

  • Create the following HTML file as webapps\ROOT\streaming\livetest.html within the Tomcat directory.
<html>
  <head>
    <script type='text/javascript' src='jwplayer.js'>
    </script>

  </head>
  <body>
    <div id='mediaspace'>This text will be replaced</div>
    <script type='text/javascript'>
      jwplayer('mediaspace').setup({
        'flashplayer': 'player.swf',
        'image': 'preview.jpg',
        'file': 'mystream',
        'streamer': 'rtmp://localhost/myapp',
        'controlbar': 'none',
        'autostart': 'true',
        'width': '470',
        'height': '320'
      });
    </script>
  </body>
</html>
  • Publish the FLV file as a stream to server using the Flazr client:
    client.bat -live -host localhost -app myapp mystream video.flv You can send it multiple times:
    client.bat -loop 100 -live -host localhost -app myapp mystream video.flv
  • Now browse to http://localhost:8080/streaming/livetest.html. You should see the stream playing.

You can use FFMpeg to do the same thing, and we need to use it in the next section:
    ffmpeg -i video.flv -re -f flv rtmp://localhost/myapp/mystream

For more information see the FFMpeg Streaming Guide.

Streaming a Webcam

This re-uses the stream we set up above.
  • List the devices on your machine using FFMpeg to find the name of your webcam and microphone:
    ffmpeg -f dshow -list_devices true -i dummy
  • List the options on your webcam device using its name - this will tell you what sizes and frame rates it supports:
    ffmpeg -y -f dshow -list_options true -i video="<webcam-name>"
  • Stream your webcam using FFMpeg in the following way:
    ffmpeg -y -loglevel warning -f dshow
      -i video="<webcam-name>":audio="<mic-name>"
      -r <frame-rate> -s <width>x<height> -threads 2
      -f flv rtmp://localhost/myapp/mystream
  • Browse to http://localhost:8080/streaming/livetest.html and you should see your webcam output.

For me the capture command is:
  ffmpeg -y -loglevel warning -f dshow -i
    video="HP HD Webcam [Fixed]":audio="Integrated Microphone Array (ID"
    -r 15 -s 320x240 -threads 2
    -f flv rtmp://localhost/myapp/mystream

For more info see: FFMpeg Guide to Capturing Webcam Input.

Things to bear in mind

  • Licenses -Flazr is LGPL.  JWPlayer has a number of conditions attached to its commercial use.  FFMpeg is mainly LGPL but some options are GPL.
  • No security implications has been looked at.  Without further work (firewalls etc) anyone could send and receive streams with your Flazr server.

Thursday, 27 September 2012

Outputting CDATA sections with JAXB

Here is a non-deprecated way to output a JAXB object model with some elements wrapped in CDATA sections.

Previously you could do this with an Apache OutputFormat as shown on the JAXB site.  This class which was non-standard, is now deprecated with a message about using the DOM Level 3 LSSerializer which is part of the Load and Save API.  I tried and tried, but I can not see a way to use that to output CDATA sections, so I have come up with a different way.

The secret is to marshal your JAXB object into a DOM and then use a null XSLT transformer.  Transformers allow you to name the elements you want wrapped in CDATA tags.

// Generating context uses resources - do it once and reuse across threads
JAXBContext jaxbContext = JAXBContext.newInstance(FWCoreContainer.class);

// Marshaller is not thread-safe
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

// If validating using a schema
SchemaFactory factory = SchemaFactory.newInstance(
    XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(xsd.getURL());
jaxbMarshaller.setSchema(schema);

// Create an empty DOM document
// DocumentBuilderFactory is not thread-safe
DocumentBuilderFactory docBuilderFactory =
    DocumentBuilderFactory.newInstance();
Document document =
    docBuilderFactory.newDocumentBuilder().newDocument();

// Marshall the feed object into the empty document.
jaxbMarshaller.marshal(jaxbObject, document);

// Transform the DOM to the output stream
// TransformerFactory is not thread-safe
TransformerFactory transformerFactory =
    TransformerFactory.newInstance();
Transformer nullTransformer = transformerFactory.newTransformer();
nullTransformer.setOutputProperty(OutputKeys.INDENT, "yes");
nullTransformer.setOutputProperty(
    OutputKeys.CDATA_SECTION_ELEMENTS,
     "myElement myOtherElement");
nullTransformer.transform(new DOMSource(document),
     new StreamResult(writer/stream));

Tuesday, 21 August 2012

How to use JavaMail to Send EMail with Embedded Images

A lot of us use email clients that are setup to not display external images by default.  We do this so that those senders of spam don't know that we have read the email.  The downside is that a lot of our incoming email looks rubbish.

I assume that most of the email that gets through the spam filter is not trying to track me, and that the use of external images is simply a lack of understanding of how to use embedded images.


Here is my example of using JavaMail to send email with embedded inline images:
// HTML we want to send.
// Note the "cid:" that precedes the image reference.
String html =
        "<html>" +
        "  <head>" +
        "    <style type='text/css'>" +
        "      body {background-color:blue}" +
        "      p {color:white}" +
        "    </style>" +
        "  </head>" +
        "  <body>" +
        "    <p><img src='cid:icon'></p>" +
        "    <p>Dear Bill</p>\n" +
        "    <p>This is a test mail.</p>" +
        "  </body>" +
        "</html>";

// Create Email content
// Use related (rather than mixed) mime subtype,
// otherwise Thunderbird will not display images inline.
Multipart multipart = new MimeMultipart("related");

// Create the HTML message part and add it to the email content.
MimeBodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(html, "text/html");
multipart.addBodyPart(messageBodyPart);

// Read the image from a file and add it to the email content.
// Note the "<>" added around the content ID used in the HTML above.
// Setting Content-Type does not seem to be required.
// I guess it is determined from the file type
MimeBodyPart iconBodyPart = new MimeBodyPart();
DataSource iconDataSource = new FileDataSource(new File("./icon.jpg"));
iconBodyPart.setDataHandler(new DataHandler(iconDataSource));
iconBodyPart.setDisposition(Part.INLINE);
iconBodyPart.setContentID("<icon>");
iconBodyPart.addHeader("Content-Type", "image/jpeg");
multipart.addBodyPart(iconBodyPart);

// Create the connection to the SMTP server to send the email.
final String smtpAccountUsername = "myUsername";
final String smtpAccountPassword = "myPassword";
final String smtpServer = "smtp.mymailserver.com";
final String smtpServerPort = "587";

Properties props = new Properties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.host", smtpServer);
props.put("mail.smtp.port", smtpServerPort);

Session session = Session.getInstance(props,
    new javax.mail.Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication(
                    smtpAccountUsername, smtpAccountPassword);
        }
    });

// Create email, set the content and send.
MimeMessage mail = new MimeMessage(session);
mail.setRecipients(RecipientType.TO, InternetAddress.parse("bill@example.com"));
mail.setSubject("Mail Subject Line");
mail.setContent(multipart);

Transport.send(mail);

Sunday, 19 August 2012

How to use XInclude in XSLT stylesheets

XInclude is not enabled by default when using the built-in XSLT processor in Java 1.6.  The underlying Apache Xalan uses Apache Xerces which does support it, but this is not directly accessible through the standard API.

Here is an example of how we would normally use XSLT:

// During app startup
TransformerFactory transformerFactory =
                TransformerFactory.newInstance();
Templates stylesheet = transformerFactory.newTemplates(
                new StreamSource("stylesheet.xsl"));

// Transformation
Source source = new StreamSource(new File("input.xml"));
Result result = new StreamResult(new File("output.xml"));
stylesheet.newTransformer().transform(source, result);
To use XInclude within an XSLT stylesheet we need to read it using a DocumentBuilder which has been created a DocumentBuilderFactory.  This has the methods that allows us to enable XInclude.
// During app startup
DocumentBuilderFactory documentBuilderFactory =
                DocumentBuilderFactory.newInstance(); documentBuilderFactory.setXIncludeAware(true);
documentBuilderFactory.setNamespaceAware(true);
// We don't want xml:base and xml:lang attributes in the output.
documentBuilderFactory.setFeature(
                "http://apache.org/xml/features/xinclude/fixup-base-uris", false);
documentBuilderFactory.setFeature(
                "http://apache.org/xml/features/xinclude/fixup-language", false);

DocumentBuilder docBuilder =
                documentBuilderFactory.newDocumentBuilder();
Document stylesheetDoc = docBuilder.parse("stylesheet.xsl");
Source stylesheetSource = new DOMSource(stylesheetDoc);

TransformerFactory transformerFactory =
                TransformerFactory.newInstance();
Templates stylesheet =
                transformerFactory.newTemplates(stylesheetSource);

// Transformation
Source source = new StreamSource(new File("input.xml"));
Result result = new StreamResult(new File("output.xml"));
stylesheet.newTransformer().transform(source, result);
It is bit more verbose than the orginal, but this is the only way I have found to do this.
The stylesheet this uses might look like this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xi="http://www.w3.org/2001/XInclude"
      exclude-result-prefixes="xi xsl xml">
  <xsl:output method="html" />

  <xsl:template match="/myRootElement">
        <xi:include href="mail.html" parse="xml" />
  </xsl:template>

</xsl:stylesheet>
And the included sub-stylesheet that uses the data elements within the myRootElements might be:
  <html xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <body>
     <p><xsl:value-of select="name"/></p>
     <p><xsl:value-of select="email"/></p>
     <ul>
       <xsl:for-each select="list/a">
         <li><xsl:value-of select="."/></li>
       </xsl:for-each>
     </ul>
   </body>
  </html>
Of course you may wish to use XInclude within the input XML, in which case you need to build a DOMSource for the transformation in the same way.