The answers provided in How do I get a sound file’s total time in Java? work well for wav files, but not for mp3 files.
They are (given a file):
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioInputStream.getFormat();
long frames = audioInputStream.getFrameLength();
double durationInSeconds = (frames+0.0) / format.getFrameRate();
and:
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioInputStream.getFormat();
long audioFileLength = file.length();
int frameSize = format.getFrameSize();
float frameRate = format.getFrameRate();
float durationInSeconds = (audioFileLength / (frameSize * frameRate));
They give the same correct result for wav files, but wrong and different results for mp3 files.
Any idea what do I have to do to get the mp3 file's duration?
Using MP3SPI:
private static void getDurationWithMp3Spi(File file) throws UnsupportedAudioFileException, IOException {
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(file);
if (fileFormat instanceof TAudioFileFormat) {
Map<?, ?> properties = ((TAudioFileFormat) fileFormat).properties();
String key = "duration";
Long microseconds = (Long) properties.get(key);
int mili = (int) (microseconds / 1000);
int sec = (mili / 1000) % 60;
int min = (mili / 1000) / 60;
System.out.println("time = " + min + ":" + sec);
} else {
throw new UnsupportedAudioFileException();
}
}
Here is the way I get the total time of a file .mp3, I'm using the library is Jlayer 1.0.1
Header h = null;
FileInputStream file = null;
try {
file = new FileInputStream(filename);
} catch (FileNotFoundException ex) {
Logger.getLogger(MP3.class.getName()).log(Level.SEVERE, null, ex);
}
bitstream = new Bitstream(file);
try {
h = bitstream.readFrame();
} catch (BitstreamException ex) {
Logger.getLogger(MP3.class.getName()).log(Level.SEVERE, null, ex);
}
int size = h.calculate_framesize();
float ms_per_frame = h.ms_per_frame();
int maxSize = h.max_number_of_frames(10000);
float t = h.total_ms(size);
long tn = 0;
try {
tn = file.getChannel().size();
} catch (IOException ex) {
Logger.getLogger(MP3.class.getName()).log(Level.SEVERE, null, ex);
}
//System.out.println("Chanel: " + file.getChannel().size());
int min = h.min_number_of_frames(500);
return h.total_ms((int) tn)/1000;
Here is a detailed explanation of the MP3 File structure
http://www.autohotkey.com/forum/topic29420.html
Use jave-1.0.1.jar library.
It passed my test on wave file formats such as: wav-pcm8/16,
wav-alaw, wav-ulaw, wav-gsm, wav-adpcm;
It passed my test on some MP3 file formats: cbr vs vbr, stereo vs
SingleChannel;
It can support video formats, which I haven't tested on.
Code sample:
File source = new File("C:\\22.mp3");
Encoder encoder = new Encoder();
try {
MultimediaInfo mi = encoder.getInfo(source);
long ls = mi.getDuration();
System.out.println("duration(sec) = "+ ls/1000);
} catch (Exception e) {
e.printStackTrace();
}
I've tried Jlayer 1.0.1, but it failed for some MP3 format. As for another libaray jaudiotagger-2.0.3.jar, it works fine for MP3 formats, but it can only support wav-pcm8/16.
I'm old-fashioned in this, but I always simply get the specs for MP3, write a function that searches the right bits, and find it out.
If Winamp can determine this by reading the bitstream of an MP3 file, then so can I right?
And so can you, I believe in you mate!
Related
i started to use thumbnailator library to make thumbnail in a Spring Boot project
But i'm facing a problem when i try to delete the file, i got an exception telling me the file is being used by another process. I pretty new with Java and i can't figured out where does the problem might come from and what process should i stop/close:
File originalFile = mediaUtils.saveFile(pathOriginal, file);
String path = mediaUtils.resolvePath(imageDir, name, false, image.getCreation());
mediaUtils.saveJPG(originalFile, file.getContentType(), WIDTH_IMAGE_SIZE, path);
String pathThumb = mediaUtils.resolvePath(imageDir, name, true, image.getCreation());
mediaUtils.saveJPG(originalFile, file.getContentType(), WIDTH_IMAGE_SIZE_THUMB, pathThumb);
public File saveFile(String filePath, MultipartFile file) {
try {
Path path = Paths.get(getPath(filePath));
Files.createDirectories(path.getParent());
Files.copy(file.getInputStream(), path);
return new File(path.toString());
} catch (IOException e) {
LOG.error("could not save file", e);
throw new FileException("could not create file: " + getPath(filePath), e);
}
}
private void saveJPG(InputStream imageInputStream, File file, String contentType, int newWidth, String outputPath) {
try {
// verify it is an image
if (!Arrays.asList("image/png", "image/jpeg").contains(contentType)) {
throw new IllegalArgumentException("The file provided is not a valid image or is not supported (should be png or jpeg): " + contentType);
}
// Create input image
BufferedImage inputImage = ImageIO.read(imageInputStream);
newWidth = newWidth > inputImage.getWidth() ? inputImage.getWidth() : newWidth;
double ratio = (double) inputImage.getWidth() / (double) inputImage.getHeight();
int scaledHeight = (int) (newWidth / ratio);
Path path = Paths.get(baseUrl + outputPath + ".jpg");
Thumbnails.of(file)
.size(newWidth, scaledHeight)
.toFile(path.toFile());
LOG.info("writing image to {}", path);
} catch (IOException e) {
LOG.error("could not write image", e);
}
}
Thanks for any advice or help :)
You should make sure to close your inputstreams and files, after you've used them.
Otherwise things like you've mentioned, happen. A process does block your file.
So instead of using simple try-catch-blocks I would recommend to use try-with-resources which will close the underlying streams and files. For example:
try(InputStream imageInputStream = new FileInputStream(...)) {
// do your stuff
}
After the code in the brackets is done or an exception occured, the inputstream will be closed.
I use ZipEntry and ZipInputStream and ZipFile in java to get some information of zip archive. But still I cannot get one information.
Python can do like this:
otazip = zipfile.ZipFile(sys.argv[1], 'r')
load_info = otazip.getinfo('load.bin')
load_offset = load_info.header_offset + len(load_info.FileHeader())
Can anyone help me to get the load_offsetusing java like python does?
In Java there is no method to get offset from ZipEntry.
I tried below solution and it's working fine for me:
public long getZipEntryOffset() {
String path = ota.zip;
final String loadString = "load.bin";
long offset = 0;
File zipFile = new File(path);
ZipInputStream zis = new ZipInputStream(new FileInputStream(path));
try {
ZipEntry loadEntry = zisp.getNextEntry();
while( loadEntry != null){
long fileSize = 0;
long extra = loadEntry.getExtra() == null ? 0 : loadEntry.getExtra().length;
String fileName = loadEntry.getName();
Offset += 30 + loadEntry.getName().length() + extra;
if(!loadEntry.isDirectory()){
fileSize = loadEntry.getCompressedSize();
}
if (fileName.equals(loadString )) {
loadSize = loadEntry.getSize();
break;
}
offset += fileSize;
loadEntry = zisp.getNextEntry();
}
zisp.closeEntry();
}finally {
zis.close();
}
return offset ;
}
The offset is comprised of 30 bytes of standard zip header info + the file name length + some possible extra OS-specific flags + the data itself
I am looking to take a large video files (3hours+) and pass in segments of the video that I would like to split.
For example, I could pass in a 3 hour video - and want from 00:10 to 00:11 to be split into a separate file.
I currently have the below code - that takes my video and splits it into a split no. of segments, but how would I go about splitting the video by time instead?
Code:
try {
File file = new File("//Users//robeves//Desktop//Videos to split//TestVideo.mp4");//File read from Source folder to Split.
if (file.exists()) {
String videoFileName = file.getName().substring(0, file.getName().lastIndexOf(".")); // Name of the videoFile without extension
File splitFile = new File("//Users//robeves//Desktop//Videos to split//Converted//"+ videoFileName);//Destination folder to save.
if (!splitFile.exists()) {
splitFile.mkdirs();
System.out.println("Directory Created -> "+ splitFile.getAbsolutePath());
}
int i = 01;// Files count starts from 1
InputStream inputStream = new FileInputStream(file);
String videoFile = splitFile.getAbsolutePath() +"/"+ String.format("%02d", i) +"_"+ file.getName();// Location to save the files which are Split from the original file.
OutputStream outputStream = new FileOutputStream(videoFile);
System.out.println("File Created Location: "+ videoFile);
int totalPartsToSplit = 20;// Total files to split.
int splitSize = inputStream.available() / totalPartsToSplit;
int streamSize = 0;
int read = 0;
while ((read = inputStream.read()) != -1) {
if (splitSize == streamSize) {
if (i != totalPartsToSplit) {
i++;
String fileCount = String.format("%02d", i); // output will be 1 is 01, 2 is 02
videoFile = splitFile.getAbsolutePath() +"/"+ fileCount +"_"+ file.getName();
outputStream = new FileOutputStream(videoFile);
System.out.println("File Created Location: "+ videoFile);
streamSize = 0;
}
}
outputStream.write(read);
streamSize++;
}
inputStream.close();
outputStream.close();
System.out.println("Total files Split ->"+ totalPartsToSplit);
} else {
System.err.println(file.getAbsolutePath() +" File Not Found.");
}
} catch (Exception e) {
e.printStackTrace();
}
}
If you do want to be able to play the segments individually, then the above code may not work if it is just splitting the file at arbitrary points, as many video formats need to finish on a good 'boundary' to enable proper playback.
As Binkan suggests, using a video library like ffmpeg, either in cmd line, wrapped cmd line or by using its associated C libraries, will allow you safely split a video in most common formats.
For example the following ffmpeg cmd line will create a segment from an mp4 video:
ffmpeg -i inputVideo.mp4 -ss 00:00:00 -t 00:00:10 -c copy outputVideoSegment.mp4
The following code uses this utility in a 'wrapper' to segment a video file into chunks:
int chunkSize = videoDurationSecs/(numberOfChunks + 1);
int startSecs = 0;
for (int i=0; i<numberOfChunks; i++) {
//Create video chunk
String startTime = convertSecsToTimeString(startSecs);
int endSecs = startSecs + ((i+1)*chunkSize);
if (endSecs > videoDurationSecs) {
//make sure rounding does not mean we go beyond end of video
endSecs = videoDurationSecs;
}
String endTime = convertSecsToTimeString(endSecs);
//Call ffmpeg to create this chunk of the video using a ffmpeg wrapper
String argv[] = {"ffmpeg", "-i", videoPath,
"-ss",startTime, "-t", endTime,
"-c","copy", segmentVideoPath[i]};
int ffmpegWrapperReturnCode = ffmpegWrapper(argv);
}
String convertSecsToTimeString(int timeSeconds) {
//Convert number of seconds into hours:mins:seconds string
int hours = timeSeconds / 3600;
int mins = (timeSeconds % 3600) / 60;
int secs = timeSeconds % 60;
String timeString = String.format("%02d:%02d:%02d", hours, mins, secs);
return timeString;
}
Examples of wrappers are here, but you can also use the ffmpeg libraries directly if you would rather avoid the wrapper approach (which does have the disadvantage that ffmpeg cmd line is not really intended to be wrapped in this way):
http://www.sauronsoftware.it/projects/jave/
https://github.com/jhotovy/android-ffmpeg (Android based)
I'm trying to write a file from an AudioInputStream. The stream can read the original file just fine (as far as I can tell). But when it tries to write, it writes what should be a 15 KB file in 44 bytes. Windows Media Player gives the error "either the file type is unsupported, or WMP doesn't recognize the codec used to convert the file."
I got most of this code from http://docs.oracle.com/javase/tutorial/sound/converters.html. I've looked all over StackOverflow but nothing seemed to pertain to this problem.
I've tried getting the format of the AudioInputStream and it's definitely a .wav. As far as I can tell, a codec is software that converts analog to digital data, and both the wav file and the AudioInputStream are digital already.
EDIT: It looks like the stream is writing the WAVE file header to my new file, but nothing else. That's where my 44 byte file is coming from.
Here's the code. I think the problem is in writeFile() but I included the rest just in case:
public static void getFile()
{
try
{
File test = new File("C:\\Users\\Audrey\\Steganography\\correctamundo.wav"); //file to be written from
AudioInputStream stream = AudioSystem.getAudioInputStream(test);
AudioFileFormat format = AudioSystem.getAudioFileFormat(test);
int bytesPerFrame = stream.getFormat().getFrameSize();
byte[] b = new byte[30000]; //array of arbitrary size to hold the bytes in stream
if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) //not sure why this is necessary
{
bytesPerFrame = 1;
}
int i = 0;
while(stream.available() > 0)
{
byte currentByte = (byte)stream.read();
b[i] = currentByte; //read bytes to array
System.out.println(b[i] + " " +(i+1)); //test statement
i++;
}
writeFile(format, stream);
} catch (IOException | UnsupportedAudioFileException e)
{
e.printStackTrace();
}
}
public static void writeFile (AudioFileFormat format, AudioInputStream stream) //format and stream created in getFile() under the same names
{
try
{
File fileOut = new File("C:\\Users\\Audrey\\Steganography\\testFile2.wav");
AudioFileFormat.Type fileType = format.getType(); //type of file the AudioInputStream can write to, since format refers to stream
if (AudioSystem.isFileTypeSupported(fileType, stream))
{
AudioSystem.write(stream, fileType, fileOut);
System.out.println(stream.getFrameLength()); //test statement
}
System.out.println(fileType); //test statement
}
catch (IOException e)
{
e.printStackTrace();
}
}
I'm trying to downsample a .wav audio from 22050 to 8000 using AudioInputStream but the conversion returns me 0 data bytes. Here is the code:
AudioInputStream ais;
AudioInputStream eightKhzInputStream = null;
ais = AudioSystem.getAudioInputStream(file);
if (ais.getFormat().getSampleRate() == 22050f) {
AudioFileFormat sourceFileFormat = AudioSystem.getAudioFileFormat(file);
AudioFileFormat.Type targetFileType = sourceFileFormat.getType();
AudioFormat sourceFormat = ais.getFormat();
AudioFormat targetFormat = new AudioFormat(
sourceFormat.getEncoding(),
8000f,
sourceFormat.getSampleSizeInBits(),
sourceFormat.getChannels(),
sourceFormat.getFrameSize(),
8000f,
sourceFormat.isBigEndian());
eightKhzInputStream = AudioSystem.getAudioInputStream(targetFormat, ais);
int nWrittenBytes = 0;
nWrittenBytes = AudioSystem.write(eightKhzInputStream, targetFileType, file);
I already checked AudioSystem.isConversionSupported(targetFormat, sourceFormat) and it returns true. Any idea?
I have just tested your code with different audio files and everything seems to work just fine. I can only guess, that you are either testing your code with an empty audio file (bytes == 0) or, that the file you try to convert is not supported by the Java Audio System.
Try using another input file and/or convert your input file to a compatible file, and it should work.
Here is the main method, that worked for me:
public static void main(String[] args) throws InterruptedException, UnsupportedAudioFileException, IOException {
File file = ...;
File output = ...;
AudioInputStream ais;
AudioInputStream eightKhzInputStream = null;
ais = AudioSystem.getAudioInputStream(file);
AudioFormat sourceFormat = ais.getFormat();
if (ais.getFormat().getSampleRate() == 22050f) {
AudioFileFormat sourceFileFormat = AudioSystem.getAudioFileFormat(file);
AudioFileFormat.Type targetFileType = sourceFileFormat.getType();
AudioFormat targetFormat = new AudioFormat(
sourceFormat.getEncoding(),
8000f,
sourceFormat.getSampleSizeInBits(),
sourceFormat.getChannels(),
sourceFormat.getFrameSize(),
8000f,
sourceFormat.isBigEndian());
if (!AudioSystem.isFileTypeSupported(targetFileType) || ! AudioSystem.isConversionSupported(targetFormat, sourceFormat)) {
throw new IllegalStateException("Conversion not supported!");
}
eightKhzInputStream = AudioSystem.getAudioInputStream(targetFormat, ais);
int nWrittenBytes = 0;
nWrittenBytes = AudioSystem.write(eightKhzInputStream, targetFileType, output);
System.out.println("nWrittenBytes: " + nWrittenBytes);
}
}