Opublikowano:

[Tutorial] .NET Video Converter

Xabe.FFmpeglogo

[Tutorial] .NET Video Converter z wykorzystaniem wrappera

Jakiś czas temu utknąłem próbując przekonwertować sporą ilość plików. A chciałem tylko nasłuchiwać na nowe pliki w danym folderze. Jestem leniwy więc oczywiście chciałem to maksymalnie zautomatyzować.
Ten tutorial demonstruje prostą implementacje konwertera wideo używając ffmpeg’a opakowanego przez Xabe.FFmpeg.
Aplikacja będzie napisana w .NET Core 2.0 ale rozwiązanie zadziała zarówno dobrze w zwykłym .NET Frameworku czy z wykorzystaniem GUI.

Kroki są proste:

  1. Aplikacja pobiera wszystkie pliki z folderu zawierającego pliki wideo
  2. Konwertuje wszystkie pliki nie-mp4 do mp4

Utworzenie projektu

Najpierw stworzyłem nowy projekt w .NET Core 2.0. Utworzenie projektu MyVideoConverter

Skoppiowałem wszystkie pliki który zamierzam przetworzyć do “C:\movies”. Folder z plikami wideo

Następnie utworzyłem metodę pobierającą wszystkie pliki z podanego folderu (“C:\movies” w tym przypadku) pomijając pliki mp4 (ze względu na to że jest to format docelowy i nie chciałbym konwertować ponownie tych plików do mp4). Run().GetAwaiter().GetResult() w metodzie Main pozwoli mi użyć await które będzie pomocne później.

using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace MyVideosConverter
{
class Program
{
static void Main(string[] args)
{
Run().GetAwaiter().GetResult();
}
private static async Task Run()
{
Queue filesToConvert = new Queue(GetFilesToConvert("C:\\movies"));
await Console.Out.WriteLineAsync($"Find {filesToConvert.Count()} files to convert.");
}
private static IEnumerable GetFilesToConvert(string directoryPath)
{
//Return all files excluding mp4 because I want convert it to mp4
return new DirectoryInfo(directoryPath).GetFiles().Where(x => x.Extension != ".mp4");
}
}
}

Instalacja Xabe.FFmpeg

Następna rzeczą jest instalacja Xabe.FFmpeg. Jego dokumentacja jest dostępna tutaj.

Użyłem do tego paczki NuGet.

PM> Install-Package Xabe.FFmpeg

Xabe.FFmpeg do działania wymaga plików FFmpega. Najprostszym sposobem ich uzyskania jest ściągnięcie ich podczas działania programu. Wystarczy dodać

using Xabe.FFmpeg;

...i tego na końcu metody Run().
//Set directory where app should look for FFmpeg 
FFmpeg.ExecutablesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FFmpeg");
//Get latest version of FFmpeg. It's great idea if you don't know if you had installed FFmpeg.
await FFmpeg.GetLatestVersion();

Wywołanie konwersji

Dodajmy nową metodę obsługującą konwersję
private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Zapis pliku do tego samego folderu ze zmienionym rozszerzeniem
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
await Conversion.ToMp4(fileToConvert.FullName, outputFileName).Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}

I linę wywołującą tą metodę w Run(). Dodatkowo dodamy Console.In.ReadLine() żeby poczekać z zamknięciem programu do zobaczenia przez nas wiadomości w konsoli. Teraz ta metoda powinno wyglądać tak:

private static async Task Run()
{
Queue filesToConvert = new Queue(GetFilesToConvert("C:\\movies"));
await Console.Out.WriteLineAsync($"Find {filesToConvert.Count()} files to convert.");
//Set directory where the app should look for FFmpeg executables.
FFmpeg.ExecutablesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FFmpeg");
//Get latest version of FFmpeg. It's great idea if you don't know if you had installed FFmpeg.
await FFmpeg.GetLatestVersion();
//Run conversion
await RunConversion(filesToConvert);
Console.In.ReadLine();
}

Po wciśnięciu F5 wszystko powinno skompilować się bez problemów i powinno otworzyć się okienko konsoli. Po kilku chwilach powinniśmy mieć informację w konsoli. Zrzut z konsoli bez loga

W folderze z plikami znajdziemy teraz kilka plików więcej z roszerzeniem mp4. (Proszę wybaczcie mi że liczba plików nie zgadza się z tą na samym początku, wprowadziłem w międzyczasie małe zmiany). Folder z plikami wideo po konwersji

Więcej informacji

Dobrze więc. Wszystko zadziałała ale co z logami? Mamy tylko informację o tym że coś została przekonwertowane.

Teraz dodamy trochę wartościowych informacji. Najlepszym sposobem jest nasłuchiwanie na zmiany statusu konwersji z ffmpeg'a. Na szczęście Xabe.FFmpeg udostępnia nam zdarzenie do którego możemy się podpiąć. Zmodyfikujmy trochę metodę RunConversion

private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Save file to the same location with changed extension
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
//SetOverwriteOutput to overwrite files. It's useful when we already run application before
var conversion = Conversion.ToMp4(fileToConvert.FullName, outputFileName).SetOverwriteOutput(true);
//Add log to OnProgress
conversion.OnProgress += async (sender, args) =>
{
//Show all output from FFmpeg to console
await Console.Out.WriteLineAsync($"[{args.Duration}/{args.TotalLength}][{args.Percent}%] {fileToConvert.Name}");
};
//Start conversion
await conversion.Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}

Po zmianach konsola pokazuje o wiele bardziej wartościowe informację zawierające ile sekund z danego pliku zostało już przetworzone. Zrzut z konsoli po dodaniu logów

Co jeżeli chcemy zmienić parametry pliku wynikowego? Wywołajmy kilka dodatkowych method na obiekcie conversion.

private static async Task RunConversion(Queue filesToConvert)
{
while(filesToConvert.TryDequeue(out FileInfo fileToConvert))
{
//Save file to the same location with changed extension
string outputFileName = Path.ChangeExtension(fileToConvert.FullName, ".mp4");
var mediaInfo = await MediaInfo.Get(fileToConvert);
var videoStream = mediaInfo.VideoStreams.First();
var audioStream = mediaInfo.AudioStreams.First();
//Change some parameters of video stream
videoStream
//Rotate video counter clockwise
.Rotate(RotateDegrees.CounterClockwise)
//Set size to 480p
.SetSize(VideoSize.Hd480)
//Set codec which will be used to encode file. If not set it's set automatically according to output file extension
.SetCodec(VideoCodec.H264);
//Create new conversion object
var conversion = Conversion.New()
//Add video stream to output file
.AddStream(videoStream)
//Add audio stream to output file
.AddStream(audioStream)
//Set output file path
.SetOutput(outputFileName)
//SetOverwriteOutput to overwrite files. It's useful when we already run application before
.SetOverwriteOutput(true)
//Disable multithreading
.UseMultiThread(false)
//Set conversion preset. You have to chose between file size and quality of video and duration of conversion
.SetPreset(ConversionPreset.UltraFast);
//Add log to OnProgress
conversion.OnProgress += async (sender, args) =>
{
//Show all output from FFmpeg to console
await Console.Out.WriteLineAsync($"[{args.Duration}/{args.TotalLength}][{args.Percent}%] {fileToConvert.Name}");
};
//Start conversion
await conversion.Start();
await Console.Out.WriteLineAsync($"Finished converion file [{fileToConvert.Name}]");
}
}

Wszystkie pliki są dostępne w github repository. Nie krępuj się kopiować tego, rozprowadzać i tak dalej. Oczywiście odpowiemy na wszystkie pytania.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *