Crear tu Programa para Subtitular Videos con C#
Introducción
¿Alguna vez has querido subtitular tus propios videos pero te has encontrado con la barrera de no saber cómo hacerlo sin depender de costosas herramientas o servicios? Hoy, te enseñaré cómo puedes crear un programa totalmente gratuito en C# para subtitular videos, utilizando tecnologías accesibles y gratiutas.
Preparación del Entorno de Desarrollo
Para comenzar, necesitarás lo siguiente:
- Visual Studio: Nuestro ambiente de desarrollo donde escribiremos y ejecutaremos nuestro código.
- FFmpeg: Esta herramienta gratuita nos ayudará a extraer el audio del video, que es un paso crucial en el proceso de subtitulación.
- VOSK: Un modelo de reconocimiento de voz que utilizaremos para convertir nuestro audio en texto.
- Un video de prueba: Para demostrar cómo funciona nuestro subtitulador.
Puedes encontrar los enlaces para descargar FFmpeg y VOSK en la descripción del video.
Estructura del Proyecto
Una vez que tengas tu entorno configurado con las herramientas necesarias, el siguiente paso es estructurar tu aplicación. En este caso, he optado por una aplicación de consola debido a su simplicidad. La estructura del proyecto incluye:
- Carpeta FFmpeg: Aquí colocarás todos los archivos descargados de FFmpeg.
- Carpeta del modelo VOSK: Donde guardarás el modelo de lenguaje descargado.
- Paguete NuGet: Vosk.
- Clases Principales:
Program: La clase principal que inicia todo el proceso.Audio: Se encarga de la extracción del audio del video.Word: Utilizada para identificar las diferentes palabras.VoskResult: Convierte lo que devuelve VOSK en un array de palabras.Srt: Aquí se genera el archivo de subtítulos.VideoTranscriber: Coordina la extracción de audio y la generación de subtítulos.
Proceso de Subtitulación
El funcionamiento del programa es bastante directo:
- Extracción del Audio: Utilizando FFmpeg, extraemos el audio del video.
internal void ExtractAudio( string videoPath, string audioOutputPath)
{
var startInfo = new ProcessStartInfo
{
FileName = this.ffmpegPath, //ruta a ffmpeg.exe
Arguments = $"-i \"{videoPath}\" -vn -acodec pcm_s16le -ar 16000 -ac 1 \"{audioOutputPath}\"",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
using (var process = Process.Start(startInfo))
{
process.WaitForExit();
}
}
- Transcripción del Audio a Texto: Con el audio extraído, usamos VOSK para convertir el audio en texto. Durante este proceso, se maneja la transcripción para asegurar que los subtítulos se corten adecuadamente basados en el silencio detectado o la longitud del texto.
StringBuilder srtContent = new StringBuilder();
Vosk.Vosk.SetLogLevel(0);
Model model = new Model(this.modelPath); // ruta al modelo vosk
using (var rec = new VoskRecognizer(model, 16000))
{
rec.SetMaxAlternatives(0);
rec.SetWords(true);
using (var stream = File.OpenRead(audioFilePath))
{
byte[] buffer = new byte[4096];
int subtitleIndex = 1;
StringBuilder currentBlock = new StringBuilder();
float lastStart = 0;
float lastEnd = 0;
float silenceThreshold = 0.1f; // 100 milisegundos
VoskResult voskResult = new VoskResult();
while (stream.Read(buffer, 0, buffer.Length) > 0)
{
if (rec.AcceptWaveform(buffer, buffer.Length))
{
var result = rec.Result();
ProcessResults(voskResult, result, ref lastStart, ref lastEnd, ref subtitleIndex, ref currentBlock, ref srtContent, silenceThreshold);
}
else
{
Console.WriteLine("Partial Result: " + rec.PartialResult());
}
}
ProcessResults(voskResult, rec.FinalResult(), ref lastStart, ref lastEnd, ref subtitleIndex, ref currentBlock, ref srtContent, silenceThreshold);
if (currentBlock.Length > 0)
{
srtContent.Append(FormatSrtBlock(subtitleIndex, currentBlock.ToString().Trim(), lastStart, new Audio(this.ffmpegPath).GetAudioDurationInSeconds(audioFilePath)));
}
}
}
- Generación del Archivo SRT: Finalmente, el texto procesado se utiliza para crear un archivo de subtítulos en formato SRT, que es el estándar para muchos reproductores de video.
private void ProcessResults(VoskResult voskResult, string results, ref float lastStart, ref float lastEnd, ref int subtitleIndex, ref StringBuilder currentBlock, ref StringBuilder srtContent, float silenceThreshold)
{
var words = voskResult.VoskResultToWords(results);
foreach (var word in words)
{
// Comprobamos silencio y longitud de línea
if (word.Start - lastEnd > silenceThreshold || currentBlock.Length + word.Text.Length + 1 > this.maxLetters)
{
// Iniciar un nuevo bloque de subtítulo si hay silencio suficiente o si se excede el límite de caracteres
if (currentBlock.Length > 0)
{
srtContent.Append(FormatSrtBlock(subtitleIndex++, currentBlock.ToString(), lastStart, lastEnd));
currentBlock.Clear();
}
lastStart = word.Start;
}
currentBlock.Append($"{word.Text} ");
lastEnd = word.End;
}
}
//convertir currentBlock(result) a string y guardar en el fichero .srt
using (StreamWriter writer = new StreamWriter(this.srtPath, false, Encoding.UTF8))
{
writer.Write(result);
}
Código y Ejemplo Práctico
Para quienes deseen ver el código en detalle y seguir el proceso paso a paso, recomiendo pausar y revisar cada segmento del video donde se explica el código. Además, he compartido el código completo en mi GitHub para aquellos que prefieren descargarlo directamente y experimentar por su cuenta.
Conclusión
Crear un subtitulador de videos en C# es un proyecto accesible que no solo te permite aprender más sobre el manejo de archivos multimedia y el procesamiento de audio, sino que también te proporciona una herramienta extremadamente útil que puedes personalizar según tus necesidades.


