I am currently working on java media player that can play mkv format. I am using VLCJ, everything is working except when I try to change audio track which is not working.
here is the code
public class mediaplayer {
private static JFileChooser filechooser = new JFileChooser();
public mediaplayer() {
}
public static void main(String[] args) {
String vlcPath = "", mediaPath = "";
File ourfile;
filechooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
filechooser.showSaveDialog(null);
ourfile = filechooser.getSelectedFile();
mediaPath = ourfile.getAbsolutePath();
EmbeddedMediaPlayerComponent mediacom = new EmbeddedMediaPlayerComponent();
JFrame frame = new JFrame();
frame.setContentPane(mediacom);
frame.add(canvas);
frame.setLocation(100, 100);
frame.setSize(1050, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
MediaPlayer mplayer = mediacom.getMediaPlayer();
mplayer.playMedia(mediaPath);
mplayer.setAudioTrack(1);
}
}
In libVLC versions before vlc 2.0.5 the native API call to set the audio track was bugged.
With the fix in libVLC 2.0.5, setting the audio track works reliably but you can not just assume a simple index from 0..N and you can not assume sequential track numbers - you must enumerate the audio tracks by calling mediaPlayer.getAudioDescriptions(). The returned TrackDescription objects contain an audio track identifier that should be used with mediaPlayer.setAudioTrack().
To disable audio, you can select the audio track identifier of the track with a a description of "Disable".
Also be aware that you might not be able to set the audio track immediately after calling mediaPlayer.playMedia(). Media is started asynchronously and you may need to wait until the media has actually started and/or has been parsed before the track information is available.
6 years later
The API have grown a lot
If you want to stop or choose an audio track you can use this code snippet and adapt it, the idea is to wait for the player to start using a process then switching to what you need, in my case is the audio disabling
new Thread(
() -> {
try {
while(!empc.mediaPlayer().status().isPlaying())Thread.sleep(500);
empc.mediaPlayer().audio().trackDescriptions().stream()
.filter(td -> td.description().equals("Disable"))
.forEach(t -> empc.mediaPlayer().audio().setTrack(t.id()));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
).start();
empc is my media player instance {the embed one for more precision in the player component}
Related
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.
I'm using 320 kbps roughly 1 hour long MP3 files. The project I'm working on would seek in a collection of music inside an MP3 file so that it can shuffle the songs. I would give the timestamps to the program and it would seek to the song. It would work if JavaFX's seek method wasn't highly inaccurate.
After using MediaPlayer.seek(duration) The MediaPlayer.getCurrentTime() returns the duration we seeked to as expected. However if we listen to the mp3 file(either without seeking or in an external mp3 player) we realize that the time reported and reality is very different, sometimes even seconds.
For example MediaPlayer.seek(Duration.millis(2000)) results seeking to 0 seconds. A 2 second failure rate is not acceptable.
With WAV it seems to work. Though it does not with MP3.
The two workarounds I think so far are possible:
Writing an MP3 Decoder and Player which doesn't have the bug
Using uncompressed WAV files
Does anyone know anything better?
If anyone needs the source code there isn't much more in it:
public static void main(String[] args) {
MediaPlayer player = null;
JFXPanel fxPanel = new JFXPanel(); //To initialize JavaFX
try {
String url = new File("E:\\Music\\test.mp3").toURI().toURL().toExternalForm();
player = new MediaPlayer(new Media(url));
System.out.println("File loaded!");
} catch (MalformedURLException e) {
//e.printStackTrace();
System.out.println("Error with filename!");
System.exit(0);
}
player.play();
System.out.println("Playing!");
while (true)
{
Scanner reader = new Scanner(System.in);
String string = reader.nextLine();
if (string.equals("Exit")) System.exit(0);
else if (string.equals("Seek"))
{
player.seek(Duration.millis(2000)); //this seeks to the beggining of the file
player.pause();
try {
Thread.sleep(100); //player.getCurrentTime() doesn't update immediately
} catch (InterruptedException e) { }
System.out.println("Time: " + player.getCurrentTime().toMillis() + " / " + player.getTotalDuration().toMillis());
player.play();
}
}
}
I would recommend using the javazoom library. It is an open source java library that already has this stuff written without errors(At least none that I found).
Source
http://www.javazoom.net/index.shtml
Place your call to the seek method off the UI thread or your UI will hang.
new Thread(() ->player.seek(Duration.millis(2000))).start();
I wrote a program in Java using the pi4j lib to make sound whenever a (physical) button is clicked. This program works, but it now plays all the sounds interchangeably. I want that when you click on 2,3,4 or more buttons you only hear one sound.
This is the code I hope you can help.
public class ButtonSoundsProject{
public static void main(String args[]) throws InterruptedException {
System.out.println("Toy has been started!");
// create gpio controller
final GpioController gpio = GpioFactory.getInstance();
// provision gpio pin #02 as an input pin with its internal pull down resistor enabled
GpioPinDigitalInput[] pins = {
gpio.provisionDigitalInputPin(RaspiPin.GPIO_00, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_01, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_03, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_04, PinPullResistance.PULL_DOWN),
gpio.provisionDigitalInputPin(RaspiPin.GPIO_05, PinPullResistance.PULL_DOWN),};
final ArrayList<String> soundList = new ArrayList<String>();
soundList.add("/home/pi/Sounds/Sound1.wav");
soundList.add("/home/pi/Sounds/Sound2.wav");
soundList.add("/home/pi/Sounds/Sound3.wav");
soundList.add("/home/pi/Sounds/Sound4.wav");
soundList.add("/home/pi/Sounds/Sound5.wav");
soundList.add("/home/pi/Sounds/Sound6.wav");
soundList.add("/home/pi/Sounds/Sound7.wav");
soundList.add("/home/pi/Sounds/Sound8.wav");
soundList.add("/home/pi/Sounds/Sound9.wav");
soundList.add("/home/pi/Sounds/Sound10.wav");
soundList.add("/home/pi/Sounds/Sound11.wav");
soundList.add("/home/pi/Sounds/Sound12.wav");
// create and register gpio pin listener
GpioPinListenerDigital listener = new GpioPinListenerDigital() {
#Override
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
// display pin state on console
final int randomNum = 0 + (int) (Math.random() * 12);
System.out.println(randomNum);
System.out.println(" --> GPIO PIN STATE CHANGE: " + event.getPin() + " = " + event.getState());
InputStream in;
try {
System.out.println(soundList.get(randomNum).toString());
String filepath = soundList.get(randomNum).toString();
in = new FileInputStream(new File(filepath));
AudioStream as = new AudioStream(in);
AudioPlayer.player.start(as);
} catch (Exception ex) {
ex.printStackTrace();
}
}
};
gpio.addListener(listener, pins);
for (;;) {
Thread.sleep(500);
}
}
}
As stated in the comments, I can't give you advise regarding the AudioStream and AudioPlayer classes because I don't seem to have those in my JDK. Since my method is similar, I'll give you what I have, and you can hopefully take it from there.
Basically, the solution is to stop and/or "mute" that audio clip. This is how I accomplish it using the javax.sound package.:
private Clip currentAudioClip; // Keep a reference to the current clip being played
public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
// Call this every time regardless.
// If nothing is playing, this will do nothing.
stopAudio();
String filepath = soundList.get(randomNum)
URL soundFileUrl = new File(filePath).toURI().toURL();
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFileUrl);
Line.Info lineInfo = new Line.Info(Clip.class);
Line line = AudioSystem.getLine(lineInfo);
currentAudioClip = (Clip) line;
currentAudioClip.open(audioInputStream);
audioClip.start();
// Alternative if you want to loop continuously. Comment out the `.start` line to use this.
// audioClip.loop(Clip.LOOP_CONTINUOUSLY);
}
public void stopAudio(){
if(audioClip != null){
muteLine(); // A gotcha I discovered (see explanation below)
audioClip.stop();
// audioClip.loop(0); // if you chose to loop, use this instead of `.stop()`
audioClip.flush();
audioClip = null;
}
}
public void muteLine(){
BooleanControl muteControl = (BooleanControl) audioClip.getControl(BooleanControl.Type.MUTE);
if(muteControl != null){
muteControl.setValue(true); // True to mute the line, false to unmute
}
}
In short, every time a pin state change event is fired, the previous audio clip will be ceased, and a new one should play. You shouldn't get any sound overlapping with this.
Also note that this is a slight modification of my original code, so let me know if there are any issues
Note about the GOTCHA
I wrote a question over on the Raspberry PI Stackexchange about an odd problem I encountered. The problem was that I discovered my audio clip would not cease playing on command. It would continue playing for a seemingly arbitrary amount of time. The stranger thing is that I only observed this while testing the app on the raspberry; it worked perfectly fine on my local machine (and even on several other machines).
It is possible my issue is related to the "looping" of my clip; if that is the case, and you simply want the clip to play for its length and no further, you may not encounter that issue, and you can probably dispense with the "muting" code I included. However, if you do observe the same issue, at least you have a solution.
Hope this helps, let me know if you have any questions.
I try to use VLCj to get access to web-cameras. I am using this code:
public static void main(String[] args) {
// Create player.
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory();
DirectMediaPlayer mediaPlayer = mediaPlayerFactory.newDirectMediaPlayer(
320, 240,
new RenderCallback() {
#Override
public void display(Memory arg0) {
// Do nothing.
}
});
// Options setup.
String[] options = new String[]{};
String mrl = "v4l2:///dev/video0"; // Linux
// Start preocessing.
mediaPlayer.startMedia(mrl, options);
BufferedImage bufImg;
for (int i = 0; i < 1000; ++i) {
bufImg = mediaPlayer.getSnapshot();
// Do something with BufferedImage...
// ...
}
// Stop precessing.
mediaPlayer.stop();
mediaPlayer = null;
System.out.println("Finish!");
}
And this code partially works -- I can get and work with BufferedImage, but:
I got an error in to output: [0x7f0a4c001268] main vout display error: Failed to set on top
When main loop is finished and camera was disabled program don't finished! I see Finish! message, but program not return control into IDE or console.
UPD:
I am using openSUSE 12.2 x64, VLC 2.0.3 installed and working properly for all video files, library VLCj 2.1.0.
This code working properly:
public static void main(String[] args) {
// Configure player factory.
String[] VLC_ARGS = {
"--intf", "dummy", // no interface
"--vout", "dummy", // we don't want video (output)
"--no-audio", // we don't want audio (decoding)
"--no-video-title-show", // nor the filename displayed
"--no-stats", // no stats
"--no-sub-autodetect-file", // we don't want subtitles
"--no-inhibit", // we don't want interfaces
"--no-disable-screensaver", // we don't want interfaces
"--no-snapshot-preview", // no blending in dummy vout
};
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory(VLC_ARGS);
// Create player.
HeadlessMediaPlayer mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer();
// Select input device.
String mrl = "v4l2:///dev/video0"; // Linux
// Start processing.
mediaPlayer.startMedia(mrl);
BufferedImage bufImg;
for (int i = 0; i < 1000; ++i) {
bufImg = mediaPlayer.getSnapshot();
// Do something with BufferedImage...
// ...
}
// Stop processing.
mediaPlayer.stop();
// Finish program.
mediaPlayer.release();
mediaPlayerFactory.release();
}
Re your native window: VLCj opens a shared instance to the VLC library.
A headless media palyer is NOT intended to have a video or audio output!
In fact, if you need anything to play (and not to stream to anywhere else) you need to create either an output window or use a direct media player (may be much more complicated)
So, if a headless player needs to play something it opens a native window to perform the playback!
Source: http://www.capricasoftware.co.uk/wiki/index.php?title=Vlcj_Media_Players
Re the error: the video display component MUST be the top component of the panel, window or whereever it is added to. Otherwise it will throw the error
main vout display error: Failed to set on top
Furthermore, if you put anything over the component it will destroy the video output which won't work anymore!
Anyway, I don't know how the DirectMediaPlayer works in detail but VLCj has some weird behaviour... Maybe getSnapshot() needs a video display component but I'm not sure.
Re your not finishing program: you join to finish your own thread. This can't work because your thread "sleeps" until the other thread who is waited for has been terminated but as this is your own thread it "sleeps" and won't terminate.
You can test this behaviour with this short code in a main method:
System.out.println("Test start");
Thread.currentThread().join();
System.out.println("Test stop");
You will NEVER reach the "Test stop" statement.
Similar to this question, I would like to play a WAV file in a Java application - however, I would also like the ability to pause, resume, and restart the sound. I'm guessing I can restart by pausing and then just creating a new sound, but how would I pause and resume in the first place?
Note that my sound is ~15minutes and 152.8mb. If there is a way to do this with an MP3 file (same length, 20.8mb) that would be even better.
For playing WAV files, see the answers to this question:
Problem with Javas Audio Clips on frequent playback of beep sounds
For playing MP3s, you can use JLayer which is a fairly small jar (100k I think, maybe smaller) that you can bundle with your application.
Here's a fairly decent example of how to use it:
MP3.java (from How to play an MP3 file in Java)
/*************************************************************************
* Compilation: javac -classpath .:jl1.0.jar MP3.java (OS X)
* javac -classpath .;jl1.0.jar MP3.java (Windows)
* Execution: java -classpath .:jl1.0.jar MP3 filename.mp3 (OS X / Linux)
* java -classpath .;jl1.0.jar MP3 filename.mp3 (Windows)
*
* Plays an MP3 file using the JLayer MP3 library.
*
* Reference: http://www.javazoom.net/javalayer/sources.html
*
*
* To execute, get the file jl1.0.jar from the website above or from
*
* http://www.cs.princeton.edu/introcs/24inout/jl1.0.jar
*
* and put it in your working directory with this file MP3.java.
*
*************************************************************************/
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javazoom.jl.player.Player;
public class MP3 {
private String filename;
private Player player;
// constructor that takes the name of an MP3 file
public MP3(String filename) {
this.filename = filename;
}
public void close() { if (player != null) player.close(); }
// play the MP3 file to the sound card
public void play() {
try {
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
player = new Player(bis);
}
catch (Exception e) {
System.out.println("Problem playing file " + filename);
System.out.println(e);
}
// run in new thread to play in background
new Thread() {
public void run() {
try { player.play(); }
catch (Exception e) { System.out.println(e); }
}
}.start();
}
// test client
public static void main(String[] args) {
String filename = args[0];
MP3 mp3 = new MP3(filename);
mp3.play();
// do whatever computation you like, while music plays
int N = 4000;
double sum = 0.0;
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
sum += Math.sin(i + j);
}
}
System.out.println(sum);
// when the computation is done, stop playing it
mp3.close();
// play from the beginning
mp3 = new MP3(filename);
mp3.play();
}
}
Similar to this question, I would like to play a WAV file in a Java application - however, I would also like the ability to pause, resume, and restart the sound. I'm guessing I can restart by pausing and then just creating a new sound, but how would I pause and resume in the first place?
The javax.sound.sampled.Clip would be ideal for this, except for the fact that most implementations of Clip will not load more than 2 seconds of stereo, 16 bit, 44.1KHz sound! For that reason I developed BigClip. BigClip can handle sounds that are as big as the available memory.
Note that my sound is ~15minutes and 152.8mb. If there is a way to do this with an MP3 file (same length, 20.8mb) that would be even better.
Sure thing. As mentioned in the JavaSound tag info page..
MP3 decoding support
The Java Sound API does not support many formats of sampled sound internally. In a 1.6.0_24 Oracle JRE getAudioFileTypes() will generally return {WAVE, AU, AIFF}. An MP3 decoder at least, is close by. The mp3plugin.jar of the Java Media Framework supports decoding MP3s.
I currently use BigClip & the mp3plugin.jar Jar in the DukeBox player. Given 1024Meg of memory, it can easily load both the 17:12 of the 1812 Overture, & 15:38 of Bolero (the two longest tracks in my favorites play list). I mention 'both' since it will load the next track while playing the current one.
As an aside, beware of looking at code that mentions the sun.audio packages (mentioned in both linked threads). This package and/or it's classes might be moved or removed in the next release (at Oracle's discretion) & have not been necessary since Java 1.3.