mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
Experimental ffmpeg plugin
This commit is contained in:
320
CUETools.Codecs.ffmpeg/AudioDecoder.cs
Normal file
320
CUETools.Codecs.ffmpeg/AudioDecoder.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using FFmpeg.AutoGen;
|
||||
|
||||
namespace CUETools.Codecs.ffmpegdll
|
||||
{
|
||||
public unsafe class AudioDecoder : IAudioSource, IDisposable
|
||||
{
|
||||
private static void RegisterLibrariesSearchPath(string path)
|
||||
{
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
SetDllDirectory(path);
|
||||
break;
|
||||
//case PlatformID.Unix:
|
||||
//case PlatformID.MacOSX:
|
||||
// string currentValue = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
|
||||
// if (string.IsNullOrWhiteSpace(currentValue) == false && currentValue.Contains(path) == false)
|
||||
// {
|
||||
// string newValue = currentValue + Path.PathSeparator + path;
|
||||
// Environment.SetEnvironmentVariable(LD_LIBRARY_PATH, newValue);
|
||||
// }
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32", SetLastError = true)]
|
||||
private static extern bool SetDllDirectory(string lpPathName);
|
||||
|
||||
public AudioDecoder(DecoderSettings settings, string path, Stream IO)
|
||||
{
|
||||
m_settings = settings;
|
||||
|
||||
_path = path;
|
||||
|
||||
m_stream = (IO != null) ? IO : new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
case PlatformID.Win32S:
|
||||
case PlatformID.Win32Windows:
|
||||
var myPath = new Uri(typeof(AudioDecoder).Assembly.CodeBase).LocalPath;
|
||||
var current = System.IO.Path.GetDirectoryName(myPath);
|
||||
var probe = Environment.Is64BitProcess ? "x64" : "win32";
|
||||
while (current != null)
|
||||
{
|
||||
var ffmpegDirectory = System.IO.Path.Combine(current, probe);
|
||||
if (Directory.Exists(ffmpegDirectory))
|
||||
{
|
||||
Console.WriteLine($"FFmpeg binaries found in: {ffmpegDirectory}");
|
||||
RegisterLibrariesSearchPath(ffmpegDirectory);
|
||||
break;
|
||||
}
|
||||
current = Directory.GetParent(current)?.FullName;
|
||||
}
|
||||
break;
|
||||
//case PlatformID.Unix:
|
||||
//case PlatformID.MacOSX:
|
||||
// var libraryPath = Environment.GetEnvironmentVariable(LD_LIBRARY_PATH);
|
||||
// RegisterLibrariesSearchPath(libraryPath);
|
||||
// break;
|
||||
}
|
||||
|
||||
pkt = ffmpeg.av_packet_alloc();
|
||||
if (pkt == null)
|
||||
throw new Exception("Unable to initialize the decoder");
|
||||
|
||||
decoded_frame = ffmpeg.av_frame_alloc();
|
||||
if (decoded_frame == null)
|
||||
throw new Exception("Could not allocate audio frame");
|
||||
|
||||
ffmpeg.avcodec_register_all();
|
||||
|
||||
codec = ffmpeg.avcodec_find_decoder(m_settings.Codec);
|
||||
if (codec == null)
|
||||
throw new Exception("Codec not found");
|
||||
|
||||
parser = ffmpeg.av_parser_init((int)codec->id);
|
||||
if (parser == null)
|
||||
throw new Exception("Parser not found\n");
|
||||
|
||||
c = ffmpeg.avcodec_alloc_context3(codec);
|
||||
if (c == null)
|
||||
throw new Exception("Could not allocate audio codec context");
|
||||
//c->refcounted_frames == 0;
|
||||
|
||||
/* open it */
|
||||
if (ffmpeg.avcodec_open2(c, codec, null) < 0)
|
||||
throw new Exception("Could not open codec");
|
||||
|
||||
data_buf = new byte[AUDIO_INBUF_SIZE];
|
||||
data_size = 0;
|
||||
data_offs = 0;
|
||||
m_decoded_frame_offset = 0;
|
||||
m_decoded_frame_size = 0;
|
||||
|
||||
fill();
|
||||
|
||||
//switch(c->sample_fmt)
|
||||
//{
|
||||
// case AVSampleFormat.AV_SAMPLE_FMT_S16:
|
||||
// break;
|
||||
// case AVSampleFormat.AV_SAMPLE_FMT_S32:
|
||||
// break;
|
||||
// default:
|
||||
// throw new Exception("Bad sample format");
|
||||
//}
|
||||
|
||||
bytes_per_sample = ffmpeg.av_get_bytes_per_sample(c->sample_fmt);
|
||||
/* This should not occur, checking just for paranoia */
|
||||
if (bytes_per_sample < 0) throw new Exception("Failed to calculate data size");
|
||||
|
||||
pcm = new AudioPCMConfig(
|
||||
c->bits_per_raw_sample, c->channels, c->sample_rate,
|
||||
(AudioPCMConfig.SpeakerConfig)0); // c->channel_layout;
|
||||
//_sampleCount = MACLibDll.c_APEDecompress_GetInfo(pAPEDecompress, APE_DECOMPRESS_FIELDS.APE_DECOMPRESS_TOTAL_BLOCKS, 0, 0).ToInt64();
|
||||
_sampleCount = -1;
|
||||
_sampleOffset = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
//if (m_StreamIO != null)
|
||||
//{
|
||||
// m_StreamIO.Dispose();
|
||||
// m_StreamIO = null;
|
||||
//}
|
||||
if (m_stream != null)
|
||||
{
|
||||
m_stream.Dispose();
|
||||
m_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
AVCodecContext* c1 = c;
|
||||
ffmpeg.avcodec_free_context(&c1);
|
||||
c = c1;
|
||||
//c = null;
|
||||
|
||||
ffmpeg.av_parser_close(parser);
|
||||
parser = null;
|
||||
|
||||
AVFrame* decoded_frame1 = decoded_frame;
|
||||
ffmpeg.av_frame_free(&decoded_frame1);
|
||||
decoded_frame = decoded_frame1;
|
||||
//decoded_frame = null;
|
||||
|
||||
AVPacket* pkt1 = pkt;
|
||||
ffmpeg.av_packet_free(&pkt1);
|
||||
pkt = pkt1;
|
||||
//pkt = null;
|
||||
}
|
||||
|
||||
~AudioDecoder()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
private DecoderSettings m_settings;
|
||||
|
||||
public IAudioDecoderSettings Settings => m_settings;
|
||||
|
||||
public AudioPCMConfig PCM => pcm;
|
||||
|
||||
public string Path => _path;
|
||||
|
||||
public long Length => _sampleCount;
|
||||
|
||||
public long Position
|
||||
{
|
||||
get => _sampleOffset;
|
||||
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
//_bufferOffset = 0;
|
||||
//_bufferLength = 0;
|
||||
//_sampleOffset = value;
|
||||
//int res = MACLibDll.c_APEDecompress_Seek(pAPEDecompress, (int)value);
|
||||
//if (0 != res)
|
||||
// throw new Exception("unable to seek:" + res.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public long Remaining => _sampleCount < 0 ? -1 : _sampleCount - _sampleOffset;
|
||||
|
||||
public void Close()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
const int AUDIO_INBUF_SIZE = 65536;
|
||||
const int AUDIO_REFILL_THRESH = 4096;
|
||||
|
||||
private void fill()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (m_decoded_frame_size > 0)
|
||||
return;
|
||||
int ret = ffmpeg.avcodec_receive_frame(c, decoded_frame);
|
||||
if (ret == ffmpeg.AVERROR_EOF)
|
||||
return;
|
||||
if (ret != ffmpeg.AVERROR(ffmpeg.EAGAIN))
|
||||
{
|
||||
if (ret < 0) throw new Exception("Error during decoding");
|
||||
m_decoded_frame_offset = 0;
|
||||
m_decoded_frame_size = decoded_frame->nb_samples;
|
||||
return;
|
||||
}
|
||||
if (pkt->size != 0)
|
||||
{
|
||||
/* send the packet with the compressed data to the decoder */
|
||||
ret = ffmpeg.avcodec_send_packet(c, pkt);
|
||||
if (ret < 0) throw new Exception("Error submitting the packet to the decoder");
|
||||
pkt->size = 0;
|
||||
continue;
|
||||
}
|
||||
if (data_size < AUDIO_REFILL_THRESH)
|
||||
{
|
||||
Array.Copy(data_buf, data_offs, data_buf, 0, data_size);
|
||||
data_offs = 0;
|
||||
int len = m_stream.Read(data_buf, data_size, data_buf.Length - data_size);
|
||||
data_size += len;
|
||||
if (data_size <= 0) return;
|
||||
}
|
||||
fixed (byte* data = &data_buf[data_offs])
|
||||
ret = ffmpeg.av_parser_parse2(parser, c, &pkt->data, &pkt->size,
|
||||
data, data_size, ffmpeg.AV_NOPTS_VALUE, ffmpeg.AV_NOPTS_VALUE, 0);
|
||||
if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
|
||||
return;
|
||||
if (ret < 0)
|
||||
throw new Exception("Error while parsing");
|
||||
data_offs += ret;
|
||||
data_size -= ret;
|
||||
}
|
||||
}
|
||||
|
||||
public int Read(AudioBuffer buff, int maxLength)
|
||||
{
|
||||
buff.Prepare(this, maxLength);
|
||||
|
||||
long buffOffset = 0;
|
||||
long samplesNeeded = buff.Length;
|
||||
long _channelCount = pcm.ChannelCount;
|
||||
|
||||
while (samplesNeeded != 0)
|
||||
{
|
||||
if (m_decoded_frame_size == 0)
|
||||
{
|
||||
fill();
|
||||
if (m_decoded_frame_size == 0)
|
||||
break;
|
||||
}
|
||||
long copyCount = Math.Min(samplesNeeded, m_decoded_frame_size);
|
||||
|
||||
switch (c->sample_fmt)
|
||||
{
|
||||
case AVSampleFormat.AV_SAMPLE_FMT_S32:
|
||||
{
|
||||
byte* ptr = decoded_frame->data[0u] + c->channels * bytes_per_sample * m_decoded_frame_offset;
|
||||
int rshift = 32 - pcm.BitsPerSample;
|
||||
int* smp = (int*)ptr;
|
||||
fixed (int* dst_start = &buff.Samples[buffOffset, 0])
|
||||
{
|
||||
int* dst = dst_start;
|
||||
int* dst_end = dst_start + copyCount * c->channels;
|
||||
while (dst < dst_end)
|
||||
*(dst++) = *(smp++) >> rshift;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
samplesNeeded -= copyCount;
|
||||
buffOffset += copyCount;
|
||||
m_decoded_frame_offset += copyCount;
|
||||
m_decoded_frame_size -= copyCount;
|
||||
_sampleOffset += copyCount;
|
||||
}
|
||||
|
||||
buff.Length = (int)buffOffset;
|
||||
return buff.Length;
|
||||
}
|
||||
|
||||
AVPacket* pkt;
|
||||
AVFrame* decoded_frame;
|
||||
AVCodec* codec;
|
||||
AVCodecParserContext* parser;
|
||||
AVCodecContext* c;
|
||||
|
||||
long _sampleCount, _sampleOffset;
|
||||
AudioPCMConfig pcm;
|
||||
string _path;
|
||||
Stream m_stream;
|
||||
long m_decoded_frame_offset;
|
||||
long m_decoded_frame_size;
|
||||
|
||||
byte[] data_buf;
|
||||
int data_size;
|
||||
int data_offs;
|
||||
|
||||
int bytes_per_sample;
|
||||
}
|
||||
}
|
||||
30
CUETools.Codecs.ffmpeg/CUETools.Codecs.ffmpeg.csproj
Normal file
30
CUETools.Codecs.ffmpeg/CUETools.Codecs.ffmpeg.csproj
Normal file
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net45;netstandard2.0</TargetFrameworks>
|
||||
<Version>2.1.7.0</Version>
|
||||
<AssemblyName>CUETools.Codecs.ffmpegdll</AssemblyName>
|
||||
<RootNamespace>CUETools.Codecs.ffmpegdll</RootNamespace>
|
||||
<Product>CUETools</Product>
|
||||
<Description>A library for encoding various files using official encoder.</Description>
|
||||
<Copyright>Copyright (c) 2018 Grigory Chudov</Copyright>
|
||||
<Authors>Grigory Chudov</Authors>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputPath>..\bin\$(Configuration)\plugins</OutputPath>
|
||||
<RepositoryUrl>https://github.com/gchudov/cuetools.net</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<Company />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ProjectReference>
|
||||
<Private>False</Private>
|
||||
</ProjectReference>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CUETools.Codecs\CUETools.Codecs.csproj" />
|
||||
<ProjectReference Include="..\..\FFmpeg.AutoGen\FFmpeg.AutoGen\FFmpeg.AutoGen.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
68
CUETools.Codecs.ffmpeg/DecoderSettings.cs
Normal file
68
CUETools.Codecs.ffmpeg/DecoderSettings.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using FFmpeg.AutoGen;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CUETools.Codecs.ffmpegdll
|
||||
{
|
||||
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public abstract class DecoderSettings: IAudioDecoderSettings
|
||||
{
|
||||
#region IAudioDecoderSettings implementation
|
||||
|
||||
[Browsable(false)]
|
||||
public string Name => "ffmpeg";
|
||||
|
||||
[Browsable(false)]
|
||||
public Type DecoderType => typeof(AudioDecoder);
|
||||
|
||||
[Browsable(false)]
|
||||
public int Priority => 1;
|
||||
|
||||
[Browsable(false)]
|
||||
public abstract string Extension { get; }
|
||||
|
||||
public IAudioDecoderSettings Clone()
|
||||
{
|
||||
return MemberwiseClone() as IAudioDecoderSettings;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public abstract AVCodecID Codec { get; }
|
||||
|
||||
// [DisplayName("Version")]
|
||||
// [Description("Library version")]
|
||||
// public string Version => Marshal.PtrToStringAnsi(MACLibDll.GetVersionString());
|
||||
}
|
||||
|
||||
public class MLPDecoderSettings : DecoderSettings, IAudioDecoderSettings
|
||||
{
|
||||
public override string Extension => "mlp";
|
||||
public override AVCodecID Codec => AVCodecID.AV_CODEC_ID_MLP;
|
||||
public MLPDecoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
}
|
||||
public class FLACDecoderSettings : DecoderSettings, IAudioDecoderSettings
|
||||
{
|
||||
public override string Extension => "flac";
|
||||
public override AVCodecID Codec => AVCodecID.AV_CODEC_ID_FLAC;
|
||||
public FLACDecoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
}
|
||||
|
||||
public class WavPackDecoderSettings : DecoderSettings, IAudioDecoderSettings
|
||||
{
|
||||
public override string Extension => "wv";
|
||||
public override AVCodecID Codec => AVCodecID.AV_CODEC_ID_WAVPACK;
|
||||
public WavPackDecoderSettings()
|
||||
{
|
||||
this.Init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ namespace CUETools.Codecs
|
||||
formats.Add("mp3", new CUEToolsFormat("mp3", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("mp3", false), null));
|
||||
formats.Add("ogg", new CUEToolsFormat("ogg", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("ogg", false), null));
|
||||
formats.Add("opus", new CUEToolsFormat("opus", CUEToolsTagger.TagLibSharp, false, true, false, true, null, encodersViewModel.GetDefault("opus", false), null));
|
||||
formats.Add("mlp", new CUEToolsFormat("mlp", CUEToolsTagger.APEv2, true, false, false, false, null, null, decodersViewModel.GetDefault("mlp")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MACLibDll", "..\ThirdParty\
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.MACLib", "..\CUETools.Codecs.MACLib\CUETools.Codecs.MACLib.csproj", "{8FE405A0-E837-4088-88AD-B63652E34790}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUETools.Codecs.ffmpeg", "..\CUETools.Codecs.ffmpeg\CUETools.Codecs.ffmpeg.csproj", "{A2C09014-C430-4E58-A323-306CCDF313C5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFmpeg.AutoGen", "..\..\FFmpeg.AutoGen\FFmpeg.AutoGen\FFmpeg.AutoGen.csproj", "{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -745,6 +749,30 @@ Global
|
||||
{8FE405A0-E837-4088-88AD-B63652E34790}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8FE405A0-E837-4088-88AD-B63652E34790}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{8FE405A0-E837-4088-88AD-B63652E34790}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5}.Release|x64.Build.0 = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|Win32.ActiveCfg = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|Win32.Build.0 = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|Win32.ActiveCfg = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|Win32.Build.0 = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923}.Release|x64.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -805,6 +833,8 @@ Global
|
||||
{1A87F412-BA74-4DBB-9F77-FD55C042FB63} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
||||
{CEE4A179-49FC-4D51-B045-F717D3A6C13A} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
||||
{8FE405A0-E837-4088-88AD-B63652E34790} = {93B7AE1D-DEF6-4A04-A222-5CDE09DF262D}
|
||||
{A2C09014-C430-4E58-A323-306CCDF313C5} = {93B7AE1D-DEF6-4A04-A222-5CDE09DF262D}
|
||||
{B0A0C17A-C1BC-4CB1-BE1E-F545F54A7923} = {8B179853-B7D6-479C-B8B2-6CBCE835D040}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C634D169-5814-4203-94B6-6A11371DDA95}
|
||||
|
||||
2
ThirdParty/flac
vendored
2
ThirdParty/flac
vendored
Submodule ThirdParty/flac updated: 8f4f296372...900bf23141
2
ThirdParty/openclnet
vendored
2
ThirdParty/openclnet
vendored
Submodule ThirdParty/openclnet updated: 0d5dba44e2...e4619a09e5
Reference in New Issue
Block a user