DGDemux development
Re: DGDemux development
Please test with DGIndexNV and tell me if that is OK. DGDemux/DGDemuxGUI security may not be implemented exactly the same.
Re: DGDemux development
well - doesn't matter: running DGDemux from cmd line without parameters or starting the GUI - nothing happens...
running DGIndexNV will show missing nvcuda + nvcuvid .dlls
will read up parts in your links about WMI and check... thx!
running DGIndexNV will show missing nvcuda + nvcuvid .dlls
will read up parts in your links about WMI and check... thx!
Re: DGDemux development
The only hope is to solve your WMI problem on that machine. In the third link there is one guy that claims to have solved it.
Re: DGDemux development
Hello,
I tried MBSerialNumber and I got the same result:
The WMI Diagnosis Utility has got me this:
https://pastebin.com/5T9pHvWC
PS. Even if it says Windows Server 2012, this is Windows Server 2019.
Meanwhile I can not find DGIndexNV, only DGIndex (included in MeGUI) and it's working fine.
I tried MBSerialNumber and I got the same result:
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
The WMI Diagnosis Utility has got me this:
https://pastebin.com/5T9pHvWC
PS. Even if it says Windows Server 2012, this is Windows Server 2019.
Meanwhile I can not find DGIndexNV, only DGIndex (included in MeGUI) and it's working fine.
Re: DGDemux development
DGIndex does not use WMI as it is unsecured. You won't be able to run DGIndexNV without nVidia graphics.
Yes, I could detect the error and fall back to an HDD serial, but this opens holes in my security so I won't do it just to cover an occasional server machine with broken WMI.
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.
Yes, I could detect the error and fall back to an HDD serial, but this opens holes in my security so I won't do it just to cover an occasional server machine with broken WMI.
Re: DGDemux development
Ok, I'll try to figure this out.
Thanks anyway.
Thanks anyway.
Re: DGDemux development
Yeah, I'm a developer
I think I've found a way to determine exactly at which gaps frames should be deleted for optimum sync. Turns out that for Monsters University, you wanna cut at 129 of the total 134 gaps.
What I do is keep track of a THD overrun (how much longer the THD stream is than the video stream) accumulator and only cut a THD frame if the accumulated overrun is >= 40 samples. Simple example:
Segment 0: Video is 5 frames (= 0.2085417 sec), Audio is 251 frames (= 0.209167 sec). Overrun is 0.00062497 sec, or 30 samples. Overrun accumulator is now 30 samples. This is less than 40, so we don't cut segment 0's THD stream.
Segment 1: Video is 10 frames (= 0.417083 sec), Audio is 501 frames (= 0.4175 sec). Overrun is 0.000417 sec, or 20 samples. Overrun accumulator is now 50 samples. This is more than 40, so we cut a frame off the end of segment 1's THD stream. Overrun accumulator is now 10 samples.
Segment 2: ...
We never cut the last segment.
This process always keeps desync between 0 and 40 samples. It may be possible to improve upon this to keep desync between -20 and +20 samples, but I haven't explored that yet.
I implemented a powershell script that simulates this process for any given disc. At the end you'll get a summary of which segments should be cut, what the total length of the video and processed and unprocessed audio is, and what the ultimate desync at the end is (6 samples for Monsters University). You will need ffmpeg and your truehd.exe in your path. I don't know what the minimum powershell version is, but it works with powershell 5.1.
Examples:
Tell me watcha think
EDIT 2020-05-03: Updated script to include desync stats.
I think I've found a way to determine exactly at which gaps frames should be deleted for optimum sync. Turns out that for Monsters University, you wanna cut at 129 of the total 134 gaps.
What I do is keep track of a THD overrun (how much longer the THD stream is than the video stream) accumulator and only cut a THD frame if the accumulated overrun is >= 40 samples. Simple example:
Segment 0: Video is 5 frames (= 0.2085417 sec), Audio is 251 frames (= 0.209167 sec). Overrun is 0.00062497 sec, or 30 samples. Overrun accumulator is now 30 samples. This is less than 40, so we don't cut segment 0's THD stream.
Segment 1: Video is 10 frames (= 0.417083 sec), Audio is 501 frames (= 0.4175 sec). Overrun is 0.000417 sec, or 20 samples. Overrun accumulator is now 50 samples. This is more than 40, so we cut a frame off the end of segment 1's THD stream. Overrun accumulator is now 10 samples.
Segment 2: ...
We never cut the last segment.
This process always keeps desync between 0 and 40 samples. It may be possible to improve upon this to keep desync between -20 and +20 samples, but I haven't explored that yet.
I implemented a powershell script that simulates this process for any given disc. At the end you'll get a summary of which segments should be cut, what the total length of the video and processed and unprocessed audio is, and what the ultimate desync at the end is (6 samples for Monsters University). You will need ffmpeg and your truehd.exe in your path. I don't know what the minimum powershell version is, but it works with powershell 5.1.
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"
EDIT 2020-05-03: Updated script to include desync stats.
Re: DGDemux development
Wow, domy. Thanks. Gonna take a while to digest that, especially as we have just had a big lunch and we'll all take a nap now. You'll want full processing power for our reply!
Superficially, though, it looks like our Bresenham-like algorithm combined with our safe THD frame deletion. I mentioned earlier that it remained as an option.
Superficially, though, it looks like our Bresenham-like algorithm combined with our safe THD frame deletion. I mentioned earlier that it remained as an option.
Re: DGDemux development
Just did a few tests with the dgdemux_test version. I renamed it to dgdemux and copied it over the old one.
I have tried with Cars and Dory. Cars have 95 m2ts files and Dory have 54 m2ts files.
I demuxed all audio tracks and the video, and then muxed them with mkvmerge.
Cars didn't have any E-AC3 tracks, but Dory had no less than three E-AC3 tracks.
Both mkv files, as far as I can judge, are in sync for all audios.
I have not tried to demux the audios with an older version of dgdemux/dgindexnv, and mux them and compare. Actually I think I will go and do that and compare the two.
renols
I have tried with Cars and Dory. Cars have 95 m2ts files and Dory have 54 m2ts files.
I demuxed all audio tracks and the video, and then muxed them with mkvmerge.
Cars didn't have any E-AC3 tracks, but Dory had no less than three E-AC3 tracks.
Both mkv files, as far as I can judge, are in sync for all audios.
I have not tried to demux the audios with an older version of dgdemux/dgindexnv, and mux them and compare. Actually I think I will go and do that and compare the two.
renols
Re: DGDemux development
No point, really. Previous versions gave fine sync but did not play nice with ffmpeg. The only difference should be that the earlier versions:
1. Use Bresenham so they may not delete a frame at every gap. The following post shows the difference is only 4ms for MONSTERS.
2. Delete different frames (arbitrary versus always the minor preceding a double).
Thank you for your testing, renols!
Re: DGDemux development
@domy
Great work and proof of concept!
Your method combines the Bresenham-like algorithm with the safe frame deletion, as I mentioned. Comparing that to simply adjusting each double as I do now we have theoretically a difference of about 4ms [(134 - 129) / 1200]. Seems too small to justify the significant added complexity. But I agree that it is theoretically superior.
You said you could maybe cut the maximum desync by half (40 to 20 samples). What is your thinking there?
Catch ya later, bro. I have to go help Sherman with some stuff. Impressive, he learned to code pretty decently in less than a week.
Great work and proof of concept!
Your method combines the Bresenham-like algorithm with the safe frame deletion, as I mentioned. Comparing that to simply adjusting each double as I do now we have theoretically a difference of about 4ms [(134 - 129) / 1200]. Seems too small to justify the significant added complexity. But I agree that it is theoretically superior.
You said you could maybe cut the maximum desync by half (40 to 20 samples). What is your thinking there?
Catch ya later, bro. I have to go help Sherman with some stuff. Impressive, he learned to code pretty decently in less than a week.
Re: DGDemux development
Thanks!
If it turns out that we can only safely cut at most 1 frame per segment, the method in my script is as good as it gets I think. The desync will usually be between 0 and +40 samples, but it might temporarily climb higher depending on how much overrun the segments have. 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. It tends to even out and decrease again, at least on the two discs that I analyzed, so this is really a very micro optimization
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.
If it turns out that we can only safely cut at most 1 frame per segment, the method in my script is as good as it gets I think. The desync will usually be between 0 and +40 samples, but it might temporarily climb higher depending on how much overrun the segments have. 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. It tends to even out and decrease again, at least on the two discs that I analyzed, so this is really a very micro optimization
Re: DGDemux development
Interesting, 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.
EDIT: Did you test the MONSTERS EAC3 stream? We have an unconfirmed report that it is off by 4 seconds using my test build. renols and myself could not duplicate it.
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.
EDIT: Did you test the MONSTERS EAC3 stream? We have an unconfirmed report that it is off by 4 seconds using my test build. renols and myself could not duplicate it.
Re: DGDemux development
Don't know if this is relevant anymore but finally found a UHD with several m2ts files and thd+atmos
Main playlist has 32 m2ts files
Demuxed video, thd, and embedded ac3 tracks
video 7947.940 sec
thd 7947.929 sec
ac3 7947.968 sec
Main playlist has 32 m2ts files
Demuxed video, thd, and embedded ac3 tracks
video 7947.940 sec
thd 7947.929 sec
ac3 7947.968 sec
Re: DGDemux development
Thank you, gonca. Looks good. Perhaps the embedded AC3 could be closer. What disk is that?
Re: DGDemux development
The movie is Hellboy, from 2004
It is a 2 hour 12 minute 27 second version
It is a 2 hour 12 minute 27 second version
Re: DGDemux development
OK, thank you. I have that one too. I'll have a look at the embedded AC3 to see if it can be tightened up. Deleting an addition 32ms AC3 frame would bring it to within 4ms of the video duration. Maybe we need to consider end of stream to be a "gap". Problem is there is no more stream to delete a frame from. On the other hand, if it's just a little extra audio at the end, no big deal.
Re: DGDemux development
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.
It seems there's a playback issue though: at around 1:29:17, the audio blanks out very shortly (mpv player). The same spot plays fine in Audacity however; not sure what's going on there.
Re: DGDemux development
OK, thank you, I'll look into that. Please let me know if you discover anything.
Re: DGDemux development
Tiny issue: you can push the abort button even before starting a demux and a "woosh" plays.
Re: DGDemux development
I consider that to be a feature.
Re: DGDemux development
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
Re: DGDemux development
You don't even have to load a playlist
I turn up the volume and hit abort a few times
Gets rid of people that are annoying you
Good feature
Turn up the volume real loud and put the phone close to the speaker
Good for telemarketers
Re: DGDemux development
Right on!
To tell the truth, earlier I used to get things crashed and stuck. Easier to open the GUI and hit Abort than to mess around with task manager. The Abort will kill any instances it finds, stuck or not. Probably could change it now but why bother?
Build 24 will fix the THD, add a no gaps adjustment option (typically for research and debugging), and add a Show button which will show the demux command line that would be executed but without actually doing the demuxing. I use it to get the command line for inserting in VS debugging properties, but it can be useful for other purposes.
Magnus versus the Yank starts in 25 minutes. Should be EXCITING! I don't know who to root for. Me like Magnus, me like Yanks.
https://chess24.com/en/watch/live-tourn ... al-4/2/1/1
To tell the truth, earlier I used to get things crashed and stuck. Easier to open the GUI and hit Abort than to mess around with task manager. The Abort will kill any instances it finds, stuck or not. Probably could change it now but why bother?
Build 24 will fix the THD, add a no gaps adjustment option (typically for research and debugging), and add a Show button which will show the demux command line that would be executed but without actually doing the demuxing. I use it to get the command line for inserting in VS debugging properties, but it can be useful for other purposes.
Magnus versus the Yank starts in 25 minutes. Should be EXCITING! I don't know who to root for. Me like Magnus, me like Yanks.
https://chess24.com/en/watch/live-tourn ... al-4/2/1/1