MP3 Stream puffern vor abspielen wie die meisten Flash Player

Looky

Erfahrenes Mitglied
Hi@all,

die meisten Flashplayer (Youtube usw) haben einen Statusbalken an dem man sehen kann, wieviel der Datei bereits zwischengepuffert ist. Gibt es dafür auch eine Methodik in Java. Vielleicht eine lib oder ähnliches die das übernimmt?

Hintergrund:
Ich möchte MP3s in meiner Applikation abspielen die aber auf einem Webserver liegen. Bei einer langsammen Verbdindung hakelt das, deshalb soll erst gepuffert werden...

Danke
Christian
 
Hallo,

je nachdem welche MP3 Player Bibliothek du verwendest kannst du als Datenquelle einen Wrapper um den eigentlichen InputStream statt direkt den InputStream zu übergeben.
Dieser Wrapper übernimmt dann das Buffering. Dabei blockert dieser Anfangs beim lesen so lange bis genug Daten über einen separten Thread in den Buffer geladen wurden. Danach fängt der Player an aus diesem Buffer zu spielen. Parallel dazu wird der Buffer immer weiter vom separten Thread befüllt.

Gruß Tom
 
Hallo,

schau mal hier:
Java:
/**
 * 
 */
package de.tutorials;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javazoom.jl.player.Player;

/**
 * @author Tom
 * 
 */
public class Mp3PlayerWithBufferingExample {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		Player player = new Player(

		new AsynchronousBufferedInputStreamWrapper(new URL(
				"http://www.boysetsfire.org/downloads/DearGeorge.mp3")
				.openStream(), 10 * 96 * 1024)

		// new BufferedInputStream(
		// new
		// URL("http://www.boysetsfire.org/downloads/DearGeorge.mp3").openStream
		// ()
		// , 128 * 1024)

		);
		player.play();
		
		player.close();
	}

	static class AsynchronousBufferedInputStreamWrapper extends InputStream {

		volatile BufferedInputStream wrappedInputStream;
		volatile ConcurrentLinkedQueue<byte[]> dataQueue;
		volatile int overallBytesConsumed;
		volatile int overallBytesBuffered;
		volatile int bufferSizeInBytes;

		byte[] currentByteBuffer;
		int currentBufferPosition;
		ExecutorService executorService;

		volatile boolean eof = false;
		
		Runnable buffering = new Runnable() {
			public void run() {

				int maxDataQueueSize = 4;
				

				int currentBytesBuffered = overallBytesBuffered
						- overallBytesConsumed;

				while (!eof) {
					try {
						if (currentBytesBuffered < bufferSizeInBytes
								|| dataQueue.size() < maxDataQueueSize) {

							System.out.println("Buffering...");

							ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

							while (currentBytesBuffered < bufferSizeInBytes) {
								byte[] buffer = new byte[bufferSizeInBytes];
																
								int bytesRead = wrappedInputStream.read(buffer);

								if (bytesRead == -1) { // EOF
									System.out.println("EOF");
									eof = true;
									break;
								}

								byteArrayOutputStream.write(buffer, 0,
										bytesRead);

								overallBytesBuffered += bytesRead;

								currentBytesBuffered = overallBytesBuffered
										- overallBytesConsumed;

								System.out.println("Buffered: "
										+ currentBytesBuffered + " Current bytes buffered: " + currentBytesBuffered);
							}

							if (!eof) {
								dataQueue.add(byteArrayOutputStream
										.toByteArray());
								currentBytesBuffered = 0;
							}

						} else {
							TimeUnit.MILLISECONDS.sleep(5L);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				
				System.out.println("Finished Buffering");
			}
		};

		public AsynchronousBufferedInputStreamWrapper(InputStream inputStream,
				int bufferSizeInBytes) {
			this.wrappedInputStream = new BufferedInputStream(inputStream,
					bufferSizeInBytes);
			this.bufferSizeInBytes = bufferSizeInBytes;
			this.dataQueue = new ConcurrentLinkedQueue<byte[]>();

			this.executorService = Executors.newSingleThreadExecutor();
			executorService.execute(buffering);
		}

		@Override
		public int read() throws IOException {
			waitForCurrentByteBuffer();
			if (reachedEndOfStream()){
				return -1;
			}
			byte b = currentByteBuffer[currentBufferPosition];
			currentBufferPosition++;
			overallBytesConsumed++;
			return b & 0xFF;
		}

		private boolean reachedEndOfStream() {
			return overallBytesConsumed == overallBytesBuffered;
		}

		private void waitForCurrentByteBuffer() {
			if (currentByteBuffer == null
					|| currentBufferPosition > currentByteBuffer.length - 1) {
				currentByteBuffer = null;
				while (currentByteBuffer == null && !reachedEndOfStream()) {
					currentByteBuffer = dataQueue.poll();
					currentBufferPosition = 0;
					try {
						TimeUnit.MILLISECONDS.sleep(5L);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}

		@Override
		public void close() throws IOException {
			super.close();
			this.executorService.shutdownNow();
		}
	}
}

Gruß Tom
 
e man und ich dachte ich wäre ganz gut in java, die hälfte der dinge die du benutzt hast kenn ich gar nicht.

ja also bevor ich das einbaue werde ich mir jedes einzelne teil mal genau angucken... aber ey, perfekt, danke, bist defibnit8iv mein held!

Nur eine frage.

Was meinst du eigentlich mit der Zeile:

return b & 0xFF;

und warum tust du das? also b & 0xFF?

Christian

PS: Seh ich das richtig, dass du pufferst bis du den Puffer voll hast und nicht bis die Datei zu ende is..? und wenn der Puffer zuneige geht dann pufferst du weiter...?
 
mhm also ich habe da noch ein seltsammes problem.

Ich habe deinen Aufruf geändert und mache das nun mit BAsicPlayer, da dort die Pause Methode implementiert ist.

Siehe hier:
Java:
		BasicPlayer p = new BasicPlayer();
		p.open(new AsynchronousBufferedInputStreamWrapper(new URL(
				"http://www.boysetsfire.org/downloads/DearGeorge.mp3")
				.openStream(), 10 * 96 * 1024)
);
		p.play();

Dummerweise bekomme ich aber folgende Fehlermeldung:
Java:
Exception in thread "main" java.io.IOException: mark/reset not supported
	at java.io.InputStream.reset(InputStream.java:331)
	at org.tritonus.share.sampled.file.TAudioFileReader.getAudioFileFormat(TAudioFileReader.java:184)
	at javax.sound.sampled.AudioSystem.getAudioFileFormat(AudioSystem.java:985)
	at javazoom.jlgui.basicplayer.BasicPlayer.initAudioInputStream(Unknown Source)
	at javazoom.jlgui.basicplayer.BasicPlayer.initAudioInputStream(Unknown Source)
	at javazoom.jlgui.basicplayer.BasicPlayer.open(Unknown Source)
	at Sandkasten.Testplayer.main(Testplayer.java:31)

(z. 31 ist das p.open)

Könntest du mir dabei vielleicht nochmal helfen? Komm da echt nicht weiter.

Danke
Christian
 
Hallo,

Was meinst du eigentlich mit der Zeile:
return b & 0xFF;

Das brauche ich um die binäre Repräsentation der gelesenen Bytes beizubehalten. Beispiel:
Im Buffer steht an der aktuellen Position -1 -> b = -1

Die read Methode von InputStream gibt aber einen int zurück.
Wenn ich nur einfach dieses b mit -1 so zurück gehen lasse sieht die binäre Repräsentation als int so aus:
11111111111111111111111111111111
Wenn ich -1 als byte jedoch mit 0xFF verunde dann bekomme ich:
11111111

Damit beschränken wir den Wertebereich des gelesenen bytes b als int mit 0 >= b <= 255 bzw. -1 falls EOF erreicht.
Die JavaLayer Mp3 Lib verlangt die Daten in diesem Format.


PS: Seh ich das richtig, dass du pufferst bis du den Puffer voll hast und nicht bis die Datei zu ende is..? und wenn der Puffer zuneige geht dann pufferst du weiter...?
Nicht ganz, je nach Buffer Einstellung wird nur dann gebuffert, wenn die Anzahl der gebufferten Bytes unter eine bestimmte Marke fällt. Am Anfang
dauert es etwas länger bis der Buffer komplett aufgefüllt ist. Anschließend wird immer wieder nur noch so viel nachgeladen das der Buffer mit ausreichend
vielen bytes gefüllt ist, bis das EOF erreicht wurde.

Gruß Tom
 
aha, danke. ich werde wohl noch ein bisschen lesen müssen umn das zu verstehen was du da geschrieben hast.

Könntest du dir nochmal das problem mit der exception ansehen? Habe schon alles mögliche probiert.
 
hi

diesen fehler den du bekommst wenn du open mit einem inputstream öffen willst kommt von einem fehler in der org.tritonus.share library, wenn du das ganze mit einem file objekt öffnest dann kannst du das file abspielen. im falle den inputstreams wird in der library die reset methode aufgerufen und die wird in dem fall nicht supported

hab genau den selben fehler wie du ... ich arbeite gerade an einem fix

lg carboncopy
 
Moin,

ich bin auch schon etwas weiter und habe das Problem auch schon erkannt. Dummerweise komme ich auch nicht weiter.

Schrieb mir eine PN mit deiner Mail Adresse, vielleicht können wir da ein kleines Entwicklungsprojekt von machen.. Da könnte jeder von uns beiden von profitieren..;)

LG
Chriz
 

Neue Beiträge

Zurück