Xuggler "Could not write header" error - java

I am making a screen recording application with Xuggler. I've basically encapsulated Java Code Geeks' Xuggler tutorial code into a runnable class for the actual recording. It should run just like the tutorial, but I'm getting some (actually a ton of) errors. The link to that code is here: JavaCodeGeeks. I'm not trying to take credit for this entire block of code.
Here is what I have so far:
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.swing.JOptionPane;
import src.dtf.gui.GUI;
import com.xuggle.mediatool.IMediaWriter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.xuggler.ICodec;
public class ScreenRecorder implements Runnable {
//Booleans to run and to pause. (Pausing not implemented yet)
boolean running = true;
boolean paused = false;
//Some variables
private GUI gui;
private Toolkit tk;
private String path, name, outputFilename;
private int fps;
private long startTime;
private Rectangle recArea;
private Dimension bounds;
//Declare the MediaWriter
private IMediaWriter writer;
//Constructor
public ScreenRecorder(GUI gui) {
//Set the GUI to the one that I'm using (Another class
this.gui = gui;
//Initialize variables, based on previous user input.
tk = Toolkit.getDefaultToolkit();
path = gui.getPath();
name = JOptionPane.showInputDialog("Please enter a name for your video file:");
outputFilename = path + "\\" + name + ".mp4";
fps = gui.getFPS();
if (gui.fullscreenChecked()) {
recArea = new Rectangle(0, 0, tk.getScreenSize().width,
tk.getScreenSize().height);
} else {
recArea = gui.getArea();
}
bounds = new Dimension(recArea.width, recArea.height);
}
//Start method
public void start() {
gui.disableButtons();
gui.changeRecordButton(false);
running = true;
}
//Run method
public void run() {
//Initialize
init();
long lastTime = System.currentTimeMillis();
int updateTime = 1000 / fps;
startTime = System.nanoTime();
while (running) {
//Limit updates
if (System.currentTimeMillis() - lastTime >= updateTime) {
//Ensure the recording is not paused
if (!paused) {
//If the user has stopped, stop
if (!gui.isRecording()) {
stop();
}
//Take a screenshot and convert it
BufferedImage frame = takeScreenshot();
BufferedImage bgrScreen = convertImage(frame, BufferedImage.TYPE_3BYTE_BGR);
//Encode video
writer.encodeVideo(0, bgrScreen, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
System.out.println("Recording...");
} else if (paused) {
System.out.println("Paused...");
}
}
}
}
private void init() {
//Make sure the given directory exists
checkFile();
//Ensure there is not already a file of the same name
checkFilename();
//Make the writer
writer = ToolFactory.makeWriter(outputFilename);
writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4, bounds.width, bounds.height);
}
//Method for checking if the directory exists
private void checkFile() {
if (!(new File(path).exists())) {
gui.resetPath();
JOptionPane.showMessageDialog(gui, "ERROR: File path does not exist!");
System.out.println("ERRR");
stop();
}
}
//Method for checking if the given filename exists
private void checkFilename() {
if (new File(path + "\\" + name + ".mp4").exists()) {
JOptionPane.showMessageDialog(gui, "ERROR: File already exists!!");
stop();
}
}
//Method for converting the BufferedImage (Thanks JavaCodeGeeks)
private BufferedImage convertImage(BufferedImage b, int targetType) {
BufferedImage image;
if (b.getType() == targetType) {
image = b;
} else {
image = new BufferedImage(b.getWidth(), b.getHeight(), targetType);
image.getGraphics().drawImage(b, 0, 0, null);
}
return image;
}
//Method for taking a screenshot
private BufferedImage takeScreenshot() {
try {
Robot r = new Robot();
return r.createScreenCapture(recArea);
} catch (AWTException e) {
e.printStackTrace();
return null;
}
}
//Stop method
public void stop() {
gui.enableButtons();
gui.changeRecordButton(true);
//Make sure the writer has been initialized. (Not an incorrect filename or anything)
if (writer != null) {
//Close the writer
writer.close();
}
//End thread
running = false;
}
}
And here's the error that's thrown:
17:46:48.076 [Thread-2] ERROR org.ffmpeg - [mp4 # 000000000028F660] no streams
17:46:48.123 [Thread-2] ERROR com.xuggle.xuggler - Error: could not write header for container (../../../../../../../csrc/com/xuggle/xuggler/Container.cpp:827)
I tried to fix it by adding the isHeaderWritten() if statement in the stop method, but that never gets called at all, so it must be somewhere else (or within that if statement). I don't know what line of my code throws the error, because it only gives me these two errors, which point to Xuggler, not my code. When I execute this, it creates an mp4 file but its size is 0 bytes and the file won't play. I could really use some help because I have no idea what these errors even mean, so it's hard to debug them. Thanks!

We're probably going to get in trouble for not honoring the Q&A-format of this website, so I'm just going to list some things you can try.
The error you are getting regarding the header is also raised when you don't specifically call writeHeader(). You are also getting an error concerning a 'missing stream'. This suggests that Xuggler is missing some information it needs to properly add the video stream and open the writer. So start by debugging your Java application to figure out which specific line is causing the error.
Also, try rendering each output frame to a JFrame, just before you write it. This will allow you to verify whether the BufferedImages you want to write have the proper content.
The file you write to is an MP4, which will make Xuggler draw a couple of conclusions on desired output parameters, but you best not rely on that. Try setting the pixel format, bitrate, frame rate and time base yourself. This is why suggested you use a buffer: you would be able to write frames at a specific interval, which will guarantee the proper frame rate. The way you've set it up now will result in a variable frame rate, which some codecs and containers will not appreciate. The type of buffer isn't that relevant, you could even use an ArrayList. But naturally some data structures will be more efficient than others.
Some codecs and file containers are a lot more forgiving than others. So try out some other codecs as well, such as H.264. You could try changing the file container as well, but MP4 usually works fine in Xuggler.
This is unrelated to your current problem, because this has nothing to do with the output file being empty. But you should watch out for the timestamps with which you write frames. The first video frame should be at timestamp 0, but because you capture and encode in the same while-loop, the first frame will have a much higher timestamp. Also, when you pause, your application won't write any frame but the timestamp will still increase. This will cause a 'hole' in your video when you later resume recording, a small time window without any video data.

Related

"Bad src image pointers" error after calling setFrameNumber() on FFmpegFrameGrabber in JavaCV

I am in the process of creating a small video editor and currently trying to get video files to display in the preview window. To do that, I want to get a frame of a video at a specific position using JavaCVs FFmpegFrameGrabber.
I have figured out a way of doing this, by setting the frameNumber variable of the grabber to the needed frame. However, this results in only the first frame of the file being displayed and some information about the file being printed out repeatedly (tell me if you need to see it, it's just kind of long and messy) alongside the error:
[swscaler # 000001927a7d3000] bad src image pointers
This is my frame grabbing class:
public class Video {
private FFmpegFrameGrabber grabber;
private final static Java2DFrameConverter converter = new Java2DFrameConverter();
public Video(File file) {
this.grabber = new FFmpegFrameGrabber(file);
try {
this.grabber.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public BufferedImage grabFrame(int framePos) throws Exception {
BufferedImage frame;
grabber.grabImage(); // Without this done before, the image is just black
grabber.setFrameNumber(framePos);
frame = converter.convert(grabber.grabImage());
return frame;
}
}
I am very thankful for your answers!

How to get SQLite 'VACUUM' Progress

Is there a way to get the progress of sqlite 'VACUUM'?I am using this line of code here in Java:
connection1.createStatement().executeUpdate("VACUUM");
The User(MySelf & I) has to wait from some seconds to some minutes,i know that the actual .db file is being overriten with the help of a journal file that is created through the execution of the command.
Can i get an estimation using JAVA IO or something?Thanks for help..
No.
The SQLite C API has a progress handler, but it's probably not exposed by your Java driver, and the vacuum processing is implemented with a different mechanism that does not report the progress anyway.
You could try to look at the current size of the database file and of any temporary files, but it is practically impossible to get the name of the latter.
I found the answer to my question.So i know the size of the actual .db file and i wrote a Service in javaFX which calculates every 50 miliseconds the size of .db-journal file.So i check very frequently the size of journal file to see how of % is builded based on actual .db file:
package windows;
import java.io.File;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
/** Get the progress of Vacuum Operation */
public class VacuumProgress extends Service<Void> {
File basicFile;
File journalFile;
/**
* Starts the Vacuum Progress Service
*
* #param basicFile
* #param journalFile
*/
public void start(File basicFile, File journalFile) {
this.basicFile = basicFile;
this.journalFile = journalFile;
reset();
start();
}
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
System.out.println("Started...");
long bfL = basicFile.length();
while (!journalFile.exists()) {
Thread.sleep(50);
System.out.println("Journal File not yet Created!");
}
long jfL = journalFile.length();
while (jfL <= bfL) {
updateProgress(jfL = journalFile.length(), bfL);
Thread.sleep(50);
}
System.out.println("Exited Vacuum Progress Service");
return null;
}
};
}
}

How to communicate with a Virtual Machine in Java?

I have made a little Java program that asks a person to enter a pin code. As soon as the pin code is entered, it reads into a "bdd.txt" file in which all the pins are stored and then it displays :) if good and :( if wrong. Simple application so far.
What I want to do is to move that "database" file into a Virtual Machine on my computer (such as Ubuntu for example), and then do the same thing. This way, it won't be local anymore, since the file will not be located at the root of my project anymore.
Here is what my application looks like :
As you can see, the app starts, the user is asked to enter is pin code. If this is a good one, the app is done, if not he has 2 more tries left until the app stops.
When the pin is entered, my program checks in "bdd.txt" if the pin is there or not. It plays the database role:
To understand what I need, it is necessary to assimilate this program to something that needs to be secure. We do not want the pins database at the same place as the program (or the device in real life). So we put it on a Virtual Machine and we have to communicate between my Windows7 Java program in Eclipse and the bdd.txt file on VMWare Player's Ubuntu.
My question is how is that possible ? How do I need to change my code to let my program reach something on my VM ? Is there a specifig technology I should use for it ? Do I need to do some configurations first ?
Here is my code :
import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import javax.swing.*;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;
import java.awt.*;
import java.awt.event.*;
public class Main extends JFrame {
private static final long serialVersionUID = 1L;
private JPanel container = new JPanel();
private JPasswordField p1 = new JPasswordField(4);
private JLabel label = new JLabel("Enter Pin: ");
private JButton b = new JButton("OK");
public Main() {
this.setTitle("NEEDS");
this.setSize(300, 500);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
container.add(p1);
JPanel top = new JPanel();
PlainDocument document =(PlainDocument)p1.getDocument();
b.addActionListener(new BoutonListener());
top.add(label);
top.add(p1);
p1.setEchoChar('*');
top.add(b);
document.setDocumentFilter(new DocumentFilter(){
#Override
public void replace(DocumentFilter.FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
String string =fb.getDocument().getText(0, fb.getDocument().getLength())+text;
if(string.length() <= 4)
super.replace(fb, offset, length, text, attrs); //To change body of generated methods, choose Tools | Templates.
}
});
this.setContentPane(top);
this.setVisible(true);
}
class BoutonListener implements ActionListener {
private final AtomicInteger nbTry = new AtomicInteger(0);
ArrayList<Integer> pins = readPinsData(new File("bdd.txt"));
#SuppressWarnings("deprecation")
public void actionPerformed(ActionEvent e) {
if (nbTry.get() > 2) {
JOptionPane.showMessageDialog(null,
"Pin blocked due to 3 wrong tries");
return;
}
final String passEntered=p1.getText().replaceAll("\u00A0", "");
if (passEntered.length() != 4) {
JOptionPane.showMessageDialog(null, "Pin must be 4 digits");
return;
}
//JOptionPane.showMessageDialog(null, "Checking...");
//System.out.println("Checking...");
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
boolean authenticated = false;
if (pins.contains(Integer.parseInt(passEntered))) {
JOptionPane.showMessageDialog(null, ":)");
authenticated = true;
}
if (!authenticated) {
JOptionPane.showMessageDialog(null, ":(");
nbTry.incrementAndGet();
}
return null;
}
};
worker.execute();
}
}
//Function to read/access my bdd.txt file
static public ArrayList<Integer> readPinsData(File dataFile) {
final ArrayList<Integer> data=new ArrayList<Integer>();
try {
BufferedReader reader = new BufferedReader(new FileReader(dataFile));
String line;
try {
while ((line = reader.readLine()) != null) {
try {
data.add(Integer.parseInt(line));
} catch (NumberFormatException e) {
e.printStackTrace();
System.err.printf("error parsing line '%s'\n", line);
}
}
} finally {
reader.close();
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("error:"+e.getMessage());
}
return data;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Main();
}
});
}
}
Any ideas ? Thanks,
Florent.
A shared folder will certainly work, but there seems little point in having a VM at all, because the PIN file is also on your host machine, and java is reading it directly.
Maybe you need a client/server architecture?
You program with the UI will be the client. The client will be configured with a means of calling the server (IP address and port). The client does not have access to the bdd.txt file, but the server does.
On your VM, you have another java application, the Server. Your server listens for requests from the client. The request will contain a PIN entered by the user. The server then checks it against the PINs in the file, and responds with a yes or no. Your client receives the yes/no response from the server, and reports the result back to the user.
Read about Sockets programming here to get started
There are two things you would need to do:
Share a folder between your host OS and your VM. This will allow your Virtual Machine to access files from the host operating system. You would want to put your pin file in this folder.
Have your application read the pin file from the shared folder. This would mean changing this line:
ArrayList<Integer> pins = readPinsData(new File("bdd.txt"));
Right now, this code is reading the file bdd.txt from the current directory the user is in, which I assume is the directory your executable is in. Instead, you want this to point to the pin file in your shared directory. To make your code as flexible as possible, you may want to pass in the path to the pin file as a command line argument when you start the program.

Recompile OpenCV Java for Eclipse

I'm using OpenCV for a object detection project. I'm trying to read frames from a stored video file using VideoCapture, but in OpenCV Java there is no current implementation. I followed instructions in this post: open video file with opencv java, to edit the source files of OpenCV Java to allow this functionality. The problem is I don't know how to recompile the files? - since I just added the downloaded opencv jar file into my eclipse project originally.
You should probably try JavaCV, an OpenCV wrapper for Java.
This post shows what you need to download/install to get things working on your system, but I'm sure you can find more updated posts around the Web.
One of the demos I present during my OpenCV mini-courses contains a source code that uses JavaCV to load a video file and display it on a window:
import static com.googlecode.javacv.cpp.opencv_core.*;
import static com.googlecode.javacv.cpp.opencv_imgproc.*;
import static com.googlecode.javacv.cpp.opencv_highgui.*;
import com.googlecode.javacv.OpenCVFrameGrabber;
import com.googlecode.javacv.FrameGrabber;
public class OpenCV_tut4
{
public static void main(String[] args)
{
FrameGrabber grabber = new OpenCVFrameGrabber("demo.avi");
if (grabber == null)
{
System.out.println("!!! Failed OpenCVFrameGrabber");
return;
}
cvNamedWindow("video_demo");
try
{
grabber.start(); // initialize video capture
IplImage frame = null;
while (true)
{
frame = grabber.grab(); // capture a single frame
if (frame == null)
{
System.out.println("!!! Failed grab");
break;
}
cvShowImage("video_demo", frame);
int key = cvWaitKey(33);
if (key == 27) // ESC was pressed, abort!
break;
}
}
catch (Exception e)
{
System.out.println("!!! An exception occurred");
}
}
}

How can I get a frame sample (jpeg) from a video (mov)

I want to get a frame sample (jpeg) from a video file (mov) with java. Is there an easy way to do this. When I search in google all I can find is to make mov from multiple jpgs. I dont know maybe I cannot find the right keywords.
I know that the original question is solved, nevertheless, I am posting this answer in case anyone else got stuck like I did.
Since yesterday, I have tried everything, and I mean everything to do this. All available Java libraries are either out of date, not maintained anymore or lack any kind of usable documentation (seriously??!?!)
I tried JFM (old and useless), JCodec (no documentation whatsoever), JJMpeg (looks promising but is very difficult and cumbersome to use due to lack of Java-class documentation), OpenCV auto-Java builds and a few bunch of other libraries that I cannot remember.
Finally, I decided to take a look at JavaCV's (Github link) classes and voila! It contains FFMPEG bindings with detailed documentations.
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.0</version>
</dependency>
Turns out there is a very easy way to extract video frames from a video file to a BufferedImage and by extension a JPEG file. The class FFmpegFrameGrabber could be easily used for grabbing individual frames and converting them to BufferedImage. A code sample is as follows:
FFmpegFrameGrabber g = new FFmpegFrameGrabber("textures/video/anim.mp4");
g.start();
Java2DFrameConverter converter = new Java2DFrameConverter();
for (int i = 0 ; i < 50 ; i++) {
Frame frame = g.grabImage(); // It is important to use grabImage() to get a frame that can be turned into a BufferedImage
BufferedImage bi = converter.convert(frame);
ImageIO.write(bi, "png", new File("frame-dump/video-frame-" + System.currentTimeMillis() + ".png"));
}
g.stop();
Basically, this code dumps the first 50 frames of the video and saves them as a PNG file. The good thing is that the internal seek function, works on actual frames not keyframes (a problem that I had with JCodec)
You can refer to the JavaCV's homepage to find out more about other classes that can be used for capturing frames from WebCams etc. Hope this answer helps :-)
Xuggler does the job. They even give a sample code which does exactly what I need. Link is below
http://xuggle.googlecode.com/svn/trunk/java/xuggle-xuggler/src/com/xuggle/mediatool/demos/DecodeAndCaptureFrames.java
And I've modified the code in this link such that it saves only the first frame of the video.
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.image.BufferedImage;
import com.xuggle.mediatool.IMediaReader;
import com.xuggle.mediatool.MediaListenerAdapter;
import com.xuggle.mediatool.ToolFactory;
import com.xuggle.mediatool.event.IVideoPictureEvent;
import com.xuggle.xuggler.Global;
/**
* * #author aclarke
* #author trebor
*/
public class DecodeAndCaptureFrames extends MediaListenerAdapter
{
private int mVideoStreamIndex = -1;
private boolean gotFirst = false;
private String saveFile;
private Exception e;
/** Construct a DecodeAndCaptureFrames which reads and captures
* frames from a video file.
*
* #param filename the name of the media file to read
*/
public DecodeAndCaptureFrames(String videoFile, String saveFile)throws Exception
{
// create a media reader for processing video
this.saveFile = saveFile;
this.e = null;
IMediaReader reader = ToolFactory.makeReader(videoFile);
// stipulate that we want BufferedImages created in BGR 24bit color space
reader.setBufferedImageTypeToGenerate(BufferedImage.TYPE_3BYTE_BGR);
// note that DecodeAndCaptureFrames is derived from
// MediaReader.ListenerAdapter and thus may be added as a listener
// to the MediaReader. DecodeAndCaptureFrames implements
// onVideoPicture().
reader.addListener(this);
// read out the contents of the media file, note that nothing else
// happens here. action happens in the onVideoPicture() method
// which is called when complete video pictures are extracted from
// the media source
while (reader.readPacket() == null && !gotFirst);
if (e != null)
throw e;
}
/**
* Called after a video frame has been decoded from a media stream.
* Optionally a BufferedImage version of the frame may be passed
* if the calling {#link IMediaReader} instance was configured to
* create BufferedImages.
*
* This method blocks, so return quickly.
*/
public void onVideoPicture(IVideoPictureEvent event)
{
try
{
// if the stream index does not match the selected stream index,
// then have a closer look
if (event.getStreamIndex() != mVideoStreamIndex)
{
// if the selected video stream id is not yet set, go ahead an
// select this lucky video stream
if (-1 == mVideoStreamIndex)
mVideoStreamIndex = event.getStreamIndex();
// otherwise return, no need to show frames from this video stream
else
return;
}
ImageIO.write(event.getImage(), "jpg", new File(saveFile));
gotFirst = true;
}
catch (Exception e)
{
this.e = e;
}
}
}
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.bytedeco.javacpp.opencv_core.IplImage;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FrameGrabber.Exception;
public class Read{
public static void main(String []args) throws IOException, Exception
{
FFmpegFrameGrabber frameGrabber = new FFmpegFrameGrabber("C:/Users/Digilog/Downloads/Test.mp4");
frameGrabber.start();
IplImage i;
try {
i = frameGrabber.grab();
BufferedImage bi = i.getBufferedImage();
ImageIO.write(bi,"png", new File("D:/Img.png"));
frameGrabber.stop();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Here's how with BoofCV:
String fileName = UtilIO.pathExample("tracking/chipmunk.mjpeg");
MediaManager media = DefaultMediaManager.INSTANCE;
ConfigBackgroundBasic configBasic = new ConfigBackgroundBasic(30, 0.005f);
ImageType imageType = ImageType.single(GrayF32.class);
BackgroundModelMoving background = FactoryBackgroundModel.movingBasic(configBasic, new PointTransformHomography_F32(), imageType);
SimpleImageSequence video = media.openVideo(fileName, background.getImageType());
ImageBase nextFrame;
while(video.hasNext()) {
nextFrame = video.next();
// Now do something with it...
}
maybe this will help you:
Buffer buf = frameGrabber.grabFrame();
// Convert frame to an buffered image so it can be processed and saved
Image img = (new BufferToImage((VideoFormat) buf.getFormat()).createImage(buf));
buffImg = new BufferedImage(img.getWidth(this), img.getHeight(this), BufferedImage.TYPE_INT_RGB);
//TODO saving the buffImg
for more informations:
How to take single snapshots from a webcam?
Below it is shown the essential code to request frames from media files.
For the complete source code and video demo:
"Media File Processing" example using Marvin Framework..
public class MediaFileExample implements Runnable{
private MarvinVideoInterface videoAdapter;
private MarvinImage videoFrame;
public MediaFileExample(){
try{
// Create the VideoAdapter used to load the video file
videoAdapter = new MarvinJavaCVAdapter();
videoAdapter.loadResource("./res/snooker.wmv");
// Start the thread for requesting the video frames
new Thread(this).start();
}
catch(MarvinVideoInterfaceException e){e.printStackTrace();}
}
#Override
public void run() {
try{
while(true){
// Request a video frame
videoFrame = videoAdapter.getFrame();
}
}catch(MarvinVideoInterfaceException e){e.printStackTrace();}
}
public static void main(String[] args) {
MediaFileExample m = new MediaFileExample();
}
}

Categories