Spielen Sie Audio aus einem Stream mit C # ab

99

Gibt es in C # eine Möglichkeit, Audio (z. B. MP3) direkt von einem System.IO.Stream abzuspielen , das beispielsweise von einer WebRequest zurückgegeben wurde, ohne die Daten vorübergehend auf der Festplatte zu speichern?


Lösung mit NAudio

Mit Hilfe von NAudio 1.3 ist Folgendes möglich:

  1. Laden Sie eine MP3-Datei von einer URL in einen MemoryStream
  2. Konvertieren Sie MP3-Daten nach dem vollständigen Laden in Wave-Daten
  3. Spielen Sie die Wellendaten mit der WaveOut -Klasse von NAudio ab

Es wäre schön gewesen, sogar eine halb geladene MP3-Datei abspielen zu können, aber dies scheint aufgrund des NAudio- Bibliotheksdesigns unmöglich zu sein .

Und dies ist die Funktion, die die Arbeit erledigt:

    public static void PlayMp3FromUrl(string url)
    {
        using (Stream ms = new MemoryStream())
        {
            using (Stream stream = WebRequest.Create(url)
                .GetResponse().GetResponseStream())
            {
                byte[] buffer = new byte[32768];
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
            }

            ms.Position = 0;
            using (WaveStream blockAlignedStream =
                new BlockAlignReductionStream(
                    WaveFormatConversionStream.CreatePcmStream(
                        new Mp3FileReader(ms))))
            {
                using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    waveOut.Init(blockAlignedStream);
                    waveOut.Play();                        
                    while (waveOut.PlaybackState == PlaybackState.Playing )                        
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }
Martin
quelle
4
Schön zu sehen, dass du es zum Laufen gebracht hast. Es wäre nicht zu viel Arbeit, es beim Streaming richtig wiederzugeben. Das Hauptproblem ist, dass der Mp3FileReader derzeit erwartet, die Länge im Voraus zu kennen. Ich werde eine Demo für die nächste Version von NAudio
Mark Heath
@ Mark Heath hast du das Problem bereits gelöst und die Demo in der aktuellen NAudio-Version hinzugefügt oder ist sie noch in deiner Pipline?
Martin
Angst noch nicht, obwohl mit Änderungen in NAudio 1.3 nicht zu viele Anpassungen erforderlich sind, um es zum Laufen zu bringen.
Mark Heath
Mark: Muss ich in NAudio Änderungen vornehmen, damit es funktioniert, weil ich gerade NAudio1.3 heruntergeladen habe, aber den obigen Code ohne Änderung akzeptiert, aber andererseits eine Ausnahme auslöst, die so etwas wie "ACM-Konvertierung nicht möglich" besagt.
Mubashar
Übrigens

Antworten:

56

Bearbeiten: Die Antwort wurde aktualisiert, um Änderungen in den letzten Versionen von NAudio widerzuspiegeln

Es ist möglich, die von mir geschriebene NAudio Open Source .NET- Audiobibliothek zu verwenden . Es sucht nach einem ACM-Codec auf Ihrem PC, um die Konvertierung durchzuführen. Der mit NAudio gelieferte Mp3FileReader erwartet derzeit eine Neupositionierung innerhalb des Quelldatenstroms (er erstellt einen Index von MP3-Frames im Voraus), sodass er nicht für das Streaming über das Netzwerk geeignet ist. Sie können die Klassen MP3Frameund AcmMp3FrameDecompressorin NAudio jedoch weiterhin verwenden, um gestreamte MP3-Dateien im laufenden Betrieb zu dekomprimieren.

Ich habe in meinem Blog einen Artikel veröffentlicht, in dem erklärt wird, wie ein MP3-Stream mit NAudio wiedergegeben wird . Im Wesentlichen haben Sie einen Thread, der MP3-Frames herunterlädt, dekomprimiert und in einem speichert BufferedWaveProvider. Ein anderer Thread wird dann mit dem BufferedWaveProviderals Eingabe wiedergegeben.

Mark Heath
quelle
3
Die NAudio- Bibliothek wird jetzt mit einer Beispielanwendung namens Mp3StreamingDemo ausgeliefert, die alles bieten sollte, was zum Live-Streaming einer MP3- Datei aus dem Netzwerk erforderlich ist.
Martin
Ist es möglich, Ihre Bibliothek zu verwenden, um Mikrofone / Leitungen live in ein Android-Gerät einzugeben?
Realtebo
10

Die SoundPlayer- Klasse kann dies tun. Es sieht so aus, als müssten Sie nur die Stream- Eigenschaft auf den Stream setzen und dann aufrufen Play.

bearbeiten
Ich glaube nicht, dass es MP3-Dateien abspielen kann; es scheint auf .wav beschränkt zu sein. Ich bin nicht sicher, ob es irgendetwas im Framework gibt, das eine MP3-Datei direkt abspielen kann. Alles, was ich dazu finde, beinhaltet entweder die Verwendung eines WMP-Steuerelements oder die Interaktion mit DirectX.

OwenP
quelle
1
Ich habe seit Jahren versucht, eine .NET-Bibliothek zu finden, die MP3-Codierung und -Decodierung durchführt, aber ich glaube nicht, dass sie existiert.
MusiGenesis
NAudio sollte die Arbeit für Sie erledigen - zumindest zum Entschlüsseln (siehe erster Beitrag)
Martin
4
SoundPlayer hat schlimme Probleme mit GC und spielt zufällig Müll ab, es sei denn, Sie verwenden die offizielle Microsoft-Problemumgehung (klicken Sie auf Problemumgehungen): connect.microsoft.com/VisualStudio/feedback/…
Roman
SoundPlayer + memoryStream hat einen Fehler beim Entwerfen. Wenn Sie das erste, zweite oder dritte Mal spielen, werden in der Mitte Ihres Audios seltsame Geräusche hinzugefügt.
bh_earth0
5

Bass kann genau das. Spielen Sie von Byte [] im Speicher oder von einem Through-File-Delegaten, bei dem Sie die Daten zurückgeben, damit Sie spielen können, sobald Sie über genügend Daten verfügen, um die Wiedergabe zu starten.

Slugster
quelle
3

Ich habe die Starterquelle des Themas leicht geändert, damit jetzt eine nicht vollständig geladene Datei abgespielt werden kann. Hier ist es (beachten Sie, dass es sich nur um ein Beispiel handelt und ein Ausgangspunkt ist; Sie müssen hier einige Ausnahmen und Fehler behandeln):

private Stream ms = new MemoryStream();
public void PlayMp3FromUrl(string url)
{
    new Thread(delegate(object o)
    {
        var response = WebRequest.Create(url).GetResponse();
        using (var stream = response.GetResponseStream())
        {
            byte[] buffer = new byte[65536]; // 64KB chunks
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                var pos = ms.Position;
                ms.Position = ms.Length;
                ms.Write(buffer, 0, read);
                ms.Position = pos;
            }
        }
    }).Start();

    // Pre-buffering some data to allow NAudio to start playing
    while (ms.Length < 65536*10)
        Thread.Sleep(1000);

    ms.Position = 0;
    using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
    {
        using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
        {
            waveOut.Init(blockAlignedStream);
            waveOut.Play();
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                System.Threading.Thread.Sleep(100);
            }
        }
    }
}
ReVolly
quelle
Hast du deinen Code getestet? Wenn ja, mit welcher Version von NAudio hat es funktioniert?
Martin
Außerdem sieht Ihr Code so aus, als sei er ziemlich fehleranfällig für Threading-Probleme. Sind Sie sicher, dass Sie wissen, was Sie tun?
Martin
1) Ja, ich habe es getan. 2) Mit der neuesten Version. 3) Es ist nur ein Proof of Concept, wie ich in meiner vorherigen Nachricht angegeben habe.
ReVolly
Wie definieren Sie "neueste Version"? Ist es Version 1.3 oder die Quelle bei Revision 68454?
Martin
@ Martin Entschuldigung, ich bin schon lange nicht mehr hier. Die von mir verwendete NAudio.dll hat Version 1.3.8.
ReVolly
2

Ich habe die in der Frage veröffentlichte Quelle optimiert, um die Verwendung mit der TTS-API von Google zu ermöglichen, um die Frage hier zu beantworten :

bool waiting = false;
AutoResetEvent stop = new AutoResetEvent(false);
public void PlayMp3FromUrl(string url, int timeout)
{
    using (Stream ms = new MemoryStream())
    {
        using (Stream stream = WebRequest.Create(url)
            .GetResponse().GetResponseStream())
        {
            byte[] buffer = new byte[32768];
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
        }
        ms.Position = 0;
        using (WaveStream blockAlignedStream =
            new BlockAlignReductionStream(
                WaveFormatConversionStream.CreatePcmStream(
                    new Mp3FileReader(ms))))
        {
            using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
            {
                waveOut.Init(blockAlignedStream);
                waveOut.PlaybackStopped += (sender, e) =>
                {
                    waveOut.Stop();
                };
                waveOut.Play();
                waiting = true;
                stop.WaitOne(timeout);
                waiting = false;
            }
        }
    }
}

Rufen Sie auf mit:

var playThread = new Thread(timeout => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(relatedLabel.Text), (int)timeout));
playThread.IsBackground = true;
playThread.Start(10000);

Beenden mit:

if (waiting)
    stop.Set();

Beachten Sie, dass ich das ParameterizedThreadDelegateim obigen Code verwende und der Thread mit gestartet wird playThread.Start(10000);. Der 10000 repräsentiert maximal 10 Sekunden Audio, das abgespielt werden muss. Daher muss er optimiert werden, wenn die Wiedergabe Ihres Streams länger dauert. Dies ist erforderlich, da die aktuelle Version von NAudio (v1.5.4.0) Probleme zu haben scheint, festzustellen, wann der Stream abgespielt wird. Möglicherweise wurde es in einer späteren Version behoben, oder es gibt eine Problemumgehung, für deren Suche ich mir nicht die Zeit genommen habe.

M.Babcock
quelle
1

NAudio umschließt die WaveOutXXXX-API. Ich habe mir die Quelle nicht angesehen, aber wenn NAudio die Funktion waveOutWrite () so verfügbar macht, dass die Wiedergabe nicht bei jedem Aufruf automatisch gestoppt wird, sollten Sie in der Lage sein, das zu tun, was Sie wirklich wollen, nämlich die Wiedergabe zu starten Audiostream, bevor Sie alle Daten erhalten haben.

Mit der Funktion waveOutWrite () können Sie "weiterlesen" und kleinere Audioblöcke in die Ausgabewarteschlange stellen. Windows spielt die Blöcke automatisch nahtlos ab. Ihr Code müsste den komprimierten Audiostream nehmen und ihn im laufenden Betrieb in kleine Teile von WAV-Audio konvertieren. Dieser Teil wäre wirklich schwierig - alle Bibliotheken und Komponenten, die ich je gesehen habe, führen eine MP3-zu-WAV-Konvertierung für jeweils eine ganze Datei durch. Wahrscheinlich besteht Ihre einzige realistische Chance darin, WMA anstelle von MP3 zu verwenden, da Sie einfache C # -Wrapper um das Multimedia-SDK schreiben können.

MusiGenesis
quelle
1

Ich habe die MP3-Decoder-Bibliothek verpackt und sie für .NET- Entwickler als mpg123.net verfügbar gemacht .

Enthalten sind die Beispiele zum Konvertieren von MP3-Dateien in PCM und zum Lesen von ID3- Tags.

Daniel Mošmondor
quelle
Toll! Ich freue mich darauf, es mir dieses Wochenende anzusehen.
Larry Smithmier
1

Ich habe es nicht von einem WebRequest versucht, aber sowohl der Windows Media Player ActiveX und das Mediaelement (von WPF ) Komponenten ist in der Lage zu spielen und MP3 - Streams Pufferung.

Ich verwende es, um Daten aus einem SHOUTcast- Stream abzuspielen, und es hat großartig funktioniert. Ich bin mir jedoch nicht sicher, ob es in dem von Ihnen vorgeschlagenen Szenario funktioniert.

Ramiro Berrelleza
quelle
0

Ich habe FMOD immer für solche Dinge verwendet, weil es für nichtkommerzielle Zwecke kostenlos ist und gut funktioniert.

Trotzdem würde ich gerne zu etwas wechseln, das kleiner (FMOD ist ~ 300k) und Open Source ist. Super Bonuspunkte, wenn es vollständig verwaltet wird, damit ich es mit meiner EXE-Datei kompilieren / zusammenführen kann und nicht besonders vorsichtig sein muss, um die Portabilität auf andere Plattformen zu gewährleisten ...

(FMOD bietet auch Portabilität, aber Sie benötigen offensichtlich unterschiedliche Binärdateien für unterschiedliche Plattformen.)

Roman Starkov
quelle