# MP3 Stream puffern vor abspielen wie die meisten Flash Player



## Looky (1. Juli 2008)

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


----------



## Thomas Darimont (2. Juli 2008)

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


----------



## Looky (2. Juli 2008)

moin tom. genau das habe ich schon mehrfaach versucht es aaber nie hinbekommen. vielleicht kannst du dir mal den code aangucken und was dazu schreiben...?


hier ist schon ein thread mit meinem cde..

http://www.tutorials.de/forum/java/306099-mp3-von-einem-server-streamen.html

thx


----------



## Thomas Darimont (3. Juli 2008)

Hallo,

schau mal hier:

```
/**
 * 
 */
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


----------



## Looky (4. Juli 2008)

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...?


----------



## Looky (4. Juli 2008)

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:

```
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:

```
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


----------



## Thomas Darimont (5. Juli 2008)

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


----------



## Looky (5. Juli 2008)

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.


----------



## CarbonCopy (6. August 2008)

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


----------



## Looky (7. August 2008)

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


----------



## Johannes7146 (2. September 2009)

Wenn ihr das ganze hier klärt profitieren wir da vielleicht sogar alle von und nicht nur ihr beiden...

EDIT: garnicht gesehen das der Thread schon länger ruht.

Aber evtl könntet ihr ja die Lösung des Problemes posten.


----------



## Fasibio (22. Oktober 2013)

Ich weiß der Thread ist alt aber da ich gerade eine Lösung gefunden habe (auf wenn sie böse ist )
dachte ich ich poste sie mal... 

ich erbe von FileInputStream und überschreibe die Methode reset.



```
public class PlayFileInputStream extends FileInputStream{

		public PlayFileInputStream(File file) throws FileNotFoundException {
			super(file);
			
		}

		public PlayFileInputStream(FileDescriptor fdObj) {
			super(fdObj);
		}

		public PlayFileInputStream(String name) throws FileNotFoundException {
			super(name);
		}

		@Override
		public synchronized void reset() throws IOException {
			// TODO Auto-generated method stub
			//super.reset();
		}
}
```

Ein Object dieser Klasse nehme ich jetzt und übergebe es dem Player.

Gruß 
Fabian


----------

