none
Свой клиент для ShoutCAST. Как передать поток? RRS feed

  • Вопрос

  • Доброго времени суток!
    Делаю свой очень простой клиент для ShoutCast. Хочу сделать чтобы всё что играет в колонках передавалось на сервер.
    Использую NAudio и NAudio.Lame (для кодирования). TcpClient для передачи данных. Довольно плохо разбираюсь в этом.
    Но Я смог авторизоваться на сервере, и создать точку, поток (или как ни правильно называются). Но при передачи буффера на сервер, тот ругался на не соответствия формату кодека. Стал кодировать в mp3. Но получилось только сохранить на диск а потом передавать на сервер, как сделать Stream перекодированных данных и послать на сервер не понял.
    Может кто разбирался с этим помогите кто что знает.

    P.S. Пакеты отправляет но слышно рывками. Скорее всего это разрывы между пакетами. Либо это заголовки (mp3 заголовки) каждого отправленного пакета. Надо как-то отправлять это цельным потоком без символов конца и начала файла возможно.

    using System;
    using System.Text;
    using NAudio;
    using System.IO;
    using NAudio.Wave;
    using NAudio.Wave.SampleProviders;
    using System.Net.Sockets;
     
    namespace Radio
    {
     
    	class Program
        {
            static BufferedWaveProvider waveProvider;
            static ShoutCastHelper netshout;
            static void Main(string[] args)
            {
                //создаем клиент
                netshout = new ShoutCastHelper("10.11.13.215", 4200);
                //пытаемся создать поток
                if (netshout.CreateStream())
                {
                    waveProvider = new BufferedWaveProvider(new WaveFormat());
     
                    //слушаем колонки по-умолчанию
                    var waveIn = new WasapiLoopbackCapture();
                    waveIn.DataAvailable += WaveIn_DataAvailable;
                    
                    //начинаем записывать
                    waveIn.StartRecording();
                }
     
                Console.ReadKey();
            }
     
            private static void WaveIn_DataAvailable(object sender, WaveInEventArgs e)
            {
                //пытаемся послать с колонок на сервер
     
                //чтобы это не было
                waveProvider.AddSamples(e.Buffer, 0, e.BytesRecorded);
     
                //делаем котопса из буфера
                byte[] buf = new byte[waveProvider.BufferLength];
                int o = waveProvider.Read(buf, 0, buf.Length);
                           
                //кодер. читаем исходный поток и записываем в файл
                var lam = new NAudio.Lame.LameMP3FileWriter("out.mp3", new WaveFormat(), NAudio.Lame.LAMEPreset.ABR_128);
                lam.Write(buf, 0, buf.Length);
                lam.Close();
     
                //читаем файл. что за велосипед?
                var fileBufMp3 = File.ReadAllBytes("out.mp3");
     
                //шлём на сервер
                netshout.SendSteam(fileBufMp3);
     
                //очистка
                waveProvider.ClearBuffer();
            }
        }
     
        public class ShoutCastHelper
        {
            string answerServer;
            public bool isConnect { get; set; }
     
            NetworkStream stream;
            TcpClient client;
            
     
            public ShoutCastHelper(string server, int port)
            {
                Console.WriteLine("Connection ...");
     
                
                client = new TcpClient(server, port);
     
                //посылаем пароль для авторизации точки #1
                Byte[] data = System.Text.Encoding.ASCII.GetBytes("123\n");
     
                // получем Stream для чтения и записи на сервер
                stream = client.GetStream();
     
                //посылаем пароль
                stream.Write(data, 0, data.Length);
     
                System.Threading.Thread.Sleep(50);  //ждём для подключения
     
                data = new Byte[256]; //генерирум новый буфер для ответа      
     
                Int32 bytes = stream.Read(data, 0, data.Length);
                answerServer = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
     
                //проверяем на авторизацию
                isConnect = answerServer.Contains("OK2");
                if (isConnect)
                    Console.WriteLine("Connect! WOW!");
                //мы прошли автризацию. Мы молодцы! Ђ  
     
            }
     
            public bool CreateStream()
            {
                if (!isConnect)
                    return false;
     
                try
                {
                    //назначаем параметры потока
                    string radioArgs = "icy-name:MY RADIO\n" +
                    "ice-url:127.0.0.1:4200\n" +
                    "ice-genre:BDSM\n" +
                    "ice-bitrate:128\n" +
                    "ice-private:0\n" +
                    "ice-public:1\n" +
                    "ice-description:This is My Radio\n" +
                    "content-type:audio/mp3\n\n";
     
                    var data = System.Text.Encoding.ASCII.GetBytes(radioArgs);
                    stream = client.GetStream();
                    stream.Write(data, 0, data.Length);
     
                    Console.WriteLine("Create Stream!");
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    return false;
                }
     
                return true;
            }
     
            public void SendSteam(byte[] buffer)
            {
                var nullData = System.Text.Encoding.ASCII.GetBytes(String.Empty);
                stream.Write(nullData, 0, nullData.Length);
                //послать к чёр ... на сервер
                stream.Write(buffer, 0, buffer.Length);
            }
            
        }
    }





    • Изменено Серпико 6 декабря 2016 г. 14:52 код
    6 декабря 2016 г. 14:47

Ответы

Все ответы

  • "Либо это заголовки (mp3 заголовки) каждого отправленного пакета", насколько я знаю, у формата MPEG нет общего файлового заголовка, есть заголовки отдельных кадров. Думаю проблема в задержке записи/ считывания файла. Есть ли в NAudio возможность сформировать mp3 в памяти без записи в файл?

     Вообще я считаю, правильное решение должно основываться на DirectShow и использовать:

    -свой исходный фильтр, получающий данные с аудио-выхода,

    -любой имеющийся фильтр-компрессор MP3 потока,

    -свой фильтр вывода, отправляющий данные на сервер.

    Реализации DirectShow под .Net есть, я не знаю, позволяют ли они создавать свои фильтры (а это здесь похоже понадобиться)

    8 декабря 2016 г. 5:40
  • Я не знаю как такоё делать. Но я решил немного переделать основу написал привязку к библиотеке libshout.dll (x86) Архив

    Но как мне даже элементарно передать файл на сервер не понятно.

    Есть ли в NAudio возможность сформировать mp3 в памяти без записи в файл?

    Возможно! Я нашёл способ. Но результат тот же. Я делаю как-то в корне не верно всё.

    Я понимаю что правильно использовать какой-нибудь DSP или фильтр как ты написал, но я хотел самый элементарный способ передачи хотя бы просто готовый mp3-файл. Ты знаешь как это сделать?



    16 декабря 2016 г. 17:50
  • Есть идея. Что если вместо

                var fileBufMp3 = File.ReadAllBytes("out.mp3");
     
                //шлём на сервер
                netshout.SendSteam(fileBufMp3);

    сделать так:

    int buffer_size=100;

    int result;//amount of bytes actually read

    byte[] buffer;//bytes read from file

    byte[] send_mas;//bytes to send into server

    FileStream fs=new FileStream("out.mp3");

    using(fs){

    while(true)

    {

    buffer=new byte[buffer_size];

    result=fs.Read(buffer,0,buffer_size);

    if(result<=0)break;//end of file

    send_mas=new byte[result];

    System.Array.Copy(buffer,send_mas,result);

    //шлём на сервер
    netshout.SendSteam(send_mas);

    } }

    параметр buffer_size нужно будет опытным путем подобрать оптимальный
    • Изменено VadimTagil 16 декабря 2016 г. 19:13
    16 декабря 2016 г. 19:09
  • Прости, я твой способ еще не попробовал. Но у меня наконец-то что-то получилось. Причём без прерываний и тому-подобное. Я просто читаю файл и передаю его. Но не слышу что именно сейчас воспроизводится. Ссылка на проект

        class Program
        {
            static Libshout icecast;
            static byte[] buff = new byte[4096];
            static int read;

            static void Main(string[] args)
            {
                string filename = "";
                if (args.Count() > 0)
                    filename = args[0];
                else return;

                icecast = new Libshout();
                icecast.setProtocol(0);
                icecast.setHost("127.0.0.1");
                icecast.setPort(8000);
                icecast.setPassword("hackme");
                icecast.setFormat(Libshout.FORMAT_MP3);
                icecast.setPublic(true);
                icecast.setName("radio");
                icecast.setMount("/live");
                icecast.open();

                //подключились
                if (icecast.isConnected())
                    Console.WriteLine("Connect!");
                else Console.WriteLine(icecast.GetError());

                //читаем файл
                BinaryReader reader = new BinaryReader(File.Open(filename, FileMode.Open));
                int total = 0;
                while (true)
                {
                    //читаем буфер
                    read = reader.Read(buff, 0, buff.Length);
                    total = total + read;

                    Console.WriteLine("nbsp; "+reader.BaseStream.Position);
                    //если прочитан не весь, то передаем
                    if (read > 0)
                    {
                        icecast.send(buff, read);    //пауза, синхронизация внутри метода
                    }
                    else break;  //уходим
                    
                }

                Console.WriteLine("Done!");
                Console.ReadKey(true);
                icecast.close();            
            }
        }

    Мне кажется правильнее всего сделать как-то так:

    Создать бесконечный поток (музыка в правильной кодировке), периодически обновлять его при каких-то событиях, посылать этот поток на сервер.

    Может как-то и иначе это делается. Но мне кажется без DirectShow тут можно обойтись, возможно все равно использовать библиотеки для аудио, но простыми средствами.





    16 декабря 2016 г. 19:49
  • вот тут нашел: http://stackoverflow.com/questions/19058530/change-format-from-wav-to-mp3-in-memory-stream-in-naudio

    у LameMP3FileWriter есть конструктор, который первым аргументом принимает Stream. можно ему попробовать скормить MemoryStream:

    public static byte[] ConvertWavToMp3(byte[] wavFile)
            {
    
                using(var retMs = new MemoryStream())
                using (var ms = new MemoryStream(wavFile))
                using(var rdr = new WaveFileReader(ms))
                using (var wtr = new LameMP3FileWriter(retMs, rdr.WaveFormat, 128))
                {
                    rdr.CopyTo(wtr);
                    return retMs.ToArray();
                }
    
    
            }

    17 декабря 2016 г. 8:05
  • Чего я только не перепробовал. Все равно задержки идут. Тут надо уметь работать с потоками правильно. Причём с теми что постоянно идут.
    27 декабря 2016 г. 16:18