Skip to content

Commit

Permalink
Reorganize FFmpeg wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
ConfusedPolarBear committed Oct 31, 2022
1 parent ff9ba16 commit af89e5f
Showing 1 changed file with 121 additions and 121 deletions.
242 changes: 121 additions & 121 deletions ConfusedPolarBear.Plugin.IntroSkipper/FFmpegWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,38 +98,6 @@ public static bool CheckFFmpegVersion()
}
}

/// <summary>
/// Run an FFmpeg command with the provided arguments and validate that the output contains
/// the provided string.
/// </summary>
/// <param name="arguments">Arguments to pass to FFmpeg.</param>
/// <param name="mustContain">String that the output must contain. Case insensitive.</param>
/// <param name="bundleName">Support bundle key to store FFmpeg's output under.</param>
/// <param name="errorMessage">Error message to log if this requirement is not met.</param>
/// <returns>true on success, false on error.</returns>
private static bool CheckFFmpegRequirement(
string arguments,
string mustContain,
string bundleName,
string errorMessage)
{
Logger?.LogDebug("Checking FFmpeg requirement {Arguments}", arguments);

var output = Encoding.UTF8.GetString(GetOutput(arguments, string.Empty, false, 2000));
Logger?.LogTrace("Output of ffmpeg {Arguments}: {Output}", arguments, output);
ChromaprintLogs[bundleName] = output;

if (!output.Contains(mustContain, StringComparison.OrdinalIgnoreCase))
{
Logger?.LogError("{ErrorMessage}", errorMessage);
return false;
}

Logger?.LogDebug("FFmpeg requirement {Arguments} met", arguments);

return true;
}

/// <summary>
/// Fingerprint a queued episode.
/// </summary>
Expand Down Expand Up @@ -158,58 +126,6 @@ public static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode)
return Fingerprint(episode, mode, start, end);
}

/// <summary>
/// Fingerprint a queued episode.
/// </summary>
/// <param name="episode">Queued episode to fingerprint.</param>
/// <param name="mode">Portion of media file to fingerprint.</param>
/// <param name="start">Time (in seconds) relative to the start of the file to start fingerprinting from.</param>
/// <param name="end">Time (in seconds) relative to the start of the file to stop fingerprinting at.</param>
/// <returns>Numerical fingerprint points.</returns>
private static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode, int start, int end)
{
// Try to load this episode from cache before running ffmpeg.
if (LoadCachedFingerprint(episode, mode, out uint[] cachedFingerprint))
{
Logger?.LogTrace("Fingerprint cache hit on {File}", episode.Path);
return cachedFingerprint;
}

Logger?.LogDebug(
"Fingerprinting [{Start}, {End}] from \"{File}\" (id {Id})",
start,
end,
episode.Path,
episode.EpisodeId);

var args = string.Format(
CultureInfo.InvariantCulture,
"-ss {0} -i \"{1}\" -to {2} -ac 2 -f chromaprint -fp_format raw -",
start,
episode.Path,
end - start);

// Returns all fingerprint points as raw 32 bit unsigned integers (little endian).
var rawPoints = GetOutput(args, string.Empty);
if (rawPoints.Length == 0 || rawPoints.Length % 4 != 0)
{
Logger?.LogWarning("Chromaprint returned {Count} points for \"{Path}\"", rawPoints.Length, episode.Path);
throw new FingerprintException("chromaprint output for \"" + episode.Path + "\" was malformed");
}

var results = new List<uint>();
for (var i = 0; i < rawPoints.Length; i += 4)
{
var rawPoint = rawPoints.Slice(i, 4);
results.Add(BitConverter.ToUInt32(rawPoint));
}

// Try to cache this fingerprint.
CacheFingerprint(episode, mode, results);

return results.ToArray();
}

/// <summary>
/// Transforms a Chromaprint into an inverted index of fingerprint points to the last index it appeared at.
/// </summary>
Expand Down Expand Up @@ -295,6 +211,75 @@ public static TimeRange[] DetectSilence(QueuedEpisode episode, int limit)
return silenceRanges.ToArray();
}

/// <summary>
/// Gets Chromaprint debugging logs.
/// </summary>
/// <returns>Markdown formatted logs.</returns>
public static string GetChromaprintLogs()
{
// Print the FFmpeg detection status at the top.
// Format: "* FFmpeg: `error`"
// Append two newlines to separate the bulleted list from the logs
var logs = string.Format(
CultureInfo.InvariantCulture,
"* FFmpeg: `{0}`\n\n",
ChromaprintLogs["error"]);

// Always include ffmpeg version information
logs += FormatFFmpegLog("version");

// Don't print feature detection logs if the plugin started up okay
if (ChromaprintLogs["error"] == "okay")
{
return logs;
}

// Print all remaining logs
foreach (var kvp in ChromaprintLogs)
{
if (kvp.Key == "error" || kvp.Key == "version")
{
continue;
}

logs += FormatFFmpegLog(kvp.Key);
}

return logs;
}

/// <summary>
/// Run an FFmpeg command with the provided arguments and validate that the output contains
/// the provided string.
/// </summary>
/// <param name="arguments">Arguments to pass to FFmpeg.</param>
/// <param name="mustContain">String that the output must contain. Case insensitive.</param>
/// <param name="bundleName">Support bundle key to store FFmpeg's output under.</param>
/// <param name="errorMessage">Error message to log if this requirement is not met.</param>
/// <returns>true on success, false on error.</returns>
private static bool CheckFFmpegRequirement(
string arguments,
string mustContain,
string bundleName,
string errorMessage)
{
Logger?.LogDebug("Checking FFmpeg requirement {Arguments}", arguments);

var output = Encoding.UTF8.GetString(GetOutput(arguments, string.Empty, false, 2000));
Logger?.LogTrace("Output of ffmpeg {Arguments}: {Output}", arguments, output);
ChromaprintLogs[bundleName] = output;

if (!output.Contains(mustContain, StringComparison.OrdinalIgnoreCase))
{
Logger?.LogError("{ErrorMessage}", errorMessage);
return false;
}

Logger?.LogDebug("FFmpeg requirement {Arguments} met", arguments);

return true;
}

/// <summary>
/// Runs ffmpeg and returns standard output (or error).
/// If caching is enabled, will use cacheFilename to cache the output of this command.
Expand Down Expand Up @@ -392,6 +377,58 @@ private static ReadOnlySpan<byte> GetOutput(
}
}

/// <summary>
/// Fingerprint a queued episode.
/// </summary>
/// <param name="episode">Queued episode to fingerprint.</param>
/// <param name="mode">Portion of media file to fingerprint.</param>
/// <param name="start">Time (in seconds) relative to the start of the file to start fingerprinting from.</param>
/// <param name="end">Time (in seconds) relative to the start of the file to stop fingerprinting at.</param>
/// <returns>Numerical fingerprint points.</returns>
private static uint[] Fingerprint(QueuedEpisode episode, AnalysisMode mode, int start, int end)
{
// Try to load this episode from cache before running ffmpeg.
if (LoadCachedFingerprint(episode, mode, out uint[] cachedFingerprint))
{
Logger?.LogTrace("Fingerprint cache hit on {File}", episode.Path);
return cachedFingerprint;
}

Logger?.LogDebug(
"Fingerprinting [{Start}, {End}] from \"{File}\" (id {Id})",
start,
end,
episode.Path,
episode.EpisodeId);

var args = string.Format(
CultureInfo.InvariantCulture,
"-ss {0} -i \"{1}\" -to {2} -ac 2 -f chromaprint -fp_format raw -",
start,
episode.Path,
end - start);

// Returns all fingerprint points as raw 32 bit unsigned integers (little endian).
var rawPoints = GetOutput(args, string.Empty);
if (rawPoints.Length == 0 || rawPoints.Length % 4 != 0)
{
Logger?.LogWarning("Chromaprint returned {Count} points for \"{Path}\"", rawPoints.Length, episode.Path);
throw new FingerprintException("chromaprint output for \"" + episode.Path + "\" was malformed");
}

var results = new List<uint>();
for (var i = 0; i < rawPoints.Length; i += 4)
{
var rawPoint = rawPoints.Slice(i, 4);
results.Add(BitConverter.ToUInt32(rawPoint));
}

// Try to cache this fingerprint.
CacheFingerprint(episode, mode, results);

return results.ToArray();
}

/// <summary>
/// Tries to load an episode's fingerprint from cache. If caching is not enabled, calling this function is a no-op.
/// This function was created before the unified caching mechanism was introduced (in v0.1.7).
Expand Down Expand Up @@ -507,43 +544,6 @@ private static string GetFingerprintCachePath(QueuedEpisode episode, AnalysisMod
}
}

/// <summary>
/// Gets Chromaprint debugging logs.
/// </summary>
/// <returns>Markdown formatted logs.</returns>
public static string GetChromaprintLogs()
{
// Print the FFmpeg detection status at the top.
// Format: "* FFmpeg: `error`"
// Append two newlines to separate the bulleted list from the logs
var logs = string.Format(
CultureInfo.InvariantCulture,
"* FFmpeg: `{0}`\n\n",
ChromaprintLogs["error"]);

// Always include ffmpeg version information
logs += FormatFFmpegLog("version");

// Don't print feature detection logs if the plugin started up okay
if (ChromaprintLogs["error"] == "okay")
{
return logs;
}

// Print all remaining logs
foreach (var kvp in ChromaprintLogs)
{
if (kvp.Key == "error" || kvp.Key == "version")
{
continue;
}

logs += FormatFFmpegLog(kvp.Key);
}

return logs;
}

private static string FormatFFmpegLog(string key)
{
/* Format:
Expand Down

0 comments on commit af89e5f

Please sign in to comment.