How to use ImageIO to save multiple BufferedImages to a file - java

I tried both of the following options:
1.
BufferedImage Buffered_Image;
MemoryCacheImageOutputStream MemoryCache_OutputStream =
new MemoryCacheImageOutputStream(new FileOutputStream("C:/Test.mov",false));
while (notFinished) // Main recording loop.
{
Buffered_Image=robot.createScreenCapture(); // Capture Screen image.
try { ImageIO.write(Buffered_Image,"png",MemoryCache_OutputStream); }
catch (Exception e) { e.printStackTrace(); }
}
2.
BufferedImage Buffered_Image;
ImageWriter writer;
try
{
ImageOutputStream ios=ImageIO.createImageOutputStream(new File("C:/Test.mov"));
Iterator writers=ImageIO.getImageWritersByFormatName("png");
while (writers.hasNext())
{
writer=(ImageWriter)writers.next();
writer.setOutput(ios);
Out(writer.toString()+" canInsertImage : "+writer.canInsertImage(0));
// Got this: com.sun.imageio.plugins.png.PNGImageWriter#19fcc69
// canInsertImage : false
}
}
catch (Exception e) { }
cntPics=0;
while (notFinished) // Main recording loop.
{
Buffered_Image=robot.createScreenCapture(); // Capture Screen image.
writer.write(null,new IIOImage(Buffered_Image,null,null),null);
if (writer.canInsertImage(-1)) {
// Append image at highest index
writer.writeInsert(-1,new IIOImage(Buffered_Image,null,null),null);
} else Out("Writer can’t append image Id : "+cntPics);
cntPics++;
}
Neither of them worked, what's the correct way to save multiple PNG images to a file?
Edit:
You are right, I found a java program called Krut that can record screen sessions, but it uses JPEGImageEncoder, the image quality isn't as good as I want, so I wonder if I can use ImageIO to encode the sequence.
If ImageIO can't do it, my next question would be is there a stand alone open source PNGImageEncoder that I can use to encode it? I know there are open source PNGImageEncoders, but they tend to be tangled in projects and hard to get all the supporting files out of it, any ideas? Thanks!

It looks like you're trying to create a video (MOV) file by writing multiple PNG files in a row. This isn't going to work. You'll probably have to find a third-party library for encoding your images into a video file (which is itself may be a good SO question).
EDIT: I should also note that you may actually be able to get video by writing multiple JPG images in a row to get a form of MJPEG (Motion JPEG) but for other formats such as MOV you're going to need an actual encoder.

What are you trying to do? Re-inventing MNG? Even if you can write multiple PNG images in the same file, it makes a compound file understood by no program (except those you might write).
If, as suggested by Marc, you want to make a movie, you might want to look at QuickTime for Java (QTJava). It is the solution used by Processing to make movies out of animations/frames. It has several quality/formats, from the worst (but small files) to the highest quality (high file sizes as result).

Related

Unable to access resources outside the package in Java

I am currently developing a game, and have encountered a very burdensome problem.
I want to draw a picture on the screen but every time I try to read the resource / picture I get NULL. I tried 2 kinds of methods to read the picture but still couldn't.
But when I moved the image into the package that contains the class from which I was trying to read the image the image appeared.
So the problem is that i just can't access resources outside of the current package. And i need to know how can i do that, how can i access this resource. It has to be a resource that I can use even after exporting the game to a JAR file.
Code I tried (The first one is buffered image and the second is just image type:
try {
image = ImageIO.read(getClass().getResource("blocks.png"));
} catch (IOException e) {
e.printStackTrace();
}
--------------------------------------------------------------
image = new ImageIcon(getClass().getResource("blocks.png))).getImage();
I hope the question is clear enough.
Try creating a package called "resources", instead of a folder, and then access your image as "resources/blocks.png".

Detecting Audio File Bit Rate - Processing / Java

Trying to build a little app for sorting through audio files based on some of their properties. Have managed to grab the Sample Rate and Bit Depth using Minim but can't find anything anywhere for getting the Bit Rate?
Happy to look at taking the program to Javascript if needed but just desperate to find a method for detecting bit rate of a given file.
EDIT: Attempted to try and form an equation based off file size but cannot find a method for detecting MP3 file size either.
You can use jaudiotagger
You will need to download the jar, I managed to get it from maven central
Go to Sketch -> Add File... and select the downloaded jar, it should be added in a folder named code within your sketch folder.
Assuming you have placed an mp3 file in your data folder named audio.mp3 the following code should work, printing out the bit rate in the terminal.
import org.jaudiotagger.audio.mp3.*;
import org.jaudiotagger.audio.AudioFileIO;
void setup() {
File f = new File(dataPath("audio.mp3"));
try {
MP3File mp3 = (MP3File) AudioFileIO.read(f);
MP3AudioHeader audioHeader = mp3.getMP3AudioHeader();
println("" + audioHeader.getBitRate());
}
catch(Exception e) {
e.printStackTrace();
}
}
JAudiotagger supports a variety of file formats and you can use the relevant classes and methods for each one of these.
I suggest you take a look at the javadoc. Be careful of the examples though, the one I used in order to answer your question seems to be faulty, as you can see I had to swap getAudioHeader with getMP3AudioHeader.

Playing audio in java using Sun.audio

I want just to perform a simple task. (I'm a java newbie). I want to play an audio clip when a button is clicked.
here's the part of my code(which I did exactly by copying a tutorial from Youtube.)
private void btnPlayActionPerformed(java.awt.event.ActionEvent evt) {
InputStream in;
try{
in=new FileInputStream(new File("C:\\Users\\Matt\\Documents\\dong.wav"));
AudioStream timeupsound=new AudioStream(in);
AudioPlayer.player.start(timeupsound);
}
catch(Exception e){
JOptionPane.showMessageDialog(null, e);
}
}
But the problem is, this doesn't work.
It throws and IOException saying: "could not create audio stream from input stream".
My question is, what am I doing wrong? (as I clearly saw this code work in that youtube video, and I've used the same code. Please help. and once again, I'm a newbie);
The sun package classes should be causing some informative warnings at compile time. Heed them. Don't use classes in that package hierarchy. They are undocumented, are not guaranteed from one Java version to the next, and will probably not be available in a non-Oracle JRE at all.
Instead use the Java Sound based Clip to play audio. See the info. page for working examples.
Note
It might be the WAV is encoded in a format that Java Sound does not support. Media formats are typically 'container formats' that might be encoded using any number of different Codecs. It is likely that WAV is using a more compressive Codec such as MP3 or OGG internally.
I don't know of a Service Provider Interface for the OGG format, but if they are encoded as MP3, you might be able to get it working using the MP3 SPI. See the info. page linked above for details.
Tip
Also change code of the form:
catch (Exception e) { ..
To
catch (Exception e) {
e.printStackTrace(); // very informative! ...

Is it possible to use Sikuli to assert that images are the same in GUI-less mode?

I have a testing server which runs headless. One test I want is to check that an image served off a particular URL matches some reference image.
Is there an API in Sikuli which can directly accept an image as a stream and compare it with some other image taken from the local resource file? Unfortunately, there is no complete tutorial on Sikuli's Java API, all I've found is tutorials that assume that there is a display available.
I'll be glad to see any examples or at least links to the needed parts of Sikuli javadocs. Also, suggestions for other approaches are welcome.
To use Sikuli you need
A base image on which the other image will be searched.
The image which will be searched within the other image.
If image 1 is your local resource image, you can create a org.sikuli.Finder instance with the path to the image and the Region of this image which will be searched.
Example (java level):
finder = new Finder("path/to/image", new Region(0, 0, <imgwidth>, <imgheight>));
If image 1 is your stream, you have to make a BufferedImage out of it somehow (I do not know the best way to do this).
Then you can make a org.sikuli.ScreenImage from this BufferedImage with the help of an java.awt.Rectangle and an org.sikuli.Region.
finder = new Finder(new ScreenImage(new Rectangle(0,0,<imgwidth>,<imgheight>), bufferedImage), new Region(0,0,<imgwidth>,<imgheight>))
After you created the finder from image 1, you can search image 2 within this image.
Again, you have two possibilities.
If the second image is your local resource image, you can create an org.sikuli.Pattern object with the file location:
pattern = new Pattern("path/to/image.png");
Else, if this is your stream, you have to make a BufferedImage out of the stream somehow. You can then create a pattern from this image:
pattern = new Pattnern(bufferedImage);
As a last step, you can now run the finder to search for the pattern:
finder.find(pattern);
You can check if the finder found anything with:
finder.hasNext();
And you should be able to iterate all findings with:
for (Match m : finder):
//do something with the match
I hope I could help you although your question is already some weeks old.
Below code helps for asserting images
//take screenshots
File scrFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);
try {
//copy it some location
FileUtils.copyFile(scrFile, new File("C:\\screenshot.png"));
Finder f = new Finder("C:\\screenshot.png");
System.out.println("abc");
f.find("C:\\chrome3.png", 0.95);
while(f.hasNext()){
System.out.println("found");
Match m= f.next();
f.destroy();
}
}
catch (IOException e)
{
e.printStackTrace();
}

Java/JAudiotagger: Mp3 wrong ID3 tag size

I'm building an mp3 tagging application using the JAudiotagger library. My application reads the mp3 metadata fine, and can write the metadata fine too, except for the artworks. So my problem is as follows:
When I add a number of artworks in the mp3 file, and save it, the file is getting bigger, which makes sense.
But when I remove one or all the artworks, the file size doesn't get smaller.
The actual problem lies in the ID3v2 tag of my mp3 file. When I remove an artwork, it is actually removed from the tag, but the tag size itself doesn't shrink at all.
The method I'm using when deleting an artwork is this:
// Get the artworkList from the parentFrame.
List<Artwork> list = parentFrame.getArtworkList();
// Get the tag from the parentFrame's mp3File.
AbstractID3v2Tag tag = parentFrame.getTag();
// Get the index of the artwork the user is currently looking at (and
// wants to delete too).
int visibleArtworkIndex = parentFrame.getVisibleArtworkIndex();
// Remove it from the list.
list.remove(visibleArtworkIndex);
// Update the parentFrame's copy of the artworkList.
parentFrame.setArtworkList(list);
// Update the tag (delete its whole artwork field).
tag.deleteArtworkField();
// If the list has more artworks left, add them to the tag.
if (!list.isEmpty()) {
Iterator<Artwork> iterator = list.iterator();
while (iterator.hasNext()) {
try {
tag.addField(iterator.next());
} catch (FieldDataInvalidException e1) {
e1.printStackTrace();
}
}
}
, which actually removes an artwork from the list, and then updates the tag itself by deleting all of its artworks and copying them all over again from the updated list.
My attempts for a solution were:
To create a new tag from the updated old tag (after calling tag.deleteArtworkField()), then adding the artworks to the new tag, but the new tag had the same size as the old one.
To trim the mp3 file just before saving it by using tag.adjustPadding(File fileToBeTrimmed, int sizeToStoreTagBeforeAudioInBytes, long audioStartByte), which adjusts the length of the padding at the beginning of the MP3 file. The problem here is that I know the wrong tag size only and not the correct, so I can't trim the mp3 correctly and I end up losing audio data.
To illustrate the problem better I have included some images:
The mp3 file before:
The mp3 file after the removal of one artwork. Notice the tag kept its previous size although it has less artworks:
And how the file should be:
I hope anyone has any ideas. Thanks in advance.
This is fixed as of today.
By default jaudiotagger does not reclaim space when you make the metadata smaller, but now if you set
TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);
before saving changes it will reclaim unnecessary padding to give the minimum file size possible.
That is actually intended behavior, this is a sort of optimization.
When you add data to the ID3v2 tag and there is not enough space, the entire file needs to be rewritten to make enough space. When you remove data, the ID3v2 is just updated to contain the data and unused space is only simply marked as free (it would be recycled when you add more data again).
Look for a "release unused space in tag" call in your library. You need to tell it explicitly that the free space should be released.
Edit: Looking at the Javadoc, I believe you need to set this option before working with your files:
TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);
The methods
TagOptionSingleton.getInstance().setId3v2PaddingWillShorten(true);
TagOptionSingleton.getInstance().setOriginalSavedAfterAdjustingID3v2Padding(true);
seem to be not fully implemented (as of Jan 2018). For example, check http://www.jthink.net/jaudiotagger/maven/apidocs/org/jaudiotagger/tag/mp4/Mp4TagCreator.html
to see that the class Mp4TagCreator has not implemented a padding when converting the metadata to raw data:
padding - TODO padding parameter currently ignored
For mp3-files I have a workaround, using the library mp3agic https://github.com/mpatric/mp3agic. Unlike jaudiotagger, which was last updated 2015, it is still updated. On android you need to use version 0.9.0, since 0.9.1 uses java.nio.file-classes, which is not supported by android, https://github.com/mpatric/mp3agic/issues/141.
The workaround is to simply create a new tag and copy the tag data, then write it to a new file. If succesfull, replace the old file with the new file. The new file will be smaller than the original file if you do not copy the cover image. I believe this should be also possible with jaudiotagger, but did not manage to do so. Here is how with mp3agic:
try {
Mp3File song = new Mp3File(location,false);
if (song.hasId3v2Tag()){
ID3v2 oritag=song.getId3v2Tag();
byte[] image=oritag.getAlbumImage();
if(image!=null){
if (image.length > 10) {
song = new Mp3File(location, true);
oritag=song.getId3v2Tag();
ID3v24Tag newtag = new ID3v24Tag();
// copy metadata
newtag.setArtist(oritag.getArtist());
newtag.setArtistUrl(oritag.getArtistUrl());
newtag.setOriginalArtist(oritag.getOriginalArtist());
newtag.setArtistUrl(oritag.getArtistUrl());
newtag.setAlbum(oritag.getAlbum());
newtag.setAlbumArtist(oritag.getAlbumArtist());
newtag.setAudiofileUrl(oritag.getAudiofileUrl());
newtag.setAudioSourceUrl(oritag.getAudioSourceUrl());
newtag.setUrl(oritag.getUrl());
newtag.setGenre(oritag.getGenre());
newtag.setGrouping(oritag.getGrouping());
newtag.setTitle(oritag.getTitle());
newtag.setTrack(oritag.getTrack());
newtag.setPublisher(oritag.getPublisher());
newtag.setPublisherUrl(oritag.getPublisherUrl());
newtag.setCopyright(oritag.getCopyright());
newtag.setCopyrightUrl(oritag.getCopyrightUrl());
newtag.setComposer(oritag.getComposer());
newtag.setCommercialUrl(oritag.getCommercialUrl());
newtag.setComment(oritag.getComment());
newtag.setYear(oritag.getYear());
newtag.setKey(oritag.getKey());
newtag.setRadiostationUrl(oritag.getRadiostationUrl());
newtag.setPaymentUrl(oritag.getPaymentUrl());
song.setId3v2Tag(newtag);
try {
song.save(location + "intermed");
File from = new File(location + "intermed");
// if successfull then replace old file with new file
if(from.exists()) {
File file = new File(location);
long sizeold = file.length();
file.delete();
File to = new File(location);
long sizenew = from.length();
from.renameTo(to);
freedspace += sizeold - sizenew;
}
} catch (IOException | NotSupportedException e) {
e.printStackTrace();
}
}
}
}
} catch (IOException | UnsupportedTagException | InvalidDataException e) {
e.printStackTrace();
}
Remarks: I implemented this in my AudioCleanup-App, https://play.google.com/store/apps/details?id=com.gluege.audiocleanup&hl=en, it works on mp3-files. I did not manage to remove album covers in other file types. If someone has a solution, please share it.
I dislike the Id3-standard, especially the padding. It's a waste of precious space on smartphones. I have seen albums where each song contained the same 1MB cover image.

Categories

Resources