diff --git a/FFMpegCore.Test/ArgumentBuilderTest.cs b/FFMpegCore.Test/ArgumentBuilderTest.cs index 54620a5..9cf7e39 100644 --- a/FFMpegCore.Test/ArgumentBuilderTest.cs +++ b/FFMpegCore.Test/ArgumentBuilderTest.cs @@ -334,7 +334,7 @@ public void Builder_BuildString_SubtitleHardBurnFilter() .HardBurnSubtitle(SubtitleHardBurnOptions .Create(subtitlePath: "sample.srt") .SetCharacterEncoding("UTF-8") - .SetOriginalSize(1366,768) + .SetOriginalSize(1366, 768) .SetSubtitleIndex(0) .WithStyle(StyleOptions.Create() .WithParameter("FontName", "DejaVu Serif") @@ -479,10 +479,21 @@ public void Builder_BuildString_DynamicAudioNormalizerWithValuesFormat() { var str = FFMpegArguments.FromFileInput("input.mp4") .OutputToFile("output.mp4", false, - opt => opt.WithAudioFilters(filterOptions => filterOptions.DynamicNormalizer(125, 13, 0.9215, 5.124, 0.5458,false,true,true, 0.3333333))) + opt => opt.WithAudioFilters(filterOptions => filterOptions.DynamicNormalizer(125, 13, 0.9215, 5.124, 0.5458, false, true, true, 0.3333333))) .Arguments; Assert.AreEqual("-i \"input.mp4\" -af \"dynaudnorm=f=125:g=13:p=0.92:m=5.1:r=0.5:n=0:c=1:b=1:s=0.3\" \"output.mp4\"", str); } + + [TestMethod] + public void Builder_BuildString_Audible_AAXC_Decryption() + { + var str = FFMpegArguments.FromFileInput("input.aaxc", false, x => x.WithAudibleEncryptionKeys("123", "456")) + .MapMetaData() + .OutputToFile("output.m4b", true, x => x.WithTagVersion(3).DisableChannel(Channel.Video).CopyChannel(Channel.Audio)) + .Arguments; + + Assert.AreEqual("-audible_key 123 -audible_iv 456 -i \"input.aaxc\" -map_metadata 0 -id3v2_version 3 -vn -c:a copy \"output.m4b\" -y", str); + } } } \ No newline at end of file diff --git a/FFMpegCore.Test/FFMpegArgumentProcessorTest.cs b/FFMpegCore.Test/FFMpegArgumentProcessorTest.cs index 8443d0d..6e30999 100644 --- a/FFMpegCore.Test/FFMpegArgumentProcessorTest.cs +++ b/FFMpegCore.Test/FFMpegArgumentProcessorTest.cs @@ -94,5 +94,20 @@ public void Concat_Escape() var arg = new DemuxConcatArgument(new[] { @"Heaven's River\05 - Investigation.m4b" }); arg.Values.Should().BeEquivalentTo(new[] { @"file 'Heaven'\''s River\05 - Investigation.m4b'" }); } + + [TestMethod] + public void Audible_Aaxc_Test() + { + var arg = new AudibleEncryptionKeyArgument("123", "456"); + arg.Text.Should().Be($"-audible_key 123 -audible_iv 456"); + } + + + [TestMethod] + public void Audible_Aax_Test() + { + var arg = new AudibleEncryptionKeyArgument("62689101"); + arg.Text.Should().Be($"-activation_bytes 62689101"); + } } } \ No newline at end of file diff --git a/FFMpegCore/FFMpeg/Arguments/AudibleEncryptionKeyArgument.cs b/FFMpegCore/FFMpeg/Arguments/AudibleEncryptionKeyArgument.cs new file mode 100644 index 0000000..0f514dc --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/AudibleEncryptionKeyArgument.cs @@ -0,0 +1,28 @@ +namespace FFMpegCore.Arguments +{ + public class AudibleEncryptionKeyArgument : IArgument + { + private readonly bool _aaxcMode; + + private readonly string _key; + private readonly string _iv; + + private readonly string _activationBytes; + + + public AudibleEncryptionKeyArgument(string activationBytes) + { + _activationBytes = activationBytes; + } + + public AudibleEncryptionKeyArgument(string key, string iv) + { + _aaxcMode = true; + + _key = key; + _iv = iv; + } + + public string Text => _aaxcMode ? $"-audible_key {_key} -audible_iv {_iv}" : $"-activation_bytes {_activationBytes}"; + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/ID3V2VersionArgument.cs b/FFMpegCore/FFMpeg/Arguments/ID3V2VersionArgument.cs new file mode 100644 index 0000000..e18d93b --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/ID3V2VersionArgument.cs @@ -0,0 +1,14 @@ +namespace FFMpegCore.Arguments +{ + public class ID3V2VersionArgument : IArgument + { + private readonly int _version; + + public ID3V2VersionArgument(int version) + { + _version = version; + } + + public string Text => $"-id3v2_version {_version}"; + } +} diff --git a/FFMpegCore/FFMpeg/Arguments/MapMetadataArgument.cs b/FFMpegCore/FFMpeg/Arguments/MapMetadataArgument.cs new file mode 100644 index 0000000..afec731 --- /dev/null +++ b/FFMpegCore/FFMpeg/Arguments/MapMetadataArgument.cs @@ -0,0 +1,64 @@ +using FFMpegCore.Extend; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace FFMpegCore.Arguments +{ + public class MapMetadataArgument : IInputArgument, IDynamicArgument + { + private readonly int? _inputIndex; + + public string Text => GetText(null); + + /// + /// Null means it takes the last input used before this argument + /// + /// + public MapMetadataArgument(int? inputIndex = null) + { + _inputIndex = inputIndex; + } + + public string GetText(IEnumerable? arguments) + { + arguments ??= Enumerable.Empty(); + + var index = 0; + if (_inputIndex is null) + { + index = arguments + .TakeWhile(x => x != this) + .OfType() + .Count(); + + index = Math.Max(index - 1, 0); + } + else + { + index = _inputIndex.Value; + } + + return $"-map_metadata {index}"; + } + + public Task During(CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } + + public void Post() + { + } + + public void Pre() + { + } + + + } +} diff --git a/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs index ca6628a..7b3da7a 100644 --- a/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs +++ b/FFMpegCore/FFMpeg/FFMpegArgumentOptions.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; + using FFMpegCore.Arguments; using FFMpegCore.Enums; @@ -66,6 +67,11 @@ public FFMpegArgumentOptions WithAudioFilters(Action audioFi public FFMpegArgumentOptions ForcePixelFormat(string pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); public FFMpegArgumentOptions ForcePixelFormat(PixelFormat pixelFormat) => WithArgument(new ForcePixelFormat(pixelFormat)); + public FFMpegArgumentOptions WithAudibleEncryptionKeys(string key, string iv) => WithArgument(new AudibleEncryptionKeyArgument(key, iv)); + public FFMpegArgumentOptions WithAudibleActivationBytes(string activationBytes) => WithArgument(new AudibleEncryptionKeyArgument(activationBytes)); + public FFMpegArgumentOptions WithTagVersion(int id3v2Version = 3) => WithArgument(new ID3V2VersionArgument(id3v2Version)); + + public FFMpegArgumentOptions WithArgument(IArgument argument) { Arguments.Add(argument); diff --git a/FFMpegCore/FFMpeg/FFMpegArguments.cs b/FFMpegCore/FFMpeg/FFMpegArguments.cs index a93f1bd..c100c85 100644 --- a/FFMpegCore/FFMpeg/FFMpegArguments.cs +++ b/FFMpegCore/FFMpeg/FFMpegArguments.cs @@ -50,6 +50,13 @@ public FFMpegArguments WithGlobalOptions(Action configure public FFMpegArguments AddMetaData(string content, Action? addArguments = null) => WithInput(new MetaDataArgument(content), addArguments); public FFMpegArguments AddMetaData(IReadOnlyMetaData metaData, Action? addArguments = null) => WithInput(new MetaDataArgument(MetaDataSerializer.Instance.Serialize(metaData)), addArguments); + + /// + /// Maps the metadata of the given stream + /// + /// null means, the previous input will be used + public FFMpegArguments MapMetaData(int? inputIndex = null, Action? addArguments = null) => WithInput(new MapMetadataArgument(inputIndex), addArguments); + private FFMpegArguments WithInput(IInputArgument inputArgument, Action? addArguments) { var arguments = new FFMpegArgumentOptions();