FFmpeg Caches - Axwabo/SecretLabNAudio GitHub Wiki
Caching with FFmpeg
To reduce reading overhead, or to compress files, you can use the SimpleFileCache
Caches invoke FFmpeg to transcode audio.
[!TIP] See also: streaming from disk
The CacheAsync method returns an Awaitable
of a tuple: (string Output, SaveCacheError? Error)
You must handle the case when an error occurred (if it's not null).
[!CAUTION] Interacting with Unity on a separate can lead to the entire game process crashing.
Examples
Use Cached File
The UseCachedFile extension method uses the cached file if the original was cached.
Otherwise, the player falls back to the original.
using SecretLabNAudio.FFmpeg.Caches;
using SecretLabNAudio.FFmpeg.Extensions;
// use cached version or fall back to original
audiioPlayer.UseCachedFile("/path/to/original.mp3");
if (SimpleFileCache.Shared.TryGetPath("/path/to/original2.mp3", out var cachedPath))
// do something with the cached path
Cache & Play
using SecretLabNAudio.Core;
using SecretLabNAudio.Core.Pools;
using SecretLabNAudio.Core.Extensions;
using SecretLabNAudio.FFmpeg.Caches;
using SecretLabNAudio.FFmpeg.Extensions;
using UnityEngine;
public static async Awaitable CacheAndPlay(string path)
{
var (output, error) = await SimpleFileCache.Shared
.CacheIfUpdatedAsync(path, OptimizeFor.ReadingSpeed);
await Awaitable.MainThreadAsync(); // for good measure
if (error is not null)
Logger.Error($"Couldn't cache {path}\n{error.ToHumanReadableString()}");
else
AudioPlayerPool.Rent(SpeakerSettings.GloballyAudible)
.UseFile(output)
.PoolOnEnd();
}
// event handler
public static void OnRoundStarted() => _ = CacheAndPlay("/path/to/music.m4a");
[!TIP] If you know that your method will be called from the main thread, you can omit
Awaitable.MainThreadAsync()
Cache On Load
Since Unity doesn't support blocking the thread until an Awaitable completes,
you must handle logic asynchronously.
using System.Linq;
using LabApi.Loader;
using SecretLabNAudio.FFmpeg.Caches;
using SecretLabNAudio.FFmpeg.Extensions;
using UnityEngine;
// plugin class
public override void Enabled()
{
var directory = this.GetConfigDirectory().CreateSubdirectory("Audio");
_ = CacheInitialAsync(directory.FullName);
}
private static async Awaitable CacheInitialAsync(string directory)
{
var results = await SimpleFileCache.Shared
.CacheAllUpdatedAsync(directory, OptimizeFor.FileSize);
var success = results.Count(static e => e.Error is null);
if (success != 0)
Logger.Info($"Completed caching of {success}/{results.Length} file(s)");
if (success == results.Length)
return;
Logger.Error(string.Join("\n", results
.Select(static e => e.Error?.ToHumanReadableString())
.Where(static e => e != null)));
}
Timeout
To limit the amount of time a caching process can take, use a CancellationTokenSource
[!IMPORTANT] Make sure to dispose of the
CancellationTokenSourceby writing ausingblock, or a manual call toDisposein a utility class.
using System;
using System.Threading;
using SecretLabNAudio.FFmpeg.Caches;
using UnityEngine;
public static async Awaitable CacheAsync(string path)
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await SimpleFileCache.Shared.CacheAsync(path, OptimizeFor.FileSize, cts.Token);
}
// is the same as
public static async Awaitable CacheAsync(string path)
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5));
await SimpleFileCache.Shared.CacheAsync(path, OptimizeFor.FileSize, cts.Token);
}