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

Silent channels detection #7

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions Source/CLI/CLI_Help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ return_value Help(ostream& Out, const char* Name, bool Full)
" Use the old AAC format without extensions.\n"
" By default HE-AAC (AAC with SBR extension) is used.\n"
"\n"
" --keep-silent\n"
" Ignore detetion of silent (also the one with a bit of noise) channels\n"
" and encode them.\n"
" By default silent channels are discarded if last 7 channels are detected\n"
" as having no relevant content\n"
"\n"
<< endl;

return ReturnValue_OK;
Expand Down
2 changes: 1 addition & 1 deletion Source/CLI/CLI_Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
int main(int argc, const char* argv[])
{
// Environment
setlocale(LC_ALL, "");
setlocale(LC_ALL, "C");

// Configure
Core C;
Expand Down
4 changes: 4 additions & 0 deletions Source/CLI/CommandLine_Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ return_value Parse(Core& C, int argc, const char* argv_ansi[], LPCWSTR argv[])
{
C.KeepTemp = true;
}
else if (strcmp(argv_ansi[i], "--keep-silent") == 0)
{
C.KeepSilent = true;
}
else if (!strcmp(argv_ansi[i], "--legacy-aac"))
{
C.LegacyAac = true;
Expand Down
9 changes: 4 additions & 5 deletions Source/CLI/LeaveSD.rc
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <winresrc.h>

VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,1,4,0
PRODUCTVERSION 0,1,4,0
FILEVERSION 0,1,5,0
PRODUCTVERSION 0,1,5,0
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x1L
Expand All @@ -18,13 +18,12 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "MediaArea.net"
VALUE "FileDescription", ""
VALUE "FileVersion", "0,1,4,0"
VALUE "FileVersion", "0.1.5.0"
VALUE "InternalName", "LeaveSD"
VALUE "LegalCopyright", "MediaArea.net"
VALUE "OriginalFilename", "LeaveSD.exe"
VALUE "ProductName", "LeaveSD"
VALUE "ProductVersion", "0,1,4,0"
VALUE "ProductVersion", "0.1.5.0"
END
END
BLOCK "VarFileInfo"
Expand Down
2 changes: 1 addition & 1 deletion Source/Common/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ enum return_value
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#define Program_Version "0.1.4"
#define Program_Version "0.1.5"
//---------------------------------------------------------------------------
125 changes: 98 additions & 27 deletions Source/Common/Core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,31 +335,31 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)
}
else
HasVideo = true;
bool HasAudio;
int SourceChannelCount;
ThreadData.ChannelCount = MI.Get(Stream_Audio, 0, __T("Channel(s)"));
if (!MI.Count_Get(Stream_Audio) || MI.Get(Stream_Audio, 0, __T("Format_Version")).empty())
{
EraseBeginEnd.push_back({ __T(",\r\n\"--default-track-flag\","), __T("_7.aac\"") });
WarningMessages.push_back("no audio detected");
HasAudio = false;
SourceChannelCount = 0;
}
else if (ThreadData.ChannelCount == __T("1"))
{
WarningMessages.push_back("1-ch audio detected");
HasAudio = true;
SourceChannelCount = 1;
}
else if (ThreadData.ChannelCount.empty() || ThreadData.ChannelCount == __T("8"))
{
if (ThreadData.ChannelCount.empty())
ThreadData.ChannelCount = __T("8"); // In practice files we got have a channel_configuration of 0 and in practice they have 8 channels
HasAudio = true;
SourceChannelCount = 8;
}
else
{
Data.Finished(Dest, { "Audio channel count not supported" }, {});
return;
}
if (HasAudio && MI.Get(Stream_Audio, 0, __T("Format")) != __T("AAC"))
if (SourceChannelCount && MI.Get(Stream_Audio, 0, __T("Format")) != __T("AAC"))
{
Data.Finished(Dest, { "Only AAC audio is supported" }, {});
return;
Expand Down Expand Up @@ -439,7 +439,7 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)
};

// Decode audio
if (HasAudio)
if (SourceChannelCount)
{
system(AdaptTemplate(__T("LeaveSD_Decode.txt")).c_str());
Data.Delete(TempNamePrefix + __T(".aac"));
Expand All @@ -460,15 +460,76 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)
}

// Encode audio
if (HasAudio)
int DestChannelCount = SourceChannelCount;
if (SourceChannelCount)
{
// Probing silence
if (SourceChannelCount > 1 && !KeepSilent)
{
system(AdaptTemplate(__T("LeaveSD_Probe.txt"), {}, {}, {}).c_str());
File ProbeF;
ProbeF.Open(TempNamePrefix + __T("_log_probe.txt"));
char* ProbeC = new char[ProbeF.Size_Get()];
ProbeF.Read((int8u*)ProbeC, ProbeF.Size_Get());
string Probe(ProbeC, ProbeF.Size_Get());
delete[] ProbeC;
vector<float> Probe_Levels, Probe_Peaks;
size_t Pos1 = (size_t)-1;
for (;;)
{
Pos1 = Probe.find(" RMS level dB: ", Pos1 + 1);
if (Pos1 == string::npos)
break;
Probe_Levels.push_back((float)atof(Probe.c_str() + Pos1 + 15));
}
size_t Pos2 = (size_t)-1;
for (;;)
{
Pos2 = Probe.find(" RMS peak dB: ", Pos2 + 1);
if (Pos2 == string::npos)
break;
Probe_Peaks.push_back((float)atof(Probe.c_str() + Pos2 + 14));
}
if (Probe_Levels.size() == 9 && Probe_Peaks.size() == 9)
{
float Level = -100;
float Peak = -100;
for (size_t i = 1; i < 8; i++) // Channel 0 and summary excluded
{
if (Level < Probe_Levels[i])
Level = Probe_Levels[i];
if (Peak < Probe_Peaks[i])
Peak = Probe_Peaks[i];
}
if (Level < SilenceLevel && Peak < SilencePeak)
{
if (Probe_Levels[0] < SilenceLevel || Probe_Peaks[0] < SilencePeak)
{
WarningMessages.push_back("silence detected in all channels");
}
else
{
WarningMessages.push_back("silence detected in all channels but the first one"
" (RMS level " + to_string((int)(Level - 0.5)) +
" RMS peak " + to_string((int)(Peak - 0.5)) + ")"
" so 1-ch audio encoded");
DestChannelCount = 1;
}
}
}
}

// Encode
EraseBeginEnd.clear();
Replace.clear();
if (MI.Get(Stream_Audio, 0, __T("Channel(s)")) == __T("1"))
if (SourceChannelCount == 1)
{
EraseBeginEnd.push_back({ __T(" -map_channel 0.0.1"), __T("7.aac\"") });
Replace.push_back({ __T("-ac 8"), __T("-ac 2") });
}
if (DestChannelCount == 1)
{
EraseBeginEnd.push_back({ __T(" -map_channel 0.0.1"), __T("7.aac\"") });
}
if (LegacyAac)
{
Replace.push_back({ __T(" -profile:a aac_he"), String() });
Expand Down Expand Up @@ -555,11 +616,11 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)
{
EraseBeginEnd.push_back({ __T(",\r\n\"--sync\","), __T(".avc\"") });
}
if (!HasAudio)
if (!DestChannelCount)
{
EraseBeginEnd.push_back({ __T(",\r\n\"--sync\",\r\n\"0:%DELAY_A%\",\r\n\"--language\",\r\n\"0:mul"), __T("_7.aac\"") });
}
if (MI.Get(Stream_Audio, 0, __T("Channel(s)")) == __T("1"))
if (DestChannelCount == 1)
{
EraseBeginEnd.push_back({ __T(",\r\n\"--sync\",\r\n\"0:%DELAY_A%\",\r\n\"--language\",\r\n\"0:ara"), __T("_7.aac\"") });
}
Expand Down Expand Up @@ -636,16 +697,22 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)

// Check
ThreadData.IsChecking = true;
uint64_t PacketCount[2];
uint64_t PacketCheckingCount[2];
uint64_t PacketCount[2][8];
uint64_t PacketCheckingCount[2][8];
uint64_t Duration, CheckingDuration;
Duration = Ztring(MI.Get(Stream_General, 0, __T("Duration"))).To_int64u();
PacketCount[0] = Ztring(MI.Get(Stream_Video, 0, __T("FrameCount"))).To_int64u();
PacketCount[1] = Ztring(MI.Get(Stream_Audio, 0, __T("FrameCount"))).To_int64u();
PacketCount[0][0] = Ztring(MI.Get(Stream_Video, 0, __T("FrameCount"))).To_int64u();
for (size_t i = 0; i < DestChannelCount; i++)
{
PacketCount[1][i] = Ztring(MI.Get(Stream_Audio, i, __T("FrameCount"))).To_int64u();
}
MI.Open(Dest);
CheckingDuration = Ztring(MI.Get(Stream_General, 0, __T("Duration"))).To_int64u();
PacketCheckingCount[0] = Ztring(MI.Get(Stream_Video, 0, __T("FrameCount"))).To_int64u();
PacketCheckingCount[1] = Ztring(MI.Get(Stream_Audio, 0, __T("FrameCount"))).To_int64u();
PacketCheckingCount[0][0] = Ztring(MI.Get(Stream_Video, 0, __T("FrameCount"))).To_int64u();
for (size_t i = 0; i < DestChannelCount; i++)
{
PacketCheckingCount[1][i] = Ztring(MI.Get(Stream_Audio, i, __T("FrameCount"))).To_int64u();
}
if (Duration && CheckingDuration)
{
uint64_t Ratio = Duration < 20000 ? 10 : 1;
Expand Down Expand Up @@ -673,27 +740,31 @@ void Core::Convert(size_t ID, size_t FilePos, bool FullCheck)
return to_string(Count) + " (" + out.str() + "%)";
};
bool LaunchFullCheck = false;
if (CheckingDuration == 0 || PacketCheckingCount[0] + PacketCheckingCount[1] == 0)
if (CheckingDuration == 0 || PacketCheckingCount[0][0] + PacketCheckingCount[1][0] == 0)
{
Data.Finished(Dest, { "can not read output file" }, {});
return;
}
vector<string> ErrorMessages;
if (PacketCount[0] != PacketCheckingCount[0])
ErrorMessages.push_back(WithPercent((int64_t)(PacketCount[0] - PacketCheckingCount[0]), PacketCount[0]) + " missing video packets");
if (PacketCount[1] / (LegacyAac ? 1 : 2) != PacketCheckingCount[1] && PacketCheckingCount[1] + 10 < PacketCount[1] / (LegacyAac ? 1 : 2)) // Temporary: there is some small issues in MediaInfo counting
if (PacketCount[0][0] != PacketCheckingCount[0][0])
ErrorMessages.push_back(WithPercent((int64_t)(PacketCount[0][0] - PacketCheckingCount[0][0]), PacketCount[0][0]) + " missing video packets");
for (size_t i = 0; i < DestChannelCount; i++)
{
ErrorMessages.push_back(WithPercent((int64_t)(PacketCount[1] - PacketCheckingCount[1]), PacketCount[1]) + " missing audio packets");
LaunchFullCheck = true;
if (PacketCount[1][i] / (LegacyAac ? 1 : 2) != PacketCheckingCount[1][i] && PacketCheckingCount[1][i] + 10 < PacketCount[1][i] / (LegacyAac ? 1 : 2)) // Temporary: there is some small issues in MediaInfo counting
{
ErrorMessages.push_back(WithPercent((int64_t)(PacketCount[1][i] - PacketCheckingCount[1][i]), PacketCount[1][i]) + " missing audio packets");
LaunchFullCheck = true;
break;
}
}
if (HasAudio)
if (DestChannelCount)
{
if (!ThreadData.Stats_InvalidAudioPackets.empty())
WarningMessages.push_back(WithPercent(ThreadData.Stats_InvalidAudioPackets.size(), PacketCount[1]) + " invalid AAC syncs in audio packet (skipped)");
WarningMessages.push_back(WithPercent(ThreadData.Stats_InvalidAudioPackets.size(), PacketCount[1][0]) + " invalid AAC syncs in audio packet (skipped)");
if (!ThreadData.Stats_InvalidAacPackets.empty())
WarningMessages.push_back(WithPercent(ThreadData.Stats_InvalidAacPackets.size(), PacketCount[1]) + " invalid AAC packets (replaced by silent)");
WarningMessages.push_back(WithPercent(ThreadData.Stats_InvalidAacPackets.size(), PacketCount[1][0]) + " invalid AAC packets (replaced by silent)");
if (ThreadData.Stats_AudioPacketInvalidSize)
WarningMessages.push_back(WithPercent(ThreadData.Stats_AudioPacketInvalidSize, PacketCount[1]) + " invalid audio packets (skipped)");
WarningMessages.push_back(WithPercent(ThreadData.Stats_AudioPacketInvalidSize, PacketCount[1][0]) + " invalid audio packets (skipped)");
}
if (ThreadData.Stats_JunkBytes)
WarningMessages.push_back(to_string(ThreadData.Stats_JunkBytes) + " junk bytes");
Expand Down
3 changes: 3 additions & 0 deletions Source/Common/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class Core
bool ForceExistingFiles = false;
bool SkipExistingFiles = false;
bool LegacyAac = false;
bool KeepSilent = false;
float SilenceLevel = -70;
float SilencePeak = -50;

bool Scan = false;

Expand Down
2 changes: 1 addition & 1 deletion Source/Templates/LeaveSD_Encode.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ffmpeg.exe -y -f s16le -ar 44.1k -ac 8 -i "%TEMPPATH%.aif" -map_channel 0.0.0 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_0.aac" -map_channel 0.0.1 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_1.aac" -map_channel 0.0.2 "%TEMPPATH%_2.aac" -map_channel 0.0.3 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_3.aac" -map_channel 0.0.4 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_4.aac" -map_channel 0.0.5 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_5.aac" -map_channel 0.0.6 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_6.aac" -map_channel 0.0.7 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_7.aac" >"%TEMPPATH%_log_encode.txt" 2>&1
ffmpeg.exe -y -f s16le -ar 44.1k -ac 8 -i "%TEMPPATH%.aif" -map_channel 0.0.0 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_0.aac" -map_channel 0.0.1 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_1.aac" -map_channel 0.0.2 -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_2.aac" -map_channel 0.0.3 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_3.aac" -map_channel 0.0.4 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_4.aac" -map_channel 0.0.5 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_5.aac" -map_channel 0.0.6 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_6.aac" -map_channel 0.0.7 -b:a 48k -c:a libfdk_aac -profile:a aac_he "%TEMPPATH%_7.aac" >"%TEMPPATH%_log_encode.txt" 2>&1
1 change: 1 addition & 0 deletions Source/Templates/LeaveSD_Probe.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ffmpeg.exe -y -f s16le -ar 44.1k -ac 8 -i "%TEMPPATH%.aif" -af astats -f null - >"%TEMPPATH%_log_probe.txt" 2>&1