DGDemux development

User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

Please test with DGIndexNV and tell me if that is OK. DGDemux/DGDemuxGUI security may not be implemented exactly the same.
DAE avatar
stRAf3r
Posts: 4
Joined: Mon Mar 30, 2020 1:15 pm

Re: DGDemux development

Post by stRAf3r »

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! :salute:
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

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.
DAE avatar
WGZ
Posts: 4
Joined: Tue Apr 28, 2020 11:29 am

Re: DGDemux development

Post by WGZ »

Hello,
I tried MBSerialNumber and I got the same result:
D:\USERS\WGZ>MBSerialNumber.exe
Connected to ROOT\CIMV2 WMI namespace
returned 1, count 0
No objects found
Cleaning up...
Cleanup successfull
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".
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.
:|
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

DGIndex does not use WMI as it is unsecured. You won't be able to run DGIndexNV without nVidia graphics.
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.
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.

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.
DAE avatar
WGZ
Posts: 4
Joined: Tue Apr 28, 2020 11:29 am

Re: DGDemux development

Post by WGZ »

Ok, I'll try to figure this out.
Thanks anyway.
DAE avatar
domy
Posts: 28
Joined: Fri Mar 20, 2020 10:50 am

Re: DGDemux development

Post by domy »

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.

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."
}

Examples:

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"
Tell me watcha think :)

EDIT 2020-05-03: Updated script to include desync stats.
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

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.
DAE avatar
renols
Posts: 150
Joined: Tue Feb 22, 2011 2:34 am

Re: DGDemux development

Post by renols »

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
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

renols wrote:
Fri May 01, 2020 12:54 pm
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.
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!
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

@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. :wow:
DAE avatar
domy
Posts: 28
Joined: Fri Mar 20, 2020 10:50 am

Re: DGDemux development

Post by domy »

Thanks!
Rocky wrote:
Fri May 01, 2020 1:01 pm
You said you could maybe cut the maximum desync by half (40 to 20 samples). What is your thinking there?
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 :)
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

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.
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.
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.

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.
DAE avatar
Guest

Re: DGDemux development

Post by Guest »

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
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

Thank you, gonca. Looks good. Perhaps the embedded AC3 could be closer. What disk is that?
DAE avatar
Guest

Re: DGDemux development

Post by Guest »

The movie is Hellboy, from 2004
It is a 2 hour 12 minute 27 second version
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

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. :scratch:
DAE avatar
domy
Posts: 28
Joined: Fri Mar 20, 2020 10:50 am

Re: DGDemux development

Post by domy »

Rocky wrote:
Sat May 02, 2020 8:37 am
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.
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.
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

OK, thank you, I'll look into that. Please let me know if you discover anything.
DAE avatar
Guest 2
Posts: 903
Joined: Mon Sep 20, 2010 2:18 pm

Re: DGDemux development

Post by Guest 2 »

Tiny issue: you can push the abort button even before starting a demux and a "woosh" plays. :D
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

I consider that to be a feature. ;)
DAE avatar
domy
Posts: 28
Joined: Fri Mar 20, 2020 10:50 am

Re: DGDemux development

Post by domy »

Rocky wrote:
Sat May 02, 2020 8:37 am
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.
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:

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 ...
I also added some desync stats at the end to compare different strategies (I've updated the script in my previous post). These are the simulated desync stats for the Monsters University disc (values are given in samples):

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
Cutting 1 frame when desync is above 20 or 40 samples is a toss-up. One has more consistent, but higher desync. The other has lower average desync, but with higher variation. Cutting as many frames as needed to get below 20 samples desync, however, gets the average and median down to nearly zero, with comparable variation to the other strategies.
DAE avatar
Guest

Re: DGDemux development

Post by Guest »

Guest 2 wrote:
Sun May 03, 2020 4:56 am
Tiny issue: you can push the abort button even before starting a demux and a "woosh" plays. :D
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
:lol:
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

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. :scratch:

https://chess24.com/en/watch/live-tourn ... al-4/2/1/1
User avatar
Rocky
Posts: 3621
Joined: Fri Sep 06, 2019 12:57 pm

Re: DGDemux development

Post by Rocky »

domy wrote:
Sun May 03, 2020 7:53 am
[lots of great stuff]
Cool, domy, thank you. I'll check it out after the chess.
Post Reply