thanks so much in advance for helping me with this seemingly tiny thing - yet I can't figure it out. MP4 Video/audio playback works just fine, yet I can't set the position in the video.
Here's my stripped down code:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import javax.swing.JPanel;
import com.sun.jna.NativeLibrary;
import java.util.logging.Level;
import java.util.logging.Logger;
import uk.co.caprica.vlcj.binding.RuntimeUtil;
import uk.co.caprica.vlcj.player.base.ControlsApi;
import uk.co.caprica.vlcj.player.base.MediaApi;
import uk.co.caprica.vlcj.player.base.MediaPlayer;
import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent;
import uk.co.caprica.vlcj.player.component.EmbeddedMediaPlayerComponent;
import uk.co.caprica.vlcj.player.component.callback.FilledCallbackImagePainter;
import uk.co.caprica.vlcj.player.component.callback.FixedCallbackImagePainter;
import uk.co.caprica.vlcj.player.component.callback.ScaledCallbackImagePainter;
import uk.co.caprica.vlcj.player.embedded.EmbeddedMediaPlayer;
import uk.co.caprica.vlcj.player.renderer.RendererItem;
import uk.co.caprica.vlcjplayer.event.TickEvent;
import uk.co.caprica.vlcjplayer.view.action.mediaplayer.MediaPlayerActions;
public class TestClass extends JPanel {
private EmbeddedMediaPlayerComponent ourMediaPlayer;
TestClass(){
//NativeLibrary.addSearchPath(RuntimeUtil.getLibVlcLibraryName(), "C:\\Program Files\\VideoLAN\\VLC");
ourMediaPlayer = new EmbeddedMediaPlayerComponent();
/* Set the canvas */
Canvas c = new Canvas();
c.setBackground(Color.black);
c.setVisible(true);
/* Set the layout */
this.setLayout(new BorderLayout());
/* Add the canvas */
this.add(c, BorderLayout.CENTER);
this.setVisible(true);
this.add(ourMediaPlayer);
}
public void play() {
/* Play the video */
System.out.println("Starting...");
ourMediaPlayer.mediaPlayer().controls().setPosition((float) 0.5); // NOPE
ourMediaPlayer.mediaPlayer().media().play("/home/manfred/ExtraDisk/Work/BTL/Movement2022/walking.mp4"); // works
ourMediaPlayer.mediaPlayer().controls().stop(); // works
ourMediaPlayer.mediaPlayer().controls().setPosition((float) 0.5); //NOPE
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Logger.getLogger(TestClass.class.getName()).log(Level.SEVERE, null, ex);
}
ourMediaPlayer.mediaPlayer().controls().setPosition((float) 0.5); //NOPE
ourMediaPlayer.mediaPlayer().controls().setTime(2000); // NOPE
ourMediaPlayer.mediaPlayer().controls().start(); //works
//System.time.sleep(2);
System.out.println("Started!");
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Logger.getLogger(TestClass.class.getName()).log(Level.SEVERE, null, ex);
}
ourMediaPlayer.mediaPlayer().controls().stop(); // works
}
}
Playback via .mediaPlayer().media().play() works, so does start and stop via .mediaPlayer().controls().start() and .mediaPlayer().controls().stop().
What doesn't work is .mediaPlayer().controls().setTime(xx) and .mediaPlayer().controls().setPosition(xx), basically nothing happens.
What am I not doing right here? Is this a threading issue? Anyone have any working minimal examples?
Thanks again, any help is greatly appreciated!
It is not possible to use the API to set the time/position before playback has started.
LibVLC operates asynchronously for many operations. Just calling play() does not mean that playback has started, so setting the time/position immediately after play() is called will not (always) work.
There are at least two approaches you can use:
Wait for a media player "ready" event, and set the time/position (this will fire an event each time the media player is ready, so each time you play it, although you can write a one-shot listener that unregisters itself if you only want to do it the first time you play).
public static void main(String[] args) throws Exception {
MediaPlayer mediaPlayer = new MediaPlayerFactory().mediaPlayers().newMediaPlayer();
mediaPlayer.events().addMediaPlayerEventListener(new MediaPlayerEventAdapter() {
#Override
public void mediaPlayerReady(MediaPlayer mediaPlayer) {
mediaPlayer.controls().setTime(10000);
}
});
mediaPlayer.media().play("/home/movies/whatever.mp4");
Thread.currentThread().join();
}
With this first approach there is the small risk that you will see one or two video frames rendered before skipping occurs.
Use media options to set the start time (in seconds, including fractions of seconds like 10.5):
public static void main(String[] args) throws Exception {
MediaPlayer mediaPlayer = new MediaPlayerFactory().mediaPlayers().newMediaPlayer();
mediaPlayer.media().play("/home/movies/whatever.mp4", ":start-time=10");
Thread.currentThread().join();
}
Thanks to caprica's ingenious insights, this snippet actually works (don't know why, but it does - and that's all that matters for now):
ourMediaPlayer.mediaPlayer().media().play("/home/manfred/ExtraDisk/Work/BTL/Movement2022/walking.mp4"); // works
ourMediaPlayer.mediaPlayer().controls().stop(); // works
ourMediaPlayer.mediaPlayer().controls().start(); // works
ourMediaPlayer.mediaPlayer().controls().setTime(5000); // WORKS
Still a bit of a mystery, but I'll take it!
Related
I can't for the life of me figure out Exlusive Fullscreen. It keeps throwing 'Invalid display mode' no matter which of my display modes I try.
I've tried doing this as the official Java tutorials show, and as other people's tutorials show. I keep getting the same error thrown. The try-finally, as suggested in the official Java tutorials, gives me the same issues. I've already made sure I'm using my supported modes.
Could this have something to do with my laptop's display being overclocked from 75Hz to 100Hz? I have of course tried supplying my own DisplayMode with DisplayMode.REFRESH_RATE_UNKNOWN.
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.JFrame;
public class Main {
private static GraphicsEnvironment gfxEnv;
private static GraphicsDevice gfxDevice;
private static DisplayMode defaultDisplayMode;
public static void main(String[] args) {
init();
}
private static void init() {
gfxEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
gfxDevice = gfxEnv.getDefaultScreenDevice();
defaultDisplayMode = gfxDevice.getDisplayMode();
DisplayMode[] displayModes = gfxDevice.getDisplayModes();
DisplayMode dm1920x1080x75 = displayModes[displayModes.length - 1];
System.out.println(dm1920x1080x75);
JFrame window = new JFrame();
window.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
aMethodIWantToCallElseWhere();
}
});
window.setUndecorated(true);
window.setResizable(false);
if (gfxDevice.isFullScreenSupported()) {
gfxDevice.setFullScreenWindow(window);
if (gfxDevice.isDisplayChangeSupported())
gfxDevice.setDisplayMode(dm1920x1080x75);
}
}
public static void aMethodIWantToCallElseWhere() {
if (gfxDevice.isDisplayChangeSupported())
gfxDevice.setDisplayMode(defaultDisplayMode);
gfxDevice.setFullScreenWindow(null);
}
}
Console output:
1920x1080x32bpp#75Hz
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Invalid display mode
at java.desktop/sun.awt.Win32GraphicsDevice.setDisplayMode(Win32GraphicsDevice.java:452)
at Main.aMethodIWantToCallElseWhere(Main.java:48)
at Main$1.keyTyped(Main.java:33)
at...
This does enter fullscreen without any issues. But exiting it is where the problem appears. I've tried this in the suggested try-finally way, which throws the exact same error - 'Invalid display mode'.
I've played around with a bunch of different ideas, but I'm now out of them.
Am I missing something?
I'd like to avoid the try-finally, just so that I can change modes on user request. I hope that's possible.
EDIT
So I tried setting my display back from 100Hz to 75Hz. That solved it. How would I go about getting it running with an overclocked display that doesn't even appear in getDisplayModes?
I play sound effects (WAV files) in a game with javax.sound.sampled.Clip. However, the effect that has 2 seconds is not played entirely, only a part of it. If I add clip.loop(2) then the sound is played multiple times correctly, i.e. the whole 2 seconds. What is wrong and how to fix it?
package com.zetcode;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JOptionPane;
public enum SoundEffect {
EXPLODE("resources/explosion.wav"),
HITGROUND("resources/impact.wav"),
CANNON("resources/cannon.wav");
private Clip clip;
SoundEffect(String soundFileName) {
try {
URL url = this.getClass().getClassLoader().getResource(soundFileName);
try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url)) {
clip = AudioSystem.getClip();
clip.open(audioInputStream);
}
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
JOptionPane.showMessageDialog(null, "Cannot play sound", "Error",
JOptionPane.ERROR_MESSAGE);
}
}
public void play() {
if (clip.isRunning()) {
clip.stop();
}
clip.setFramePosition(0);
clip.start();
}
static void init() {
values();
}
}
Are you aware and accounting for the fact that when a clip is played (via start() method), the thread playing the code is a daemon? Unlike regular threads, a daemon thread will not prevent a Java program from closing. If your program ends before the sound has finished executing, the daemon status thread also ends even if it is not finished.
Are you trying to run the sample code SoundClipTest or SoundEffectDemo? It looks to me like SoundClipTest is going to exit as soon as the program executes the start() method, whereas the SoundEffectDemo will persist and allow the sound to finish playing.
** Edit 1 **
Am just now noticing that you have made your SoundEffect an enum. I've never seen that approach used before.
One approach I've used is to create a class called GameSound, and have its constructor preload each Clip individually and hold them in memory, ready for playback. Then, I create a method or methods for their playback.
gameSound.playExplosion();
The GameSound class would have instance variables for each sound effect.
private Clip explosion;
private Clip cannon;
And GameSound constructor would have:
URL url = this.getClass().getResource("resources/explosion.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
explosion = AudioSystem.getClip();
explosion.open(ais);
and GameSound would have method:
public void playExplosion()
{
if (explosion.isRunning()) {
explosion.stop();
}
explosion.setFramePosition(0);
explosion.start();
}
If you prefer to have the play method take an argument and use that on a switch structure to select which sound to play, that is fine, too, especially as it eliminates some code duplication.
This approach might work better (easy to add to GameSound as code gets more complex), and shouldn't be too difficult to code. I'd be curious to learn whether or not changing to this plan makes the problem go away or not.
** Edit 2 **
Sorry for mistakes in the above code. I fixed them when I made the following example. One thought, though, when I went to listen to the sound effects you linked, several of the explosions were kind of truncated to begin with. Have you played the sounds in another app (like Windows Groove Music, or from the website where you got them) and verified that they are different when you play them in your code?
Here is a quickie "GameSound". I downloaded the Explosion+2.wav file from your reference as it was longer than the others and has a clear ending, and am referencing that.
package stackoverflow.janbodnar;
import java.io.IOException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
public class GameSound {
private final Clip explosion;
public GameSound() throws LineUnavailableException,
IOException, UnsupportedAudioFileException
{
URL url = this.getClass().getResource(
"resources/Explosion+2.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
explosion = AudioSystem.getClip();
explosion.open(ais);
}
public void playExplosion()
{
if (explosion.isRunning()) {
explosion.stop();
}
explosion.setFramePosition(0);
explosion.start();
}
}
And here is a simple Swing button that calls GameSound.
package stackoverflow.janbodnar;
import java.io.IOException;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class GameSoundTest extends JFrame
{
private static final long serialVersionUID = 1L;
private final GameSound gameSound;
public GameSoundTest() throws LineUnavailableException,
IOException, UnsupportedAudioFileException
{
gameSound = new GameSound();
JButton button = new JButton("Play Explosion");
button.addActionListener(e -> gameSound.playExplosion());
getContentPane().add(button);
}
private static void createAndShowGUI()
{
JFrame frame;
try
{
frame = new GameSoundTest();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.setBounds(0, 0, 200, 200);
frame.setVisible(true);
}
catch (LineUnavailableException | IOException
| UnsupportedAudioFileException e)
{
e.printStackTrace();
}
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
This all assumes, of course, that there is a subfolder called "resources" with the explosion SFX in it.
Edit: Got it to work. My problem was I was using cmd to compile which exited the vm before the delay ended. Switched to jGrasp and the program worked as intended. Next I need to learn how to actually make a java applet to properly run on my computer. Thanks for your help everyone
I'm trying to set an alarm of sorts using java. I'd like to open a webpage after a set delay. The code below compiles and runs without errors or warnings but running the code does nothing. Just starts and stops the program. I have a feeling the issue arises from how I catch the exceptions but I'm not sure. I also am a little lost on what the actionPerformed() method does. Any help or insight is greatly appreciated
import java.awt.Desktop;
import java.net.URI;
import java.net.URISyntaxException;
import java.io.IOException;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.Timer;
public class YtAlarmTest
{
public static void main (String [] args)
{
String url = "https://stackoverflow.com/questions/ask";
int delay = 1000;
ActionListener task = new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
try
{
if (Desktop.isDesktopSupported())
{
Desktop.getDesktop().browse(new URI(url));
}
}
catch (URISyntaxException e)
{
System.out.println("exception");
}
catch (IOException e)
{
System.out.println("exceptio");
}
}
};
new Timer(delay, task).start();
}
}
Music works just fine when i click on the box, but music won't stop when I click on it again. Then if i click on the unchecked box again, the music plays again so 2 times at once! Please help me with stopping the music!
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {
InputStream inputStream = getClass().getResourceAsStream("panda - desiigner (donald trump remix).au");
AudioStream audioStream = null;
try {
audioStream = new AudioStream(inputStream);
} catch (IOException ex) {
Logger.getLogger(kekFrame.class.getName()).log(Level.SEVERE, null, ex);
}
int check;
if(jCheckBox1.isSelected() == true){
check = 1;
} else {
check = 0;
}
switch (check) {
case 1 : AudioPlayer.player.start(audioStream);
System.out.println("Music has started playing");
break;
case 0 : AudioPlayer.player.stop(audioStream);
System.out.println("Music has stopped playing");
break;
}
}
/**
* #param args the command line arguments
*/
// Variables declaration - do not modify
private javax.swing.JCheckBox jCheckBox1;
Assuming you have properly created a Clip, and can access both it and a boolean (isSelected, say) that reflects the state of the JCheckBox, the following simple code should work:
if (isSelected)
{
clip.setFramePosition(0);
clip.start();
}
else
{
clip.stop();
}
This can be included in the JCheckBox's ActionListener.
More information about using Clips can be found on the Java Tutorial's "Audio Thread" with specifics about the Clips here. There are clearer examples elsewhere if you do a search on how to use a Java Clip. The official Java Tutorial for audio emphasizes background and high-level concepts at the expense of practical examples, imho, which makes for difficult reading for those of us who are just starting out.
sun.audio.AudioPlayer is no longer supported! Even if it works on your PC, there is no guarantee it will work on other systems. Unfortunately, this is a situation where obsolete code examples live on in blogs and unofficial tutorials, which can happen a lot as languages evolve and the parties that wrote the tutorials don't update or maintain their posts.
In response to the OP's request, here is an example adapted from one of my JavaFX gui's. I don't use Swing anymore and am not keen to go back.
In the code where the JavFX Button is being built:
btnPlay = new Button("Play");
btnPlay.setOnAction(e -> handlePlay(e));
This calls the following method:
private void handlePlay(ActionEvent e)
{
if (!isPlaying)
{
clip.setFramePosition(0);
clip.start();
((Button)e.getSource()).setText("STOP");
isPlaying = true;
}
else
{
clip.stop();
((Button)e.getSource()).setText("PLAY");
isPlaying = false;
}
}
In this code, isPlaying is an instance variable that in this case merely tells us whether the button is on or off. The clip may very well play to its end and stop on its own while the button is still in a "playing" state. It would require wiring up a LineListener to have the button toggle back when the clip finishes playing.
Maybe you can adapt the above into something useful? It seems to me the choice of JCheckBox is dubious, and a JToggleButton might be a better choice. Examples of writing Listeners for Swing Buttons can be found here.
First question/post on here, hopefully I've done it right!
Using java, I need a method to somehow add audio files to a queue and play the next file once the last one has finished because at the minute they just play over the top of each other. I am using Audiosystem to play the sound files.
I thought of using an array to store the sound clips waiting to be played but got stumped and didn't know where to go from there.
Hopefully someone can help, thanks.
import javax.sound.midi.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
public class MidiPlayer{
public static void main(String[] args) {
try {
Sequencer sequencer = MidiSystem.getSequencer();
if (sequencer == null)
throw new MidiUnavailableException();
sequencer.open();
FileInputStream is = new FileInputStream("music.mid");
Sequence Seq = MidiSystem.getSequence(is);
sequencer.setSequence(Seq);
sequencer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
here is a sample code that shows you how to play MIDI files in your java program, hope it helps