Portaudio: kein Ton
-
Hallo,
seit einiger Zeit versuche ich, via Portaudio dekodierte mp3 Streams, d.h. PCM-Daten, wiederzugeben. Leider hat sich hierbei bislang noch kein Erfolg eingestellt. Die Daten werden von diversen Servern via Socket empfangen und in 100k große Arrays aus unsigned char gespeichert. Daraufhin werden sie durch libmpg123 dekodiert, was anstandslos funktioniert (ich habe sie zeitweise via libsndfile in eine wav-Datei geschrieben, welche sich problemlos abspielen ließ). Nun möchte ich jedoch, dass die Daten von Portaudio wiedergegeben werden. Ich bekomme keinerlei Fehlermeldungen, jedoch ist kein Ton zu hören. Mein Code sieht folgendermaßen aus:
int PlayStream::myMemberCallback (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { unsigned empf = 0; int aktuell = 0; *in = 0; *out = 0; do { aktuell = recv (Socket, in + empf, 100000 - empf, 0); empf += aktuell; } while (empf < 100000); ret = mpg123_decode (mpg, in, 100000, out, 1000000, &size); std::cout << mpg123_plain_strerror (ret) << std::endl; output = out; return paContinue; }
Bislang habe ich bei den Portaudioeinstellungen immer paFramesPerBufferUnspeciefied verwendet. Da ich nicht weiß, wie ich mir die Framezahl berechnen kann, hielt ich dies für die beste Lösung. Dennoch weiß ich nicht, ob dies wirklich ein guter Weg ist, oder ob man die Anzahl nicht doch besser ausrechnen sollte (wenn ja, mit welche Formel?).
Ich habe Portaudio folgendermaßen initialisiert:outputParameters.device = Pa_GetDefaultOutputDevice (); outputParameters.channelCount = 2; outputParameters.sampleFormat = paUInt8; outputParameters.suggestedLatency = Pa_GetDeviceInfo (outputParameters.device) -> defaultLowOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; error = Pa_OpenStream (&stream, NULL, &outputParameters, rate, paFramesPerBufferUnspecified, paNoFlag, &PlayStream::myPaCallback, this);
Ich bin für jede Hilfe dankbar
-
Hallo!
Das Problem liegt in Zeile 12. Die Zuweisung
output = out
kopiert nicht den Inhelt, sondern nur den Zeiger. Den Inhalt kannst du z.B. mit man: memcpy kopieren.
Außerdem musst du eine bestimme Anzahl von Frames in denoutput
Buffer schreiben. Nämlich genauframeCount
. UmframeCount
in Bytes umzurechnen, musst duframeCount
mit der Anzahl der Kanäle (Mono = 1, Sereo = 2) und mit der Größe eines Samples (in Bytes: 8Bit = 1Byte, 16Bit = 2Byte) multiplizieren.
Außerdem solltest du im Callback keine zeitkritischen Sachen machen, wie das Laden und dekodieren des MP3-Streams.Bei mir sieht die Callback-Funktion z.B. so aus:
int pa::pacb(void *inputBuffer, void *outputBuffer, unsigned long int framesPerBuffer, PaTimestamp outTime, void *userData) { pa *patmp = static_cast<pa*>(userData); int bytesreq = framesPerBuffer*2*2; // stereo, 16bit while(bytesreq) { int bytes = patmp->input->read((char*)outputBuffer, bytesreq); if(bytes == -1) { memset(outputBuffer, bytesreq, 0); return 1; // stop playback } else { bytesreq -= bytes; outputBuffer = static_cast<void*> (static_cast<char*>(outputBuffer) + bytes); } } return 0; // continue playback }
patmp->input ist hier ein Ringbuffer, der von einem anderen Thread mit Daten gefüllt wird.
Edit: BTW: Das ist noch v18.
-
Hallo,
ich habe die Funktion folgendermaßen abgeändert:
int PlayStream::myMemberCallback (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { unsigned empf = 0; int aktuell = 0; do { aktuell = recv (Socket, in + empf, 100000 - empf, 0); empf += aktuell; } while (empf < 100000); ret = mpg123_decode (mpg, in, 100000, out, 10000000, &size); std::cout << mpg123_plain_strerror (ret) << std::endl; memcpy (output, out, size); return paContinue; }
Leider erhalte ich beim Aufruf von memcpy in Z. 10 ein Segmentation Fault. Muss ich evtl. noch Speicher für output reservieren oder wie lässt sich dieser Fehler sonst erklären?
mastercpp schrieb:
Außerdem solltest du im Callback keine zeitkritischen Sachen machen, wie das Laden und dekodieren des MP3-Streams.
Das weiß ich. Allerdings bin ich zur Zeit schon froh, wenn ich überhaupt etwas höre, weshalb ich solche "Optimierungen" erst nachher angehen möchte.
mastercpp schrieb:
Um frameCount in Bytes umzurechnen, musst du frameCount mit der Anzahl der Kanäle (Mono = 1, Sereo = 2) und mit der Größe eines Samples (in Bytes: 8Bit = 1Byte, 16Bit = 2Byte) multiplizieren.
Dies sollte sich doch erübrigen, wenn ich als Argument paFramesPerBufferUnspecified angebe, oder?
Danke für die Hilfe
-
Hallo!
basti33 schrieb:
Leider erhalte ich beim Aufruf von memcpy in Z. 10 ein Segmentation Fault. Muss ich evtl. noch Speicher für output reservieren oder wie lässt sich dieser Fehler sonst erklären?
Der Segmentation Fault kommt vermutlich daher, dass du mehr Daten nach
output
kopieren möchtest, als dort Platz reserviert ist. Wie oben schon gesagt, musst du genauframeCount
Frames nachoutput
kopieren (nicht mehr und nicht weniger).
Ich weiß leider nicht, wie die "Profis" das machen, aber ich glaube am einfachsten ist es, die vonmpg123_decode
dekodierten Frames in einen FIFO-Puffer zwischen zu speichern.basti33 schrieb:
mastercpp schrieb:
Um frameCount in Bytes umzurechnen, musst du frameCount mit der Anzahl der Kanäle (Mono = 1, Sereo = 2) und mit der Größe eines Samples (in Bytes: 8Bit = 1Byte, 16Bit = 2Byte) multiplizieren.
Dies sollte sich doch erübrigen, wenn ich als Argument paFramesPerBufferUnspecified angebe, oder?
paFramesPerBufferUnspecified
bedeutet nur, dass PortAudio selbst entscheidet, wie viele Frames er haben möchte.