Skip to content

Commit

Permalink
fix: exception on FFmpeg proxy file creation (#22)
Browse files Browse the repository at this point in the history
Make sure that a proxy file info is set before triggering the proxy file generation.
  • Loading branch information
protyposis authored Jan 26, 2024
1 parent b6ccac2 commit ba619ab
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/Aurio.FFmpeg.UnitTest/Aurio.FFmpeg.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Xunit.SkippableFact" Version="1.4.13" />
</ItemGroup>

<ItemGroup>
Expand Down
82 changes: 82 additions & 0 deletions src/Aurio.FFmpeg.UnitTest/FFmpegSourceStreamTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.IO;
using Xunit;

namespace Aurio.FFmpeg.UnitTest
{
public class FFmpegSourceStreamTests
{
[Fact]
public void CreateWaveProxy_ProxyFileInfoIsNull()
{
FileInfo fileInfo = new("X:\\test.file");
FileInfo? proxyFileInfo = null;

void act() => FFmpegSourceStream.CreateWaveProxy(fileInfo, proxyFileInfo);

Assert.Throws<ArgumentNullException>(act);
}

[SkippableFact]
public void SuggestWaveProxyFileInfo_WithoutDirectory_Windows()
{
Skip.IfNot(OperatingSystem.IsWindows());

var fileInfo = new FileInfo("X:\\folder\\file.wav");

var proxyFileInfo = FFmpegSourceStream.SuggestWaveProxyFileInfo(fileInfo);

Assert.Equal("X:\\folder\\file.wav.ffproxy.wav", proxyFileInfo.FullName);
}

[SkippableFact]
public void SuggestWaveProxyFileInfo_WithoutDirectory_Unix()
{
Skip.If(OperatingSystem.IsWindows());

var fileInfo = new FileInfo("/folder/file.wav");

var proxyFileInfo = FFmpegSourceStream.SuggestWaveProxyFileInfo(fileInfo);

Assert.Equal("/folder/file.wav.ffproxy.wav", proxyFileInfo.FullName);
}

[SkippableFact]
public void SuggestWaveProxyFileInfo_WithDirectory_Windows()
{
Skip.IfNot(OperatingSystem.IsWindows());

var fileInfo = new FileInfo("X:\\folder\\file.wav");
var directoryInfo = new DirectoryInfo("Y:\\temp\\dir");

var proxyFileInfo = FFmpegSourceStream.SuggestWaveProxyFileInfo(
fileInfo,
directoryInfo
);

Assert.Equal(
"Y:\\temp\\dir\\87c18cf1c8d8e07552df4ecc1ef629995fca9c59ad47ccf7eb4816de33590af7.ffproxy.wav",
proxyFileInfo.FullName
);
}

[SkippableFact]
public void SuggestWaveProxyFileInfo_WithDirectory_Unix()
{
Skip.If(OperatingSystem.IsWindows());

var fileInfo = new FileInfo("/folder/file.wav");
var directoryInfo = new DirectoryInfo("/temp/dir");

var proxyFileInfo = FFmpegSourceStream.SuggestWaveProxyFileInfo(
fileInfo,
directoryInfo
);

Assert.Equal(
"/temp/dir/e2c5b9282d156c5bdbf95639ca2ce2516b096e4828b7aa16620cb317cc90b3d9.ffproxy.wav",
proxyFileInfo.FullName
);
}
}
}
2 changes: 2 additions & 0 deletions src/Aurio.FFmpeg/FFmpegAudioStreamFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public FFmpegAudioStreamFactory()

public IAudioStream OpenFile(FileInfo fileInfo, FileInfo proxyFileInfo = null)
{
proxyFileInfo ??= FFmpegSourceStream.SuggestWaveProxyFileInfo(fileInfo);

if (FFmpegSourceStream.WaveProxySuggested(fileInfo))
{
Console.WriteLine("File format with known seek problems, creating proxy file...");
Expand Down
74 changes: 47 additions & 27 deletions src/Aurio.FFmpeg/FFmpegSourceStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ namespace Aurio.FFmpeg
{
public class FFmpegSourceStream : IAudioStream
{
public const string ProxyFileExtension = ".ffproxy.wav";

private Stream sourceStream;
private FFmpegReader reader;
private AudioProperties properties;
Expand Down Expand Up @@ -287,6 +289,11 @@ public void Dispose()

public static FileInfo CreateWaveProxy(FileInfo fileInfo, FileInfo proxyFileInfo)
{
if (proxyFileInfo == null)
{
throw new ArgumentNullException(nameof(proxyFileInfo));
}

if (proxyFileInfo.Exists)
{
Console.WriteLine("Proxy already existing, using " + proxyFileInfo.Name);
Expand Down Expand Up @@ -339,39 +346,16 @@ out Type type
}

/// <summary>
/// Creates a Wave format proxy file in the same directory and with the same name as the specified file,
/// if no storage directory is specified (i.e. if it is null). If a storage directory is specified, the proxy
/// file will be stored in the specified directory with a hashed file name to avoid name collisions and
/// file overwrites. The story directory option is convenient for the usage of temporary or working directories.
/// Creates a Wave format proxy file for the given file and optional directory according
/// to <see cref="SuggestWaveProxyFileInfo(FileInfo, DirectoryInfo)"/>.
/// </summary>
/// <param name="fileInfo">the file for which a proxy file should be created</param>
/// <param name="storageDirectory">optional directory where the proxy file will be stored, can be null</param>
/// <returns>the FileInfo of the proxy file</returns>
public static FileInfo CreateWaveProxy(FileInfo fileInfo, DirectoryInfo storageDirectory)
{
if (storageDirectory == null)
{
// Without a storage directory, store the proxy file beside the original file
var proxyFileInfo = new FileInfo(fileInfo.FullName + ".ffproxy.wav");
return CreateWaveProxy(fileInfo, proxyFileInfo);
}
else
{
// With a storage directory specified, store the proxy file with a hashed name
// (to avoid name collision / overwrites) in the target directory (e.g. a temp or working directory)
using (var sha256 = SHA256.Create())
{
byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(fileInfo.FullName));
string hashString = BitConverter
.ToString(hash)
.Replace("-", "")
.ToLowerInvariant();
var proxyFileInfo = new FileInfo(
Path.Combine(storageDirectory.FullName, hashString + ".ffproxy.wav")
);
return CreateWaveProxy(fileInfo, proxyFileInfo);
}
}
var proxyFileInfo = SuggestWaveProxyFileInfo(fileInfo, storageDirectory);
return CreateWaveProxy(fileInfo, proxyFileInfo);
}

/// <summary>
Expand Down Expand Up @@ -399,6 +383,42 @@ public static bool WaveProxySuggested(FileInfo fileInfo)
);
}

/// <summary>
/// Creates a proxy file info for the provided file with the <see cref="ProxyFileExtension"/>.
///
/// If a storage directory is specified, the proxy file will be located in the specified directory
/// with a hashed file name to avoid name collisions. This option is is convenient when using
/// temporary or working directories.
///
/// If no storage diretory is specified, the proxy file will be located in the same directory and
/// with the same name as the specified file.
/// </summary>
/// <param name="fileInfo">the file for which a proxy file should be created</param>
/// <param name="storageDirectory">optional directory where the proxy file will be stored (can be null)</param>
/// <returns>the FileInfo of the suggested proxy file</returns>
public static FileInfo SuggestWaveProxyFileInfo(
FileInfo fileInfo,
DirectoryInfo storageDirectory = null
)
{
if (storageDirectory == null)
{
// Without a storage directory, store the proxy file beside the original file
return new FileInfo(fileInfo.FullName + ProxyFileExtension);
}
else
{
// With a storage directory specified, store the proxy file with a hashed name
// (to avoid name collision / overwrites) in the target directory (e.g. a temp or working directory)
using var sha256 = SHA256.Create();
byte[] hash = sha256.ComputeHash(Encoding.Unicode.GetBytes(fileInfo.FullName));
string hashString = BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
return new FileInfo(
Path.Combine(storageDirectory.FullName, hashString + ProxyFileExtension)
);
}
}

public class FileNotSeekableException : Exception
{
public FileNotSeekableException()
Expand Down

0 comments on commit ba619ab

Please sign in to comment.