record MJPEG stream from Ip Camera

hi all,

I have some problems when I try to save the streaming data provided from my Ip camera to a file. I think it is the best way to record the MJPEG streaming in order to preview later the video from this file.

I have three classesAxisCamera, to connect to the camera, preview the MJPEG stream and save that into a file; RecordFile which takes an InputStream object and save continuosly the stream into a file; andPlayFile which read continously the data from a file and try to render the stream into a Frame.

all my aplication works great, but when I try to render this stream from the file to the Frame continously, only the first image go throw the Frame and when occours this Error:

this Error is produced when the class PlayFile tries to read the second image from the file...

Can anybody helpe?

thanks a lot!

faival@hotmail.com

com.sun.image.codec.jpeg.ImageFormatException:Not a JPEG file: starts with 0x43 0x45

at sun.awt.image.codec.JPEGImageDecoderImpl.readJPEGStream(Native Method)

at sun.awt.image.codec.JPEGImageDecoderImpl.decodeAsBufferedImage(Unknown Source)

This is my code:

CLASS AXISCAMERA

import java.net.*;

import java.nio.IntBuffer;

import com.sun.image.codec.jpeg.*;

import java.io.*;

import java.awt.*;

import java.awt.event.*;

import java.awt.image.*;

import javax.swing.*;

publicclass AxisCameraextends JPanelimplements Runnable{

publicboolean useMJPGStream =true;

InputStream isRecord;

DataInputStream dis;

FileOutputStream dos;

File recordFile;

private Image image=null;

public Dimension imageSize =null;

publicboolean connected =false;

privateboolean initCompleted =false;

HttpURLConnection huc=null, hucR=null;

URL u;

Component parent;

private String ip =null;

private String credentials =null;

private String jpgURL=null;

private String mjpgURL=null;

private Dimension dimension;

privatevolatile Thread blinker;

private Thread thisThread;

private RecordFile record;

/** Creates a new instance of AxisCamera */

public AxisCamera (Component parent_,CameraAccount account, Dimension dimension){

boolean openFile =false;

parent = parent_;

this.ip= account.getIp();

this.credentials =account.getCredentials();

this.dimension= dimension;

recordFile =new File("C:\\Documents and Settings\\oks_stud\\workspace\\Scenario2\\data\\video");

try{

dos =new FileOutputStream(recordFile);

openFile =true;

}catch (FileNotFoundException e){

e.printStackTrace();

}

if (openFile){

blinker =new Thread(this);

blinker.start();

init();

}

}

privatevoid init(){

String width =Integer.toString(dimension.width);

String height=Integer.toString(dimension.height);

this.jpgURL="http://"+this.ip+"/axis-cgi/jpg/image.cgi?resolution="+width+"x"+height;

this.mjpgURL="http://"+this.ip+"/axis-cgi/mjpg/video.cgi?resolution="+width+"x"+height;

}

public String getURL(){

return this.mjpgURL;

}

public Dimension getDimension(){

return this.dimension;

}

publicvoid connect(){

try{

u =new URL(mjpgURL);

huc = (HttpURLConnection) u.openConnection();

hucR= (HttpURLConnection)u.openConnection();

if(credentials !=null){

huc.setDoInput(true);

hucR.setDoInput(true);

huc.setRequestProperty("Authorization",credentials);

hucR.setRequestProperty("Authorization", credentials);

hucR.connect();

huc.connect();

}

InputStream is = huc.getInputStream();

isRecord = hucR.getInputStream();

connected =true;

BufferedInputStream bis =new BufferedInputStream(is);

dis=new DataInputStream(bis);

if (!initCompleted) initDisplay();

}catch(IOException e){//incase no connection exists wait and try again, instead of printing the error

e.printStackTrace();

try{

huc.disconnect();

Thread.sleep(60);

}catch(InterruptedException ie){huc.disconnect();connect();}

connect();

}catch(Exception e){;}

}

publicvoid initDisplay(){//setup the display

if (useMJPGStream)readMJPGStream();

else{readJPG();disconnect();}

imageSize =new Dimension(image.getWidth(this), image.getHeight(this));

setPreferredSize(imageSize);

parent.setSize(imageSize);

parent.validate();

initCompleted =true;

}

publicvoid disconnect(){

try{

if(connected){

dis.close();

connected =false;

}

}catch(Exception e){;}

}

publicvoid paint(Graphics g){//used to set the image on the panel

if (image !=null)

g.drawImage(image, 0, 0,this);

}

publicvoid readStream(){//the basic method to continuously read the stream

try{

if (useMJPGStream){

thisThread = Thread.currentThread();

record =new RecordFile(isRecord, dos);

Thread recordT =new Thread(record);

readMJPGStream();

recordT.start();

while (blinker == thisThread){

readMJPGStream();

parent.repaint();

}

}else{

while (blinker == thisThread){

connect();

readJPG();

parent.repaint();

disconnect();

}

}

disconnect();

}catch(Exception e){;}

}

publicvoid readMJPGStream(){//preprocess the mjpg stream to remove the mjpg encapsulation

readLine(4,dis);//discard the first 4 lines

readJPG();// Decode the JPEG image.

readLine(1,dis);//discard the last line

}

publicvoid readJPG(){//read the embedded jpeg image

try{

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(dis);

image = decoder.decodeAsBufferedImage();

}catch(Exception e){e.printStackTrace();disconnect();}

}

publicvoid readLine(int n, DataInputStream dis){//used to strip out the header lines

for (int i=0; i<n;i++){

readLine(dis);

}

}

publicvoid readLine(DataInputStream dis){

try{

boolean end =false;

String lineEnd ="\n";//assumes that the end of the line is marked with this

byte[] lineEndBytes = lineEnd.getBytes();

byte[] byteBuf =newbyte[lineEndBytes.length];

while(!end){

dis.read(byteBuf,0,lineEndBytes.length);

String t =new String(byteBuf);

//System.out.print(t); //uncomment if you want to see what the lines actually look like

if(t.equals(lineEnd)) end=true;

}

}catch(Exception e){e.printStackTrace();}

}

publicvoid changeResolution(Dimension dimension){

this.dimension= dimension;

init();

}

publicvoid run(){

connect();

readStream();

}

publicvoid stop(){

//record.stop();

blinker =null;

}

publicvoid play(){

blinker=new Thread(this);

blinker.start();

//run();

}

}

CLASS RECORDFILE

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

publicclass RecordFileimplements Runnable{

privatevolatile Thread blinker;

private InputStream input;

private FileOutputStream output;

byte[] byteBuf;

public RecordFile(InputStream input, FileOutputStream output){

this.input= input;

this.output= output;

String lineEnd ="\n";//assumes that the end of the line is marked with this

byte[] lineEndBytes = lineEnd.getBytes();

byteBuf =newbyte[lineEndBytes.length];

blinker =new Thread(this);

blinker.start();

}

publicvoid run(){

Thread thisThread = Thread.currentThread();

try{

while(blinker == thisThread && input.read(byteBuf, 0, byteBuf.length)!=-1){

output.write(byteBuf, 0, byteBuf.length);

}

}catch (IOException e){

e.printStackTrace();

}

}

publicvoid stop(){

blinker =null;

}

}

CLASS PLAYFILE

import java.awt.Component;

import java.awt.Dimension;

import java.awt.Graphics;

import java.awt.Image;

import java.awt.Toolkit;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import javax.swing.JPanel;

import com.sun.image.codec.jpeg.JPEGCodec;

import com.sun.image.codec.jpeg.JPEGImageDecoder;

publicclass PlayFileextends JPanelimplements Runnable{

private Component parent;

private FileInputStream inputFile;

private Dimension dimension;

privatevolatile Thread blinker;

private Image image=null;

public PlayFile(String path, Component parent, Dimension dim){

this.parent = parent;

this.dimension= dim;

try{

inputFile =new FileInputStream(path);

}catch (FileNotFoundException e){

e.printStackTrace();

}

init();

blinker =new Thread(this);

blinker.start();

}

publicvoid run(){

Thread thisThread = Thread.currentThread();

while(blinker == thisThread){

readStream();

parent.repaint();

}

}

publicvoid stop(){

blinker =null;

}

privatevoid init(){

readStream();

Dimension imageSize =new Dimension(image.getWidth(this), image.getHeight(this));

setPreferredSize(imageSize);

parent.setSize(imageSize);

parent.validate();

}

publicvoid paint(Graphics g){

if (image !=null)

g.drawImage(image, 0, 0,this);

}

privatevoid readStream(){

readLine(4,inputFile);

readJPG();

readLine(1,inputFile);

System.out.println("LEO");

}

privatevoid readLine(int n, FileInputStream input){

for (int i=0; i><n;i++){

readLine(input);

}

}

privatevoid readLine(FileInputStream input){

try{

boolean end =false;

String lineEnd ="\n";//assumes that the end of the line is marked with this

byte[] lineEndBytes = lineEnd.getBytes();

byte[] byteBuf =newbyte[lineEndBytes.length];

while(!end){

if(input.read(byteBuf,0,lineEndBytes.length)== -1){stop();}

String t =new String(byteBuf);

//System.out.print(t); //uncomment if you want to see what the lines actually look like

if(t.equals(lineEnd)) end=true;

}

}catch(Exception e){e.printStackTrace();}

}

privatevoid readJPG(){

try{

JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(inputFile);

image = decoder.decodeAsBufferedImage();

}catch(Exception e){e.printStackTrace();stop();}

}

}

>

[23172 byte] By [faivalina] at [2007-11-27 3:51:07]
# 1

I got the same problem when reading images from an input stream.

This problem is due to that, in my opinion, the current position of the

input stream is not exactly at the start of the raw jpeg data in the

stream.

Although, I haven't played around with your code, I think the problem

might be the order or the data of the saved file might not be the same

as the original mjpeg stream. That's why even though you could

preview images from the mjpeg stream, you couldn't read them from

the saved file.

If it's the case, you can either re-check the way you saved data or

you can write a method to detect the boundary between images

in the saved file to make sure when you actuall read an image,

the stream's position is at the start of that image data in the file.

LearnMorea at 2007-7-12 8:55:05 > top of Java-index,Security,Cryptography...
# 2
Hi LearnMore,and do you know how can I make the method to detect the boundary between images? thanks in advance!Pablo
faivalina at 2007-7-12 8:55:05 > top of Java-index,Security,Cryptography...
# 3

It really depends on how your server produces the MJPEG stream.

I think, at the begining of each image, there'd be something similar to:

--AStringForBoundary

Content-Type: image/jpeg

OtherCustomerHeaderLines

You can read each line and check for the string boundary that

usually starts with "--". Then you can read (and so, get rid of) other

header lines before decoding the image's data.

LearnMorea at 2007-7-12 8:55:05 > top of Java-index,Security,Cryptography...
# 4

hi LearnMore,

can you post me a simple method to continuosly read the images from a stream, throw away that lines and prepare the stream at the start of the image , and then decode that image?

Or have you any code to get the mjpeg stream from an Axis ip camera and generate a movie(.mov , .avi) at the same time?

thank you a lot!

Pablo

faivalina at 2007-7-12 8:55:05 > top of Java-index,Security,Cryptography...
# 5

I haven't done any work related to IP-camera. So, sorry I can't help you with

reading images from IP camera and generating a movie. But I guess, you might

find a solution in some posts of this forum or if I remember correctly,

in the book "Killer Java Games", there's a chapter discussing this issue

of making movies in Java.

I just happened to work on a project that requires reading images from

a MJPEG stream. Unfortunately, it is a commercial project and I'd be very

unprofessional if I expose some code of that project here even though

I wrote the code myself. Anyway, I used a algorithm similar to what you

guys did in this thread ((I implemented it abit differently, though)

http://forum.java.sun.com/thread.jspa?threadID=494920&start=0&tstart=0

One thing I noticed is that after I read the first image successfully, there were

still some data, which I haven't got time to investigate what kind of data they

are, before I encountered the boundary between successive images and other

customized header lines. So after reading an image, I just read each line of

data and check if it starts with the boundary string. If I got to the boundary,

then I start to read (and remove other header lines) before I start reading the

next image. Again, similar to the post mentioned above, this method

requires you read the server manual (or print out the boundary and header

lines in case you don't have such document available). But it does give you

easy access to custom header lines.

Alternatively, you can detect the start of the raw JPEG data like one guy in the

above post did.

Good luck, mate!

LearnMorea at 2007-7-12 8:55:05 > top of Java-index,Security,Cryptography...