Clase “Comunicador”:

	Esta clase principal del programa, es la que contiene el método main(). Se encarga de construir el entorno gráfico, detectar los dispositivos de captura, y lanzar los transmisores y los receptores cuando el usuario lo indique.

Constantes de la clase:
	private static final String TITULO = "Videoconferencia";
- Título de la ventana del programa.
	private static final int ANCHOTEXTO = 15;
- Ancho de los cuadros de texto.

Variables miembro:
	private JTextField ipLocal;
	private JTextField puertoLocal;
	private JTextField puertoVideoLocal;
	private JTextField ipRemota;
	private JTextField puertoRemoto;
	private JTextField puertoVideoRemoto;
- Cuadros de texto para introducir valores.
	private JButton IP;
- Botón para generar la dirección IP local automáticamente.
	private JButton Iniciar;
	private JButton Recibir;
	private JButton IniciarVideo;
	private JButton RecibirVideo;
- Botones para controlar el programa.
	private JLabel Estado;
- Etiqueta que almacena los nombres de los autores (nosotros).
	private Config config;
- Almacena la configuración del programa.
	private boolean iniciado;
	private boolean recibiendo;
	private boolean iniciadoVideo;
	private boolean recibiendoVideo;
- Flags que indican el estado del programa.
	private Vector deviceList;
	private Vector deviceVideoList;
- Vectores de dispositivos.
	private CaptureDeviceInfo captureAudioDevice;
	private CaptureDeviceInfo captureVideoDevice;
- Información de los dispositivos seleccionados.
	private JList listaAudioDevice;
	private JList listaVideoDevice;
- Listas de dispositivos.
	private TransmisorAudio tx;
	private ReceptorAudio rx;
	private TransmisorVideo tx2;
	private ReceptorVideo rx2;
- Transmisores y receptores.

Constructor de la clase:
	Inicializa las variables miembro y genera el entorno gráfico.
public Comunicador()
{
	System.out.println(Misc.ind() + "Inicializando variables...");
	captureAudioDevice = null;
	captureVideoDevice = null;
	//ponemos título a la ventana
	System.out.println(Misc.ind() + "Poniendo titulo...");
	setTitle(TITULO);
	//creamos una configuración con las IPs local y remota y los puertos
	System.out.println(Misc.ind() + "Creando configuracion...");
	Misc.aumentarInd();
	config = new Config();
	Misc.reducirInd();
	//creamos una lista de dispositivos de captura de audio (formato de audio lineal)
	System.out.println(Misc.ind() + "Creando lista de dispositivos de captura de audio...");
	Misc.aumentarInd();
	deviceList = CaptureDeviceManager.getDeviceList(new AudioFormat(AudioFormat.LINEAR, 44100, 8, 1));
	for (int i = 0; i < deviceList.size(); i++)
		System.out.println(Misc.ind() + ((CaptureDeviceInfo)deviceList.elementAt(i)).getName());
	//comprobamos que haya dispositivos de captura de audio
	//en caso de haberlo, seleccionamos el primero por defecto
	if (deviceList.size() > 0)
		captureAudioDevice = (CaptureDeviceInfo)deviceList.firstElement();
	else
	{
		captureAudioDevice = null;
		System.out.println(Misc.ind() + "No se han encontrado dispositivos de captura de audio...");
	}
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Lista de dispositivos de captura de audio creada...");
	//creamos una lista de dispositivos de captura de video (formato de video RGB)
	System.out.println(Misc.ind() + "Creando lista de dispositivos de captura de video...");
	Misc.aumentarInd();
	deviceVideoList = CaptureDeviceManager.getDeviceList(new VideoFormat(VideoFormat.RGB, new Dimension(352, 288), Format.NOT_SPECIFIED, null, Format.NOT_SPECIFIED));
	for (int i = 0; i < deviceVideoList.size(); i++)
		System.out.println(Misc.ind() + ((CaptureDeviceInfo)deviceVideoList.elementAt(i)).getName());
	//comprobamos que haya dispositivos de captura de video
	//en caso de haberlo, seleccionamos el primero por defecto
	if (deviceVideoList.size() > 0)
		captureVideoDevice = (CaptureDeviceInfo)deviceVideoList.firstElement();
	else
	{
		captureVideoDevice = null;
		System.out.println(Misc.ind() + "No se han encontrado dispositivos de captura de video...");
	}
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Lista de dispositivos de captura de audio creada...");

	//creamos el panel principal de la aplicación con su layout
	GridBagLayout gridBagLayout = new GridBagLayout();
	GridBagConstraints gbc;
	System.out.println(Misc.ind() + "Creando panel principal...");
	Misc.aumentarInd();
	JPanel p = new JPanel();
	p.setLayout(gridBagLayout);
	//creamos el subpanel de configuración local
	System.out.println(Misc.ind() + "Creando panel de configuracion local...");
	Misc.aumentarInd();
	JPanel panelConfiguracionLocal = crearPanelConfiguracionLocal();
	Misc.reducirInd();
	//aplicamos su configuración y lo insertamos en el panel principal
	System.out.println(Misc.ind() + "Insertando panel de configuracion local...");
	gbc = new GridBagConstraints();
	gbc.gridx = 0;
	gbc.gridy = 0;
	gbc.gridwidth = 2;
	gbc.anchor = GridBagConstraints.CENTER;
	gbc.fill = GridBagConstraints.BOTH;
	gbc.insets = new Insets(10, 5, 0, 0);
	((GridBagLayout)p.getLayout()).setConstraints(panelConfiguracionLocal, gbc);
	p.add(panelConfiguracionLocal);
	//creamos el subpanel de configuración remota
	System.out.println(Misc.ind() + "Creando panel de configuracion remota...");
	Misc.aumentarInd();
	JPanel panelConfiguracionRemota = crearPanelConfiguracionRemota();
	Misc.reducirInd();
	//aplicamos su configuración y lo insertamos en el panel principal
	System.out.println(Misc.ind() + "Insertando panel de configuracion remota...");
	gbc = new GridBagConstraints();
	gbc.gridx = 1;
	gbc.gridy = 1;
	gbc.weightx = 1.0;
	gbc.weighty = 1.0;
	gbc.anchor = GridBagConstraints.CENTER;
	gbc.fill = GridBagConstraints.BOTH;
	gbc.insets = new Insets(10, 5, 0, 0);
	((GridBagLayout)p.getLayout()).setConstraints(panelConfiguracionRemota, gbc);
	p.add(panelConfiguracionRemota);
	//creamos el subpanel de botones
	System.out.println(Misc.ind() + "Creando panel de botones...");
	Misc.aumentarInd();
	JPanel panelBotones = crearPanelBotones();
	Misc.reducirInd();
	//aplicamos su configuración y lo insertamos en el panel principal
	System.out.println(Misc.ind() + "Insertando panel de botones...");
	gbc = new GridBagConstraints();
	gbc.gridx = 1;
	gbc.gridy = 2;
	gbc.weightx = 1.0;
	gbc.weighty = 1.0;
	gbc.anchor = GridBagConstraints.CENTER;
	gbc.fill = GridBagConstraints.BOTH;
	gbc.insets = new Insets(10, 5, 0, 0);
	((GridBagLayout)p.getLayout()).setConstraints(panelBotones, gbc);
	p.add(panelBotones);
	//creamos el subpanel de etiquetas
	System.out.println(Misc.ind() + "Creando panel de etiquetas...");
	Misc.aumentarInd();
	JPanel panelEstado = crearPanelEstado();
	Misc.reducirInd();

	//aplicamos su configuración y lo insertamos en el panel principal
	System.out.println(Misc.ind() + "Insertando panel de etiquetas...");
	gbc = new GridBagConstraints();
	gbc.gridx = 1;
	gbc.gridy = 3;
	gbc.weightx = 1.0;
	gbc.weighty = 1.0;
	gbc.anchor = GridBagConstraints.CENTER;
	gbc.fill = GridBagConstraints.BOTH;
	gbc.insets = new Insets(10, 5, 0, 0);
	((GridBagLayout)p.getLayout()).setConstraints(panelEstado, gbc);
	p.add(panelEstado);
	Misc.reducirInd();
	//aplicamos la configuración al panel principal		System.out.println(Misc.ind() + "Insertando panel principal...");
	getContentPane().add(p);
	listaAudioDevice.addMouseListener(this);
	listaVideoDevice.addMouseListener(this);
	addWindowListener(this);
    pack();
    //hacemos visible el panel principal
    setVisible(true);
	System.out.println(Misc.ind() + "Tareas de inicializacion finalizadas...");
}

Métodos de la clase:
Main():
	Se ejecuta al iniciar el programa, se encarga de crear la clasel “Comunicador”.
public static void main(String[] args) { new Comunicador(); }

crearEtiqueta():
	Este método crea una etiqueta y la inserta en el panel:
private JLabel crearEtiqueta(JPanel p, int x, int y, String etiqueta)
{
	//creamos la etiqueta
	JLabel label = new JLabel(etiqueta);
	//configuramos la etiqueta
	System.out.println(Misc.ind() + "Insertando etiqueta " + etiqueta);
	GridBagConstraints gbc = new GridBagConstraints();
	gbc.gridx = x;
	gbc.gridy = y;
	gbc.weightx = 0.0;
	gbc.weighty = 0.0;
	gbc.anchor = GridBagConstraints.EAST;
	gbc.fill = GridBagConstraints.NONE;
	gbc.insets = new Insets(5, 5, 0, 5);
	((GridBagLayout)p.getLayout()).setConstraints(label, gbc);
	//insertamos la etiqueta
	p.add(label);
	//devolvemos la etiqueta
	return label;
}

crearCuadroTexto():
	Este método crea un cuadro de texto, añade un notificador y lo inserta en el panel:
private JTextField crearCuadroTexto(JPanel p, int x, int y, int a, String texto)
{
	//creamos el cuadro de texto
	JTextField t = new JTextField(a);
	t.addActionListener(this);
	//configuramos el cuadro de texto
	System.out.println(Misc.ind() + "Insertando cuadro de texto " + texto);
	GridBagConstraints gbc = new GridBagConstraints();
	gbc.gridx = x;
	gbc.gridy = y;
	gbc.weightx = 0.0;
	gbc.weighty = 0.0;
	gbc.anchor = GridBagConstraints.WEST;
	gbc.fill = GridBagConstraints.NONE;
	gbc.insets = new Insets( 5,5,0,5);
	((GridBagLayout)p.getLayout()).setConstraints(t, gbc);
	//insertamos el cuadro de texto
	p.add(t);
	//colocamos texto por defecto al cuadro de texto
	t.setText(texto);
	//devolvemos el cuadro de texto
	return t;
}

crearBoton():
	Este método crea un botón, añade un notificador y lo inserta en el panel:
private JButton crearBoton(JPanel p, int x, int y, String texto)
{
	//creamos el boton
	JButton t = new JButton(texto);
	t.addActionListener(this);
	//configuramos el boton
	System.out.println(Misc.ind() + "Insertando boton " + texto);
	GridBagConstraints gbc = new GridBagConstraints();
	gbc.gridx = x;
	gbc.gridy = y;
	gbc.weightx = 0.0;
	gbc.weighty = 0.0;
	gbc.anchor = GridBagConstraints.WEST;
	gbc.fill = GridBagConstraints.NONE;
	gbc.insets = new Insets( 5,5,0,5);
	((GridBagLayout)p.getLayout()).setConstraints(t, gbc);
	//insertamos el boton
	p.add(t);
	//devolvemos el boton
	return t;
}

crearPanelScroll():
	Este método crea un panel con scroll y lo inserta en el panel:
private void crearPanelScroll(JPanel p, int x, int y, JList lista)
{
	//creamos el panel scroll
	JScrollPane panelScrollAudioDevices = new JScrollPane(lista, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
	//configuramos el panel scroll
	System.out.println(Misc.ind() + "Insertando panel con scroll...");
	GridBagConstraints gbc = new GridBagConstraints();
	gbc.gridx = x;
	gbc.gridy = y;
	gbc.weightx = 0.0;
	gbc.weighty = 0.0;
	gbc.anchor = GridBagConstraints.CENTER;
	gbc.fill = GridBagConstraints.BOTH;
	gbc.insets = new Insets(10, 5, 0, 0);
	((GridBagLayout)p.getLayout()).setConstraints(panelScrollAudioDevices, gbc);
	p.add(panelScrollAudioDevices);
}

crearLista():
	Este método crea una lista de dispositivos de captura:
private JList crearLista(Vector deviceList, String prototipo)
{
	//generamos un vector de nombres de dispositivo
	Vector listaNombresAudioDevice = new Vector();
	for (int i = 0; i < deviceList.size(); i++)
	listaNombresAudioDevice.add(((CaptureDeviceInfo)deviceList.elementAt(i)).getName());
	//creamos la lista de dispositivos
	JList listaAudioDevice = new JList(listaNombresAudioDevice);
	listaAudioDevice.addKeyListener(this);
	//asignamos una máscara para el ancho de la lista
	listaAudioDevice.setPrototypeCellValue(prototipo);
	//devolvemos la lista
	return listaAudioDevice;
}

crearPanelConfiguracionLocal():
	Este método crea el panel para la configuración local del programa:
private JPanel crearPanelConfiguracionLocal()
{
	JPanel p = new JPanel();
	GridBagLayout gridBagLayout = new GridBagLayout();
	p.setLayout(gridBagLayout);
	//creamos una etiqueta para la IP local
	System.out.println(Misc.ind() + "Creando etiqueta de IP...");
	crearEtiqueta(p, 0, 0, "Dirección IP:");
	//creamos un cuadro de texto para la IP local
	System.out.println(Misc.ind() + "Creando cuadro de texto local...");
	ipLocal = crearCuadroTexto(p, 1, 0, ANCHOTEXTO, config.destino().ipLocal());
	//creamos un boton de búsqueda automática de la IP local
	System.out.println(Misc.ind() + "Creando boton de busqueda de IP...");
	IP = crearBoton(p, 2, 0, "Generar");
	//creamos una etiqueta para el puerto de transmisión de audio
	System.out.println(Misc.ind() + "Creando etiqueta de puerto...");
	crearEtiqueta(p, 0, 1, "Puerto de emisión de audio:");
	//creamos un cuadro de texto para el puerto de transmisión de audio
	System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
	puertoLocal = crearCuadroTexto(p, 1, 1, ANCHOTEXTO, config.destino().puertoAudioLocal());
	//creamos una etiqueta para el puerto de transmisión de video
	System.out.println(Misc.ind() + "Creando etiqueta de puerto de video...");
	crearEtiqueta(p, 0, 3, "Puerto de emisión de video:");
	//creamos un cuadro de texto para el puerto de transmisión de video
	System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
	puertoVideoLocal = crearCuadroTexto(p, 1, 3, ANCHOTEXTO, config.destino().puertoVideoLocal());
	//creamos una etiqueta para los dispositivos de captura de audio
	System.out.println(Misc.ind() + "Creando etiqueta de dispositivos de captura de audio...");
	crearEtiqueta(p, 0, 4, "Dispositivos de captura de audio:");
	//creamos una lista de nombres de dispositivos de captura de audio
	System.out.println(Misc.ind() + "Creando lista de nombres de dispositivos de captura de audio...");
	listaAudioDevice = crearLista(deviceList, "xxxxxxxxxxxxxxxxxxxxxxxxx");
	//creamos el panel con scroll para dispositivos de audio
	System.out.println(Misc.ind() + "Creando panel con scroll de dispositivos de audio...");
	crearPanelScroll(p, 0, 5, listaAudioDevice);
	//creamos una etiqueta para los dispositivos de captura de video
	System.out.println(Misc.ind() + "Creando etiqueta de dispositivos de captura de video...");
	crearEtiqueta(p, 1, 4, "Dispositivos de captura de video:");
	//creamos una lista de nombres de dispositivos de captura de video
	System.out.println(Misc.ind() + "Creando lista de nombres de dispositivos de captura de video...");
	listaVideoDevice = crearLista(deviceVideoList, "xxxxxxxxxxxxxxxxxxxxxxxxx");
	//creamos el panel con scroll para dispositivos de video
	System.out.println(Misc.ind() + "Creando panel con scroll de dispositivos de video...");
	crearPanelScroll(p, 1, 5, listaVideoDevice);
	//terminamos de configurar el panel
	System.out.println(Misc.ind() + "Poniendo titulo al panel...");
	TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Configuración local");
	p.setBorder(titledBorder);
	return p;
}

crearPanelConfiguracionRemota():
	Este método crea el panel para la configuración del equipo remoto:
private JPanel crearPanelConfiguracionRemota()
{
	JPanel p = new JPanel();
	GridBagLayout gridBagLayout = new GridBagLayout();
	p.setLayout(gridBagLayout);
	//creamos una etiqueta para para la dirección IP de destino
	System.out.println(Misc.ind() + "Creando etiqueta de IP remota...");
	crearEtiqueta(p, 0, 0, "Dirección IP:");
	//creamos un cuadro de texto para la IP del destino
	System.out.println(Misc.ind() + "Creando cuadro de texto de IP remota...");
	ipRemota = crearCuadroTexto(p, 1, 0, ANCHOTEXTO, config.destino().ipRemota());
	//creamos una etiqueta para para el puerto de audio de destino
	System.out.println(Misc.ind() + "Creando etiqueta de puerto de audio remoto...");
	crearEtiqueta(p, 0, 1, "Puerto de recepcion de audio:");
	//creamos un cuadro de texto para el puerto de audio remoto
	System.out.println(Misc.ind() + "Creando cuadro de texto de puerto de audio remoto...");
	puertoRemoto = crearCuadroTexto(p, 1, 1, ANCHOTEXTO, config.destino().puertoAudioRemoto());
	//creamos una etiqueta para para el puerto de video de destino
	System.out.println(Misc.ind() + "Creando etiqueta de puerto de video remoto...");
	crearEtiqueta(p, 0, 2, "Puerto de recepcion de video:");
	//creamos un cuadro de texto para el puerto de video remoto
	System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
	puertoVideoRemoto = crearCuadroTexto(p, 1, 2, ANCHOTEXTO, config.destino().puertoVideoRemoto());
	//terminamos de configurar el panel
	System.out.println(Misc.ind() + "Poniendo titulo al panel...");
	TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Configuración remota");
	p.setBorder( titledBorder);
	return p;
}

crearPanelBotones():
	Este método crea el panel con los botones de control del programa:
private JPanel crearPanelBotones()
{
	JPanel p = new JPanel();
	GridBagLayout gridBagLayout = new GridBagLayout();
	p.setLayout(gridBagLayout);
	//creamos un boton para iniciar la transmisión de audio
	System.out.println(Misc.ind() + "Creando boton de inicio transmision de audio...");
	Iniciar = crearBoton(p, 0, 0, "Iniciar transmisión audio");
	iniciado = false;
	//creamos un boton para iniciar la recepcion de audio
	System.out.println(Misc.ind() + "Creando boton de inicio recepcion de audio...");
	Recibir = crearBoton(p, 1, 0, "Iniciar recepción audio");
	recibiendo = false;
	//creamos un boton para iniciar la transmision de video
	System.out.println(Misc.ind() + "Creando boton de inicio transmision de video...");
	IniciarVideo = crearBoton(p, 0, 1, "Iniciar transmisión video");
	iniciadoVideo = false;
	//creamos un boton para iniciar la recepcion de video
	System.out.println(Misc.ind() + "Creando boton de inicio recepcion de video...");
	RecibirVideo = crearBoton(p, 1, 1, "Iniciar recepción video");
	recibiendoVideo = false;
	//terminamos de configurar el panel
	TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Panel de botones");
	p.setBorder(titledBorder);
	return p;
}

crearPanelEstado():
	Este método crea el panel de “Acerca de…”:
private JPanel crearPanelEstado()
{
	JPanel p = new JPanel();
	GridBagLayout gridBagLayout = new GridBagLayout();
	p.setLayout(gridBagLayout);

	System.out.println(Misc.ind() + "Creando etiqueta de estado...");
	Estado = crearEtiqueta(p, 0, 0, "José Antonio Jiménez Pérez / Jorge Barbero Pérez");
	//terminamos de configurar el panel
	TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Acerca de...");
	p.setBorder(titledBorder);
	return p;
}

actionPerformed():
	Este método gestiona los eventos del programa tales como la pulsación de los botones o el cambio de los cuadros de texto:
public void actionPerformed(ActionEvent event)
{
	//Obtenemos la fuente del evento
	Object source = event.getSource();
	//Buscamos de donde proviene el evento
	//si proviene del boton Iniciar
	if (source == Iniciar)
	{
		//comprobamos si ya está iniciado
		if (!iniciado)
		{
			//comprobamos si hay dispositivos de captura de audio
			if (captureAudioDevice != null)
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar transmision de audio...");
				Iniciar.setText("Detener transmisión audio");

				//iniciamos la transmisión
				iniciarTransmisionAudio();
				iniciado = true;
			}
			else
				System.out.println(Misc.ind() + "No hay ningun dispositivo de captura de audio...");
		}
		else
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de detener transmision de audio...");
			Iniciar.setText("Iniciar transmisión audio");
			//detenemos la transmisión
			detenerTransmisionAudio();
			iniciado = false;
		}
	}
	//si proviene del boton IniciarVideo
	else if (source == IniciarVideo)
	{
		//comprobamos si ya está iniciado
		if (!iniciadoVideo)
		{
			//comprobamos si hay dispositivos de captura de video
			if (captureVideoDevice != null)
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar transmision de video...");
				IniciarVideo.setText("Detener transmisión video");
				//iniciamos la transmisión
				iniciarTransmisionVideo();
				iniciadoVideo = true;
			}
			else
				System.out.println(Misc.ind() + "No hay ningun dispositivo de captura de video...");
		}
		else
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de detener transmision de video...");
			IniciarVideo.setText("Iniciar transmisión video");
			//detenemos la transmisión
			detenerTransmisionVideo();
			iniciadoVideo = false;
		}
	}
	//si proviene del boton Recibir
	else if (source == Recibir)
	{
		//comprobamos si ya está iniciado
		if (!recibiendo)
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar recepcion de audio...");
			Recibir.setText("Detener recepción audio");
			//iniciamos la recepcion
			iniciarRecepcionAudio();
		}
		else
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de detener recepcion de audio...");
			Recibir.setText("Iniciar recepción audio");
			//detenemos la recepcion
			detenerRecepcionAudio();
		}
		recibiendo = !recibiendo;
	}
	//si proviene del boton RecibirVideo
	else if (source == RecibirVideo)
	{
		//comprobamos si ya está iniciado
		if (!recibiendoVideo)
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar recepcion de video...");
			RecibirVideo.setText("Detener recepción video");
			//iniciamos la recepcion
			iniciarRecepcionVideo();
		}
		else
		{
			//actualizamos la etiqueta del boton
			System.out.println(Misc.ind() + "Se ha pulsado el boton de detener recepcion de video...");
			RecibirVideo.setText("Iniciar recepción video");
			//detenemos la recepcion
			detenerRecepcionVideo();
		}
		recibiendoVideo = !recibiendoVideo;
	}
	//si proviene del cuadro de texto ipLocal
	else if (source == ipLocal)
	{
		//actualizamos la configuracion
		config.destino().ipLocal(ipLocal.getText().trim());
		System.out.println(Misc.ind() + "IP local cambiada a " + config.destino().ipLocal());
	}
	//si proviene del cuadro de texto puertoLocal
	else if (source == puertoLocal)
	{
		//actualizamos la configuracion
		config.destino().puertoAudioLocal(puertoLocal.getText().trim());
		System.out.println(Misc.ind() + "Puerto local de audio cambiado a " + config.destino().puertoAudioLocal());
	}
	//si proviene del cuadro de texto puertoVideoLocal
	else if (source == puertoVideoLocal)
	{
		//actualizamos la configuracion
		config.destino().puertoVideoLocal(puertoVideoLocal.getText().trim());
		System.out.println(Misc.ind() + "Puerto local de video cambiado a " + config.destino().puertoVideoLocal());
	}
	//si proviene del cuadro de texto ipRemota
	else if (source == ipRemota)
	{
		//actualizamos la configuracion
		config.destino().ipRemota(ipRemota.getText().trim());
		System.out.println(Misc.ind() + "IP remota cambiada a " + config.destino().ipRemota());
	}
	//si proviene del cuadro de texto puertoRemoto
	else if (source == puertoRemoto)
	{
		//actualizamos la configuracion
		config.destino().puertoAudioRemoto(puertoRemoto.getText().trim());
		System.out.println(Misc.ind() + "Puerto remoto de audio cambiado a " + config.destino().puertoAudioRemoto());
	}
	//si proviene del cuadro de texto puertoVideoRemoto
	else if (source == puertoVideoRemoto)
	{
		//actualizamos la configuracion
		config.destino().puertoVideoRemoto(puertoVideoRemoto.getText().trim());
		System.out.println(Misc.ind() + "Puerto remoto de video cambiado a " + config.destino().puertoVideoRemoto());
	}
	//si proviene del boton de generar la IP
	else if (source == IP)
	{
		System.out.println(Misc.ind() + "Obteniendo IP local...");
		try
		{
			//Intentamos generar la dirección IP
			String host = InetAddress.getLocalHost().getHostAddress();
			//actualizamos el cuadro de texto implicado
			ipLocal.setText(host);
			//actualizamos la configuracion
			config.destino().ipLocal(host);
			System.out.println(Misc.ind() + "IP local cambiada a " + host);
		}
		//capturamos la excepcion
		catch(UnknownHostException e)
		{
			System.out.println(Misc.ind() + "Fallo al obtener la IP local...");
			ipLocal.setText("");
			config.destino().ipLocal("");
		}
	}
}

iniciarTransmisionAudio():
	Este método crea el transmisor de audio e inicia la transmisión:
private void iniciarTransmisionAudio()
{
	//creamos el transmisor de audio
	System.out.println(Misc.ind() + "Creando transmisor de audio");
	Misc.aumentarInd();
	tx = new TransmisorAudio(config.destino());
	Misc.reducirInd();
	//iniciamos el transmisor de audio
	System.out.println(Misc.ind() + "Iniciando transmisor de audio");
	Misc.aumentarInd();
	tx.start(captureAudioDevice);
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Transmisor de audio iniciado");
}

detenerTransmisionAudio():
	Este método destruye el transmisor de audio:
private void detenerTransmisionAudio()
{
	//detenemos el transmisor de audio
	System.out.println(Misc.ind() + "Destruyendo transmisor de audio");
	Misc.aumentarInd();
	tx.stop();
	Misc.reducirInd();
	tx = null;
	System.out.println(Misc.ind() + "Transmisor de audio destruido");
}

iniciarRecepcionAudio():
	Este método crea el receptor de audio e inicia la recepción:
private void iniciarRecepcionAudio()
{
	//creamos el receptor de audio
	System.out.println(Misc.ind() + "Creando receptor de audio");
	Misc.aumentarInd();
	rx = new ReceptorAudio(config.destino());
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Receptor de audio iniciado");
}

detenerRecepcionAudio():
	Este método destruye el receptor de audio:
private void detenerRecepcionAudio()
{
	//detenemos el receptor de audio
	System.out.println(Misc.ind() + "Destruyendo receptor de audio");
	Misc.aumentarInd();
	rx.close();
	rx = null;
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Receptor de audio destruido");
}

iniciarTransmisionVideo():
	Este método crea el transmisor de video e inicia la transmisión:
private void iniciarTransmisionVideo()
{
	//creamos el transmisor de video
	System.out.println(Misc.ind() + "Creando transmisor de video");
	Misc.aumentarInd();
	tx2 = new TransmisorVideo(config.destino());
	Misc.reducirInd();
	//iniciamos el transmisor de video
	System.out.println(Misc.ind() + "Iniciando transmisor de video");
	Misc.aumentarInd();
	tx2.start(captureVideoDevice);
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Transmisor de video iniciado");
}

detenerTransmisionVideo():
	Este método destruye el transmisor de video:
private void detenerTransmisionVideo()
{
	//detenemos el transmisor de video
	System.out.println(Misc.ind() + "Destruyendo transmisor de video");
	Misc.aumentarInd();
	tx2.stop();
	Misc.reducirInd();
	tx2= null;
	System.out.println(Misc.ind() + "Transmisor de video destruido");
}

iniciarRecepcionVideo():
	Este método crea el receptor de video e inicia la recepción:
private void iniciarRecepcionVideo()
{
	//creamos el receptor de video
	System.out.println(Misc.ind() + "Creando receptor de video");
	Misc.aumentarInd();
	rx2 = new ReceptorVideo(config.destino());
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Receptor de video iniciado");
}

detenerRecepcionVideo():
	Este método destruye el receptor de video:
private void detenerRecepcionVideo()
{
	//detenemos el receptor de video
	System.out.println(Misc.ind() + "Destruyendo receptor de video");
	Misc.aumentarInd();
	rx2.close();
	rx2 = null;
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Receptor de video destruido");
}

windowClosing():
	Este método se genera al cerrar la ventana del programa. Se encarga de guardar la configuración del programa y detener los transmisores y receptores que estén activos:
public void windowClosing(WindowEvent event)
{
	//comprobamos si se está transmitiendo o recibiendo algo
	//en caso afirmativo lo detenemos
	if (iniciado)
		detenerTransmisionAudio();
	if (recibiendo)
		detenerRecepcionAudio();
	if (iniciadoVideo)
		detenerTransmisionVideo();
	if (recibiendoVideo)
		detenerRecepcionVideo();
	//actualizamos la configuración a partir de los cuadros de texto
	config.destino().ipLocal(ipLocal.getText().trim());
	config.destino().puertoAudioLocal(puertoLocal.getText().trim());
	config.destino().puertoVideoLocal(puertoVideoLocal.getText().trim());
	config.destino().ipRemota(ipRemota.getText().trim());
	config.destino().puertoAudioRemoto(puertoRemoto.getText().trim());
	config.destino().puertoVideoRemoto(puertoVideoRemoto.getText().trim());
	//guardamos la configuración en disco
	System.out.println(Misc.ind() + "Guardando configuracion...");
	Misc.aumentarInd();
	config.write();
	Misc.reducirInd();
	System.out.println(Misc.ind() + "Configuracion guardada...");
	System.exit( 0);
}

keyReleased():
	Este método se ejecuta al soltar una tecla del teclado:
public void keyReleased(KeyEvent event)
{
	//llamamos al método que gestiona esto
	seleccion(event.getSource());
}

mouseClicked():
	Este método se ejecuta al hacer clic con el ratón:

public void mouseClicked(MouseEvent e)
{
	//llamamos al método que gestiona esto
	seleccion(e.getSource());
}

seleccion():
	Este método es llamado por los 2 anteriores (mouseClicked() y keyReleased()) y se encarga de gestionar esos 2 eventos:
private void seleccion(Object source)
{
	//si el evento proviene de la lista de dispositivos de audio
	if(source == listaAudioDevice)
	{
		//capturamos el índice
		int index = listaAudioDevice.getSelectedIndex();
		//si hay elementos, actualizamos el dispositivo de captura de audio
		if(index != -1)
		{
			System.out.println(Misc.ind() + "Actualizando el dispositivo de captura de audio...");
			captureAudioDevice = (CaptureDeviceInfo)deviceList.elementAt(index);
			Misc.aumentarInd();
			System.out.println(Misc.ind() + "Dispositivo de captura de audio cambiado a " + captureAudioDevice.getName());
			Misc.reducirInd();
		}
	}
	//si el evento proviene de la lista de dispositivos de video
	else if(source == listaVideoDevice)
	{	//capturamos el índice
		int index = listaVideoDevice.getSelectedIndex();
		//si hay elementos, actualizamos el dispositivo de captura de video
		if(index != -1)
		{
			System.out.println(Misc.ind() + "Actualizando el dispositivo de captura de video...");
			captureVideoDevice = (CaptureDeviceInfo)deviceVideoList.elementAt(index);
			Misc.aumentarInd();
			System.out.println(Misc.ind() + "Dispositivo de captura de video cambiado a " + captureVideoDevice.getName());
			Misc.reducirInd();
		}
	}
}
Métodos no utilizados:
public void windowClosed(WindowEvent event) {}
public void windowDeiconified(WindowEvent event) {}
public void windowIconified(WindowEvent event) {}
public void windowActivated(WindowEvent event) {}
public void windowDeactivated(WindowEvent event) {}
public void windowOpened(WindowEvent event) {}
public void keyPressed(KeyEvent event) {}
public void keyTyped(KeyEvent event) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}