Re: DGDemux development
Posted: Fri May 01, 2020 8:31 am
Please test with DGIndexNV and tell me if that is OK. DGDemux/DGDemuxGUI security may not be implemented exactly the same.
In the links you proposed, I tried look up for the DCOM of the last one and there is already a check on "Enable Distributed COM on this computer".D:\USERS\WGZ>MBSerialNumber.exe
Connected to ROOT\CIMV2 WMI namespace
returned 1, count 0
No objects found
Cleaning up...
Cleanup successfull
So your WMI is broken and it is nothing to do with DGDemux. Sorry, but you'll have to try to resolve this on your own, as I do not have time to try to debug this when it is clearly not an issue with DG tools, and I do not have access to a Windows server, etc.28990 16:12:25 (0) ** ERROR: WMIDiag detected issues that could prevent WMI to work properly!. Check 'C:\USERS\ADMINISTRATOR\APPDATA\LOCAL\TEMP\WMIDIAG-V2.2_2K12R2.SRV.RTM.64_SERVER_2020.05.01_16.02.38.LOG' for details.
Code: Select all
# Dependencies in PATH: ffmpeg, truehd
param (
[Parameter(Mandatory = $true)]
[string]
$StreamDirectory,
[int[]]
$Segments,
[string]
$DGDemuxM2tsList
)
function AnalyzeThdCuts([string]$m2tsDir, [int[]]$segmentMap) {
$numSegments = $segmentMap.Count
# remembers frame durations for each segments' video and audio
$segmentAudioDurations = New-Object int[] $numSegments
$segmentVideoDurations = New-Object int[] $numSegments
# remembers which segments' audio we cut (or didn't)
$cutSegments = New-Object Collections.Generic.List[Int]
$uncutSegments = New-Object Collections.Generic.List[Int]
$numCutFramesTable = @{ }
# accumulator for audio overrun
[double]$overrunAcc = 0.0
# video and audio frame accumulators
[int]$totalVideoFrames = 0
[int]$totalAudioFrames = 0
# desync tracker
$segmentDesync = New-Object int[] $numSegments
$i = 1;
foreach ($segment in $segmentMap) {
Write-Host "Processing segment $segment (Progress: $i / $numSegments)"
# update desync
$desyncSamples = SecondsToAudioSamples $overrunAcc
Write-Host ("Desync is now {0} samples." -f $desyncSamples)
$segmentDesync[$i - 1] = $desyncSamples
$m2tsfilename = $segment.ToString().PadLeft(5, '0');
$m2ts = "$m2tsDir\$m2tsfilename.m2ts";
# count video and audio frames of the current m2ts, and update the respective accumulators
$vf = CountVideoFrames "$m2ts"
$af = CountAudioFrames "$m2ts"
$totalVideoFrames += $vf
$totalAudioFrames += $af
$segmentAudioDurations[$i - 1] = $af
$segmentVideoDurations[$i - 1] = $vf
Write-Host ("V: {0:n0} frames, A: {1:n0} frames" -f $vf, $af)
# calculate how much longer the audio stream is than the video stream
$overrun = GetOverrun $vf $af
$overrunAcc += $overrun
$overrunAccSamples = (SecondsToAudioSamples $overrunAcc)
Write-Host ("Overrun for segment {0}: {1} ({2:n0} samples); accumulated: {3} ({4} samples)" -f $segment, $overrun, (SecondsToAudioSamples $overrun), $overrunAcc, $overrunAccSamples)
# we're not cutting the last audio frame
if ($i -eq $numSegments) {
Write-Host "LEAVING LAST FRAME"
$uncutSegments.Add($segment)
}
else {
$f = 0
# cut frames until overrun < 20 samples
while ($overrunAccSamples -ge 20) {
$f += 1
$overrunAccSamples -= 40
}
# only cut one frame if overrun is >= 20 samples
# if ($overrunAccSamples -ge 20) {
# $f = 1
# $overrunAccSamples -= 40
# }
# only cut one frame if overrun is >= 40 samples
# if ($overrunAccSamples -ge 40) {
# $f = 1
# $overrunAccSamples -= 40
# }
if ($f -gt 0) {
$plural = if ($f -gt 1) {"S"} else {""}
Write-Host ("CUT {0} FRAME{1}" -f $f,$plural)
$overrunAcc -= (AudioFramesToSeconds $f)
$cutSegments.Add($segment)
$numCutFramesTable[$segment] = $f
}
else {
Write-Host "LEAVE FRAME"
$uncutSegments.Add($segment)
}
}
Write-Host
$i++;
}
$numCutFrames = ($numCutFramesTable.Values | Measure-Object -Sum).Sum
$lastSegmentOverrun = (AudioFramesToSeconds $segmentAudioDurations[$numSegments - 1]) - (VideoFramesToSeconds $segmentVideoDurations[$numSegments - 1])
$lt = (VideoFramesToSeconds $totalVideoFrames) + $lastSegmentOverrun
$lactual = AudioFramesToSeconds ($totalAudioFrames - $numCutFrames)
$ldiff = $lactual - $lt
$desyncStats = $segmentDesync | Measure-Object -AllStats
$desyncMedian = GetMedian $segmentDesync
$cutSegmentsStr = $cutSegments | ForEach-Object { ("{0} ({1})" -f $_,$numCutFramesTable[$_]) }
Write-Host "STATS"
Write-Host ("Video length: {0:n0} frames ({1} seconds)" -f $totalVideoFrames, (VideoFramesToSeconds $totalVideoFrames))
Write-AudioLength "Audio length" $totalAudioFrames
Write-Host ("Cut segments: {0}/{1}" -f $numCutFrames, $numSegments)
Write-Host " Cut: $cutSegmentsStr"
Write-Host " Left: $uncutSegments"
Write-AudioLength "Audio length after cuts" ($totalAudioFrames - $numCutFrames)
Write-Host "Target audio length: $lt"
Write-Host ("Audio samples off target: {0}" -f (SecondsToAudioSamples $ldiff))
Write-Host ("Desync: Min={0}, Max={1}, Avg={2}, Median={3}, StdDev={4}" -f $desyncStats.Minimum, $desyncStats.Maximum, $desyncStats.Average, $desyncMedian, $desyncStats.StandardDeviation)
}
function Write-AudioLength([string]$description, [int]$frames) {
Write-Host ("{0}: {1:n0} frames ({2:n0} samples, {3} seconds)" -f $description, $frames, (AudioFramesToSamples $frames), (AudioFramesToSeconds $frames))
}
function CountVideoFrames($m2ts) {
$out = ffmpeg -i "$m2ts" -map 0:v:0 -c copy -f null - 2>&1 | Select-String "frame"
$frameCountLine = $out[$out.Length - 1].ToString()
$frames = ($frameCountLine -split '=')[1] -replace "[^0-9]" , ''
[int]::Parse($frames)
}
function CountAudioFrames($m2ts) {
$filename = Split-Path "$m2ts" -leaf
$thdFile = "$env:TEMP\$filename.thd"
if (Test-Path "$thdFile") {
Remove-Item "$thdFile"
}
ffmpeg -i "$m2ts" -vn -acodec copy "$thdFile" *>$null
$line = '\n' | truehd "$thdFile" | Select-String "duration"
Remove-Item "$thdFile"
$parts = $line -split ' '
$numFrames = $parts[$parts.Count - 4]
[int]::Parse($numFrames)
}
function GetOverrun([int]$videoFrames, [int]$audioFrames) {
$ad = ($audioFrames * 40) / 48000
$vd = VideoFramesToSeconds $videoFrames
$ad - $vd
}
function GetMedian([int[]]$numbers) {
$sortedNumbers = @($numbers | Sort-Object)
if ($numberSeries.Count % 2) {
# Odd, pick the middle
$sortedNumbers[($sortedNumbers.Count / 2) - 1]
} else {
# Even, average the middle two
($sortedNumbers[($sortedNumbers.Count / 2)] + $sortedNumbers[($sortedNumbers.Count / 2) - 1]) / 2
}
}
function VideoFramesToSeconds([int]$frames) {
($frames * 1001) / 24000
}
function AudioFramesToSamples([int]$frames) {
$frames * 40
}
function AudioFramesToSeconds([int]$frames) {
(AudioFramesToSamples $frames) / 48000
}
function SecondsToAudioSamples([double]$seconds) {
[int]$samples = [math]::Round($seconds * 48000, [System.MidpointRounding]::AwayFromZero)
$samples
}
function ParseDgDemuxSegmentMap([string]$dgdemuxSegmentMap) {
[int[]]$segments = $dgdemuxSegmentMap -split '\+' | ForEach-Object { $_ -replace ".m2ts", '' } | ForEach-Object { [int]::Parse($_) }
$segments
}
function EnsureExecutableIsInPath([string]$exe) {
if ($null -eq (Get-Command "$exe" -ErrorAction SilentlyContinue)) {
throw "Unable to find $exe in your PATH."
}
}
EnsureExecutableIsInPath "ffmpeg"
EnsureExecutableIsInPath "truehd"
if ($Segments) {
AnalyzeThdCuts $StreamDirectory $Segments
}
elseif ($DGDemuxM2tsList) {
$segmentMap = ParseDgDemuxSegmentMap $DGDemuxM2tsList
AnalyzeThdCuts $StreamDirectory $segmentMap
}
else {
Write-Error "You must supply either -Segments or -DGDemuxM2tsList."
}
Code: Select all
PS> .\script.ps1 -StreamDirectory "G:\BDMV\STREAM" -Segments 55,56,86
PS> .\script.ps1 -StreamDirectory "G:\BDMV\STREAM" -DGDemuxM2tsList "00055.m2ts+00056.m2ts+00086.m2ts"
No point, really. Previous versions gave fine sync but did not play nice with ffmpeg. The only difference should be that the earlier versions:
If it is safe to cut 2 (or more) minor frames off at the end of the stream, instead of only 1, you could remove frames until the accumulated overrun is less than +20 samples. Then you would end up with a desync between -20 and +20 samples at the end/start of every THD segment, which is as good as it gets without Dolby's encoder.
But still that's only 2ms desync. OK, you seek perfection. Nothing wrong with that, unless the code becomes so complicated that it becomes error-prone and hard to maintain. So for now I'm going to go initially with the simple approach. If experience forces it, we can go to Bresenham-like.For example, if you get 3 consecutive segments with an overrun of 80 samples (which is realistic), your desync would climb north of +100 samples.
Monsters University 00800 1101 I assume? Audacity reports 6228.416 seconds, which is within 0.02 of the video length, so that's pretty good.
Yeah, since we can only cut along frame boundaries, there will always be some desync, but it's possible to get it down to a range of -20..+20 samples. Here's a modified output of the script, changed to delete as many frames as needed at the end of a segment, which hopefully serves as a good example of what I mean:Rocky wrote: ↑Sat May 02, 2020 8:37 amInteresting, although I don't follow your full reasoning. I think it would be no problem to delete two minors at the end. We can delete only by frame so it is going to matter where the desync occurs relative to the frame boundaries. Maybe an example could help me to understand you better.
Code: Select all
Processing segment 55 (Progress: 1 / 14)
Desync is now 0 samples.
Overrun for segment 55: 26 samples; accumulated: 26 samples
CUT 1 FRAME
Processing segment 56 (Progress: 2 / 14)
Desync is now -14 samples.
Overrun for segment 56: 16 samples; accumulated: 2 samples
LEAVE FRAME
Processing segment 86 (Progress: 3 / 14)
Desync is now 2 samples.
Overrun for segment 86: 40 samples; accumulated: 42 samples
CUT 1 FRAME
Processing segment 58 (Progress: 4 / 14)
Desync is now 2 samples.
Overrun for segment 58: 62 samples; accumulated: 64 samples
CUT 2 FRAMES
Processing segment 74 (Progress: 5 / 14)
Desync is now -16 samples.
Overrun for segment 74: 52 samples; accumulated: 36 samples
CUT 1 FRAME
Processing segment 59 (Progress: 6 / 14)
Desync is now -4 samples.
// And so on ...
Code: Select all
// cut as many frames as needed, until accumulated desync < 20 samples
Desync: Min=-20, Max=18, Avg=-2, Median=-2, StdDev=11.289937403155264
// cut one frame when accumulated desync >= 20 samples
Desync: Min=-20, Max=40, Avg=16.962962962962962, Median=18, StdDev=13.922711718738407
// cut one frame when accumulated desync >= 40 samples
Desync: Min=0, Max=40, Avg=19.037037037037038, Median=21, StdDev=11.98718682059108
You don't even have to load a playlist