Problema con controles del JMF

Buenas tardes,
os escribo porque tengo un problema con el JMF (Java Media Framework) que no consigo resolver.
Estoy realizando un reproductor de vídeo y todo funciona bien excepto el tema de los controles.
Lo que hago simplemente es obtener un player con el "Manager.createRealizedPlayer(URL)". Tras esto obtengo el "VisualComponent" y el "ControlPanelComponent" y los añado al reproductor. Hasta aquí todo bien.
El tema es que quiero personalizar el "ControlPanelComponent", es decir, los botones (play, pause, etc), el volumen, la barra de progreso, etc.
Para esto, debo quitar el "ControlPanelComponent" de mi reproductor y añadir los controles manualmente.
Pero todos me devuelven "null" es decir, que el player no soporta estos controles.
Por ejemplo, suponiendo que "mediaPlayer" es mi el player, si hago un:
"Component volumen = mediaPlayer.getGainControl().getControlComponent()"
Me devuelve "null".
Esto sería correcto si el vídeo especificado en la URL no tuviera volumen, pero sí lo tiene!
Además, si utilizo el "ControlPanelComponent" sí que me aparece el volumen y puedo modificarlo. Cosa que no ocurre si el vídeo no tiene volumen.
Entonces, ¿qué es lo que pasa?
Por otro lado, no habiendo encontrado solución hasta el momento a este problema, me he dedicado a "crear manualmente" los controles. Por ejemplo, para la barra de progreso, he creado un "JSlider". Le he añadido un "ChangeListener" para que cuando mueva la barra se modifique el "MediaTime" del player (simplemente haciendo un "setMediaTime"). El problema es cómo conseguir que el "JSlider" se vaya moviendo según se va reproduciendo el vídeo. O lo que es lo mismo, cómo conseguir que el "JSlider" continuamente se actualice con el valor del "getMediaTime". ¿Hay alguna forma de hacerlo que no sea con Threads?
Un saludo y muchas gracias
Respuesta
1
Respecto al tema de actualizar el jslider, prueba utilizando las clases Timer y TimerTask, para que, cada cierto tiempo (un segundo por ejemplo) testees el mediaPlayer. getMediaTime() y lo apliques al value del jslider. Es un método un poco manual, pero funcionará.
Respecto a la cuestión de controlar el vídeo sin los controles estándar, tengo que buscar el código donde lo hice (ya me he peleado bastante con el JMF y lo tengo solucionado). No lo tengo en el PC desde donde escribo ahora, así que me llevará un poco más de tiempo.
Carlos, gracias por responder.
Lo de actualizar el JSlider, finalmente terminé metiendo Threads y la verdad es que me ha quedado bastante bien, pero lo del Timer quizás me sirva para otro componente.
En cuanto al volumen, conseguí algo parecido a lo que hacía con la barra de tiempos. Me resulta curioso que con el "Component volumen = mediaPlayer.getGainControl().getControlComponent();" me devolviera nulo y sin embargo haciendo un "GainControl volumen = mediaPlayer.getGainControl();" el control que me devuelve no es nulo. De hecho, gracias a esto puedo acceder a los métodos del GainControl tales como setLevel(float), getLevel, etc.
Por lo que veo, el Player sí que dispone del control, como es lógico, pero no es capaz de obtener el componente por separado.
De esta manera al final he podido personalizar más el control, así que parece que vale la pena.
El problema con el que me encuentro ahora es que los JSliders sólo permiten meter valores enteros "setValue(int)". Esto provoca saltos en la barra. En la de reproducción no me preocupa demasiado porque más o menos son saltos pequeños, pero en la del volumen, se va del mínimo al máximo. Voy a ver si pruebo un par de cosas y si no lo intentaré con una barra de progreso y ya está. De todas formas, si encuentras el código ése seguro que me puede venir muy bien.
Un saludo y muchas gracias!
Vale, ya solucioné lo de los JSliders. Sólo había que colocar un setMaximum bastante grande. El setMaximum lo que hace es poner el máximo de saltos que puede haber en el JSlider. Yo pensé que era el máximo valor, es decir, en mi caso el máximo volumen o la duración del vídeo.
Ahora me surge otro problema (joder, si es que no paran de salir problemas, je je), aunque no sé si ponerlo en otra pregunta o continuar aquí, según me digas...
De todas formas, lo pongo aquí y luego si no ya lo pongo en una pregunta aparte.
¿Hay alguna manera de capturar el primer frame de un vídeo? No encuentro forma. Necesito crear un thumnail del vídeo con el primer frame y lo único que encuentro es el createImage del VisualComponent, pero eso sólo me saca el hueco para poder poner la imagen...
Bueno, espero entonces al código ése si lo tienes por ahí.
Un saludo y gracias
Te paso esta clase para lo de sacar el primer frame (o el frame del segundo que quieras):
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.media.Buffer;
import javax.media.ConfigureCompleteEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.EndOfMediaEvent;
import javax.media.Manager;
import javax.media.MediaLocator;
import javax.media.Player;
import javax.media.PrefetchCompleteEvent;
import javax.media.RealizeCompleteEvent;
import javax.media.ResourceUnavailableEvent;
import javax.media.StopAtTimeEvent;
import javax.media.control.FrameGrabbingControl;
import javax.media.control.FramePositioningControl;
import javax.media.format.VideoFormat;
import javax.media.util.BufferToImage;
public class FrameAccess implements ControllerListener
{
Object waitObj = new Object();
boolean stateOK = true;
public static void main(String[] args){
File inFile = new File("c:/videos/a.avi");
File outFile;
int frameSeconds;
FrameAccess fa = new FrameAccess();
frameSeconds = 5;
outFile = new File("c:/videos/" + frameSeconds + ".jpg");
fa.run(inFile, frameSeconds, outFile);
}
public void run(File inFile, int segundoAextraer, File outFile){
try{
MediaLocator oLocator = new MediaLocator(inFile.toURL());
if(oLocator == null){
System.out.println("Error en el fichero de entrada");
}
System.out.println("Creando reproductor");
Manager.setHint(Manager.PLUGIN_PLAYER, Boolean.TRUE);
Player player = Manager.createRealizedPlayer(oLocator);
System.out.println("Reproductor realizado");
player.addControllerListener(this);
System.out.println("Reproductor creado: " + player);
FramePositioningControl fpc = (FramePositioningControl)player.getControl("javax.media.control.FramePositioningControl");
if(fpc == null){
System.out.println("El reproductor no soporta FramePositioningControl");
player.close();
}
System.out.println("FramePositioningControl..." + fpc);
FrameGrabbingControl fg = (FrameGrabbingControl)player.getControl("javax.media.control.FrameGrabbingControl");
player.prefetch();
System.out.println("prefetching");
if(!waitForState(player, Player.Prefetched)){
System.out.println("Error en prefetch del reproductor");
return;
}
Buffer buf = fg.grabFrame();
VideoFormat vf = (VideoFormat)buf.getFormat();
System.out.println("segundo a buscar=" + segundoAextraer);
System.out.println("frameRate=" + vf.getFrameRate());
int frameNumber = Math.round(segundoAextraer * vf.getFrameRate());
System.out.println("frame a extraer=" + frameNumber);
fpc.skip(frameNumber);
BufferToImage bufferToImage = new BufferToImage(vf);
Image im = bufferToImage.createImage(buf);
BufferedImage formatImg = new BufferedImage(160, 120, BufferedImage.TYPE_3BYTE_BGR);
// convert the image to a BufferedImage
Graphics g = formatImg.getGraphics();
g.drawImage(im, 0, 0, 160, 120, null);
try
{
Iterator writers = ImageIO.getImageWritersByFormatName("jpg");
ImageWriter writer = (ImageWriter)writers.next();
ImageOutputStream ios = ImageIO.createImageOutputStream(outFile);
//System.out.println("Out Stream Created ..." + ios);
writer.setOutput(ios);
writer.write(formatImg);
//System.out.println("Writintg the file");
g.dispose();
player.close();
ios.close();
System.out.println("END");
}
catch(IOException e)
{
System.out.println("Error :" + e);
}
}
catch(Exception e){
System.out.println("exception i " + e);
}
}
private boolean waitForState(Player p, int state ){
synchronized(waitObj){
try{
while(p.getState() < state && stateOK)
waitObj.wait();
}
catch (Exception e){
}
}
return stateOK;
}
public void controllerUpdate(ControllerEvent evt){
if(evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent){
synchronized(waitObj){
stateOK = true;
waitObj.notifyAll();
}
}
else if(evt instanceof ResourceUnavailableEvent){
synchronized(waitObj){
stateOK = false;
waitObj.notifyAll();
}
}
else if(evt instanceof EndOfMediaEvent || evt instanceof StopAtTimeEvent){
//tidyClose();
}
}
}
Muchísimas gracias Carlos. El código me viene perfecto. Además he descubierto que esta línea de código me resuelve la vida:
Manager.setHint(Manager.PLUGIN_PLAYER, Boolean.TRUE);
Con esto puedo acceder a los controles que antes no podía con el player (sin embargo al intentar crear un "processor" sí que me dejaba)
¿Qué significa exactamente? Por lo que me ha parecido entender, cuando creamos un Player se crea uno estándar de java con los controles básicos, pero al meter esa línea lo que cogemos son los controles que nos proporciona el códec. ¿Es algo así?
Por otro lado, al utilizar esa línea de código en el mío resulta que el getGainControl me devuelve null, que es lo mismo que me ocurría al utilizar el processor, pero que no me ocurría al utilizar el player sin esa línea.
¿Sabes por que podría ser?
Un saludo y muchas gracias de nuevo
Creo recordar que necesitaba esta línea para que funcionase tanto el framePositioningControl como el frameGrabbingControl. Si quieres más información, mira la documentación de la clase Manager, que explica (más o menos) qué hace el setHint si le pasas como hint la constante PLUGIN_PLAYER. Parece que activando el PLUGIN_PLAYER te permite actuar sobre los TrackControls a nivel de aplicación (que es tu caso).
Ya hace tiempo que no toco lo de JMF y algunos detalles los tengo olvidados.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas