Fixed ffmpeg cover art not showing on android as discussed.
This commit is contained in:
parent
b53aabe0e3
commit
f148650e57
@ -132,8 +132,7 @@ namespace AaxDecrypter
|
||||
|
||||
public bool Step2_DownloadAndCombine()
|
||||
{
|
||||
|
||||
var aaxcProcesser = new FFMpegAaxcProcesser(DecryptSupportLibraries.ffmpegPath);
|
||||
var aaxcProcesser = new FFMpegAaxcProcesser(downloadLicense);
|
||||
aaxcProcesser.ProgressUpdate += AaxcProcesser_ProgressUpdate;
|
||||
|
||||
string metadataPath = null;
|
||||
@ -147,10 +146,6 @@ namespace AaxDecrypter
|
||||
}
|
||||
|
||||
aaxcProcesser.ProcessBook(
|
||||
downloadLicense.DownloadUrl,
|
||||
downloadLicense.UserAgent,
|
||||
downloadLicense.AudibleKey,
|
||||
downloadLicense.AudibleIV,
|
||||
outputFileName,
|
||||
metadataPath)
|
||||
.GetAwaiter()
|
||||
@ -172,9 +167,8 @@ namespace AaxDecrypter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy all aacx metadata to m4b file.
|
||||
/// Copy all aacx metadata to m4b file, including cover art.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Step3_RestoreMetadata()
|
||||
{
|
||||
var outFile = new TagLib.Mpeg4.File(outputFileName, TagLib.ReadStyle.Average);
|
||||
@ -186,6 +180,8 @@ namespace AaxDecrypter
|
||||
|
||||
//copy all metadata fields in the source file, even those that TagLib doesn't
|
||||
//recognize, to the output file.
|
||||
//NOTE: Chapters aren't stored in MPEG-4 metadata. They are encoded as a Timed
|
||||
//Text Stream (MPEG-4 Part 17), so taglib doesn't read or write them.
|
||||
foreach (var stag in sourceTag)
|
||||
{
|
||||
destTags.SetData(stag.BoxType, stag.Children.Cast<TagLib.Mpeg4.AppleDataBox>().ToArray());
|
||||
|
||||
@ -7,96 +7,96 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace AaxDecrypter
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Download audible aaxc, decrypt, remux,and add metadata.
|
||||
/// Download audible aaxc, decrypt, and remux with chapters.
|
||||
/// </summary>
|
||||
class FFMpegAaxcProcesser
|
||||
{
|
||||
public event EventHandler<TimeSpan> ProgressUpdate;
|
||||
public string FFMpegPath { get; }
|
||||
public DownloadLicense DownloadLicense { get; }
|
||||
public bool IsRunning { get; private set; }
|
||||
public bool Succeeded { get; private set; }
|
||||
public string FFMpegStandardError => ffmpegError.ToString();
|
||||
public string FFMpegRemuxerStandardError => remuxerError.ToString();
|
||||
public string FFMpegDownloaderStandardError => downloaderError.ToString();
|
||||
|
||||
|
||||
private StringBuilder ffmpegError = new StringBuilder();
|
||||
private StringBuilder remuxerError = new StringBuilder();
|
||||
private StringBuilder downloaderError = new StringBuilder();
|
||||
private static Regex processedTimeRegex = new Regex("time=(\\d{2}):(\\d{2}):(\\d{2}).\\d{2}", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
public FFMpegAaxcProcesser(string ffmpegPath)
|
||||
public FFMpegAaxcProcesser( DownloadLicense downloadLicense)
|
||||
{
|
||||
FFMpegPath = ffmpegPath;
|
||||
FFMpegPath = DecryptSupportLibraries.ffmpegPath;
|
||||
DownloadLicense = downloadLicense;
|
||||
}
|
||||
public async Task ProcessBook(string aaxcUrl, string userAgent, string audibleKey, string audibleIV, string outputFile, string metadataPath = null)
|
||||
|
||||
public async Task ProcessBook(string outputFile, string ffmetaChaptersPath = null)
|
||||
{
|
||||
//This process gets the aaxc from the url and streams the decrypted
|
||||
//m4b to the output file.
|
||||
var StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = FFMpegPath,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = Path.GetDirectoryName(FFMpegPath),
|
||||
|
||||
};
|
||||
|
||||
if (metadataPath != null)
|
||||
{
|
||||
StartInfo.ArgumentList.Add("-ignore_chapters"); //prevents ffmpeg from copying chapter info from aaxc to output file
|
||||
StartInfo.ArgumentList.Add("true");
|
||||
}
|
||||
|
||||
StartInfo.ArgumentList.Add("-audible_key");
|
||||
StartInfo.ArgumentList.Add(audibleKey);
|
||||
StartInfo.ArgumentList.Add("-audible_iv");
|
||||
StartInfo.ArgumentList.Add(audibleIV);
|
||||
StartInfo.ArgumentList.Add("-user_agent");
|
||||
StartInfo.ArgumentList.Add(userAgent);
|
||||
StartInfo.ArgumentList.Add("-i");
|
||||
StartInfo.ArgumentList.Add(aaxcUrl);
|
||||
|
||||
if (metadataPath != null)
|
||||
{
|
||||
StartInfo.ArgumentList.Add("-f");
|
||||
StartInfo.ArgumentList.Add("ffmetadata");
|
||||
StartInfo.ArgumentList.Add("-i");
|
||||
StartInfo.ArgumentList.Add(metadataPath);
|
||||
StartInfo.ArgumentList.Add("-map_metadata");
|
||||
StartInfo.ArgumentList.Add("1");
|
||||
}
|
||||
|
||||
StartInfo.ArgumentList.Add("-c"); //copy all codecs to output
|
||||
StartInfo.ArgumentList.Add("copy");
|
||||
StartInfo.ArgumentList.Add("-f"); //force output format: mp4
|
||||
StartInfo.ArgumentList.Add("mp4");
|
||||
StartInfo.ArgumentList.Add("-movflags"); //don't add nero format chapter flags
|
||||
StartInfo.ArgumentList.Add("disable_chpl+faststart");
|
||||
StartInfo.ArgumentList.Add(outputFile);
|
||||
StartInfo.ArgumentList.Add("-y"); //overwrite existing file
|
||||
|
||||
await ProcessBook(StartInfo);
|
||||
}
|
||||
|
||||
private async Task ProcessBook(ProcessStartInfo startInfo)
|
||||
{
|
||||
//aac stream to standard output
|
||||
var downloader = new Process
|
||||
{
|
||||
StartInfo = startInfo
|
||||
StartInfo = getDownloaderStartInfo()
|
||||
};
|
||||
|
||||
//This process retreves an aac stream from standard input and muxes
|
||||
// it into an m4b along with the cover art and metadata.
|
||||
var remuxer = new Process
|
||||
{
|
||||
StartInfo = getRemuxerStartInfo(outputFile, ffmetaChaptersPath)
|
||||
};
|
||||
|
||||
IsRunning = true;
|
||||
|
||||
downloader.ErrorDataReceived += Remuxer_ErrorDataReceived;
|
||||
downloader.ErrorDataReceived += Downloader_ErrorDataReceived;
|
||||
downloader.Start();
|
||||
downloader.BeginErrorReadLine();
|
||||
|
||||
remuxer.ErrorDataReceived += Remuxer_ErrorDataReceived;
|
||||
remuxer.Start();
|
||||
remuxer.BeginErrorReadLine();
|
||||
|
||||
var pipedOutput = downloader.StandardOutput.BaseStream;
|
||||
var pipedInput = remuxer.StandardInput.BaseStream;
|
||||
|
||||
|
||||
//All the work done here. Copy download standard output into
|
||||
//remuxer standard input
|
||||
await downloader.WaitForExitAsync();
|
||||
await Task.Run(() =>
|
||||
{
|
||||
int lastRead = 0;
|
||||
byte[] buffer = new byte[32 * 1024];
|
||||
|
||||
do
|
||||
{
|
||||
lastRead = pipedOutput.Read(buffer, 0, buffer.Length);
|
||||
pipedInput.Write(buffer, 0, lastRead);
|
||||
} while (lastRead > 0 && !remuxer.HasExited);
|
||||
});
|
||||
|
||||
//Closing input stream terminates remuxer
|
||||
pipedInput.Close();
|
||||
|
||||
//If the remuxer exited due to failure, downloader will still have
|
||||
//data in the pipe. Force kill downloader to continue.
|
||||
if (remuxer.HasExited && !downloader.HasExited)
|
||||
downloader.Kill();
|
||||
|
||||
remuxer.WaitForExit();
|
||||
downloader.WaitForExit();
|
||||
|
||||
IsRunning = false;
|
||||
Succeeded = downloader.ExitCode == 0;
|
||||
Succeeded = downloader.ExitCode == 0 && remuxer.ExitCode == 0;
|
||||
}
|
||||
|
||||
private void Downloader_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Data))
|
||||
return;
|
||||
|
||||
downloaderError.AppendLine(e.Data);
|
||||
}
|
||||
|
||||
private void Remuxer_ErrorDataReceived(object sender, DataReceivedEventArgs e)
|
||||
@ -104,7 +104,7 @@ namespace AaxDecrypter
|
||||
if (string.IsNullOrEmpty(e.Data))
|
||||
return;
|
||||
|
||||
ffmpegError.AppendLine(e.Data);
|
||||
remuxerError.AppendLine(e.Data);
|
||||
|
||||
if (processedTimeRegex.IsMatch(e.Data))
|
||||
{
|
||||
@ -122,9 +122,92 @@ namespace AaxDecrypter
|
||||
|
||||
if (e.Data.Contains("aac bitstream error"))
|
||||
{
|
||||
//This happens if input is corrupt (should never happen) or if caller
|
||||
//supplied wrong key/iv
|
||||
var process = sender as Process;
|
||||
process.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private ProcessStartInfo getDownloaderStartInfo() =>
|
||||
new ProcessStartInfo
|
||||
{
|
||||
FileName = FFMpegPath,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = Path.GetDirectoryName(FFMpegPath),
|
||||
ArgumentList ={
|
||||
"-nostdin",
|
||||
"-audible_key",
|
||||
DownloadLicense.AudibleKey,
|
||||
"-audible_iv",
|
||||
DownloadLicense.AudibleIV,
|
||||
"-user_agent",
|
||||
DownloadLicense.UserAgent, //user-agent is requied for CDN to serve the file
|
||||
"-i",
|
||||
DownloadLicense.DownloadUrl,
|
||||
"-c:a", //audio codec
|
||||
"copy", //copy stream
|
||||
"-f", //force output format: adts
|
||||
"adts",
|
||||
"pipe:" //pipe output to stdout
|
||||
}
|
||||
};
|
||||
|
||||
private ProcessStartInfo getRemuxerStartInfo(string outputFile, string ffmetaChaptersPath = null)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = FFMpegPath,
|
||||
RedirectStandardError = true,
|
||||
RedirectStandardInput = true,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = Path.GetDirectoryName(FFMpegPath),
|
||||
};
|
||||
|
||||
startInfo.ArgumentList.Add("-thread_queue_size");
|
||||
startInfo.ArgumentList.Add("1024");
|
||||
startInfo.ArgumentList.Add("-f"); //force input format: aac
|
||||
startInfo.ArgumentList.Add("aac");
|
||||
startInfo.ArgumentList.Add("-i"); //read input from stdin
|
||||
startInfo.ArgumentList.Add("pipe:");
|
||||
|
||||
if (ffmetaChaptersPath is null)
|
||||
{
|
||||
//copy metadata from aaxc file.
|
||||
startInfo.ArgumentList.Add("-user_agent");
|
||||
startInfo.ArgumentList.Add(DownloadLicense.UserAgent);
|
||||
startInfo.ArgumentList.Add("-i");
|
||||
startInfo.ArgumentList.Add(DownloadLicense.DownloadUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
//copy metadata from supplied metadata file
|
||||
startInfo.ArgumentList.Add("-f");
|
||||
startInfo.ArgumentList.Add("ffmetadata");
|
||||
startInfo.ArgumentList.Add("-i");
|
||||
startInfo.ArgumentList.Add(ffmetaChaptersPath);
|
||||
}
|
||||
|
||||
startInfo.ArgumentList.Add("-map"); //map file 0 (aac audio stream)
|
||||
startInfo.ArgumentList.Add("0");
|
||||
startInfo.ArgumentList.Add("-map_chapters"); //copy chapter data from file 1 (either metadata file or aaxc file)
|
||||
startInfo.ArgumentList.Add("1");
|
||||
startInfo.ArgumentList.Add("-c"); //copy all mapped streams
|
||||
startInfo.ArgumentList.Add("copy");
|
||||
startInfo.ArgumentList.Add("-f"); //force output format: mp4
|
||||
startInfo.ArgumentList.Add("mp4");
|
||||
startInfo.ArgumentList.Add("-movflags");
|
||||
startInfo.ArgumentList.Add("disable_chpl"); //Disable Nero chapters format
|
||||
startInfo.ArgumentList.Add(outputFile);
|
||||
startInfo.ArgumentList.Add("-y"); //overwrite existing
|
||||
|
||||
return startInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user