Відтворюйте аудіо з потоку за допомогою C #


99

Чи є в C # спосіб відтворювати аудіо (наприклад, MP3) прямо з System.IO.Stream, який, наприклад, був повернутий з WebRequest, не зберігаючи дані тимчасово на диску?


Рішення з NAudio

За допомогою NAudio 1.3 можна:

  1. Завантажте MP3-файл з URL-адреси в MemoryStream
  2. Перетворення даних MP3 у хвильові дані після їх повного завантаження
  3. Відтворення даних з використанням хвильових NAudio класу WaveOut «пд.ш.

Було б непогано мати можливість навіть відтворити напівзавантажений MP3-файл, але це, здається, неможливо через дизайн бібліотеки NAudio .

І це функція, яка буде виконувати роботу:

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

4
приємно бачити, що ти це працює. Це не буде занадто багато роботи, щоб правильно грати під час трансляції. Основне питання полягає в тому, що в даний час Mp3FileReader очікує знати довжину заздалегідь. Я розглядаю, як додати демонстрацію для наступної версії NAudio
Mark Heath

@Mark Heath Ви вже вирішили проблему та додали демонстраційну версію у поточній версії NAudio чи вона все ще є у вашому каналі?
Мартін

ще не боїтесь, хоча зі змінами, внесеними в NAudio 1.3, він не зажадає занадто багато налаштувань, щоб працювати.
Марк Хіт

Позначити: Чи потрібно мені змінювати NAudio, щоб він працював, тому що я просто завантажив NAudio1.3, але він приймає вищезгаданий код без змін, але з іншого боку, викидаючи виняток, який говорить щось на кшталт "Перетворення ACM не можливе".
Мубашар

до речі, я намагаюся грати,
переходячи

Відповіді:


56

Редагувати: відповідь оновлено, щоб відобразити зміни в останніх версіях NAudio

Це можливо за допомогою аудіотехнічної бібліотеки з відкритим кодом NAudio, яку я написав. Він шукає кодек ACM на вашому ПК для перетворення. Mp3FileReader, що постачається разом з NAudio, розраховує, що він зможе повторно розміститись у вихідному потоці (він будує індекс кадрів MP3 вперед), тому не підходить для потокової передачі по мережі. Тим не менш, ви все одно можете використовувати класи MP3Frameта AcmMp3FrameDecompressorкласи в NAudio для декомпресії потокового MP3 на льоту.

Я опублікував у своєму блозі статтю, де пояснюється, як відтворювати MP3-потік за допомогою NAudio . По суті, у вас є один потік, який завантажує MP3-кадри, розпаковує їх і зберігає їх у BufferedWaveProvider. Потім інший потік відтворюється, використовуючи BufferedWaveProviderяк вхід.


3
Бібліотека NAudio зараз постачає з прикладом додаток під назвою Mp3StreamingDemo, який повинен забезпечити все, що потрібно для прямого перегляду MP3 з мережі.
Мартін

Чи можливо використовувати вашу бібліотеку для прямого введення мікрофона / лінії при введенні на пристрої Android?
realtebo

10

Клас SoundPlayer може це зробити. Схоже, все, що вам потрібно зробити, це встановити його властивість Stream на потік, а потім зателефонувати Play.

редагувати
Я не думаю, що він може відтворювати MP3-файли; видається обмеженим .wav. Я не впевнений, чи є в рамках щось, що може відтворювати MP3-файл безпосередньо. Все, про що я знаходжу, стосується використання WMP-контролю або взаємодії з DirectX.


1
Я вже багато років намагаюся знайти бібліотеку .NET, яка б кодувала та декодувала MP3, але не думаю, що вона існує.
MusiGenesis

NAudio повинен зробити роботу за вас - принаймні, для розшифровки (див. Перше повідомлення)
Мартін

4
SoundPlayer має неприємні проблеми з GC і випадково відтворюватиме сміття, якщо ви не скористаєтесь офіційним способом вирішення Microsoft (натисніть Обхідні шляхи): connect.microsoft.com/VisualStudio/feedback/…
Роман Старков

SoundPlayer + memoryStream має помилку по дизайну. коли ви граєте перший або третій раз, це додає дивного шуму посеред звуку.
bh_earth0

Обхідне посилання більше не працює, але це на Wayback Machine: web.archive.org/web/20120722231139/http://connect.microsoft.com/…
Forestrf

5

Бас може зробити саме це. Грайте з байта [] в пам'ять або через делегати файлів, через які ви повертаєте дані, так що ви можете грати, як тільки у вас буде достатньо даних, щоб почати відтворення ..


3

Я трохи змінив джерело запуску теми, тому тепер він може відтворювати неповно завантажений файл. Ось це (зауважте, що це просто зразок і це пункт, з якого слід почати; тут вам потрібно зробити деякий виняток та обробку помилок):

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

Ви перевірили свій код? Якщо так, то з якою версією NAudio це працювало?
Мартін

Крім того - ваш код виглядає так, що він досить схильний до проблем з ниткою. Ви впевнені, що знаєте, що робите?
Мартін

1) Так, я. 2) З останньою версією. 3) Це лише доказ концепції, про що я говорив у своєму попередньому повідомленні.
ReVolly

Як ви визначаєте "останню версію"? Це версія 1.3 чи джерело в редакції 68454?
Мартін

@Martin Вибачте, давно тут не було. NAudio.dll, який я використав, має версію 1.3.8.
ReVolly

2

Я переглянув джерело, розміщене у запитанні, щоб дозволити використання API Google TTS, щоб відповісти на це запитання тут :

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

Надіслати:

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

Закінчити з:

if (waiting)
    stop.Set();

Зауважте, що я використовую ParameterizedThreadDelegateв коді вище, і потік починається з playThread.Start(10000);. 10000 представляє максимум 10 секунд для відтворення аудіо, тому його потрібно буде налаштувати, якщо ваш потік займе більше часу, ніж для відтворення. Це необхідно, оскільки, здається, у поточній версії NAudio (v1.5.4.0) є проблеми, що визначають, коли потік буде відтворено. Це може бути зафіксовано в більш пізній версії або, можливо, є рішення, яке я не знайшов часу, щоб знайти.


1

NAudio обгортає API WaveOutXXXX. Я не дивився на джерело, але якщо NAudio відкриває функцію WaveOutWrite () таким чином, що автоматично не зупиняє відтворення під час кожного дзвінка, то ви повинні мати можливість робити те, що ви дійсно хочете, а саме - почати програвати аудіопотік, перш ніж ви отримаєте всі дані.

Використання функції WaveOutWrite () дозволяє "читати вперед" і скидати менші шматки аудіо у вихідну чергу - Windows автоматично відтворюватиме шматки безперебійно. Ваш код повинен взяти стиснений аудіопотік і перетворити його на невеликі шматки WAV-аудіо на ходу; ця частина була б дуже складною - всі бібліотеки та компоненти, які я коли-небудь бачив, здійснюють перетворення MP3 у WAV весь файл за один раз. Напевно, ваш єдиний реальний шанс - це зробити за допомогою WMA замість MP3, оскільки ви можете писати прості обгортки C # навколо мультимедійного SDK.



1

Я не пробував цього з WebRequest, але і Windows Media Player ActiveX, і MediaElement (від WPF ) здатні відтворювати та буферизувати MP3-потоки.

Я використовую його для відтворення даних, що надходять із потоку SHOUTcast, і це чудово працювало. Однак я не впевнений, чи спрацює це за запропонованим вами сценарієм.


0

Я завжди використовував FMOD для подібних речей, тому що він безкоштовний для некомерційного використання та працює добре.

З цього приводу я б із задоволенням перейшов на щось менше (FMOD ~ 300 кб) і з відкритим кодом. Супер бонусні бали, якщо ним повністю керувати, щоб я міг компілювати / об'єднати його з моїм .exe і не потрібно зайвий догляд, щоб отримати портативність на інші платформи ...

(FMOD теж робить портативність, але вам, очевидно, потрібні різні бінарні файли для різних платформ)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.