C# - захват потока RTP и отправка на распознавание речи

Что я пытаюсь сделать:

  • Захват потока RTP в C#
  • Переслать этот поток в System.Speech.SpeechRecognitionEngine

Я создаю робота на основе Linux, который будет принимать входной сигнал с микрофона, отправлять ему компьютер с Windows, который будет обрабатывать звук с помощью распознавания речи Microsoft и отправлять ответ обратно роботу. Робот может находиться в сотнях миль от сервера, поэтому я хотел бы сделать это через Интернет.

Что я сделал до сих пор:

  • Пусть робот сгенерирует поток RTP, закодированный в формате MP3 (доступны другие форматы) с использованием FFmpeg (робот работает на Raspberry Pi с Arch Linux)
  • Захваченный поток на клиентском компьютере с использованием элемента управления VLC ActiveX
  • Обнаружено, что SpeechRecognitionEngine имеет доступные методы:
    1. recognizer.SetInputToWaveStream ()
    2. recognizer.SetInputToAudioStream ()
    3. recognizer.SetInputToDefaultAudioDevice ()
  • Посмотрел на использование JACK для отправки вывода приложения на линейный вход, но был совершенно смущен этим.

Что мне нужно помочь с:

Я застрял на том, как на самом деле отправить поток из VLC в SpeechRecognitionEngine. VLC не раскрывает поток вообще. Есть ли способ, которым я могу просто захватить поток и передать этот объект потока в SpeechRecognitionEngine? Или RTP не является решением здесь?

Заранее спасибо за помощь.

3 ответа

Решение

После большой работы я наконец-то получил Microsoft.SpeechRecognitionEngine принять аудио поток WAVE. Вот процесс:

На Пи у меня работает ffmpeg. Я передаю аудио с помощью этой команды

ffmpeg -ac 1 -f alsa -i hw:1,0 -ar 16000 -acodec pcm_s16le -f rtp rtp://XXX.XXX.XXX.XXX:1234

На стороне сервера я создаю UDPClient и слушаю порт 1234. Я получаю пакеты в отдельном потоке. Сначала я удаляю заголовок RTP ( формат заголовка объяснен здесь) и записываю полезную нагрузку в специальный поток. Я должен был использовать SpeechStreamer класс, описанный в ответе Шона, чтобы SpeechRecognitionEngine работал. Это не работает со стандартом Memory Stream,

Единственное, что мне нужно было сделать на стороне распознавания речи, это установить входной аудиопоток вместо аудиоустройства по умолчанию.

recognizer.SetInputToAudioStream( rtpClient.AudioStream,
    new SpeechAudioFormatInfo(WAVFile.SAMPLE_RATE, AudioBitsPerSample.Sixteen, AudioChannel.Mono));

Я не проводил обширные тесты на нем (то есть позволил ему транслироваться в течение нескольких дней и посмотрел, работает ли он по-прежнему), но я могу сохранить аудиосэмпл в SpeechRecognized и это звучит великолепно. Я использую частоту дискретизации 16 кГц. Я мог бы снизить частоту до 8 кГц, чтобы уменьшить объем передаваемых данных, но я буду беспокоиться об этом, как только это станет проблемой.

Я также должен отметить, что ответ очень быстрый. Я могу произнести целое предложение и получить ответ менее чем за секунду. Соединение RTP, кажется, добавляет очень мало накладных расходов к процессу. Я должен попробовать эталонный тест и сравнить его только с использованием входного сигнала MIC.

РЕДАКТИРОВАТЬ: Вот мой класс RTPClient.

    /// <summary>
    /// Connects to an RTP stream and listens for data
    /// </summary>
    public class RTPClient
    {
        private const int AUDIO_BUFFER_SIZE = 65536;

        private UdpClient client;
        private IPEndPoint endPoint;
        private SpeechStreamer audioStream;
        private bool writeHeaderToConsole = false;
        private bool listening = false;
        private int port;
        private Thread listenerThread; 

        /// <summary>
        /// Returns a reference to the audio stream
        /// </summary>
        public SpeechStreamer AudioStream
        {
            get { return audioStream; }
        }
        /// <summary>
        /// Gets whether the client is listening for packets
        /// </summary>
        public bool Listening
        {
            get { return listening; }
        }
        /// <summary>
        /// Gets the port the RTP client is listening on
        /// </summary>
        public int Port
        {
            get { return port; }
        }

        /// <summary>
        /// RTP Client for receiving an RTP stream containing a WAVE audio stream
        /// </summary>
        /// <param name="port">The port to listen on</param>
        public RTPClient(int port)
        {
            Console.WriteLine(" [RTPClient] Loading...");

            this.port = port;

            // Initialize the audio stream that will hold the data
            audioStream = new SpeechStreamer(AUDIO_BUFFER_SIZE);

            Console.WriteLine(" Done");
        }

        /// <summary>
        /// Creates a connection to the RTP stream
        /// </summary>
        public void StartClient()
        {
            // Create new UDP client. The IP end point tells us which IP is sending the data
            client = new UdpClient(port);
            endPoint = new IPEndPoint(IPAddress.Any, port);

            listening = true;
            listenerThread = new Thread(ReceiveCallback);
            listenerThread.Start();

            Console.WriteLine(" [RTPClient] Listening for packets on port " + port + "...");
        }

        /// <summary>
        /// Tells the UDP client to stop listening for packets.
        /// </summary>
        public void StopClient()
        {
            // Set the boolean to false to stop the asynchronous packet receiving
            listening = false;
            Console.WriteLine(" [RTPClient] Stopped listening on port " + port);
        }

        /// <summary>
        /// Handles the receiving of UDP packets from the RTP stream
        /// </summary>
        /// <param name="ar">Contains packet data</param>
        private void ReceiveCallback()
        {
            // Begin looking for the next packet
            while (listening)
            {
                // Receive packet
                byte[] packet = client.Receive(ref endPoint);

                // Decode the header of the packet
                int version = GetRTPHeaderValue(packet, 0, 1);
                int padding = GetRTPHeaderValue(packet, 2, 2);
                int extension = GetRTPHeaderValue(packet, 3, 3);
                int csrcCount = GetRTPHeaderValue(packet, 4, 7);
                int marker = GetRTPHeaderValue(packet, 8, 8);
                int payloadType = GetRTPHeaderValue(packet, 9, 15);
                int sequenceNum = GetRTPHeaderValue(packet, 16, 31);
                int timestamp = GetRTPHeaderValue(packet, 32, 63);
                int ssrcId = GetRTPHeaderValue(packet, 64, 95);

                if (writeHeaderToConsole)
                {
                    Console.WriteLine("{0} {1} {2} {3} {4} {5} {6} {7} {8}",
                        version,
                        padding,
                        extension,
                        csrcCount,
                        marker,
                        payloadType,
                        sequenceNum,
                        timestamp,
                        ssrcId);
                }

                // Write the packet to the audio stream
                audioStream.Write(packet, 12, packet.Length - 12);
            }
        }

        /// <summary>
        /// Grabs a value from the RTP header in Big-Endian format
        /// </summary>
        /// <param name="packet">The RTP packet</param>
        /// <param name="startBit">Start bit of the data value</param>
        /// <param name="endBit">End bit of the data value</param>
        /// <returns>The value</returns>
        private int GetRTPHeaderValue(byte[] packet, int startBit, int endBit)
        {
            int result = 0;

            // Number of bits in value
            int length = endBit - startBit + 1;

            // Values in RTP header are big endian, so need to do these conversions
            for (int i = startBit; i <= endBit; i++)
            {
                int byteIndex = i / 8;
                int bitShift = 7 - (i % 8);
                result += ((packet[byteIndex] >> bitShift) & 1) * (int)Math.Pow(2, length - i + startBit - 1);
            }
            return result;
        }
    }

Я думаю, вы должны держать это проще. Зачем использовать RTP и специальную библиотеку для захвата RTP? Почему бы просто не взять аудиоданные с Rasperry Pi и использовать Http Post, чтобы отправить их на ваш сервер?

Помните, что System.Speech не поддерживает формат MP3. Это может быть полезно - Справка по SAPI v5.1 SpeechRecognitionEngine всегда дает один и тот же неверный результат с C#. Для System.Speech аудио должно быть в формате PCM, ULaw или ALaw. Самый надежный способ определить, какие форматы поддерживает ваш распознаватель, - опросить его с помощью RecognizerInfo.SupportedAudioFormats.

Затем вы можете опубликовать данные на вашем сервере (и использовать ContentType = "audio/x-wav"). Мы использовали формат URL, как

http://server/app/recognize/{sampleRate}/{bits}/{isStereo}

включить аудио параметры в запрос. Отправьте захваченный файл WAV в теле сообщения POST.

Единственный недостаток, с которым мы столкнулись, - это добавить заголовок файла WAV к данным, прежде чем отправлять их в System.Speech. Наши данные были PCM, но не в формате WAV. Смотрите https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ если вам нужно это сделать.

Это старая ветка, но она была полезна для проекта, над которым я работал. Но у меня были те же проблемы, что и у некоторых других людей, пытающихся использовать код dgreenheck на ПК с Windows в качестве источника.

Получил FFMpeg, работающий с этими 0 изменениями в коде, используя следующие параметры:

ffmpeg -ac 1 -f dshow -i audio="{recording device}" -ar 16000 -acodec pcm_s16le -f rtp rtp://{hostname}:{port}

В моем случае имя записывающего устройства было "Микрофон (Realtek High Definition Audio)", но я использовал следующее, чтобы получить имя записывающего устройства:

ffmpeg -list_devices true -f dshow -i dummy
Другие вопросы по тегам