I want to run the java code in command prompt.getting error: class interface or enum expected.Where to include packages(class files) in java file when java file need to run in command prompt..
My java code is:
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import java.org.xiph.speex.AudioFileWriter;
import org.xiph.speex.OggSpeexWriter;
import org.xiph.speex.PcmWaveWriter;
import java.org.xiph.speex.RawWriter;
import javax.sound.sampled.AudioFileWriter;
/**
* Java Speex Command Line Encoder.
*
* Currently this code has been updated to be compatible with release 1.0.3.
*
* #author Marc Gimpel, Wimba S.A. (mgimpel#horizonwimba.com)
* #version $Revision: 1.5 $
*/
public class JSpeexEnc
{
/** Version of the Speex Encoder */
public static final String VERSION = "Java Speex Command Line Encoder v0.9.7 ($Revision: 1.5 $)";
/** Copyright display String */
public static final String COPYRIGHT = "Copyright (C) 2002-2004 Wimba S.A.";
/** Print level for messages : Print debug information */
public static final int DEBUG = 0;
/** Print level for messages : Print basic information */
public static final int INFO = 1;
/** Print level for messages : Print only warnings and errors */
public static final int WARN = 2;
/** Print level for messages : Print only errors */
public static final int ERROR = 3;
/** Print level for messages */
protected int printlevel = INFO;
/** File format for input or output audio file: Raw */
public static final int FILE_FORMAT_RAW = 0;
/** File format for input or output audio file: Ogg */
public static final int FILE_FORMAT_OGG = 1;
/** File format for input or output audio file: Wave */
public static final int FILE_FORMAT_WAVE = 2;
/** Defines File format for input audio file (Raw, Ogg or Wave). */
protected int srcFormat = FILE_FORMAT_OGG;
/** Defines File format for output audio file (Raw or Wave). */
protected int destFormat = FILE_FORMAT_WAVE;
/** Defines the encoder mode (0=NB, 1=WB and 2=UWB). */
protected int mode = -1;
/** Defines the encoder quality setting (integer from 0 to 10). */
protected int quality = 8;
/** Defines the encoders algorithmic complexity. */
protected int complexity = 3;
/** Defines the number of frames per speex packet. */
protected int nframes = 1;
/** Defines the desired bitrate for the encoded audio. */
protected int bitrate = -1;
/** Defines the sampling rate of the audio input. */
protected int sampleRate = -1;
/** Defines the number of channels of the audio input (1=mono, 2=stereo). */
protected int channels = 1;
/** Defines the encoder VBR quality setting (float from 0 to 10). */
protected float vbr_quality = -1;
/** Defines whether or not to use VBR (Variable Bit Rate). */
protected boolean vbr = false;
/** Defines whether or not to use VAD (Voice Activity Detection). */
protected boolean vad = false;
/** Defines whether or not to use DTX (Discontinuous Transmission). */
protected boolean dtx = false;
/** The audio input file */
protected String srcFile;
/** The audio output file */
protected String destFile;
public AudioFileFormat.Type[] getAudioFileTypes() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
//#Override
public AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
//#Override
public int write(AudioInputStream stream, AudioFileFormat.Type type, OutputStream out) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
// #Override
public int write(AudioInputStream stream, AudioFileFormat.Type type, File file) throws IOException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
/**
* Builds a plain JSpeex Encoder with default values.
*/
/**
*
* #author Administrator
*/
/**
* Command line entrance:
* <pre>
* Usage: JSpeexEnc [options] input_file output_file
* </pre>
* #param args Command line parameters.
* #exception IOException
*/
}
/**
*
* #author Administrator
*/
public JSpeexEnc()
{
}
public static void main(final String[] args)
throws IOException
{
JSpeexEnc encoder = new JSpeexEnc();
if (encoder.parseArgs(args))
{
encoder.encode();
}
}
public boolean parseArgs(final String[] args, String srcFile, String destFile, int complexity, int nframes, boolean vbr, boolean vad, boolean dtx, int sampleRate, int channels, int mode, int quality, float vbr_quality, boolean printlevel, boolean WARN)
{
// make sure we have command args
if (args.length < 2) {
if (args.length==1 && (args[0].equalsIgnoreCase("-v") || args[0].equalsIgnoreCase("--version"))) {
version();
return false;
}
usage();
return false;
}
// Determine input, output and file formats
srcFile = args[args.length-2];
destFile = args[args.length-1];
if (srcFile.toLowerCase().endsWith(".wav"))
{
srcFormat = FILE_FORMAT_WAVE;
}
else
{
srcFormat = FILE_FORMAT_RAW;
}
if (destFile.toLowerCase().endsWith(".spx")) {
destFormat = FILE_FORMAT_OGG;
}
else if (destFile.toLowerCase().endsWith(".wav")) {
destFormat = FILE_FORMAT_WAVE;
}
else {
destFormat = FILE_FORMAT_RAW;
}
// Determine encoder options
for (int i=0; i<args.length-2; i++) {
if (args[i].equalsIgnoreCase("-h") || args[i].equalsIgnoreCase("--help")) {
usage();
return false;
}
else if (args[i].equalsIgnoreCase("-v") || args[i].equalsIgnoreCase("--version")) {
version();
return false;
}
else if (args[i].equalsIgnoreCase("--verbose")) {
printlevel = DEBUG;
}
else if (args[i].equalsIgnoreCase("--quiet")) {
printlevel = WARN;
}
else if (args[i].equalsIgnoreCase("-n") ||
args[i].equalsIgnoreCase("-nb") ||
args[i].equalsIgnoreCase("--narrowband")) {
mode = 0;
}
else if (args[i].equalsIgnoreCase("-w") ||
args[i].equalsIgnoreCase("-wb") ||
args[i].equalsIgnoreCase("--wideband")) {
mode = 1;
}
else if (args[i].equalsIgnoreCase("-u") ||
args[i].equalsIgnoreCase("-uwb") ||
args[i].equalsIgnoreCase("--ultra-wideband")) {
mode = 2;
}
else if (args[i].equalsIgnoreCase("-q") || args[i].equalsIgnoreCase("--quality")) {
try {
vbr_quality = Float.parseFloat(args[++i]);
quality = (int) vbr_quality;
}
catch (NumberFormatException e) {
usage();
return false;
}
}
else if (args[i].equalsIgnoreCase("-complexity")) {
try {
complexity = Integer.parseInt(args[++i]);
}
catch (NumberFormatException e) {
usage();
return false;
}
}
else if (args[i].equalsIgnoreCase("--nframes")) {
try {
nframes = Integer.parseInt(args[++i]);
}
catch (NumberFormatException e) {
usage();
return false;
}
}
else if (args[i].equalsIgnoreCase("--vbr")) {
vbr = true;
}
else if (args[i].equalsIgnoreCase("--vad")) {
vad = true;
}
else if (args[i].equalsIgnoreCase("--dtx")) {
dtx = true;
}
else if (args[i].equalsIgnoreCase("--rate")) {
try {
sampleRate = Integer.parseInt(args[++i]);
}
catch (NumberFormatException e) {
usage();
return false;
}
}
else if (args[i].equalsIgnoreCase("--stereo")) {
channels = 2;
}
else {
usage();
return false;
}
}
return true;
}
/**
* Prints the usage guidelines.
*/
public static void usage()
{
version();
System.out.println("");
System.out.println("Usage: JSpeexEnc [options] input_file output_file");
System.out.println("Where:");
System.out.println(" input_file can be:");
System.out.println(" filename.wav a PCM wav file");
System.out.println(" filename.* a raw PCM file (any extension other than .wav)");
System.out.println(" output_file can be:");
System.out.println(" filename.spx an Ogg Speex file");
System.out.println(" filename.wav a Wave Speex file (beta!!!)");
System.out.println(" filename.* a raw Speex file");
System.out.println("Options: -h, --help This help");
System.out.println(" -v, --version Version information");
System.out.println(" --verbose Print detailed information");
System.out.println(" --quiet Print minimal information");
System.out.println(" -n, -nb Consider input as Narrowband (8kHz)");
System.out.println(" -w, -wb Consider input as Wideband (16kHz)");
System.out.println(" -u, -uwb Consider input as Ultra-Wideband (32kHz)");
System.out.println(" --quality n Encoding quality (0-10) default 8");
System.out.println(" --complexity n Encoding complexity (0-10) default 3");
System.out.println(" --nframes n Number of frames per Ogg packet, default 1");
System.out.println(" --vbr Enable varible bit-rate (VBR)");
System.out.println(" --vad Enable voice activity detection (VAD)");
System.out.println(" --dtx Enable file based discontinuous transmission (DTX)");
System.out.println(" if the input file is raw PCM (not a Wave file)");
System.out.println(" --rate n Sampling rate for raw input");
System.out.println(" --stereo Consider input as stereo");
System.out.println("More information is available from: http://jspeex.sourceforge.net/");
System.out.println("This code is a Java port of the Speex codec: http://www.speex.org/");
}
/**
* Prints the version.
* #param COPYRIGHT
*/
public static void version(boolean COPYRIGHT)
{
System.out.println(VERSION);
System.out.println("using " + SpeexEncoder.VERSION);
System.out.println(COPYRIGHT);
}
/**
* Encodes a PCM file to Speex.
* #param srcFile
* #exception IOException
*/
public void encode(String srcFile)
throws IOException
{
encode(new File(srcFile), new File(destFile));
}
/**
* Encodes a PCM file to Speex.
* #param srcPath
* #param destPath
* #param channels
* #param sampleRate
* #exception IOException
*/
public void encode(final File srcPath, final File destPath, int channels, int sampleRate, boolean printlevel, boolean DEBUG, String vbr, int nframes, String vbr_quality)
throws IOException
{
byte[] temp = new byte[2560]; // stereo UWB requires one to read 2560b
final int HEADERSIZE = 8;
final String RIFF = "RIFF";
final String WAVE = "WAVE";
final String FORMAT = "fmt ";
final String DATA = "data";
final int WAVE_FORMAT_PCM = 0x0001;
// Display info
if (printlevel <= INFO) version();
if (printlevel <= DEBUG) System.out.println("");
if (printlevel <= DEBUG) System.out.println("Input File: " + srcPath);
try (DataInputStream dis = new DataInputStream(new FileInputStream(srcPath))) {
int mode;
// Prepare input stream
if (srcFormat == FILE_FORMAT_WAVE) {
// read the WAVE header
dis.readFully(temp, 0, HEADERSIZE+4);
// make sure its a WAVE header
if (!RIFF.equals(new String(temp, 0, 4)) &&
!WAVE.equals(new String(temp, 8, 4))) {
System.err.println("Not a WAVE file");
return;
}
// Read other header chunks
dis.readFully(temp, 0, HEADERSIZE);
String chunk = new String(temp, 0, 4);
int size = readInt(temp, 4);
while (!chunk.equals(DATA)) {
dis.readFully(temp, 0, size);
if (chunk.equals(FORMAT)) {
/*
typedef struct waveformat_extended_tag {
WORD wFormatTag; // format type
WORD nChannels; // number of channels (i.e. mono, stereo...)
DWORD nSamplesPerSec; // sample rate
DWORD nAvgBytesPerSec; // for buffer estimation
WORD nBlockAlign; // block size of data
WORD wBitsPerSample; // Number of bits per sample of mono data
WORD cbSize; // The count in bytes of the extra size
} WAVEFORMATEX;
*/
if (readShort(temp, 0) != WAVE_FORMAT_PCM) {
System.err.println("Not a PCM file");
return;
}
channels = readShort(temp, 2);
sampleRate = readInt(temp, 4);
if (readShort(temp, 14) != 16) {
System.err.println("Not a 16 bit file " + readShort(temp, 18));
return;
}
// Display audio info
if (printlevel <= DEBUG) {
System.out.println("File Format: PCM wave");
System.out.println("Sample Rate: " + sampleRate);
System.out.println("Channels: " + channels);
}
}
dis.readFully(temp, 0, HEADERSIZE);
chunk = new String(temp, 0, 4);
size = readInt(temp, 4);
}
if (printlevel <= DEBUG) System.out.println("Data size: " + size);
}
else {
if (sampleRate < 0) {
switch (mode) {
case 0:
sampleRate = 8000;
break;
case 1:
sampleRate = 16000;
break;
case 2:
sampleRate = 32000;
break;
default:
sampleRate = 8000;
break;
}
}
// Display audio info
if (printlevel <= DEBUG) {
System.out.println("File format: Raw audio");
System.out.println("Sample rate: " + sampleRate);
System.out.println("Channels: " + channels);
System.out.println("Data size: " + srcPath.length());
}
}
// Set the mode if it has not yet been determined
if (mode < 0) {
if (sampleRate < 100) // Sample Rate has probably been given in kHz
sampleRate *= 1000;
if (sampleRate < 12000)
mode = 0; // Narrowband
else if (sampleRate < 24000)
mode = 1; // Wideband
else
mode = 2; // Ultra-wideband
}
// Construct a new encoder
SpeexEncoder speexEncoder = new SpeexEncoder();
speexEncoder.init(mode, quality, sampleRate, channels);
if (complexity > 0) {
speexEncoder.getEncoder().setComplexity(complexity);
}
if (bitrate > 0) {
speexEncoder.getEncoder().setBitRate(bitrate);
}
if (vbr) {
speexEncoder.getEncoder().setVbr(vbr);
if (vbr_quality > 0) {
speexEncoder.getEncoder().setVbrQuality(vbr_quality);
}
}
if (vad) {
speexEncoder.getEncoder().setVad(vad);
}
if (dtx) {
speexEncoder.getEncoder().setDtx(dtx);
}
// Display info
if (printlevel <= DEBUG) {
System.out.println("");
System.out.println("Output File: " + destPath);
System.out.println("File format: Ogg Speex");
System.out.println("Encoder mode: " + (mode==0 ? "Narrowband" : (mode==1 ? "Wideband" : "UltraWideband")));
System.out.println("Quality: " + (vbr ? vbr_quality : quality));
System.out.println("Complexity: " + complexity);
System.out.println("Frames per packet: " + nframes);
System.out.println("Varible bitrate: " + vbr);
System.out.println("Voice activity detection: " + vad);
System.out.println("Discontinouous Transmission: " + dtx);
}
// Open the file writer
AudioFileWriter writer;
if (destFormat == FILE_FORMAT_OGG) {
writer = new OggSpeexWriter(mode, sampleRate, channels, nframes, vbr);
}
else if (destFormat == FILE_FORMAT_WAVE) {
nframes = PcmWaveWriter.WAVE_FRAME_SIZES[mode-1][channels-1][quality];
writer = new PcmWaveWriter(mode, quality, sampleRate, channels, nframes, vbr);
}
else {
writer = new RawWriter();
}
writer.open(destPath);
writer.writeHeader("Encoded with: " + VERSION);
int pcmPacketSize = 2 * channels * speexEncoder.getFrameSize();
while (true) {
dis.readFully(temp, 0, nframes*pcmPacketSize);
for (int i=0; i<nframes; i++)
speexEncoder.processData(temp, i*pcmPacketSize, pcmPacketSize);
int encsize = speexEncoder.getProcessedData(temp, 0);
if (encsize > 0) {
writer.writePacket(temp, 0, encsize);
}
}
writer.close();
}
}
/**
* Converts Little Endian (Windows) bytes to an int (Java uses Big Endian).
* #param data the data to read.
* #param offset the offset from which to start reading.
* #return the integer value of the reassembled bytes.
*/
protected static int readInt(final byte[] data, final int offset)
{
return (data[offset] & 0xff) |
((data[offset+1] & 0xff) << 8) |
((data[offset+2] & 0xff) << 16) |
(data[offset+3] << 24); // no 0xff on the last one to keep the sign
}
/**
* Converts Little Endian (Windows) bytes to an short (Java uses Big Endian).
* #param data the data to read.
* #param offset the offset from which to start reading.
* #return the integer value of the reassembled bytes.
*/
protected static int readShort(final byte[] data, final int offset)
{
return (data[offset] & 0xff) |
(data[offset+1] << 8); // no 0xff on the last one to keep the sign
}
}
Related
I was try to read a binary file storing the position of x, y, z ,w.
that is, they are all float such as 1.4567896 5.156986 .....etc.
I successfully read it in c++ and also java, but couldn't done in android,
all I read in is all zero...
Here is my code :
ps. I try many codes before, all failed...
public class DataIO {
private int DataSize = 640*320*4;
private float[] fDataBuffer;
private byte[] bDataBuffer;
private String sOutput;
public DataIO(){
bDataBuffer = new byte[DataSize*4];
fDataBuffer = new float[DataSize];
}
/*=============================================
* Function:
* ReadFile
* Description:
* Read binary float data to float[] buffer.
*================================================ */
public void ReadFile(String FileName){
File file = new File(FileName);
try {
FileInputStream finStream = new FileInputStream(file);
if(finStream.read(bDataBuffer) == -1){
Log.d("ReadData", "Reading Data has problem.");
}
finStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
int index = 0;
for(int i = 0; i < DataSize; i+=1){
/*int asInt = (bDataBuffer[i+0] & 0xFF)
| ((bDataBuffer[i+1] & 0xFF) << 8)
| ((bDataBuffer[i+2] & 0xFF) << 16)
| ((bDataBuffer[i+3] & 0xFF) << 24);
fDataBuffer[index] = Float.intBitsToFloat(asInt);*/
sOutput += String.format("%d ", bDataBuffer[i]);
index++;
}
}
/*=============================================
* Function:
* GetData
* Description:
* output the data by string.
*================================================ */
public String GetData(){
return sOutput;
}
}
I blocked the transformation of byte to float cuz read in byte have to be check if they are correct first.
On Android you need to use a runnable interface to constantly check a variable.
I ran into an issue trying to read from a DataOutputStream with a Scanner. The following code fails with an input mismatch exception.
DataOutputStream o = new DataOutputStream(new FileOutputStream("temp"));
o.writeByte(1);
o.flush(); o.close();
Scanner i = new Scanner(new FileInputStream("temp"));
System.out.println(i.nextByte());
i.close();
So what output streams ARE compatible with Scanner? And is there an Output/Input stream pair that are built for reading and writing all the primitive types and string lines? I really wanted a non-deprecated readLine() method, and all the input streams I've seen that have that method are deprecated.
EDIT:
The input and output streams will be across sockets for a Client/Server application. Here is the entire relevant code for each side:
SERVER
package server;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Scanner;
/**
* Server class handles all clients that wish to read a file stored on this
* server's
* database. Also connects to other servers to share some information.
*
* #author Josh Wilkins
*/
public class Server{
//Server message type constants
private static final byte REPLY = 0;
private static final byte YES = 1;
private static final byte NO = 2;
private static final byte UPDATE = 3;
private final int id;
private final ServerSocket fileServer; //connection point for clients
private HashMap<String, FileObject> files; //list of files in database, hashed by file name
/**
* Creates a new file server with given id on the specified port.
* <p>
* #param id
* #param port
* <p>
* #throws IOException
*/
public Server(int id, int port) throws IOException{
this.id = id;
fileServer = new ServerSocket(port);
files = new HashMap<>();
}
/**
* Loops forever, accepting new clients and creating threads to handle
* them.
* <p>
* #throws IOException
*/
public void acceptClients() throws IOException{
while( true ){
Socket client = fileServer.accept();
(new ClientHandler(client)).run();
}
}
/**
* Sends most updated version of specified file to specified server. Will
* only send update if the server should host the file. UNIMPLEMENTED.
* <p>
* #throws UnsupportedOperationException
*/
private void sendUpdate(int server, FileObject file){
//TODO
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Creates a new file in the database with the file name contained in the
* string file.
* <p>
* #param file the file name of the new file object
*/
private synchronized void createFileObject(String file){
//TODO: Reject files not hashed to this server
try{
files.put(file, new FileObject(file));
} catch(IOException ex){
System.err.println("Failed to create file: " + file);
ex.printStackTrace(System.err);
}
}
/**
* Determines if this server is allowed to initiate a write request for a
* file by giving the client the most current version number.
* <p>
* #param file the file name of the file in question
* <p>
* #return true if allowed to initiate, false otherwise
*/
private boolean canInitiate(String file){
int hash0 = file.hashCode() % 7;
int hash1 = (file.hashCode() + 1) % 7;
return id == hash0 || id == hash1;
}
/**
* Threaded class to handle all requests from a single client connection.
*/
class ClientHandler extends Thread{
//Client message type constants
private static final byte READ = 0;
private static final byte WRITE = 1;
private static final byte COMMIT = 2;
private static final byte ABORT = 3;
private Socket client;
private DataOutputStream out;
private Scanner in;
private HashMap<String, Integer> pendingUpdates;
/**
* Sets up data input and output streams from given client socket.
* <p>
* #param client the socket that this client is connected to
*/
public ClientHandler(Socket client) throws IOException{
this.client = client;
out = new DataOutputStream(client.getOutputStream());
in = new Scanner(client.getInputStream());
pendingUpdates = new HashMap<>();
}
/**
* Listens for messages coming from client until the connection is
* closed on the client side.
*/
#Override
public void run(){
//Loops until the client closes the connection, then hasNext returns false
while( in.hasNext() ){
//wait for the next message type to be written to the stream
byte msgType = in.nextByte();
switch( msgType ){
case READ:
parseRead();
break;
case WRITE:
parseWrite();
break;
case COMMIT:
parseCommit();
break;
case ABORT:
parseAbort();
break;
default:
}
}
//connection is no longer needed, try to clean everything up
try{
in.close();
out.close();
client.close();
} catch(IOException ex){
System.err.println("Failed to close client at " + client.getInetAddress());
ex.printStackTrace(System.err);
}
}
/**
* Parses read message from client by collecting data from the input
* stream and processing it. Assumes the message type byte has already
* been read. If the file exists in the database, its contents are
* written to the client, otherwise a null string is written.
*/
private void parseRead(){
//TODO: Reject files not hashed to this server
String file = in.nextLine().trim(); //get requested file name (should end with \n)
String contents = "";
if( files.containsKey(file) ){
try{
contents = files.get(file).getContents();
} catch(IOException ex){
System.err.println("Error reading from file: " + file);
ex.printStackTrace(System.err);
}
} else {
//TODO: Need to decide how to handle no such file
// - create file and return empty
// - just return empty, but don't create
// - return some error indicator (change file to "-1" or "File Not Found")
contents = "";
}
//send REPLY message to client
sendReply(file, contents);
}
/**
* Parses write message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. If the file does not exist, one is created immediately
* regardless of further success. If the version number is viable, or
* if this server can initiate a write for the given file, the update is
* queued with the respective FileObject. The client is sent a YES or
* NO answer depending on success of queueing the update.
*/
private void parseWrite(){
//TODO: Reject files not hashed to this server
int version = in.nextInt(); //get version first
String file = in.nextLine().trim(); //get file name
String contents = in.useDelimiter("\\Z").next(); //read entire remaining stream for contents
boolean queued = false;
//Create file if it does not exist yet.
if( !files.containsKey(file) ){
createFileObject(file);
}
//queue update to file object
try{
//only queue if version is given or if this server can initiate
if( version > 0 ){
queued = files.get(file).queueUpdate(contents, version);
} else if( version < 0 && canInitiate(file) ){
queued = files.get(file).queueUpdate(contents);
version = files.get(file).getVersion() + 1;
}
} catch(IOException ex){
System.err.println("Failed to queue update to: " + file);
ex.printStackTrace(System.err);
}
//send response to client (positive if queued, negative if not)
if( queued ){
//TODO: What happens if an update is already queued?
pendingUpdates.put(file, version);
sendYes(version, file);
} else {
sendNo(file);
}
}
/**
* Parses commit message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. If valid server id is read from the stream, an update
* message is sent to that server for the file. No response is sent to
* client.
*/
private void parseCommit(){
//TODO: Reject files not hashed to this server
int failed = in.nextInt();
String file = in.nextLine().trim();
//TODO: Handle improper commit (no pending update)
int version = pendingUpdates.remove(file);
try{
files.get(file).commitUpdate(version);
} catch(IOException ex){
System.err.println("Failed to commit: " + file + " v. " + version);
ex.printStackTrace(System.err);
}
if( failed >= 0 ){
sendUpdate(failed, files.get(file));
}
}
/**
* Parses abort message from client by collecting data form the input
* stream and processing it. Assumes the message type byte has already
* been read. Simply removes the pending update from the queues. No
* response is sent to the client.
*/
private void parseAbort(){
//TODO: Reject files not hashed to this server
String file = in.nextLine().trim();
int version = pendingUpdates.remove(file); //if no update is queued this simply returns null
//what happens if:
// Integer xObj = null;
// int x = xObj;
// print(x);
try{
files.get(file).abortUpdate(version);
} catch(IOException ex){
System.err.println("Failed to abort: " + file + " v. " + version);
ex.printStackTrace(System.err);
}
}
/**
* Sends reply message to client. Assumes partial failure is impossible.
* <p>
* #param file name of file
* #param contents data contained in file
*/
public void sendReply(String file, String contents){
try{
out.writeByte(REPLY);
out.writeChars(file + "\n"); //end file name with CR for easy reading
out.writeChars(contents);
} catch(IOException ex){
System.err.println("Error sending REPLY(" + file + ", <" + contents.length() + ">)");
ex.printStackTrace(System.err);
}
}
/**
* Sends yes message to client. Assumes partial failure is impossible.
* <p>
* #param version this updates version number
* #param file name of file
*/
public void sendYes(int version, String file){
try{
out.writeByte(YES);
out.writeInt(version);
out.writeChars(file + "\n");
} catch(IOException ex){
System.err.println("Error sending YES(" + version + ", " + file + ")");
ex.printStackTrace(System.err);
}
}
/**
* Sends reply message to client. Assumes partial failure is impossible.
* <p>
* #param file name of file
*/
public void sendNo(String file){
try{
out.writeByte(NO);
out.writeChars(file + "\n");
} catch(IOException ex){
System.err.println("Error sending NO(" + file + ")");
ex.printStackTrace(System.err);
}
}
}
}
CLIENT
import java.io.*;
import java.net.Socket;
import java.util.Random;
import java.util.Scanner;
/**
* Client will take as input the operations (WRITE or READ), the file it wishes to use,
* and the servers in the network. The client will attempted to connect to three servers
* when a WRITE is chosen and a random server for READ. The servers ranges for READ and WRITE
* are based on a hashed value of the file name + the next in the line.
* example: Sever 1 , Server 2 and Server 3;
*
* #author kattex
*/
public class Client {
private Socket socket = null;
private Scanner inputStream = null;
private DataOutputStream outputStream = null;
private boolean isConnected = false;
private int sequenceNumber = -1;
private int response = 0;
private byte READ = 0;
private byte WRITE = 1;
private byte COMMIT = 2;
private byte ABORT = 3;
private byte YES = 1;
private byte NO = 2;
private byte REPLY = 0;
/**
* Connect with server code running in local host or in any other host
*/
private void connect(String address, int port) {
try {
// socket = new Socket("localHost", 4445);
System.out.println(address + " " + port);
socket = new Socket(address, port);
//outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream = new DataOutputStream(socket.getOutputStream());
inputStream = new Scanner(socket.getInputStream());
isConnected = true;
} catch (IOException e) {
System.out.println("Unable to connect to server " + address);
}
}
/*
* Create a random integer within a min and max range
*/
public static int randInt(int min, int max, int count) {
Random rand = new Random();
System.out.println("randInt: " + min + "," + max + "," + count);
int randomNum = rand.nextInt((max - min) + 1) + min;
if (randomNum > count){
randomNum = randomNum % count;
if (randomNum < 0){
randomNum += count;
}
}
System.out.println("Random value: " + randomNum);
return randomNum;
}
/*
* Generate hash value for server numbers
*/
public static int hashFileName(String fileName, int serverCount){
int number = fileName.hashCode() % serverCount;
if (number < 0){
number += serverCount;
}
System.out.println("Hash Number: " + number);
return number;
}
/*
* Write out the contents to a file, if the file does not exist create it
*/
public static void CreateFile(String filename, String content){
try {
File file = new File(filename);
// if file doesnt exists, then create it
if (!file.exists()) {
file.createNewFile();
}
FileWriter fw = new FileWriter(file.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(content);
bw.close();
System.out.println("Done");
} catch (IOException e) {
System.out.println("Erro creating file");
e.printStackTrace();
}
}
/*
* Find the next server in the list
*/
public static int nextServer(int number, int count){
int nextInt;
number++;
nextInt = number % count;
if (number < 0){
number += count;
}
return nextInt;
}
/*
* Send the WRITE messaget to server
*/
private void sendWrite(String obj){
try {
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeByte(WRITE);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeInt(sequenceNumber);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
outputStream.writeChars(obj);
outputStream.writeChar('\n');
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
String contents = readFile(obj);
System.out.println("Contents of file " + obj);
outputStream.writeChars(contents);
System.out.print("Pause...");
(new Scanner(System.in)).nextLine();
System.out.println("sending message to server");
int msgType = inputStream.nextByte();
if (msgType == YES){
sequenceNumber = inputStream.nextInt();
String tempFileName = inputStream.nextLine().trim();
response = 1;
System.out.println("Receved YES for file " + tempFileName);
}
if (msgType == NO){
String tempFileName = inputStream.nextLine();
System.out.println("Receved NO for file " + tempFileName);
}
} catch (IOException e) {
System.out.println("Error writing WRITE message to server");
e.printStackTrace(System.err);
}
}
/*
* Read the file into a string that can be sent throught a TCP socket
*/
public String readFile(String fileName) throws FileNotFoundException{
String output = new Scanner(new File(fileName)).useDelimiter("\\Z").next();
System.out.println("File output in READFile" + output);
return output;
}
/*
* Send Abort message to the server
*/
public void sendAbort(String obj){
try {
outputStream.writeByte(ABORT);
outputStream.writeChars(obj);
outputStream.writeChar('\n');
System.out.println("sending abort message to server");
} catch (IOException e) {
e.printStackTrace();
System.out.println("Error sending ABORT message to server");
}
}
/*
* Send commit to Server
*/
public void sendCommit(int Fsvr, String obj){
try {
outputStream.writeByte(COMMIT);
outputStream.writeInt(Fsvr);
outputStream.writeBytes(obj);
System.out.println("sending Commit message to server");
} catch (IOException e) {
e.printStackTrace();
System.out.println("Error sending COMMIT message to server");
}
}
/*
* Send READ request
*/
public void sendREAD(String obj){
String u;
try {
outputStream.writeByte(READ);
outputStream.writeChars(obj);
outputStream.writeChar('n');
System.out.println("sending READ Request message to server");
byte type = inputStream.nextByte();
//File fl = new File(obj);
if (type == REPLY){
String file = inputStream.nextLine().trim();
String contents = inputStream.useDelimiter("\\z").next();
CreateFile(file, contents);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("Erorro sedning READ Request message to server");
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Client client = new Client();
int serverCount = 0;
int FSvr = -1;
int length = args.length;
String Object = "j.txt";
String type = "write";
int hashValue = hashFileName(Object, 7);
if (type.equals("write") ){
Client client2 = new Client();
Client client3 = new Client();
client.connect("localhost", 4445);
if (client.isConnected){
client.sendWrite(Object);
if (client.response == 1){
client2.sequenceNumber = client.sequenceNumber;
}
}
int nextValue = nextServer(hashValue,7);
//need to add commit message
int thirdValue = 3;
System.out.println("Server Numbers " + hashValue + " " + nextValue + " " + thirdValue);
serverCount = client.response + client2.response + client3.response;
System.out.println("Servercount " + serverCount);
if (serverCount >= 2){
if(client.response != 1 ){FSvr = hashValue; }
if(client2.response != 1){FSvr = nextValue; }
if(client3.response != 1){FSvr = thridValue;}
if(client.response == 1){client.sendCommit( FSvr,Object);}
if(client2.response == 1){client2.sendCommit(FSvr,Object);}
if(client3.response == 1){client3.sendCommit(FSvr,Object);}
}else{
if(client.response == 1 ){client.sendAbort(Object); }
if(client2.response == 1){client2.sendAbort(Object);}
if(client3.response == 1){client3.sendAbort(Object);}
}
} else {
if (type.equals("read")){
int RValue = randInt(hashValue, hashValue + 2, 7);
System.out.println("HashVlue: " + hashValue + " RValue: " + RValue);
client.sendREAD(Object);
}
}
}
}
Most of the message handling on the server side is in ClientHandler. I'm less familiar with the client side, as I didn't write it. Obviously with this implementation, there is an InputMismatchException when reading the byte msgType on the server side. I also realized while searching for the solution, that at the very least I should be adding white space between all the fields for Scanner to parse them separately, but I still have issues.
Use a DataOutputStream and a DataInputStream. Instead of lines, use writeUTF() and readUTF(). For efficiency, put buffered streams underneath, and flush the output before you read.
Scanner takes Input Streams such as System.in you are better off using BufferedReader here. E.g
BufferedReader reader = new BufferReader(new FileReader("temp"));
System.out.println(reader.readLine());
reader.close();
I am using the JpegImagesToMovie.java to convert images to a .mov file. I was wondering if there was anyway I could edit this to work with .png files as the quality of the video is not very good and changing it would improve it.
ImageIO.write(img, "png", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".png"));
ImageIO.write(img, "jpeg", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".png"));
ImageIO.write(img, "png", new File("C:\\Users\\user\\Desktop\\tmp\\" + System.currentTimeMillis() + ".jpeg"));
All three of those would produce a video (no errors through the program) but the video wouldn't play the images just open and finish.
I also tried editing the JpegImagesToMovie.java
if (!filePath.toLowerCase().endsWith(".png") && !filePath.toLowerCase().endsWith(".png")) {
continue;
But that didn't work, and I can't find anywhere else to edit. How can I get this to work with .png images?
Latest Update:
Here is my updated JpegImagesToMovies class
package maple;
/*
* #(#)JpegImagesToMovie.java 1.3 01/03/13
*
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
import java.io.*;
import java.util.*;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import javax.media.datasink.*;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
/**
* This program takes a list of JPEG image files and convert them into a
* QuickTime movie.
*/
public class JpegImagesToMovie implements ControllerListener, DataSinkListener {
static private Vector<String> getImageFilesPathsVector(
String imagesFolderPath) {
File imagesFolder = new File(imagesFolderPath);
String[] imageFilesArray = imagesFolder.list();
Vector<String> imageFilesPathsVector = new Vector<String>();
for (String imageFileName : imageFilesArray) {
if (!imageFileName.toLowerCase().endsWith("png"))
continue;
imageFilesPathsVector.add(imagesFolder.getAbsolutePath()
+ File.separator + imageFileName);
}
return imageFilesPathsVector;
}
public boolean doIt(int width, int height, int frameRate,
Vector<String> inFiles, MediaLocator outML) {
ImageDataSource ids = new ImageDataSource(width, height, frameRate,
inFiles);
Processor p;
try {
System.err
.println("- create processor for the image datasource ...");
p = Manager.createProcessor(ids);
} catch (Exception e) {
System.err
.println("Yikes! Cannot create a processor from the data source.");
return false;
}
p.addControllerListener(this);
// Put the Processor into configured state so we can set
// some processing options on the processor.
p.configure();
if (!waitForState(p, Processor.Configured)) {
System.err.println("Failed to configure the processor.");
return false;
}
// Set the output content descriptor to QuickTime.
p.setContentDescriptor(new ContentDescriptor(
FileTypeDescriptor.QUICKTIME));// FileTypeDescriptor.MSVIDEO
// Query for the processor for supported formats.
// Then set it on the processor.
TrackControl tcs[] = p.getTrackControls();
Format f[] = tcs[0].getSupportedFormats();
if (f == null || f.length <= 0) {
System.err.println("The mux does not support the input format: "
+ tcs[0].getFormat());
return false;
}
tcs[0].setFormat(f[0]);
System.err.println("Setting the track format to: " + f[0]);
// We are done with programming the processor. Let's just
// realize it.
p.realize();
if (!waitForState(p, Controller.Realized)) {
System.err.println("Failed to realize the processor.");
return false;
}
// Now, we'll need to create a DataSink.
DataSink dsink;
if ((dsink = createDataSink(p, outML)) == null) {
System.err
.println("Failed to create a DataSink for the given output MediaLocator: "
+ outML);
return false;
}
dsink.addDataSinkListener(this);
fileDone = false;
System.err.println("start processing...");
// OK, we can now start the actual transcoding.
try {
p.start();
dsink.start();
} catch (IOException e) {
System.err.println("IO error during processing");
return false;
}
// Wait for EndOfStream event.
waitForFileDone();
// Cleanup.
try {
dsink.close();
} catch (Exception e) {
}
p.removeControllerListener(this);
System.err.println("...done processing.");
return true;
}
/**
* Create the DataSink.
*/
DataSink createDataSink(Processor p, MediaLocator outML) {
DataSource ds;
if ((ds = p.getDataOutput()) == null) {
System.err
.println("Something is really wrong: the processor does not have an output DataSource");
return null;
}
DataSink dsink;
try {
System.err.println("- create DataSink for: " + outML);
dsink = Manager.createDataSink(ds, outML);
dsink.open();
} catch (Exception e) {
System.err.println("Cannot create the DataSink: " + e);
return null;
}
return dsink;
}
Object waitSync = new Object();
boolean stateTransitionOK = true;
/**
* Block until the processor has transitioned to the given state. Return
* false if the transition failed.
*/
boolean waitForState(Processor p, int state) {
synchronized (waitSync) {
try {
while (p.getState() < state && stateTransitionOK)
waitSync.wait();
} catch (Exception e) {
}
}
return stateTransitionOK;
}
/**
* Controller Listener.
*/
public void controllerUpdate(ControllerEvent evt) {
if (evt instanceof ConfigureCompleteEvent
|| evt instanceof RealizeCompleteEvent
|| evt instanceof PrefetchCompleteEvent) {
synchronized (waitSync) {
stateTransitionOK = true;
waitSync.notifyAll();
}
} else if (evt instanceof ResourceUnavailableEvent) {
synchronized (waitSync) {
stateTransitionOK = false;
waitSync.notifyAll();
}
} else if (evt instanceof EndOfMediaEvent) {
evt.getSourceController().stop();
evt.getSourceController().close();
}
}
Object waitFileSync = new Object();
boolean fileDone = false;
boolean fileSuccess = true;
/**
* Block until file writing is done.
*/
boolean waitForFileDone() {
synchronized (waitFileSync) {
try {
while (!fileDone)
waitFileSync.wait();
} catch (Exception e) {
}
}
return fileSuccess;
}
/**
* Event handler for the file writer.
*/
public void dataSinkUpdate(DataSinkEvent evt) {
if (evt instanceof EndOfStreamEvent) {
synchronized (waitFileSync) {
fileDone = true;
waitFileSync.notifyAll();
}
} else if (evt instanceof DataSinkErrorEvent) {
synchronized (waitFileSync) {
fileDone = true;
fileSuccess = false;
waitFileSync.notifyAll();
}
}
}
public static void main(String args[]) {
// changed this method a bit
if (args.length == 0)
prUsage();
// Parse the arguments.
int i = 0;
int width = -1, height = -1, frameRate = -1;
Vector<String> inputFiles = new Vector<String>();
String rootDir = null;
String outputURL = null;
while (i < args.length) {
if (args[i].equals("-w")) {
i++;
if (i >= args.length)
prUsage();
width = new Integer(args[i]).intValue();
} else if (args[i].equals("-h")) {
i++;
if (i >= args.length)
prUsage();
height = new Integer(args[i]).intValue();
} else if (args[i].equals("-f")) {
i++;
if (i >= args.length)
prUsage();
// new Integer(args[i]).intValue();
frameRate = Integer.parseInt(args[i]);
} else if (args[i].equals("-o")) {
i++;
if (i >= args.length)
prUsage();
outputURL = args[i];
} else if (args[i].equals("-i")) {
i++;
if (i >= args.length)
prUsage();
rootDir = args[i];
} else {
System.out.println(".");
prUsage();
}
i++;
}
if (rootDir == null) {
System.out
.println("Since no input (-i) forder provided, assuming this JAR is inside JPEGs folder.");
rootDir = (new File(".")).getAbsolutePath();
}
inputFiles = getImageFilesPathsVector(rootDir);
if (inputFiles.size() == 0)
prUsage();
if (outputURL == null) {
outputURL = (new File(rootDir)).getAbsolutePath() + File.separator
+ "pngs2movie.mov";
}
if (!outputURL.toLowerCase().startsWith("file:///")) {
outputURL = "file:///" + outputURL;
}
// Check for output file extension.
if (!outputURL.toLowerCase().endsWith(".mov")) {
prUsage();
outputURL += ".mov";
System.out
.println("outputURL should be ending with mov. Making this happen.\nNow outputURL is: "
+ outputURL);
}
if (width < 0 || height < 0) {
prUsage();
System.out.println("Trying to guess movie size from first image");
BufferedImage firstImageInFolder = getFirstImageInFolder(rootDir);
width = firstImageInFolder.getWidth();
height = firstImageInFolder.getHeight();
System.out.println("width = " + width);
System.out.println("height = " + height);
}
// Check the frame rate.
if (frameRate < 1)
frameRate = 30;
// Generate the output media locators.
MediaLocator oml;
if ((oml = createMediaLocator(outputURL)) == null) {
System.err.println("Cannot build media locator from: " + outputURL);
System.exit(0);
}
JpegImagesToMovie imageToMovie = new JpegImagesToMovie();
imageToMovie.doIt(width, height, frameRate, inputFiles, oml);
System.exit(0);
}
private static BufferedImage getFirstImageInFolder(String rootDir) {
File rootFile = new File(rootDir);
String[] list = (rootFile).list();
BufferedImage bufferedImage = null;
for (String filePath : list) {
if (!filePath.toLowerCase().endsWith(".png")
&& !filePath.toLowerCase().endsWith(".png")) {
continue;
}
try {
bufferedImage = ImageIO.read(new File(rootFile
.getAbsoluteFile() + File.separator + filePath));
break;
} catch (IOException e) {
e.printStackTrace();
}
}
return bufferedImage;
}
static void prUsage() {
System.err
.println("Usage: java JpegImagesToMovie [-w <width>] [-h <height>] [-f <frame rate>] [-o <output URL>] -i <input JPEG files dir Path>");
// System.exit(-1);
}
/**
* Create a media locator from the given string.
*/
#SuppressWarnings("unused")
public static MediaLocator createMediaLocator(String url) {
MediaLocator ml;
if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null)
return ml;
if (url.startsWith(File.separator)) {
if ((ml = new MediaLocator("file:" + url)) != null)
return ml;
} else {
String file = "file:" + System.getProperty("user.dir")
+ File.separator + url;
if ((ml = new MediaLocator(file)) != null)
return ml;
}
return null;
}
// /////////////////////////////////////////////
//
// Inner classes.
// /////////////////////////////////////////////
/**
* A DataSource to read from a list of JPEG image files and turn that into a
* stream of JMF buffers. The DataSource is not seekable or positionable.
*/
class ImageDataSource extends PullBufferDataSource {
ImageSourceStream streams[];
ImageDataSource(int width, int height, int frameRate,
Vector<String> images) {
streams = new ImageSourceStream[1];
streams[0] = new PngImageSourceStream(width, height, frameRate, images);
}
public void setLocator(MediaLocator source) {
}
public MediaLocator getLocator() {
return null;
}
/**
* Content type is of RAW since we are sending buffers of video frames
* without a container format.
*/
public String getContentType() {
return ContentDescriptor.RAW;
}
public void connect() {
}
public void disconnect() {
}
public void start() {
}
public void stop() {
}
/**
* Return the ImageSourceStreams.
*/
public PullBufferStream[] getStreams() {
return streams;
}
/**
* We could have derived the duration from the number of frames and
* frame rate. But for the purpose of this program, it's not necessary.
*/
public Time getDuration() {
return DURATION_UNKNOWN;
}
public Object[] getControls() {
return new Object[0];
}
public Object getControl(String type) {
return null;
}
}
/**
* The source stream to go along with ImageDataSource.
*/
class ImageSourceStream implements PullBufferStream {
Vector<String> images;
int width, height;
VideoFormat format;
int nextImage = 0; // index of the next image to be read.
boolean ended = false;
public ImageSourceStream(int width, int height, int frameRate,
Vector<String> images) {
this.width = width;
this.height = height;
this.images = images;
format = new VideoFormat(VideoFormat.JPEG, new Dimension(width,
height), Format.NOT_SPECIFIED, Format.byteArray,
(float) frameRate);
}
/**
* We should never need to block assuming data are read from files.
*/
public boolean willReadBlock() {
return false;
}
/**
* This is called from the Processor to read a frame worth of video
* data.
*/
public void read(Buffer buf) throws IOException {
// Check if we've finished all the frames.
if (nextImage >= images.size()) {
// We are done. Set EndOfMedia.
System.err.println("Done reading all images.");
buf.setEOM(true);
buf.setOffset(0);
buf.setLength(0);
ended = true;
return;
}
String imageFile = (String) images.elementAt(nextImage);
nextImage++;
System.err.println(" - reading image file: " + imageFile);
// Open a random access file for the next image.
RandomAccessFile raFile;
raFile = new RandomAccessFile(imageFile, "r");
byte data[] = null;
// Check the input buffer type & size.
if (buf.getData() instanceof byte[])
data = (byte[]) buf.getData();
// Check to see the given buffer is big enough for the frame.
if (data == null || data.length < raFile.length()) {
data = new byte[(int) raFile.length()];
buf.setData(data);
}
// Read the entire JPEG image from the file.
raFile.readFully(data, 0, (int) raFile.length());
System.err.println(" read " + raFile.length() + " bytes.");
buf.setOffset(0);
buf.setLength((int) raFile.length());
buf.setFormat(format);
buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
// Close the random access file.
raFile.close();
}
/**
* Return the format of each video frame. That will be JPEG.
*/
public Format getFormat() {
return format;
}
public ContentDescriptor getContentDescriptor() {
return new ContentDescriptor(ContentDescriptor.RAW);
}
public long getContentLength() {
return 0;
}
public boolean endOfStream() {
return ended;
}
public Object[] getControls() {
return new Object[0];
}
public Object getControl(String type) {
return null;
}
}
class PngImageSourceStream extends ImageSourceStream {
public PngImageSourceStream(int width, int height, int frameRate, Vector<String> images) {
super(width, height, frameRate, images);
// configure the new format as RGB format
format = new RGBFormat(new Dimension(width, height), Format.NOT_SPECIFIED, Format.byteArray, frameRate,
24, // 24 bits per pixel
1, 2, 3); // red, green and blue masks when data are in the form of byte[]
}
public void read(Buffer buf) throws IOException {
// Check if we've finished all the frames.
if (nextImage >= images.size()) {
// We are done. Set EndOfMedia.
System.err.println("Done reading all images.");
buf.setEOM(true);
buf.setOffset(0);
buf.setLength(0);
ended = true;
return;
}
String imageFile = (String) images.elementAt(nextImage);
nextImage++;
System.err.println(" - reading image file: " + imageFile);
// read the PNG image
BufferedImage image = ImageIO.read( new File(imageFile) );
Dimension size = format.getSize();
// convert 32-bit RGBA to 24-bit RGB
byte[] imageData = convertTo24Bit(image.getRaster().getPixels(0, 0, size.width, size.height, (int[]) null));
buf.setData(imageData);
System.err.println(" read " + imageData.length + " bytes.");
buf.setOffset(0);
buf.setLength(imageData.length);
buf.setFormat(format);
buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
}
private void convertIntByteToByte(int[] src, int srcIndex, byte[] out, int outIndex) {
// Note: the int[] returned by bufferedImage.getRaster().getPixels() is an int[]
// where each int is the value for one color i.e. the first 4 ints contain the RGBA values for the first pixel
int r = src[srcIndex];
int g = src[srcIndex+1];
int b = src[srcIndex+2];
out[outIndex] = (byte) (r & 0xFF);
out[outIndex+1] = (byte) (g & 0xFF);
out[outIndex+2] = (byte) (b & 0xFF);
}
private byte[] convertTo24Bit(int[] input) {
int dataLength = input.length;
byte[] convertedData = new byte[ dataLength * 3 / 4 ];
// for every 4 int values of the original array (RGBA) write 3
// bytes (RGB) to the output array
for (int i = 0, j = 0; i < dataLength; i+=4, j+=3) {
convertIntByteToByte(input, i, convertedData, j);
}
return convertedData;
}
}
}
I make the video using the following call in my main method
r.makeVideo("Video.mov");
And here is that method.
public void makeVideo (String movFile) throws MalformedURLException {
JpegImagesToMovie imageToMovie = new JpegImagesToMovie();
Vector<String> imgList = new Vector <String>();
File f = new File("C:\\Users\\user\\Desktop\\tmp\\");
File[] fileList = f.listFiles();
for (int i = 0; i < fileList.length; i++) {
imgList.add(fileList[i].getAbsolutePath());
}
MediaLocator ml;
if ((ml = imageToMovie.createMediaLocator(movFile)) == null) {
System.exit(0);
}
imageToMovie.doIt(width, height, (1000/125), imgList, ml);
}
Error when running:
Usage: java JpegImagesToMovie [-w ] [-h ] [-f ] [-o ] -i
Since no input (-i) forder provided, assuming this JAR is inside JPEGs folder.
Usage: java JpegImagesToMovie [-w ] [-h ] [-f ] [-o ] -i
Usage: java JpegImagesToMovie [-w ] [-h ] [-f ] [-o ] -i
Trying to guess movie size from first image
Exception in thread "main" java.lang.NullPointerException
at maple.JpegImagesToMovie.main(JpegImagesToMovie.java:342)
line 342
width = firstImageInFolder.getWidth();
Though a little late (since the question is already answered), and because coincidentally I have worked with ImageIO in the past few days, I will just add here my answer. Especially the second part of how to make JpegImagesToMovie to work with png files has not been answered and it might help someone else.
Double compression issue: As correctly identified by others, you are effectively compressing the JPEG image twice by using ImageIO.write(). The ImageIO is a utility class that based on the type of the file ("jpeg" in our case) chooses an appropriate ImageWriter. It then constructs an ImageInputStream and it passes it to the writer. Last it calls its write() method. Pretty much what #meewoK does in his answer. However every ImageWriter can take a ImageWriteParam instance that configures its details. ImageIO apparently cannot know what parameters each writer can accept, nor how it should be configured, so default settings are used. If you look at the source JPEGImageWriteParam has a default quality factor of 0.75, so you are effectively multiplying this factor with whatever quality used for the original file. (If it was 0.75 again, your final image has pretty much a 0.75 * 0.75 = 0.56 quality i.e. you lost the half of the original). Bottom line: Use ImageIO for quick reads or writes but if more control is desired the recommended approach is to configure and use an ImageWriter manually.
How to make JpegImagesToMovie to work directly with PNG files: If you look at the source of that class all the work is done in its ImageSourceStream inner class. What it does, is that it loads the bytes from the files (each file is a video frame) directly to a Processor class that creates the video. Conveniently the Processor knows how to handle JPEG format directly (look at the stream's constructor format = new VideoFormat(VideoFormat.JPEG, ...)), so feeding it with the raw file data works as expected.
To make it work with PNG format though, just replacing the file suffix as per your approach is not enough. You need to convert the PNG data to a format that Processor understands (i.e. to decode it). Below you can find a quick and dirty way to do that. Disclaimer: the approach below uses more memory since it loads the image in memory and it further manipulates it to convert it to a byte[]. So performance-wise and memory-wise is worst. However if memory and speed is not a concern, it would allow you to work directly with PNG files.
Conversion steps:
a) With an editor search and replace the string values "jpeg", "jpg" with "png". The original author has those values hard-coded and during your attempt you have missed some entries.
b) In the constructor of the ImageDataSource replace the first line with the second one :
streams[0] = new ImageSourceStream(width, height, frameRate, images); // delete this line
streams[0] = new PngImageSourceStream(width, height, frameRate, images); // add this line
c) At the end of the class add the implementation of the new PngImageSourceStream provided below.
You should now have a working copy of the original version that is able to read PNG files directly and convert them to a MOV video (Note: Not all players can play that new video because of the codec used. QuickTime and Media Player Classic worked ok for me)
Update 1: The original code of PngImageSourceStreamassumed a 32-bit PNG file, a restriction I forgot to mention. The version below is a second version that supports either 32-bit or 24-bit (i.e. with no alpha layer) images.
class PngImageSourceStream extends ImageSourceStream {
public PngImageSourceStream(int width, int height, int frameRate,
Vector<String> images) {
super(width, height, frameRate, images);
// configure the new format as RGB format
format = new RGBFormat(new Dimension(width, height),
Format.NOT_SPECIFIED, Format.byteArray, frameRate,
24, // 24 bits per pixel
1, 2, 3); // red, green and blue masks when data are in the form of byte[]
}
public void read(Buffer buf) throws IOException {
// Check if we've finished all the frames.
if (nextImage >= images.size()) {
// We are done. Set EndOfMedia.
System.err.println("Done reading all images.");
buf.setEOM(true);
buf.setOffset(0);
buf.setLength(0);
ended = true;
return;
}
String imageFile = (String) images.elementAt(nextImage);
nextImage++;
System.err.println(" - reading image file: " + imageFile);
// read the PNG image
BufferedImage image = ImageIO.read(new File(imageFile));
boolean hasAlpha = image.getColorModel().hasAlpha();
Dimension size = format.getSize();
// convert 32-bit RGBA to 24-bit RGB
byte[] imageData = convertTo24Bit(hasAlpha, image.getRaster().getPixels(0, 0, size.width, size.height, (int[]) null));
buf.setData(imageData);
System.err.println(" read " + imageData.length + " bytes.");
buf.setOffset(0);
buf.setLength(imageData.length);
buf.setFormat(format);
buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);
}
private void convertIntByteToByte(int[] src, int srcIndex, byte[] out, int outIndex) {
// Note: the int[] returned by bufferedImage.getRaster().getPixels()
// is an int[]
// where each int is the value for one color i.e. the first 4 ints
// contain the RGBA values for the first pixel
int r = src[srcIndex];
int g = src[srcIndex + 1];
int b = src[srcIndex + 2];
out[outIndex] = (byte) (r & 0xFF);
out[outIndex + 1] = (byte) (g & 0xFF);
out[outIndex + 2] = (byte) (b & 0xFF);
}
private byte[] convertTo24Bit(boolean hasAlpha, int[] input) {
int dataLength = input.length;
int newSize = (hasAlpha ? dataLength * 3 / 4 : dataLength);
byte[] convertedData = new byte[newSize];
// for every 4 int values of the original array (RGBA) write 3
// bytes (RGB) to the output array
// if there is no alpha (i.e. RGB image) then just convert int to byte
for (int i = 0, j = 0; i < dataLength; i += 3, j += 3) {
convertIntByteToByte(input, i, convertedData, j);
if (hasAlpha) {
i++; // skip an extra byte if the original image has an
// extra int for transparency
}
}
return convertedData;
}
}
Set the JPEG compression level1 to a higher quality. This will result in a larger file size, but should fix the problem of quality.
But encode them only as:
ImageIO.write(img, "jpg", new File("..." + ".jpg"));
As seen in this answer - screenshot below.
Compile and run the code in the linked answer, drag the Slider on the left up and down to see the result of that compression level in the JPEG (bottom/3rd image). The text area at the bottom will show the size in bytes at that level of 'quality'. The quality is inverse to the compression, and as you might notice from the images the JPEG at %80 quality is not only a bit 'murky', but is already significantly larger in bytes than the PNG.
Then have a careful look at the code, especially:
private Image getJpegCompressedImage(BufferedImage image) throws IOException {
float qualityFloat = (float)quality.getValue()/100f;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream );
imgWriter.setOutput( ioStream );
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
jpegParams.setCompressionQuality( qualityFloat ); // Set the compression level
imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );
ioStream.flush();
ioStream.close();
imgWriter.dispose();
jpgSize = outStream.toByteArray().length;
BufferedImage compressedImage = ImageIO.read(new ByteArrayInputStream(outStream.toByteArray()));
return compressedImage;
}
I'm taking a stab at this by bypassing ImgIO.write() and directly writing the bytes to the file using a FileOutputStream ...
I'm bypasing ImgIO.write due to numerous mentions on the internet saying imagequality cannot be controlled through this method.
Quoting from here: http://www.universalwebservices.net/web-programming-resources/java/adjust-jpeg-image-compression-quality-when-saving-images-in-java
If you've been using the imageIO.write method to save JPEG images, you may notice that some image lose quality. This is because you can't instruct the imagIO.write method to apply a certain compression quality to the images.
My first Stab:
import java.awt.image.BufferedImage;
public class Quality {
public static void main (String[] args) {
BufferedImage img;
try {
File f = new File("test" + ".jpg");
img = ImageIO.read(f);
writeJpegCompressedImage(img,"NEW" + ".jpg" );
} catch (IOException e) {
e.printStackTrace();
}
}
private static void writeJpegCompressedImage(BufferedImage image, String outFile) throws IOException {
float qualityFloat = 1f;
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
ImageWriter imgWriter = ImageIO.getImageWritersByFormatName( "jpg" ).next();
ImageOutputStream ioStream = ImageIO.createImageOutputStream( outStream );
imgWriter.setOutput( ioStream );
JPEGImageWriteParam jpegParams = new JPEGImageWriteParam( Locale.getDefault() );
jpegParams.setCompressionMode( ImageWriteParam.MODE_EXPLICIT );
jpegParams.setCompressionQuality( qualityFloat );
imgWriter.write( null, new IIOImage( image, null, null ), jpegParams );
ioStream.flush();
ioStream.close();
imgWriter.dispose();
OutputStream outputStream = new FileOutputStream ( outFile);
outStream.writeTo(outputStream);
}
}
I am having a problem with a Java Source Data Line. I need to play a tone, so I've created a Tone class that just represents a tone. Everything works fine, except that when I play the sound, the speakers pop at the beginning of the sound. Is there any way to fix this? It is for a research project and needs to run without the crackling, as that could affect results. Source code below. Thanks!
package edu.jhu.halberda.audiopanamath;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JOptionPane;
public class Tone {
public enum Channel {
LEFT, RIGHT, STEREO
};
public static final float SAMPLE_RATE = 44104; // Should be a multiple of 8
protected byte[] buf;
protected int hz, msecs;
protected double vol;
protected Channel channel;
Tone() {
} // necessary so that subclasses don't complain
public Tone(int hz, int msecs, double vol, Tone.Channel channel) {
if (hz <= 0)
throw new IllegalArgumentException("Frequency <= 0 hz");
if (msecs <= 0)
throw new IllegalArgumentException("Duration <= 0 msecs");
if (vol > 1.0 || vol < 0.0)
throw new IllegalArgumentException("Volume out of range 0.0 - 1.0");
this.channel = channel;
this.hz = hz;
this.vol = vol;
this.msecs = msecs;
generateTone();
}
private void generateTone() {
int len = (int)Math.ceil((2 * SAMPLE_RATE * msecs / 1000.0d));
if (len % 2 == 1)
len = len + 1;
buf = new byte[len];
for (int i = 0; i < buf.length /2; i++) {
double angle = (i * hz / SAMPLE_RATE) * 2.0 * Math.PI;
buf[2*i + 1] = buf[2*i] = (byte) Math.round(Math.sin(angle) * 127.0 * vol);
}
}
public void play(SourceDataLine sdl) { // takes an opened SourceDataLine
FloatControl panControl = (FloatControl) sdl
.getControl(FloatControl.Type.PAN);
if (panControl != null) { // Preferred method using built in sound
// control, but not guaranteed to be
// available
if (channel == Channel.LEFT) {
panControl.setValue(-1);
} else if (channel == Channel.RIGHT) {
panControl.setValue(1);
} else {
panControl.setValue(0);
}
} else { // fallback method is directly manipulates the buffer
if (channel != Channel.STEREO) {
int nSilenceOffset;
byte nSilenceValue = 0;
if (channel == Channel.LEFT) {
nSilenceOffset = 1;
} else {
nSilenceOffset = 0;
}
for (int i = 0; i < buf.length; i += 2) {
buf[i + nSilenceOffset] = nSilenceValue;
}
}
}
sdl.write(buf, 0, buf.length);
sdl.drain();
}
public static void main(String[] args) {
AudioFormat af = new AudioFormat(Tone.SAMPLE_RATE, 8, 2, true, false);
SourceDataLine sdl;
try {
sdl = AudioSystem.getSourceDataLine(af);
} catch (LineUnavailableException e) {
JOptionPane.showMessageDialog(null, "Couldn't get sound line");
return;
}
try {
sdl.open(af);
} catch (LineUnavailableException e) {
JOptionPane.showMessageDialog(null, "Couldn't open sound line");
return;
}
sdl.start();
Tone left = new Tone(400, 2000, .5, Tone.Channel.LEFT);
System.out.println("Playing left");
long t = System.currentTimeMillis();
left.play(sdl);
System.out.println(System.currentTimeMillis()-t);
System.out.println("Finished left");
Tone right = new Tone(400, 2000, .5, Tone.Channel.RIGHT);
System.out.println("Playing right");
right.play(sdl);
System.out.println("Finished right");
sdl.stop();
sdl.close();
}
}
Try this variant, that uses a crude fade in/fade out effect.
private void generateTone() {
int len = (int)Math.ceil((2 * SAMPLE_RATE * msecs / 1000.0d));
if (len % 2 == 1)
len = len + 1;
buf = new byte[len];
int fadeCount = 1600;
for (int i = 0; i < buf.length /2; i++) {
double fadeRate = 1.0;
double angle = (i * hz / SAMPLE_RATE) * 2.0 * Math.PI;
if (i<fadeCount) {
fadeRate = (double)i/(double)fadeCount;
} else if (i>(buf.length/2)-fadeCount) {
int bufLength = buf.length;
int buf = bufLength/2;
int countDown = buf-i;
fadeRate = (double)countDown/(double)(fadeCount);
}
buf[2*i + 1] = buf[2*i] = (byte) Math.round(
Math.cos(angle) * 127.0 * vol * fadeRate);
}
}
I'm currently using Win32ShellFolderManager2 and ShellFolder.getLinkLocation to resolve windows shortcuts in Java. Unfortunately, if the Java program is running as a service under Vista, getLinkLocation, this does not work. Specifically, I get an exception stating "Could not get shell folder ID list".
Searching the web does turn up mentions of this error message, but always in connection with JFileChooser. I'm not using JFileChooser, I just need to resolve a .lnk file to its destination.
Does anyone know of a 3rd-party parser for .lnk files written in Java I could use?
I've since found unofficial documentation for the .lnk format here, but I'd rather not have to do the work if anyone has done it before, since the format is rather scary.
Added comments (some explanation as well as credit to each contributor so far),additional check on the file magic, a quick test to see if a given file might be a valid link (without reading all of the bytes), a fix to throw a ParseException with appropriate message instead of ArrayIndexOutOfBoundsException if the file is too small, did some general clean-up.
Source here (if you have any changes, push them right to the GitHub repo/project.
package org.stackoverflowusers.file;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
/**
* Represents a Windows shortcut (typically visible to Java only as a '.lnk' file).
*
* Retrieved 2011-09-23 from http://stackoverflow.com/questions/309495/windows-shortcut-lnk-parser-in-java/672775#672775
* Originally called LnkParser
*
* Written by: (the stack overflow users, obviously!)
* Apache Commons VFS dependency removed by crysxd (why were we using that!?) https://github.com/crysxd
* Headerified, refactored and commented by Code Bling http://stackoverflow.com/users/675721/code-bling
* Network file support added by Stefan Cordes http://stackoverflow.com/users/81330/stefan-cordes
* Adapted by Sam Brightman http://stackoverflow.com/users/2492/sam-brightman
* Based on information in 'The Windows Shortcut File Format' by Jesse Hager <jessehager#iname.com>
* And somewhat based on code from the book 'Swing Hacks: Tips and Tools for Killer GUIs'
* by Joshua Marinacci and Chris Adamson
* ISBN: 0-596-00907-0
* http://www.oreilly.com/catalog/swinghks/
*/
public class WindowsShortcut
{
private boolean isDirectory;
private boolean isLocal;
private String real_file;
/**
* Provides a quick test to see if this could be a valid link !
* If you try to instantiate a new WindowShortcut and the link is not valid,
* Exceptions may be thrown and Exceptions are extremely slow to generate,
* therefore any code needing to loop through several files should first check this.
*
* #param file the potential link
* #return true if may be a link, false otherwise
* #throws IOException if an IOException is thrown while reading from the file
*/
public static boolean isPotentialValidLink(File file) throws IOException {
final int minimum_length = 0x64;
InputStream fis = new FileInputStream(file);
boolean isPotentiallyValid = false;
try {
isPotentiallyValid = file.isFile()
&& file.getName().toLowerCase().endsWith(".lnk")
&& fis.available() >= minimum_length
&& isMagicPresent(getBytes(fis, 32));
} finally {
fis.close();
}
return isPotentiallyValid;
}
public WindowsShortcut(File file) throws IOException, ParseException {
InputStream in = new FileInputStream(file);
try {
parseLink(getBytes(in));
} finally {
in.close();
}
}
/**
* #return the name of the filesystem object pointed to by this shortcut
*/
public String getRealFilename() {
return real_file;
}
/**
* Tests if the shortcut points to a local resource.
* #return true if the 'local' bit is set in this shortcut, false otherwise
*/
public boolean isLocal() {
return isLocal;
}
/**
* Tests if the shortcut points to a directory.
* #return true if the 'directory' bit is set in this shortcut, false otherwise
*/
public boolean isDirectory() {
return isDirectory;
}
/**
* Gets all the bytes from an InputStream
* #param in the InputStream from which to read bytes
* #return array of all the bytes contained in 'in'
* #throws IOException if an IOException is encountered while reading the data from the InputStream
*/
private static byte[] getBytes(InputStream in) throws IOException {
return getBytes(in, null);
}
/**
* Gets up to max bytes from an InputStream
* #param in the InputStream from which to read bytes
* #param max maximum number of bytes to read
* #return array of all the bytes contained in 'in'
* #throws IOException if an IOException is encountered while reading the data from the InputStream
*/
private static byte[] getBytes(InputStream in, Integer max) throws IOException {
// read the entire file into a byte buffer
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (max == null || max > 0) {
int n = in.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
if (max != null)
max -= n;
}
in.close();
return bout.toByteArray();
}
private static boolean isMagicPresent(byte[] link) {
final int magic = 0x0000004C;
final int magic_offset = 0x00;
return link.length >= 32 && bytesToDword(link, magic_offset) == magic;
}
/**
* Gobbles up link data by parsing it and storing info in member fields
* #param link all the bytes from the .lnk file
*/
private void parseLink(byte[] link) throws ParseException {
try {
if (!isMagicPresent(link))
throw new ParseException("Invalid shortcut; magic is missing", 0);
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if ((file_atts & is_dir_mask) > 0) {
isDirectory = true;
} else {
isDirectory = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte)0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytesToWord(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
final int file_location_info_flag_offset_offset = 0x08;
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
isLocal = (file_location_info_flag & 2) == 0;
// get the local volume and local system values
//final int localVolumeTable_offset_offset = 0x0C;
final int basename_offset_offset = 0x10;
final int networkVolumeTable_offset_offset = 0x14;
final int finalname_offset_offset = 0x18;
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
String finalname = getNullDelimitedString(link, finalname_offset);
if (isLocal) {
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset);
real_file = basename + finalname;
} else {
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
int shareName_offset_offset = 0x08;
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
+ networkVolumeTable_offset;
String shareName = getNullDelimitedString(link, shareName_offset);
real_file = shareName + "\\" + finalname;
}
} catch (ArrayIndexOutOfBoundsException e) {
throw new ParseException("Could not be parsed, probably not a valid WindowsShortcut", 0);
}
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytesToWord(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
private static int bytesToDword(byte[] bytes, int off) {
return (bytesToWord(bytes, off + 2) << 16) | bytesToWord(bytes, off);
}
}
Sam Brightman's solution is for local files only.
I added support for Network files:
Windows shortcut (.lnk) parser in Java?
http://code.google.com/p/8bits/downloads/detail?name=The_Windows_Shortcut_File_Format.pdf
http://www.javafaq.nu/java-example-code-468.html
public class LnkParser {
public LnkParser(File f) throws IOException {
parse(f);
}
private boolean isDirectory;
private boolean isLocal;
public boolean isDirectory() {
return isDirectory;
}
private String real_file;
public String getRealFilename() {
return real_file;
}
private void parse(File f) throws IOException {
// read the entire file into a byte buffer
FileInputStream fin = new FileInputStream(f);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (true) {
int n = fin.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
}
fin.close();
byte[] link = bout.toByteArray();
parseLink(link);
}
private void parseLink(byte[] link) {
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte)0x10;
if ((file_atts & is_dir_mask) > 0) {
isDirectory = true;
} else {
isDirectory = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte)0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytes2short(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
final int file_location_info_flag_offset_offset = 0x08;
int file_location_info_flag = link[file_start + file_location_info_flag_offset_offset];
isLocal = (file_location_info_flag & 2) == 0;
// get the local volume and local system values
//final int localVolumeTable_offset_offset = 0x0C;
final int basename_offset_offset = 0x10;
final int networkVolumeTable_offset_offset = 0x14;
final int finalname_offset_offset = 0x18;
int finalname_offset = link[file_start + finalname_offset_offset] + file_start;
String finalname = getNullDelimitedString(link, finalname_offset);
if (isLocal) {
int basename_offset = link[file_start + basename_offset_offset] + file_start;
String basename = getNullDelimitedString(link, basename_offset);
real_file = basename + finalname;
} else {
int networkVolumeTable_offset = link[file_start + networkVolumeTable_offset_offset] + file_start;
int shareName_offset_offset = 0x08;
int shareName_offset = link[networkVolumeTable_offset + shareName_offset_offset]
+ networkVolumeTable_offset;
String shareName = getNullDelimitedString(link, shareName_offset);
real_file = shareName + "\\" + finalname;
}
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
/**
* Returns the value of the instance variable 'isLocal'.
*
* #return Returns the isLocal.
*/
public boolean isLocal() {
return isLocal;
}
}
I can recommend this repository on GitHub:
https://github.com/BlackOverlord666/mslinks
There I've found a simple solution to create shortcuts:
ShellLink.createLink("path/to/existing/file.txt", "path/to/the/future/shortcut.lnk");
If you want to read shortcuts:
File shortcut = ...;
String pathToExistingFile = new ShellLink(shortcut).resolveTarget();
If you want to change the icon of the shortcut, use:
ShellLink sl = ...;
sl.setIconLocation("/path/to/icon/file");
You can edit most properties of the shortcutlink such as working directory, tooltip text, icon, command line arguments, hotkeys, create links to LAN shared files and directories and much more...
Hope this helps you :)
Kind regards
Josua Frank
The code plan9assembler linked to appears to work with minor modification. I think it's just the "& 0xff" to prevent sign extension when bytes are upcast to ints in the bytes2short function that need changing. I've added the functionality described in http://www.i2s-lab.com/Papers/The_Windows_Shortcut_File_Format.pdf to concatenate the "final part of the pathname" even though in practice this doesn't seem to be used in my examples. I've not added any error checking to the header or dealt with network shares. Here's what I'm using now:
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class LnkParser {
public LnkParser(File f) throws Exception {
parse(f);
}
private boolean is_dir;
public boolean isDirectory() {
return is_dir;
}
private String real_file;
public String getRealFilename() {
return real_file;
}
private void parse(File f) throws Exception {
// read the entire file into a byte buffer
FileInputStream fin = new FileInputStream(f);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
byte[] buff = new byte[256];
while (true) {
int n = fin.read(buff);
if (n == -1) {
break;
}
bout.write(buff, 0, n);
}
fin.close();
byte[] link = bout.toByteArray();
// get the flags byte
byte flags = link[0x14];
// get the file attributes byte
final int file_atts_offset = 0x18;
byte file_atts = link[file_atts_offset];
byte is_dir_mask = (byte) 0x10;
if ((file_atts & is_dir_mask) > 0) {
is_dir = true;
} else {
is_dir = false;
}
// if the shell settings are present, skip them
final int shell_offset = 0x4c;
final byte has_shell_mask = (byte) 0x01;
int shell_len = 0;
if ((flags & has_shell_mask) > 0) {
// the plus 2 accounts for the length marker itself
shell_len = bytes2short(link, shell_offset) + 2;
}
// get to the file settings
int file_start = 0x4c + shell_len;
// get the local volume and local system values
final int basename_offset_offset = 0x10;
final int finalname_offset_offset = 0x18;
int basename_offset = link[file_start + basename_offset_offset]
+ file_start;
int finalname_offset = link[file_start + finalname_offset_offset]
+ file_start;
String basename = getNullDelimitedString(link, basename_offset);
String finalname = getNullDelimitedString(link, finalname_offset);
real_file = basename + finalname;
}
private static String getNullDelimitedString(byte[] bytes, int off) {
int len = 0;
// count bytes until the null character (0)
while (true) {
if (bytes[off + len] == 0) {
break;
}
len++;
}
return new String(bytes, off, len);
}
/*
* convert two bytes into a short note, this is little endian because it's
* for an Intel only OS.
*/
private static int bytes2short(byte[] bytes, int off) {
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
}
I've also worked( now have no time for that) on '.lnk' in Java. My code is here
It's little messy( some testing trash) but local and network parsing works good. Creating links is implemented too. Please test and send me patches.
Parsing example:
Shortcut scut = Shortcut.loadShortcut(new File("C:\\t.lnk"));
System.out.println(scut.toString());
Creating new link:
Shortcut scut = new Shortcut(new File("C:\\temp"));
OutputStream os = new FileOutputStream("C:\\t.lnk");
os.write(scut.getBytes());
os.flush();
os.close();
The solution of #Code Bling does not work for me for Files in the User directory.
For Example "C:/Users/Username/Filename.txt".
The reason for that is: in The_Windows_Shortcut_File_Format.pdf
that was mentioned by #Stefan Cordes on page 6 it says that only the first 2 bits are important for volumes info.
All other bits might be filled with random garbage when the first bit of volumes info is "0".
So if it comes to:
isLocal = (file_location_info_flag & 2) == 0;
then file_location_info_flag might be "3".
This file is still local but this line of code assigns false to isLocal.
So i suggest the following adjustment to #Code Bling's code:
isLocal = (file_location_info_flag & 1) == 1;
This short code is really usefull...
But two fixes are needed:
the isPotentialValidLink improved not to load file if name doesn't end with ".lnk"
public static boolean isPotentialValidLink(final File file) {
final int minimum_length = 0x64;
boolean isPotentiallyValid = false;
if (file.getName().toLowerCase().endsWith(".lnk"))
try (final InputStream fis = new FileInputStream(file)) {
isPotentiallyValid = file.isFile() && fis.available() >= minimum_length && isMagicPresent(getBytes(fis, 32));
} catch (Exception e) {
// forget it
}
return isPotentiallyValid;
}
the offset has to be computed with 32bits not only a byte...
final int finalname_offset = bytesToDword(link,file_start + finalname_offset_offset) + file_start;
final int basename_offset = bytesToDword(link,file_start + basename_offset_offset) + file_start;
I found other non-professional technique. getting my job done.
File file=new File("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\TeamViewer.lnk");//shortcut link
FileInputStream stream=new FileInputStream(file);
DataInputStream st=new DataInputStream(stream);
byte[] bytes=new byte[(int)stream.getChannel().size()];
stream.read(bytes);
String data=new String(bytes);
int i1=data.indexOf("C:\\Program Files");
int i2=data.indexOf(".exe",i1);
System.out.println(data.substring(i1, i2+4));
The given code works well, but has a bug. A java byte is a signed value from -128 to 127. We want an unsigned value from 0 to 255 to get the correct results. Just change the bytes2short function as follows:
static int bytes2short(byte[] bytes, int off) {
int low = (bytes[off]<0 ? bytes[off]+256 : bytes[off]);
int high = (bytes[off+1]<0 ? bytes[off+1]+256 : bytes[off+1])<<8;
return 0 | low | high;
}