Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: exception on FFmpeg proxy file creation #22

Merged
merged 2 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading