Odtwarzanie dźwięku ze strumienia za pomocą C#

Czy istnieje sposób w C#, aby odtwarzać dźwięk (na przykład MP3) bezpośrednio z System.IO.Stream, który na przykład został przekierowany z WebRequest bez tymczasowego zapisywania danych na dysku?


Rozwiązanie z NAudio

Z Pomocą NAudio 1.3 możliwe jest:

    [15]} Załaduj plik MP3 z adresu URL do MemoryStream [16]}
  1. Konwertuj dane MP3 na dane Wave po całkowitym załadowaniu
  2. odtwarzanie danych wave korzystanie z klasy Waveout NAudio

Byłoby miło móc nawet odtworzyć w połowie załadowany plik MP3, ale wydaje się to niemożliwe ze względu na projekt biblioteki NAudio.

I to jest funkcja, która wykona pracę:

    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);
                    }
                }
            }
        }
    }
Author: Martin, 2008-10-09

9 answers

Edit: odpowiedź zaktualizowana, aby odzwierciedlić zmiany w ostatnich wersjach NAudio

Jest to możliwe przy użyciu NAudio otwartej biblioteki audio.NET, którą napisałem. Szuka kodeka ACM na komputerze, aby wykonać konwersję. Mp3FileReader dostarczany z NAudio obecnie oczekuje, że będzie w stanie zmienić pozycję w strumieniu źródłowym (buduje indeks ramek MP3 z przodu), więc nie jest odpowiedni do przesyłania strumieniowego przez sieć. Jednak nadal możesz użyć MP3Frame i AcmMp3FrameDecompressor zajęcia w NAudio do rozpakowywania strumieniowego MP3 w locie.

Opublikowałem artykuł na moim blogu wyjaśniający Jak odtwarzać strumień MP3 za pomocą NAudio. Zasadniczo masz jeden wątek pobierający ramki MP3, dekompresujący je i przechowujący w BufferedWaveProvider. Następnie odtwarza się kolejny wątek używając BufferedWaveProvider jako wejścia.

 47
Author: Mark Heath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-09 14:20:34

Klasa SoundPlayer może to zrobić. Wygląda na to, że wszystko, co musisz zrobić, to ustawić jego właściwość Stream na stream, a następnie wywołać Play.

edit
Nie sądzę jednak, że może odtwarzać pliki MP3; wydaje się ograniczony do .wav. Nie jestem pewien, czy jest coś w frameworku, co może odtwarzać plik MP3 bezpośrednio. Wszystko, co znajduję na ten temat, wiąże się albo z użyciem kontrolki WMP, albo z interakcją z DirectX.

 8
Author: OwenP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-08 20:47:07

Bass może to zrobić. Odtwarzanie z bajtu [] w pamięci lub pliku przelotowego, w którym zwracane są dane, dzięki czemu można odtwarzać, gdy tylko ma się wystarczającą ilość danych do rozpoczęcia odtwarzania..

 5
Author: slugster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 00:15:03

Lekko zmodyfikowałem źródło startera tematu, aby mogło teraz odtwarzać nie do końca załadowany plik. Tutaj jest (zauważ, że jest to tylko próbka i jest punktem, od którego należy zacząć; musisz zrobić tutaj obsługę wyjątków i błędów):

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);
            }
        }
    }
}
 3
Author: ReVolly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-15 23:54:27

NAudio owija API Waveoutxxx. Nie patrzyłem na źródło, ale jeśli NAudio wyświetla funkcję waveOutWrite () w sposób, który nie zatrzymuje automatycznie odtwarzania podczas każdego połączenia, powinieneś być w stanie zrobić to, co naprawdę chcesz, czyli zacząć odtwarzać strumień audio przed otrzymaniem wszystkich danych.

Użycie funkcji waveOutWrite () pozwala na "odczyt z wyprzedzeniem" i zrzut mniejszych fragmentów audio do kolejki wyjściowej-Windows automatycznie odtworzy te fragmenty bezproblemowo. Twój kod musiałby pobierać skompresowany strumień audio i konwertować go na małe kawałki audio WAV w locie; ta część byłaby naprawdę trudna - wszystkie biblioteki i Komponenty, jakie kiedykolwiek widziałem, wykonują konwersję MP3 NA WAV całego pliku na raz. Prawdopodobnie jedyną realną szansą jest użycie WMA zamiast MP3, ponieważ możesz pisać proste wrappery C# wokół multimedialnego SDK.

 1
Author: MusiGenesis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-19 22:25:02

Poprawiłem źródło zamieszczone w pytaniu, aby umożliwić korzystanie z Google TTS API, aby odpowiedzieć na pytanie tutaj :

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;
            }
        }
    }
}

Wywołanie z:

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);

Zakończ przez:

if (waiting)
    stop.Set();

Zauważ, że używam ParameterizedThreadDelegate w powyższym kodzie, a wątek zaczyna się od playThread.Start(10000);. 10000 oznacza maksymalnie 10 sekund dźwięku do odtworzenia, więc będzie musiał zostać zmodyfikowany, jeśli odtwarzanie strumienia trwa dłużej niż to. Jest to konieczne, ponieważ obecny wersja NAudio (v1.5.4.0) wydaje się mieć problem z określeniem, kiedy strumień jest gotowy do odtwarzania. Może to być naprawione w późniejszej wersji lub być może istnieje obejście, które nie poświęciłem czasu na znalezienie.

 1
Author: M.Babcock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:02

Spakowałem bibliotekę dekoderów MP3 i udostępniłem ją dla . NET jako mpg123.net .

Dołączone są próbki do konwersji plików MP3 do PCM i odczytu znaczników ID3 .

 1
Author: Daniel Mošmondor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-15 23:52:25

Zawsze używałem FMOD do takich rzeczy, ponieważ jest darmowy do użytku niekomercyjnego i działa dobrze.

To powiedziawszy, chętnie przełączyłbym się na coś mniejszego (FMOD to ~300k) i open-source. Super punkty bonusowe, jeśli jest w pełni zarządzane tak, że mogę skompilować / połączyć go z moim .exe i nie trzeba dbać o to, aby uzyskać przenośność na inne platformy...

(FMOD też ma przenośność, ale oczywiście potrzebujesz różnych binariów dla różnych platform)

 0
Author: Roman Starkov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-07 09:16:25

Nie próbowałem go z WebRequest, ale zarówno Windows Media Player ActiveX i Komponenty MediaElement (z WPF) są w stanie odtwarzać i buforować strumienie MP3.

Używam go do odtwarzania danych pochodzących ze strumieniaSHOUTcast i działało świetnie. Jednak nie jestem pewien, czy to zadziała w scenariuszu, który proponujesz.

 0
Author: Ramiro Berrelleza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-16 12:29:17