ObjectInputStream being corrupted during byte array read - java

I appear to be getting corruption when reading an ObjectInputStream. The attached snippet throws an exception prior to completion. I fixed the example to call oos.writeObject( p1 ) as suggested.
The Exception stack is as follows:
java.lang.OutOfMemoryError: Java heap space
at test.POJO.readExternal(STest.java:82)
at java.io.ObjectInputStream.readExternalData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at test.STest.test(STest.java:37)
I believe this OutOfMemoryError exception to be misleading. I added a print statement showing the readExternal(..) behavior and am seeing a large value being pulled from ObjectInputStream, this does not correlate to what was written. If DIM is set to 5 it works if set to 15 I get the above exception. If I lower the number of bytes written per array element I get more successful iterations.
package test;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import org.junit.Test;
public class STest
{
#Test
public void test() throws Exception
{
POJO p1 = new POJO();
POJO p2 = new POJO();
// Initialize and serialize POJO 1
// --------------------------------
p1.hydrate();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream( baos );
oos.writeObject( p1 );
oos.flush();
oos.close();
byte [] baSerialized = baos.toByteArray();
// Parse POJO 2
// -------------
ObjectInputStream ois = new ObjectInputStream( new ByteArrayInputStream( baSerialized ) );
p2 = (POJO)ois.readObject();
// Test Result
// ------------
byte [][] baa1 = p1._baa;
byte [][] baa2 = p2._baa;
for ( int i=0; i < baa1.length; i++ )
{
String str1 = new String( baa1[ i ] );
String str2 = new String( baa2[ i ] );
assertTrue( str1.equals( str2 ) );
}
}
}
class POJO implements Externalizable
{
protected static final int DIM = 5;
protected byte [][] _baa = null;
public POJO()
{
}
public void hydrate()
{
_baa = new byte[ DIM ][];
for ( int i = 0; i < _baa.length; i++ )
{
_baa[ i ] = ("This is a serialize and parse test, it will be interesting to see if it completes without exception, I suspect not as there appears be a bug in the JRE - " + i).getBytes();
}
}
#Override
public void readExternal( ObjectInput oi ) throws IOException, ClassNotFoundException
{
int iDim = oi.readInt();
_baa = new byte[ iDim ][];
for ( int i=0; i < iDim; i++ )
{
int iSize = oi.readInt();
System.out.println( iSize );
byte [] ba = new byte[ iSize ];
oi.read( ba );
_baa[ i ] = ba;
}
}
#Override
public void writeExternal( ObjectOutput oo ) throws IOException
{
oo.writeInt( _baa.length );
for ( int i=0; i < _baa.length; i++ )
{
oo.writeInt( _baa[ i ].length );
oo.write( _baa[ i ] );
}
}
}

p1.writeExternal(o);
That should be:
oo.writeObject(p1);
You aren't supposed to call your own writeExternal() method directly. The ObjectOutputStream does that.

Related

Webp support for java

As our network based applications fall in love with webp image formats, I found my self in need of a method or a lib which can decode it,
I have written this piece of code, but it only misses the native decoder(how ever I prefer it to be a jar lib) :
public BufferedImage decodeWebP(byte[] encoded, int w, int h) {
int[] width = new int[]{w};
int[] height = new int[]{h};
byte[] decoded = decodeRGBAnative(encoded); //here is the missing part ,
if (decoded.length == 0) return null;
int[] pixels = new int[decoded.length / 4];
ByteBuffer.wrap(decoded).asIntBuffer().get(pixels);
BufferedImage bufferedImage = new BufferedImage(width[0], height[0], BufferedImage.TYPE_INT_RGB);
// bufferedImage.setRGB(x, y, your_value);
int BLOCK_SIZE = 3;
for(int r=0; r< height[0]; r++) {
for (int c = 0; c < width[0]; c++) {
int index = r * width[0] * BLOCK_SIZE + c * BLOCK_SIZE;
int red = pixels[index] & 0xFF;
int green = pixels[index + 1] & 0xFF;
int blue = pixels[index + 2] & 0xFF;
int rgb = (red << 16) | (green << 8) | blue;
bufferedImage.setRGB(c, r, rgb);
}
}
return bufferedImage;
}
Please use OpenCV. I use maven and org.openpnp:opencv:4.5.1-2. All it takes to encode an image that is stored in a Mat is:
public static byte [] encodeWebp(Mat image, int quality) {
MatOfInt parameters = new MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, quality);
MatOfByte output = new MatOfByte();
if(Imgcodecs.imencode(".webp", image, output, parameters))
return output.toArray();
else
throw new IllegalStateException("Failed to encode the image as webp with quality " + quality);
}
I am converting it to byte [] arrays since I store it mostly in S3 and DB and rather sheldom in the filesystem.
The bitbucket link of the original answer is not available anymore, but forks from the original repository can be found, in example: https://github.com/sejda-pdf/webp-imageio
I tried using the webp-imageio implementation from the mentioned github, but after 2 days of using it in production, I got a segmentation fault coming from the native library that crashed the whole server.
I resorted to using the compiled tools provided by google here: https://developers.google.com/speed/webp/download and do a small wrapper class to call the binaries.
In my case, I needed to convert from other images formats to webp, so I needed the "cwebp" binary. The wrapper I wrote is:
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.concurrent.TimeUnit;
public class ImageWebpLibraryWrapper {
private static final String CWEBP_BIN_PATH = "somepath/libwebp-1.1.0-linux-x86-64/bin/cwebp";
public static boolean isWebPAvailable() {
if ( CWEBP_BIN_PATH == null ) {
return false;
}
return new File( CWEBP_BIN_PATH ).exists();
}
public static boolean convertToWebP( File imageFile, File targetFile, int quality ) {
Process process;
try {
process = new ProcessBuilder( CWEBP_BIN_PATH, "-q", "" + quality, imageFile.getAbsolutePath(), "-o",
targetFile.getAbsolutePath() ).start();
process.waitFor( 10, TimeUnit.SECONDS );
if ( process.exitValue() == 0 ) {
// Success
printProcessOutput( process.getInputStream(), System.out );
return true;
} else {
printProcessOutput( process.getErrorStream(), System.err );
return false;
}
} catch ( Exception e ) {
e.printStackTrace();
return false;
}
}
private static void printProcessOutput( InputStream inputStream, PrintStream output ) throws IOException {
try ( InputStreamReader isr = new InputStreamReader( inputStream );
BufferedReader bufferedReader = new BufferedReader( isr ) ) {
String line;
while ( ( line = bufferedReader.readLine() ) != null ) {
output.println( line );
}
}
}
An implementation around ImageIO is nicer, but I couldn't have a segmentation fault crashing the production server.
Sample usage:
public static void main( String args[] ) {
if ( !isWebPAvailable() ) {
System.err.println( "cwebp binary not found!" );
return;
}
File file = new File( "/home/xxx/Downloads/image_11.jpg" );
File outputFile = new File( "/home/xxx/Downloads/image_11-test.webp" );
int quality = 80;
if ( convertToWebP( file, outputFile, quality ) ) {
System.out.println( "SUCCESS" );
} else {
System.err.println( "FAIL" );
}
}
Used OpenPnP OpenCV in kotlin:
fun encodeToWebP(image: ByteArray): ByteArray {
val matImage = Imgcodecs.imdecode(MatOfByte(*image), Imgcodecs.IMREAD_UNCHANGED)
val parameters = MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, 100)
val output = MatOfByte()
if (Imgcodecs.imencode(".webp", matImage, output, parameters)) {
return output.toArray()
} else {
throw IllegalStateException("Failed to encode the image as webp")
}
}
among all searches possible , this one was best and easiest :
https://bitbucket.org/luciad/webp-imageio
not full java implemention , but very easy comparing to others
for java developers, who are coming from a search engine, I have converted #DuĊĦan Salay answer to java:
private byte[] encodeToWebP(byte[] data) {
Mat matImage = Imgcodecs.imdecode(new MatOfByte(data), Imgcodecs.IMREAD_UNCHANGED);
MatOfInt parameters = new MatOfInt(Imgcodecs.IMWRITE_WEBP_QUALITY, 100);
MatOfByte output = new MatOfByte();
Imgcodecs.imencode(".webp", matImage, output, parameters);
return output.toArray();
}
I have used apache commons to readFileToByteArray.
Also you will need to load library first in static block
static {
OpenCV.loadLocally();
}

Read lines from a .txt to a table of objects

I need help; what I'm trying to do is to put each line of a text file into an table of object.
Let's get into example :
I have that text file:
ATI
8
10
AMX
12
15
As we can see, this text file contain different types of mother boards.
So I have this constructor (which is on another class) of mother boards objects:
motherBoard(String type, int size, int max_size){
this.type = type;
this.size = size;
this.max_size = max_size;
}
So what I'm trying to do is to make a table of object where (in this example) the first object of the table would contain the 3 first lines of the text file. Then, the 2nd object of the table would contain the next 3 lines so on...
All the operations I want to do are in a method...
Here is my code so far :
public static CarteMere[] chargerEnMemoire(String nomFichier) throws IOException{
CarteMere[] newCarteMere = new CarteMere[0];
try {
FileReader reader = new FileReader( "liste.txt" );
BufferedReader buffer = new BufferedReader( reader );
int nmLines = 0;
while ( buffer.ready() ) {
nmLines++; // To evaluate the size of the table
}
buffer.close();
newCarteMere = new CarteMere[nmLines/3];
for(int i=0; i<(nmLines/3);i++){
}
} catch ( FileNotFoundException e ) {
System.out.println( e.getMessage() );
} catch ( IOException e ) {
System.out.println( e.getMessage() );
}
return newCarteMere;
}
That's where I need a push...
Start with what you know, you have a file, it contains data in groups of three lines, one's a String, the other two are numbers.
You have a class which represents that data, always easier to store data in an object where possible, so you could do something like...
try (BufferedReader br = new BufferedReader(new FileReader("liste.txt"))) {
String name = br.readLine();
int size = Integer.parseInt(br.readLine());
int max = Integer.parseInt(br.readLine());
Motherboard mb = new MotherBoard(name, size, max);
// Add it to a List or what ever else you want to do it...
} catch (IOException exp) {
exp.printStackTrace();
}
In case you have getter and setter for all 3 variables in your pojo class and you have overloaded toString() method in motherBoard class,
Integer noOfLines=0, i=0, j=0;
String [] data=null;
String sCurrentLine=null;
BufferedReader br = new BufferedReader(new FileReader("liste.txt"));
while ((sCurrentLine = br.readLine()) != null)
{
noOfLines++;
data[i]=sCurrentLine;
}
i=-1;
j=noOfLines/3;
motherBoard mb=null;
for(int x=0;x<=j;x++)
{
mb=new motherBoard(data[++i], Integer.parseInt(data[++i]), Integer.parseInt(data[++i]))
System.out.println(mb);
}
Regarding just reading the data from a file and loading it into classes, hopefully this example will help you:
MotherBoard.java
public class MotherBoard {
String type;
int size;
int max_size;
public MotherBoard (String type, int size, int max_size) {
this.type = type;
this.size = size;
this.max_size = max_size;
}
public String toString() {
return "Motherboard data : type=" + type + " size=" + size + ", max_size=" + max_size;
}
}
Solution.java:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Solution {
public static void main(String[] args) {
try {
FileReader reader = new FileReader( "liste.txt" );
BufferedReader buffer = new BufferedReader( reader );
int nmLines = 0;
int numMBs = 0;
while (buffer.ready()) {
numMBs++;
/* Not a good approach if your data isn't perfect - just an example for StackOverflow */
MotherBoard mb = new MotherBoard (buffer.readLine(),
Integer.parseInt(buffer.readLine()),
Integer.parseInt(buffer.readLine()));
System.out.println("Motherboard " + numMBs + ":");
System.out.println(mb.toString());
}
/* Probably want to do this in a finally block too */
buffer.close();
reader.close();
} catch ( FileNotFoundException e ) {
System.out.println( e.getMessage() );
} catch ( IOException e ) {
System.out.println( e.getMessage() );
}
}
}
To compile and run:
javac -cp . MotherBoard.java Solution.java
java Solution
Output:
Motherboard 1:
Motherboard data : type=ATI size=8, max_size=10
Motherboard 2:
Motherboard data : type=AMX size=12, max_size=15
So i found a way... I think i found a solution...
Here is my new code :
public static CarteMere[] chargerEnMemoire(String nomFichier) throws IOException{
CarteMere[] newCarteMere = new CarteMere[0];
try {
FileReader reader = new FileReader( nomFichier );
BufferedReader buffer = new BufferedReader( reader );
int nmLines = 0;
while ( buffer.readLine() != null) {
nmLines++; // To evaluate the size of the table
}
buffer.close();
reader.close();
//RESET THE BUFFER AND READER TO GET ON TOP OF THE FILE
FileReader reader2 = new FileReader( nomFichier );
BufferedReader buffer2 = new BufferedReader( reader2 );
newCarteMere = new CarteMere[nmLines/3];
for(int i=0; i<(nmLines/3);i++){
int forme = CarteMere.chaineFormeVersCode(buffer2.readLine());
int mem_inst = Integer.parseInt(buffer2.readLine());
int mem_max = Integer.parseInt(buffer2.readLine());
newCarteMere[i] = new CarteMere(forme, mem_inst, mem_max);
}
buffer2.close();
reader2.close();
} catch ( FileNotFoundException e ) {
System.out.println( e.getMessage() );
} catch ( IOException e ) {
System.out.println( e.getMessage() );
}
return newCarteMere;
}
But the idea of reseting the buffer and redear like that seems ugly to me... Do you guys have an idea to do the same sime but more correctly ?
Thank in advance

Loud Noise on Encrypted Voice Chat in Java Through UDP

We had to built a encrypted/decrypted voice chat through UDP. Chat is working without encryption but when I add AES code to encrypt, i hear very loud noise which is continuous periodic beep signal but at the same time I also hear decrypted conversations which is fine. I need to eliminate this noise.
We will be so grateful for your help. Thank you
Sending
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.sound.sampled.*;
public class MicPlayer {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234 ;
/** Creates a new instance of MicPlayer */
public MicPlayer() {
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Mixer.Info minfo[] = AudioSystem.getMixerInfo() ;
for( int i = 0 ; i < minfo.length ; i++ )
{
System.out.println( minfo[i] ) ;
}
if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
try {
DataLine.Info dataLineInfo = new DataLine.Info( TargetDataLine.class , getAudioFormat() ) ;
TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine( dataLineInfo ) ;
targetDataLine.open( getAudioFormat() );
targetDataLine.start();
byte tempBuffer[] = new byte[8192] ;
while( true )
{
targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer);
sendThruUDP(encrypt) ;
}
}
catch(Exception e )
{
System.out.println(" not correct " ) ;
System.exit(0) ;
}
}
}
public static AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
public static void sendThruUDP( byte soundpacket[] )
{
try
{
DatagramSocket sock = new DatagramSocket() ;
sock.send( new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ) ;
sock.close() ;
}
catch( Exception e )
{
e.printStackTrace() ;
System.out.println(" Unable to send soundpacket using UDP " ) ;
}
}
}
Receiving
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class RadioReceiver extends Thread {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 1234;
/** Creates a new instance of RadioReceiver */
public RadioReceiver() {
}
public void run()
{
byte b[] = null ;
while( true )
{
b = receiveThruUDP() ;
toSpeaker( b ) ;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
RadioReceiver r = new RadioReceiver() ;
r.start() ;
}
public static byte[] receiveThruUDP()
{
try
{
DatagramSocket sock = new DatagramSocket(PORT_TO_STREAM_TO) ;
byte soundpacket[] = new byte[8192] ;
DatagramPacket datagram = new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ;
sock.receive( datagram ) ;
sock.close() ;
return AES.decrypt(datagram.getData()); // soundpacket ;
}
catch( Exception e )
{
System.out.println(" Unable to send soundpacket using UDP " ) ;
return null ;
}
}
public static void toSpeaker( byte soundbytes[] )
{
try{
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class , getAudioFormat() ) ;
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine( dataLineInfo );
sourceDataLine.open( getAudioFormat() ) ;
sourceDataLine.start();
sourceDataLine.write( soundbytes , 0, soundbytes.length );
sourceDataLine.drain() ;
sourceDataLine.close() ;
}
catch(Exception e )
{
System.out.println("not working in speakers " ) ;
}
}
public static AudioFormat getAudioFormat()
{
float sampleRate = 44100.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
}
AES
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AES {
static String IV = "AAAAAAAAAAAAAAAA";
static String encryptionKey = "0123456789abcdef";
public static byte[] encrypt(byte[] inputcum) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(inputcum);
}
public static byte[] decrypt(byte[] cipherSound) throws Exception{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.DECRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(cipherSound);
}
}
The problem is not related to UDP or encryption. Each call to TargetDataLine.read(byte[]) fills only a part of array with remaining part being filled with leftovers from the previous calls, but you are encrypting and sending the whole array each time.
A call to TargetDataLine.read(byte[]) behaves similar to InputStream.read(byte[]) - it returns the actual number of bytes transferred into the byte array. This value must not be ignored.
For the minimum working process the code should be modified along the following guidelines:
When sending:
while( true ) {
int read = targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
byte[] encrypt = AES.encrypt(tempBuffer, 0, read);
sendThruUDP(encrypt) ;
}
When encrypting (notice that the padding is changed to PKCS5Padding to allow for input length which is not a multiple of the AES block size):
public static byte[] encrypt(byte[] plainData, int offset, int length) throws Exception
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(encryptionKey.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, key,new IvParameterSpec(IV.getBytes("UTF-8")));
return cipher.doFinal(plainData, offset, length);
}
The decrypt() method should be modified to use the same padding.
Other most obvious improvements:
Generate new random IV for each data block and send a packet that contains both the IV and the encrypted data. It will require some copying between a number of byte arrays both on the sending+encrypting and receiving+decrypting side, but reusing the same IV for multiple cipher operations on the same key is absolutely unsafe from the cryptography point of view.
Use the proper key derivation function (search for PBKDF2) instead of simply converting a password string to bytes.
Get an instance of Cipher once and then just reinitialize it with the key and a new IV. That will save a bit of CPU and memory.
#Oleg Estekhin said "The problem is not related to UDP or encryption. You are ignoring the return value of targetDataLine.read( tempBuffer , 0 , tempBuffer.length ). I am pretty sure that most of the time it reads less than the buffer size, with remaining data being garbage from previous calls"
I agree and use a CTR or GCM as a mode of operation not CBC because CBC is slower.

Microphone Streaming is Not Clear

I'm trying to stream microphone over UDP but my output is so noisy, it's not able to understand the input audio. Here is my code:
Server Side:
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.TargetDataLine;
public class MicPlayer {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 8888 ;
/** Creates a new instance of MicPlayer */
public MicPlayer() {
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Mixer.Info minfo[] = AudioSystem.getMixerInfo() ;
for( int i = 0 ; i < minfo.length ; i++ )
{
System.out.println( minfo[i] ) ;
}
if (AudioSystem.isLineSupported(Port.Info.MICROPHONE)) {
try {
DataLine.Info dataLineInfo = new DataLine.Info(
TargetDataLine.class , getAudioFormat() ) ;
final TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine( dataLineInfo ) ;
targetDataLine.open( getAudioFormat() );
targetDataLine.start();
byte tempBuffer[] = new byte[targetDataLine.getBufferSize() / 5] ;
int cnt = 0 ;
while( true )
{
targetDataLine.read( tempBuffer , 0 , tempBuffer.length );
sendThruUDP( tempBuffer ) ;
}
}
catch(Exception e )
{
System.out.println(" not correct " ) ;
System.exit(0) ;
}
}
}
public static AudioFormat getAudioFormat(){
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
public static void sendThruUDP( byte soundpacket[] )
{
try
{
DatagramSocket sock = new DatagramSocket() ;
sock.send( new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ) ;
sock.close() ;
}
catch( Exception e )
{
e.printStackTrace() ;
System.out.println(" Unable to send soundpacket using UDP " ) ;
}
}
}
I don't think client-side has problems but here is the code;
Client Side:
import java.io.DataInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class RadioReceiver extends Thread {
private static final String IP_TO_STREAM_TO = "localhost" ;
private static final int PORT_TO_STREAM_TO = 8888 ;
/** Creates a new instance of RadioReceiver */
public RadioReceiver() {
}
public void run()
{
byte b[] = null ;
while( true )
{
b = receiveThruUDP() ;
toSpeaker( b ) ;
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
RadioReceiver r = new RadioReceiver() ;
r.start() ;
}
public static byte[] receiveThruUDP()
{
try
{
DatagramSocket sock = new DatagramSocket(PORT_TO_STREAM_TO) ;
byte soundpacket[] = new byte[1230] ;
DatagramPacket datagram = new DatagramPacket( soundpacket , soundpacket.length , InetAddress.getByName( IP_TO_STREAM_TO ) , PORT_TO_STREAM_TO ) ;
sock.receive( datagram ) ;
sock.close() ; return datagram.getData() ; // soundpacket ;
}
catch( Exception e )
{
System.out.println(" Unable to send soundpacket using UDP " ) ;
return null ;
}
}
public static void toSpeaker( byte soundbytes[] )
{
try{
DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class , getAudioFormat() ) ;
SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine( dataLineInfo );
sourceDataLine.open( getAudioFormat() ) ;
sourceDataLine.start();
int cnt = 0;
sourceDataLine.write( soundbytes , 0, soundbytes.length );
sourceDataLine.drain() ;
sourceDataLine.close() ;
}
catch(Exception e )
{
System.out.println("not working in speakers " ) ;
}
}
public static AudioFormat getAudioFormat()
{
float sampleRate = 8000.0F;
//8000,11025,16000,22050,44100
int sampleSizeInBits = 16;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
}
}
I'm sure that my connection is OK but i don't have any idea why my output is so noisy. It's getting me crazy i'm working on it till 1 week, please help me. Thank You.
The reason is probably that your datagram packets are too small, which causes you to send a whole bunch of packages that creates a lot of overhead. This might result in a huge packet loss rate and make them arrive in the wrong order.
So, make your buffer size bigger:
byte tempBuffer[] = new byte[8192] ;
And this comes from the DatagramSocket.receive() JavaDoc:
This method blocks until a datagram is received. The length field of the datagram packet object contains the length of the received message. If the message is longer than the packet's length, the message is truncated.
This might be a problem as well. Try to use the same size for both sending and receiving packets.
byte soundpacket[] = new byte[8192];
Also, do not continiously open and close the AudioLine to the speakers. Do also not continuously create DatagramSockets. Create one, and keep it.

Deserialize ArrayList in Java

I have my own little serialiser class
package mypackage.shared;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Serializer {
static final String HEXES = "0123456789ABCDEF";
public static String serialize(Object o) throws IOException {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(stream);
os.writeObject(o);
os.close();
return toHex(stream.toByteArray());
}
public static Object deserialize(String hexString) throws IOException, ClassNotFoundException {
byte[] serializedBytes = toByteArray(hexString);
ByteArrayInputStream bis = new ByteArrayInputStream(serializedBytes);
ObjectInputStream ois = new ObjectInputStream(bis);
Object o = null;
o = ois.readObject();
ois.close();
return o;
}
public static String toHex( byte [] raw ) {
if ( raw == null ) {
return null;
}
final StringBuilder hex = new StringBuilder( 2 * raw.length );
for ( final byte b : raw ) {
hex.append(HEXES.charAt((b & 0xF0) >> 4))
.append(HEXES.charAt((b & 0x0F)));
}
return hex.toString();
}
private static byte[] toByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
It works great for some home made objects I have. However, as soon as I have an ArrayList of these objects it fails to deserialize them. ANy idea why that would be? I find it quite hard to debug since readObject() just fails
If serialization doesn't work it means that some object is not serializable.
Try to deserialize an arraylist containing only one home object and do it for all home objects

Categories