C#- Captura RTP Stream y envía al reconocimiento de voz
ffmpeg speech-recognition (3)
Lo que estoy tratando de lograr:
- Capturar RTP Stream en C #
- Reenviar esa secuencia al System.Speech.SpeechRecognitionEngine
Estoy creando un robot basado en Linux que tomará la entrada de micrófono, le enviará una máquina de Windows que procesará el audio usando el Reconocimiento de voz de Microsoft y enviará la respuesta nuevamente al robot. El robot puede estar a cientos de millas del servidor, por lo que me gustaría hacerlo a través de Internet.
Lo que he hecho hasta ahora
- Haga que el robot genere una secuencia RTP codificada en formato MP3 (otros formatos disponibles) utilizando FFmpeg (el robot se ejecuta en una Raspberry Pi ejecutando Arch Linux)
- Flujo capturado en la computadora del cliente usando el control VLC ActiveX
- Encontró que SpeechRecognitionEngine tiene los métodos disponibles:
- recognizer.SetInputToWaveStream ()
- recognizer.SetInputToAudioStream ()
- recognizer.SetInputToDefaultAudioDevice ()
- Busqué usar JACK para enviar la salida de la aplicación a la entrada de línea, pero me confundió por completo.
Con qué necesito ayuda:
Estoy atascado en cómo enviar la transmisión desde VLC al motor SpeechRecognitionEngine. VLC no expone la transmisión en absoluto. ¿Hay alguna manera de capturar una secuencia y pasar ese objeto de transmisión al motor SpeechRecognitionEngine? ¿O RTP no es la solución aquí?
Gracias de antemano por tu ayuda.
Creo que deberías mantenerlo más simple. ¿Por qué usar RTP y una biblioteca especial para capturar el RTP? ¿Por qué no simplemente tomar los datos de audio del Rasperry Pi y usar Http Post para enviarlos a su servidor?
Tenga en cuenta que System.Speech no es compatible con formato MP3. Esto podría ser útil - Ayuda con SAPI v5.1 SpeechRecognitionEngine siempre da el mismo resultado erróneo con C # . Para System.Speech, el audio debe estar en formato PCM, ULaw o ALaw. La forma más confiable de determinar qué formatos admite su reconocedor es interrogarlo con RecognizerInfo.SupportedAudioFormats.
Luego puede publicar los datos en su servidor (y usar ContentType = "audio / x-wav"). Hemos usado un formato Url como
http://server/app/recognize/{sampleRate}/{bits}/{isStereo}
para incluir los parámetros de audio en la solicitud. Envíe el archivo wav capturado en el cuerpo POST.
Una de las cosas con las que nos topamos es que tuvimos que agregar un encabezado de archivo WAV a los datos antes de enviarlo a System.Speech. Nuestros datos fueron PCM, pero no en formato WAV. Consulte https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ en caso de que necesite hacer esto.
Es un hilo viejo, pero fue útil para un proyecto en el que estaba trabajando. Pero tenía los mismos problemas que otras personas que trataban de usar el código de dgreenheck con una PC con Windows como fuente.
Obtuve FFMpeg trabajando con ese 0 cambios en el código usando los siguientes parámetros:
ffmpeg -ac 1 -f dshow -i audio="{recording device}" -ar 16000 -acodec pcm_s16le -f rtp rtp://{hostname}:{port}
En mi caso, el nombre del dispositivo de grabación era "Micrófono (Audio de alta definición Realtek)", pero usé lo siguiente para obtener el nombre del dispositivo de grabación:
ffmpeg -list_devices true -f dshow -i dummy
Después de mucho trabajo, finalmente conseguí que Microsoft.SpeechRecognitionEngine
aceptara una transmisión de audio WAVE. Este es el proceso:
En el Pi, tengo ffmpeg corriendo. Transmitir el audio usando este comando
ffmpeg -ac 1 -f alsa -i hw:1,0 -ar 16000 -acodec pcm_s16le -f rtp rtp://XXX.XXX.XXX.XXX:1234
En el lado del servidor, creo un UDPClient
y escucho en el puerto 1234. Recibo los paquetes en un hilo separado. Primero, quito el encabezado RTP (el formato del encabezado se explica aquí ) y escribo la carga en una transmisión especial. Tuve que usar la clase SpeechStreamer
descrita en la respuesta de Sean para que SpeechRecognitionEngine funcione. No funcionaba con una Memory Stream
estándar.
Lo único que tuve que hacer en el lado del reconocimiento de voz fue configurar la entrada a la transmisión de audio en lugar del dispositivo de audio predeterminado.
recognizer.SetInputToAudioStream( rtpClient.AudioStream,
new SpeechAudioFormatInfo(WAVFile.SAMPLE_RATE, AudioBitsPerSample.Sixteen, AudioChannel.Mono));
No he hecho pruebas extensas sobre él (es decir, dejándolo en streaming durante días y viendo si todavía funciona), pero puedo guardar la muestra de audio en SpeechRecognized
y suena genial. Estoy usando una frecuencia de muestreo de 16 KHz. Podría reducirlo a 8 KHz para reducir la cantidad de transferencia de datos, pero me preocuparé una vez que se convierta en un problema.
Debo mencionar que la respuesta es extremadamente rápida. Puedo decir una oración completa y obtener una respuesta en menos de un segundo. La conexión RTP parece agregar muy poca sobrecarga al proceso. Tendré que probar un punto de referencia y compararlo con solo usar la entrada MIC.
EDITAR: Esta es mi clase 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;
}
}