I'm trying to ingest a mp4 file and make it a timelapse. It works with the code attached below. However, the output file has frame rate of 16*originalFrameRate. Since I don't intend to play it as a slow motion video I'd prefer to drop those redundant frames to make the output file smaller.
Movie inputMovie = MovieCreator.build(fileUri);
List<Track> videoTracks = new LinkedList<>();
for (Track track : inputMovie.getTracks()) {
if (track.getHandler().equals("vide")) {
videoTracks.add(track);
}
}
final int speedByFactorOf = 16;
Movie outputMovie = new Movie();
AppendTrack appendedTracks = new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()]));
outputMovie.addTrack(new WrappingTrack(appendedTracks) {
#Override
public long[] getSampleDurations() {
long[] l = super.getSampleDurations();
for (int i = 0; i < l.length; i++) {
l[i] /= speedByFactorOf;
}
return l;
}
});
BasicContainer out = (BasicContainer) new DefaultMp4Builder().build(outputMovie);
FileChannel fc = new RandomAccessFile("timelapse.mp4", "rw").getChannel();
out.writeContainer(fc);
fc.close();
out.close();
I was unable to find any examples of how to change the output frame rate.
As stated by #tarun-lalwani if the project you're referring to is https://github.com/sannies/mp4parser then it is only able to edit the MP4 container and NOT the video / audio / media etc. held within the container. Even if you could use metadata to accelerate the FPS by sixteen times, the file size would not become any smaller because all the frames would still be within the file (just shown for a shorter duration). You would need to use something like FFmpeg (e.g. via https://github.com/bramp/ffmpeg-cli-wrapper ) or some other programmatic video editor to do what you're describing thus only keeping every sixteenth frame of video so the video file actually becomes smaller.
TLDR; mp4parser is not the correct project for editing video (as opposed to metadata) and what you want to achieve sounds like it is beyond the scope of just fiddling with the container.
Related
i made a Java application whose purpose is to offer a Print Preview for PS files.
My program uses Ghostscript and Ghost4J to load the Post Script file and produces a list of Images (one for each page) using the SimpleRenderer.render method. Then using a simple JList i show only the image corresponding to the page the user selected in JList.
This worked fine until a really big PS file occurred, causing an OutOfMemoryError when executing the code
PSDocument pdocument = new PSDocument(new File(filename));
I know that is possibile to read a file a little at a time using InputStreams, the problem is that i can't think of a way to connect the bytes that i read with the actual pages of the document.
Example, i tried to read from PS file 100 MB at a time
int buffer_size = 100000000;
byte[] buffer = new byte[buffer_size];
FileInputStream partial = new FileInputStream(filename);
partial.read(buffer, 0, buffer_size);
document.load(new ByteArrayInputStream(buffer));
SimpleRenderer renderer = new SimpleRenderer();
//how many pages do i have to read?
List<Image> images = renderer.render(document, firstpage ??, lastpage ??);
Am i missing some Ghost4J functionality to read partially a file?
Or has someone other suggestions / approaches about how to solve this problem in different ways?
I am really struggling
I found out I can use Ghost4J Core API to retrieve from a Post Script file a reduced set of pages as Images.
Ghostscript gs = Ghostscript.getInstance();
String[] gsArgs = new String[9];
gsArgs[0] = "-dQUIET";
gsArgs[1] = "-dNOPAUSE";
gsArgs[2] = "-dBATCH";
gsArgs[3] = "-dSAFER";
gsArgs[4] = "-sDEVICE=display";
gsArgs[5] = "-sDisplayHandle=0";
gsArgs[6] = "-dDisplayFormat=16#804";
gsArgs[7] = "-sPageList="+firstPage+"-"+lastPage;
gsArgs[8] = "-f"+filename;
//create display callback (capture display output pages as images)
ImageWriterDisplayCallback displayCallback = new ImageWriterDisplayCallback();
//set display callback
gs.setDisplayCallback(displayCallback);
//run PostScript (also works with PDF) and exit interpreter
try {
gs.initialize(gsArgs);
gs.exit();
Ghostscript.deleteInstance();
} catch (GhostscriptException e) {
System.out.println("ERROR: " + e.getMessage());
e.printStackTrace();
}
return displayCallback.getImages(); //return List<Images>
This solve the problem of rendering page as images in the preview.
However, i could not find a way to use Ghost4J to know total number of pages of PS file (in case the file is too big for opening it with Document.load()).
So, i am still here needing some help
I'm working on a Java Swing application where I am reading in multiple graphic files to populate a tile based map editor.
I create these tile view objects of mine through the use of a class called TileSet which essentially breaks down my images into individual files. The code itself works, but I am having an issue with my filepaths that has got me scratching my head. Here is what's going on,
TileSelector ts = new TileSelector(new TileSet("src\\resources\\minecraft.png"));
MapView mapView = new MapView(new TileSet("src\\resources\\mapviewdefault.png"));
For some reason, my TileSelector accepts the format in which I wrote the directory to the image it will use, but MapView throws an IOException (File not found!). Both files are within this directory, the only difference I can really think of is that one image is vastly larger than the other, where the smallest is loading up no issues. If I go and put the full directory to the larger image it works, but since I'm writing this program for a school project which will be given to a sponsor, I cannot keep this format as the final way I deliver the package.
Any ideas?
Also, here is the code for my TileSet, where the issue is happening according to my stack trace...
private void createTileSet(String fileName) {
try {
File file = new File(fileName);
if (!file.exists())
throw new IOException("File not found...");
image = ImageIO.read(new File(fileName));
numTilesWidth = image.getWidth() /tileWidth; //Set how many tiles on the X axis there are
numTilesHeight = image.getHeight() / tileHeight; //Set how many tiles on the Y axis there are
numTiles = numTilesHeight * numTilesWidth;
for (int h = 0; h < numTilesHeight; h++) {
for (int w = 0; w < numTilesWidth; w++) {
imagesList.add(image.getSubimage(w * tileWidth, h * tileHeight, tileWidth, tileHeight));
}
}
} catch (IOException e) {
System.out.println("IOException catch: " + e);
e.printStackTrace();
}
}
The src path will NOT exist at run time (when the application is built and packaged) and you should never reference it from within your code. The resources can't within the resources directory will not be accessible as a normal File on the disk, because they aren't, they will typically be entries within the jar file.
Instead, you need to use Class#getResource to locate a load the resources...something like...
image = ImageIO.read(getClass().getResource(fileName));
And you should call it using something like...
TileSelector ts = new TileSelector(new TileSet("/resources/minecraft.png"));
MapView mapView = new MapView(new TileSet("/resources/mapviewdefault.png"));
You should avoid using \\ in your code, as it is not cross platform compatible, you can use / generally, if you really want to, use File.separator instead
I have for example 1000 images and their names are all very similar, they just differ in the number. "ImageNmbr0001", "ImageNmbr0002", ....., ImageNmbr1000 etc.;
I would like to get every image and store them into an ImageProcessor Array.
So, for example, if I use a method on element of this array, then this method is applied on the picture, for example count the black pixel in it.
I can use a for loop the get numbers from 1 to 1000, turn them into a string and create substrings of the filenames to load and then attach the string numbers again to the file name and let it load that image.
However I would still have to turn it somehow into an element I can store in an array and I don't a method yet, that receives a string, in fact the file path and returns the respective ImageProcessor that is stored at it's end.
Also my approach at the moment seems rather clumsy and not too elegant. So I would be very happy, if someone could show me a better to do that using methods from those packages:
import ij.ImagePlus;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;
I think I found a solution:
Opener opener = new Opener();
String imageFilePath = "somePath";
ImagePlus imp = opener.openImage(imageFilePath);
ImageProcesser ip = imp.getProcessor();
That do the job, but thank you for your time/effort.
I'm not sure if I undestand what you want exacly... But I definitly would not save each information of each image in separate files for 2 reasons:
- It's slower to save and read the content of multiple files compare with 1 medium size file
- Each file adds overhead (files need Path, minimum size in disk, etc)
If you want performance, group multiple image descriptions in single description files.
If you dont want to make a binary description file, you can always use a Database, which is build for it, performance in read and normally on save.
I dont know exacly what your needs, but I guess you can try make a binary file with fixed size data and read it later
Example:
public static void main(String[] args) throws IOException {
FileOutputStream fout = null;
FileInputStream fin = null;
try {
fout = new FileOutputStream("description.bin");
DataOutputStream dout = new DataOutputStream(fout);
for (int x = 0; x < 1000; x++) {
dout.writeInt(10); // Write Int data
}
fin = new FileInputStream("description.bin");
DataInputStream din = new DataInputStream(fin);
for (int x = 0; x < 1000; x++) {
System.out.println(din.readInt()); // Read Int data
}
} catch (Exception e) {
} finally {
if (fout != null) {
fout.close();
}
if (fin != null) {
fin.close();
}
}
}
In this example, the code writes integers in "description.bin" file and then read them.
This is pretty fast in Java, since Java uses "channels" for files by default
Im writing a small fighting-game. Right now im creating Character maker with animations, combos, etc.
I allready had some problems with my sprites, because BufferedImage can not be serialized.
This was solved with PixelGrabber - when i click saveButton, this pixelGrabber grabs pixels from Image and saves them as array. This array can be serialized then. It can be deserialized, when i load this project, and used as an Image again.
Now, my question - is it possible to save .wav file as an serializable array? And after this to deserialize and use it again as an audio file?
p.s.sorry for my english
Here is a simple framework for working with WAV files: http://www.labbookpages.co.uk/audio/javaWavFiles.html
I wrote this out pretty quickly so I apologize if there are any mistakes, but here is what loading the wav file into an ArrayList would look like:
//make sure to import java.util.ArrayList;
try {
// load the file, set up the buffer
WavFile gameWav = WavFile.openWavFile( new File( "game_sound.wav" ) );
ArrayList<double> gameWavArray = new ArrayList<double>();
long framesRead = 0;
long totalFrames = gameWav.getNumFrames();
//read the buffer in 1000 frames at a time
do {
double[] gameWavBuffer = new double[1000];
// Read the frames into array, increment framesRead
framesRead = framesRead + Long.valueOf( gameWav.readFrames(gameWavBuffer, framesRead, 1000) );
//add all of the new frames to our ArrayList
Collections.addAll(gameWavArray, gameWavBuffer );
}
while (framesRead < totalFrames );
}
catch (Exception e) {
System.err.println(e);
}
For my video steganography project (in java) , I need to encode sequential PNGs into a movie file. I tried xuggler but I was getting compression.(due to which data hidden in the lsb's of the png image is getting lost the next time I extract the frames from the video)
Because I need need to retreive the hidden data later, I need to find a process to encode png images to video(preferred format: avi) in a lossless manner. The size of the new video is not a matter to me.
Hoping if someone could guide me or recommend a useful different java library to do this.
I can post my java code if required.
You can mux a sequence of PNGs into an MP4 file without transcoding ( preserving the exact original images ). To do this in pure Java use JCodec ( http://jcodec.org ):
public class SequenceMuxer {
private SeekableByteChannel ch;
private CompressedTrack outTrack;
private int frameNo;
private MP4Muxer muxer;
private Size size;
public SequenceMuxer(File out) throws IOException {
this.ch = NIOUtils.writableFileChannel(out);
// Muxer that will store the encoded frames
muxer = new MP4Muxer(ch, Brand.MP4);
// Add video track to muxer
outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25);
}
public void encodeImage(File png) throws IOException {
if (size == null) {
BufferedImage read = ImageIO.read(png);
size = new Size(read.getWidth(), read.getHeight());
}
// Add packet to video track
outTrack.addFrame(new MP4Packet(NIOUtils.fetchFrom(png), frameNo, 25, 1, frameNo, true, null, frameNo, 0));
frameNo++;
}
public void finish() throws IOException {
// Push saved SPS/PPS to a special storage in MP4
outTrack.addSampleEntry(MP4Muxer.videoSampleEntry("png ", size, "JCodec"));
// Write MP4 header and finalize recording
muxer.writeHeader();
NIOUtils.closeQuietly(ch);
}
}
To use it go like this:
public static void main(String[] args) throws IOException {
SequenceMuxer encoder = new SequenceMuxer(new File("video_png.mp4"));
for (int i = 1; i < 100; i++) {
encoder.encodeImage(new File(String.format("img%08d.png", i)));
}
encoder.finish();
}
If you download the processing framework, from www.processing.org, you can write a very simple java program to read in your images and write them out to a mov file, if you use the ANIMATION codec and specify lossless, it will be totally lossless.