﻿    <#
      .SYNOPSIS
       Video and audio playback quality assessment.

      .DESCRIPTION
       The MediaQuality assessment supports local and streaming scenarios with the Movies & TV app, Groove app, and Netflix.
       Options are available to control light or verbose tracing and playback duration.
       
      .PARAMETER PlaybackDuration
       The workload duration in minutes.
       
      .PARAMETER CustomPlaybackSource
       An optional path to a media file that is to be played for this Assessment.
       
      .PARAMETER PlaybackSource
       A PlaybackSourceEnum value, such as -3, or a video resolution, such as 1080.
       
      .PARAMETER ExtendedLogs
       A flag to signal writing out a set of extra commands lines, such as dxdiag /t, pertenant to evaluating media playback quality.
       
      .PARAMETER TraceOption
       Integer, 1 to enable the light WPR profile, any other integer to enable the verbose WPR profile.
       
      .PARAMETER StopDiskUsage
       Duration in minutes, added on top of the initial duration.
       
      .EXAMPLE
       MediaQuality -PlaybackSource 1080 -Minutes 2 -FiniteDuration
       
    #>
    
Param( 
    [parameter(Mandatory=$true)][int32]$PlaybackDuration, # specified workload duration in minutes
    [parameter(Mandatory=$false)][string]$CustomPlaybackSource,
    
    <#
        >0: local video specific resolution
        0: local video autoselect
        -1: custom local video
        -2: custom streaming video
        -3: default streaming video
        -4: netflix
        -5: default streaming audio
        -6: custom streaming audio
    #>
    
    [parameter(Mandatory=$true)][int32]$PlaybackSource, 
    [parameter(Mandatory=$false)][switch]$ExtendedLogs, 
    [parameter(Mandatory=$false)][int32]$TraceOption, 
    [parameter(Mandatory=$false)][int32]$StopDiskUsage # specified duration in minutes, added on top of initial duration
)



try {

    # Import the modules and functions
    try {
        Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'MediaAssessment.psm1') -ErrorAction Stop
    }
    catch {
        throw "Failed to import module MediaAssessment.psm1."
    }

    try {
        . $PSScriptRoot\scripts\AssessmentHelper.ps1
    }
    catch {
        throw "Failed to execute AssessmentHelper.ps1"
    }

    #*****************************************************************
    #
    #                Definitions and imports
    #
    #*****************************************************************

    # Recover the INI configuration file contents.  
    try {

        $INIFilePath = (Join-Path -Path $PSScriptRoot -ChildPath (Join-Path -Path resources -ChildPath 'MediaAssessment.ini'))
        Get-INI -INIFilePath $INIFilePath -ErrorAction Stop
    }
    catch {
        throw "Failed to import module MediaAssessment.psm1."
    }

    #set to true if you need to hang the script at the end to check console output
    $debugFlag = $false

    # Need to know where our script is so we can use full path to all our dependent files.
    $Global:InvocationPath = Split-Path -Path $MyInvocation.MyCommand.Path -Parent

    # Tools and resources
    $arch         = Get-ProcessorArchitecture
    $ResourcePath = Join-Path -path $InvocationPath resources
    $ScriptsPath  = Join-Path -path $InvocationPath scripts

    $WPTPath      = Join-Path -path $InvocationPath $arch
    $env:path     = "$WPTPath;" + $env:path

    # Analysis profiles
    
    ## WPRP_FILE
    $WPRP_FILE = Get-ConfigValue -Section 'PROFILES' -Key 'WPRP_FILE' -Default 'resources\Media.wprp'
    if (-not(Test-ValidFileName -FileName $WPRP_FILE -Extension '.wprp')) {
        throw ("Invalid WPRP_FILE name '{0}'. Must be avlid file name with .wprp extension. Please check config file PROFILES section, WPRP_FILE key." -f $WPRP_FILE)
    }   
    $WPRP_FILE = Get-RootName -FileName $WPRP_FILE
    
    ## WPAPROFILE_FILE
    $WPAPROFILE_FILE = Get-ConfigValue -Section 'PROFILES' -Key 'WPAPROFILE_FILE' -Default 'resources\MediaProfile.wpaProfile'
    if (-not(Test-ValidFileName -FileName $WPAPROFILE_FILE -Extension '.wpaprofile')) {
        throw ("Invalid WPAPROFILE_FILE name '{0}'. Must be avlid file name with .wpaProfile extension. Please check config file PROFILES section, WPAPROFILE_FILE key." -f $WPAPROFILE_FILE)
    }   
    $WPAPROFILE_FILE = Get-RootName -FileName $WPAPROFILE_FILE

    ## Regions file
    $MediaRegionsFile = Get-ConfigValue -Section 'PROFILES' -Key 'MEDIA_REGIONS_FILE' -Default 'resources\Media.regions.xml'
    if (-not(Test-ValidFileName -FileName $MediaRegionsFile -Extension '.xml')) {
        throw ("Invalid MEDIA_REGIONS_FILE name '{0}'. Must be valid file name with .xml extension. Please check config file PROFILES section, MEDIA_REGIONS_FILE key." -f $MediaRegionsFile)
    }
    $MediaRegionsFile = Get-RootName -FileName $MediaRegionsFile

    # AXE termination signal handling loop
    $Global:TerminationLoop = $true

    # results files
    $ETWLogfile = Get-ConfigValue -Section 'OUTPUT' -Key 'ETWLogfile' -Default 'Timing_MediaQuality.etl'
    if (-not(Test-ValidFileName -FileName $ETWLogfile -Extension '.etl')) {
        throw ("Invalid ETWLogfile name '{0}'. Must be a valid file name with .etl extension. Please check config file OUTPUT section, ETWLogfile key." -f $ETWLogfile)
    }

    # Add an extra Analysis_MediaQuality.etl for verbose option
    $ETWLogfileVerbose = Get-ConfigValue -Section 'OUTPUT' -Key 'ETWLogfileVerbose' -Default 'Analysis_MediaQuality.etl'
    if (-not(Test-ValidFileName -FileName $ETWLogfileVerbose -Extension '.etl')) {
        throw ("Invalid ETWLogfileVerbose name '{0}'. Must be a valid file name with .etl extension. Please check config file OUTPUT section, ETWLogfileVerbose key." -f $ETWLogfileVerbose)
    }
    
    # Precondition to starting the workload is that the test results file name is valid.
    $TestResultsFile = Get-ConfigValue -Section 'OUTPUT' -Key 'TestResultsFile' -Default 'results.txt'
    if (-not(Test-ValidFileName -FileName $TestResultsFile)) {
        throw ("Invalid TestResultsFile '{0}'. Must be a valid file name. Please check config file OUTPUT section, TestResultsFile key." -f $TestResultsFile)
    }
    
    # Allowance for the wpr tracing to stabilize before starting the workload.
        $traceSeconds = 5
        $traceString = (Get-ConfigValue -Section QUALITY_CONSTANT -Key TRACE_STABILIZATION_SECONDS -Default '5')
        if (-not ([int]::TryParse($traceString, [ref]$traceSeconds))) {
            throw ("Error converting config [QUALITY_CONSTANT][TRACE_STABILIZATION_SECONDS] value '{0}' to [int]." -f $traceString)
        }
    Set-Variable -Name TraceStabilizationSeconds -Value $traceSeconds -Option Constant
    
    # Allowance for the application to stabilize before checking on status.
        $playbackSeconds = 10
        $playbackString = ((Get-ConfigValue -Section QUALITY_CONSTANT -Key 'PLAYBACK_STABILIZATION_SECONDS' -Default '10'))
        if (-not ([int]::TryParse($playbackString, [ref]$playbackSeconds))) {
            throw ("Error converting config [QUALITY_CONSTANT][PLAYBACK_STABILIZATION_SECONDS] value '{0}' to [int]." -f $playbackString)
        }
    Set-Variable -Name PlaybackStabilizationSeconds -Value $playbackSeconds -Option Constant
    
    # Allowance for the media playback to stop earlier than the user expected.
    $toleranceSeconds  = 10
        $tolerenceString = (Get-ConfigValue -Section QUALITY_CONSTANT -Key PLAYBACK_TIME_TOLERANCE_SECONDS -Default '10')
        if (-not ([int]::TryParse($tolerenceString, [ref]$toleranceSeconds))) {
            throw ("Error converting config [QUALITY_CONSTANT][PLAYBACK_TIME_TOLERANCE_SECONDS] value '{0}' to [int]." -f $tolerenceString)
        }
    Set-Variable -Name PlaybackTimeToleranceSeconds -Value $toleranceSeconds -Option Constant

    # Netflix pre-load stabilization time in seconds before playing the video
    $stabilizationSeconds = 10
        $stabilizationString = (Get-ConfigValue -Section SCENARIO_AUTOMATION -Key NETFLIX_STABILIZATION_SECONDS -Default '10')
        if (-not ([int]::TryParse($stabilizationString, [ref]$stabilizationSeconds))) {
            throw ("Error converting config [SCENARIO_AUTOMATION][NETFLIX_STABILIZATION_SECONDS] value '{0}' to [int]." -f $stabilizationString)
        }
    Set-Variable -Name NetflixStabilizationSeconds -Value $stabilizationSeconds -Option Constant
    
    #*****************************************************************
    #
    #               AXE setup
    #
    #*****************************************************************

    # AXE environment and framework
    $AXEResultsPath = (Get-Item env:\AssessmentResultsPath).value
    $AXETempPath = (Get-Item env:\AssessmentTempPath).value
    $AxeBinPath = (Get-Item env:\AssessmentAxeBinPath).value
    $AxeCoreNet = "$AxeBinPath\Microsoft.Assessments.Core.dll"
    
    # XML results file
    $XMLResultsFile = "$AXEResultsPath\results.xml"

    # logs folder
    $LogsPath = "$AXEResultsPath\logs"
    if (-not (test-path $LogsPath)) {
        New-Item $LogsPath -type directory
    }

    # load up AXE framework from AXE DLL
    # initialize Runtime and create Logger helper object
    $boolRunningUnderAxe = Initialize-AxeLogger($AxeCoreNet)
    
    $WPTPath | Log-Info

    #*****************************************************************
    #
    #                Playback source selection
    #
    #*****************************************************************
    
    $Playback = Select-PlaybackSource  -PlaybackSource         $PlaybackSource `
                                       -CustomPlaybackSource   $CustomPlaybackSource `
                                       -Architecture           $arch

    # check system resolution and log it
    if ( $PlaybackSource -eq ([int]([PlaybackSourceEnum]::DEFAUL_LOCAL_AUTOSELECT)) ) {
        "SYSRES=$(Get-ScreenResolution)" | Out-File "$LogsPath\resolution.log" -ErrorAction SilentlyContinue -Force
    }
    
    # select which WPR profile to use, and which synchronization protocol, if any, to use
    $profile = "$WPRP_FILE!VideoQualityProfile"
    
    if      ($PlaybackSource -eq [PlaybackSourceEnum]::NETFLIX) {
    
    } 
    elseif (($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_AUDIO) `
       -or  ($PlaybackSource -eq [PlaybackSourceEnum]::CUSTOM_STREAMING_AUDIO)) {

        $profile      = "$WPRP_FILE!AudioQualityProfile"
    }
     elseif (($PlaybackSource -eq [PlaybackSourceEnum]::CUSTOM_STREAMING_VIDEO) `
        -or  ($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_VIDEO)) {

    }

    # Add $profileVerbose with verbose option for Analysis_MediaQuality.etl
    $profileVerbose = "$profile.verbose"

    # select a light or verbose WPR profile based supplied switch.
    try {
        switch ([TraceOption]$TraceOption)
        {
            ([TraceOption]::LIGHT)   {$profile = "$profile.light"  ;break}
            ([TraceOption]::VERBOSE) {$profile = "$profile.verbose";break}
        }
    }
    catch {
        throw ("Invalid trace option '{0}'." -f $TraceOption)
    }
         
    #*****************************************************************
    #
    #                Workload preparation
    #
    #*****************************************************************

    "Check UWP Video Player Policy..." | Log-Info
    DisableWow64FsRedirection
    Set-UwpVideoPlayerPolicy
    RevertWow64FsRedirection

    if ($ExtendedLogs) {
        "Capture log files..." | Log-Info
        DisableWow64FsRedirection
        Write-AuxQualityLogs -LogsPath $LogsPath
        RevertWow64FsRedirection
    }

    "Cancel tracing, just in case some sessions are already running..." | Log-Info
    if (-not ((wpr -status) -match "not recording")) { wpr -cancel }

    "Install applicable playback packages..." | Log-Info
    Install-PlaybackPackage $Playback.Workload $arch
    
    "Launch applicable streaming media to allow time for library sync..." | Log-Info
    Start-StreamingWorkload -PlaybackSource $PlaybackSource
    
    "Stop playback processes..." | Log-Info
    Stop-Workload
    
    "Check battery..." | Log-Info
    Test-IsOnBattery -LogsPath $LogsPath -Keyword "BEFORE"

    #*****************************************************************
    #
    #                Tracing and termination timers
    #
    #*****************************************************************
    
    "Set timer for playback duration ($PlaybackDuration minutes)..." | Log-Info
    # set timer to stop workload after X minutes
    $Timer_FiniteDuration = New-Object Timers.Timer
    $Timer_FiniteDuration.Interval = $PlaybackDuration*60*1000
    $Timer_FiniteDuration.AutoReset = $false

    # define trigger action to gracefully exit AXE signal processing loop
    $Action_FiniteDuration = { $Global:TerminationLoop = $false } 

    # register event/action and start timer
     "Register timer object..." | Log-Info
    Register-ObjectEvent -InputObject $Timer_FiniteDuration -EventName elapsed -SourceIdentifier Timer_FiniteDuration -Action $Action_FiniteDuration
    
    
    #*****************************************************************
    #
    #                Actual workload!
    #
    #*****************************************************************

    #start tracing and stabilize
    "Start tracing with profile: $profile ..." | Log-Info
    wpr -start $profile -filemode
    if ($LASTEXITCODE -ne 0) {throw ("Error starting wpr profile '{0}'." -f $profile)}

    Start-Sleep -Seconds $TraceStabilizationSeconds

    # prepare to play Netflix
    if ($Playback.Workload -eq 'NETFLIX') {
        Start-Executable $Playback.ExePath '-appuid "4DF9E0F8.Netflix_mcm4njqhnhss8!Netflix.App"'
        Start-Sleep -Seconds $NetflixStabilizationSeconds
    }
    
    if ($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_VIDEO)
    {
        ### Buy in Windows Store the default content to be streamed that is free
        "Adding the default video to be streamed to user's library" | Log-Info
        $SearchString = Get-ConfigValue -Section 'STREAMING' -Key 'DEFAULT_SEARCH_VIDEO' -Default 'Remaking the Legend'
        Add-StreamingContent -VideoSearchString $SearchString -Playback $Playback
    }

    if ($PlaybackSource -eq [PlaybackSourceEnum]::DEFAULT_STREAMING_AUDIO) {
        Configure-GrooveRepeat
    }

    # start playing the video
    ("Launch playback source: ExePath={0} ArgumentList={1}..." -f $Playback.ExePath, $Playback.ArgumentList[0] ) | Log-Info
    $OutPut = Start-Executable $Playback.ExePath $Playback.ArgumentList

    "ScenarioAutomationTool results..." | Log-Info
    $OutPut | Log-Info

    "Start playback duration timer..." | Log-Info
    $Timer_FiniteDuration.start()

    # let app stabilize
    Start-Sleep -Seconds $PlaybackStabilizationSeconds

    #*****************************************************************
    #
    #                AXE termination signal handling
    #
    #*****************************************************************

    "Wait for playback end or user triggered cancellation..." | Log-Info
    # check if AXE tried to cancel the assessment as the job end condition is met
    if ( $boolRunningUnderAxe ) {
        while(($Global:TerminationLoop) -and (-not ($Global:axeSupport.CancelEvent.WaitOne( 5 * 1000 )) )) {}
    }
    
    "Playback completed..." | Log-Info

    # stop playback handling process
    "Stop playback processes..." | Log-Info
    Stop-Workload


    "Stop tracing and saving file $AXEResultsPath\$ETWLogfile..." | Log-Info
    wpr -stop "$AXEResultsPath\$ETWLogfile" ""
    if ($LASTEXITCODE -ne 0) {throw "Error stopping wpr ETW $AXEResultsPath\$ETWLogfile"}

    #*****************************************************************
    #
    #                Actual workload!
    #                (for Analysis_MediaQuality.etl)
    #
    #*****************************************************************

    if ( -not ($Global:axeSupport.Canceled) ) {

        # stop timer and reset AXE termination signal
        $Timer_FiniteDuration.Stop()
        $Global:TerminationLoop = $true

        # start tracing and stabilize
        "Start tracing with profile for verbose option: $profileVerbose ..." | Log-Info
        wpr -start $profileVerbose -filemode
        if ($LASTEXITCODE -ne 0) {throw ("Error starting wpr profile '{0}'." -f $profile)}

        Start-Sleep -Seconds $TraceStabilizationSeconds

        # prepare to play Netflix
        if ($Playback.Workload -eq 'NETFLIX') {
            Start-Executable $Playback.ExePath '-appuid "4DF9E0F8.Netflix_mcm4njqhnhss8!Netflix.App"'
            Start-Sleep -Seconds $NetflixStabilizationSeconds
        }

        # start playing the video
        ("Launch playback source: ExePath={0} ArgumentList={1}..." -f $Playback.ExePath, $Playback.ArgumentList[0] ) | Log-Info
        $OutPut = Start-Executable $Playback.ExePath $Playback.ArgumentList

        "ScenarioAutomationTool results..." | Log-Info
        $OutPut | Log-Info

        "Start playback duration timer..." | Log-Info
        $Timer_FiniteDuration.Start()

        # let app stabilize
        Start-Sleep -Seconds $PlaybackStabilizationSeconds

    #*****************************************************************
    #
    #                AXE termination signal handling
    #                (for Analysis_MediaQuality.etl)
    #
    #*****************************************************************

        "Wait for playback end or user triggered cancellation..." | Log-Info
        # check if AXE tried to cancel the assessment as the job end condition is met
        if ( $boolRunningUnderAxe ) {
            while(($Global:TerminationLoop) -and (-not ($Global:axeSupport.CancelEvent.WaitOne( 5 * 1000 )) )) {}
        }

        "Playback completed..." | Log-Info

        # stop playback handling process
        "Stop playback processes..." | Log-Info
        Stop-Workload

        "Stop tracing and saving file $AXEResultsPath\$ETWLogfileVerbose..." | Log-Info
        wpr -stop "$AXEResultsPath\$ETWLogfileVerbose" ""
        if ($LASTEXITCODE -ne 0) {throw "Error stopping wpr ETW $AXEResultsPath\$ETWLogfileVerbose"}
    }

    #*****************************************************************
    #
    #                Results processing
    #
    #*****************************************************************

    "Export CSV results to $AXEResultsPath ..." | Log-Info
    $ETWFullPathLogfile = "$AXEResultsPath\$ETWLogfile"
    wpaexporter -i "$ETWFullPathLogfile" -profile "$WPAPROFILE_FILE" -outputfolder "$AXEResultsPath" | out-file "$AXEResultsPath\exporter.log"

    # Write a text file summarizing critical measurements for this run.
    
    # Start with an empty text file to contain the critical measurements.
    try {
        New-Item -Path $AXEResultsPath -Name $TestResultsFile -Type File
    }
    catch {
        throw ("Error creating critical measurements summary file '{0}\{1}'." -f $AXEResultsPath, $TestResultsFile)
    }

    # all csv files generated by wpaexporter -i <Path of $ETWLogfile>
    $timingsCSV = "$AXEResultsPath\Regions_of_Interest_Regions.csv"
    $audioGlitchCountCSV = "$AXEResultsPath\Generic_Events_AudioGlitchCount.csv"
    $audioGlitchCountTraceloggerCSV = "$AXEResultsPath\Generic_Events_AudioGlitchCountTracelogger.csv"
    $videoGlitchCountCSV = "$AXEResultsPath\Generic_Events_VideoGlitchCount.csv"

    # For ARM* processors, the execution processor architecture is x86.
    # For x86 execution processor architecture, the virtual space might not be enough to put a large MediaQuality.etl into memory.
    # If MediaQuality.etl is too large to fit into memory, the result csv files will not be generated.
    # We will inform user about this.
    if (($env:PROCESSOR_ARCHITECTURE -eq "x86")) {
        if ((Test-Path -Path $ETWFullPathLogfile -PathType Leaf)) { # $ETWFullPathLogfile exists?
            if ((Test-Path -Path $timingsCSV -PathType Leaf) -and
                (Test-Path -Path $audioGlitchCountCSV -PathType Leaf) -and
                (Test-Path -Path $audioGlitchCountTraceloggerCSV -PathType Leaf) -and
                (Test-Path -Path $videoGlitchCountCSV -PathType Leaf)) {
            }
            else {
                #check file size of MediaQuality.etl
                if ((Get-Item $ETWFullPathLogfile).length -gt 600mb) {
                    $X86EmulationETWTooLargeMsg = @"
$ETWLogfile is too large to fit into memory for $env:PROCESSOR_ARCHITECTURE excution processor architecture!
To generate the result, please copy $ETWLogfile, {0}, and {1} to an AMD64 processor architecture machine and issue the following command:
wpaexporter -i "<Path of $ETWLogfile>" - profile "<Path of {0}>" -outputfolder "<Directory of Output>" > "<Directory of Output>\exporter.log"
"@
                    throw ("$X86EmulationETWTooLargeMsg" -f (Split-Path $WPAPROFILE_FILE -Leaf), (Split-Path $MediaRegionsFile -Leaf))
                }
            }
        }
    }

    # Write the critical timings measurements.
    Test-FileExists -Path $timingsCSV -FromInvocation $MyInvocation

    $timings = import-csv "$AXEResultsPath\Regions_of_Interest_Regions.csv" -Header region,duration | Select-Object -Skip 1
    if ($timings -eq $null) {
        throw ("Error importing CSV file '{0}'." -f $timingsCSV)
    }

    $Metric_StartupDuration  = ($timings | where {$_.region -eq "MediaQuality/StartupDuration"}).duration
    if (-not(Test-IsMetric -Metric $Metric_StartupDuration)) {
        throw "Missing required metric MediaQuality/StartupDuration.duration."
    }
    $Metric_StartupDuration  = [int] $Metric_StartupDuration
    
    $Metric_PlaybackDuration = ($timings | where {$_.region -eq "MediaQuality/PlaybackDuration"}).duration
    if (-not(Test-IsMetric -Metric $Metric_PlaybackDuration)) {
        throw "Missing required metric MediaQuality/PlaybackDuration.duration."
    }
    $Metric_PlaybackDuration = [int] $Metric_PlaybackDuration

    "Metric_StartupDuration  = $Metric_StartupDuration"  | out-file "$AXEResultsPath\$TestResultsFile" -append -Force
    "Metric_PlaybackDuration = $Metric_PlaybackDuration" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 

    # Halt if the media stopped playing earlier than expected.
    $PassDuration = $Metric_StartupDuration + $Metric_PlaybackDuration

    if (($PassDuration + $PlaybackTimeToleranceSeconds) -lt $PlaybackDuration) {
         throw "The media content failed to playback properly @ $PassDuration seconds"
    }

    "--------------------------------" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 


    # audio stats
    $audiostats = import-csv "$AXEResultsPath\Generic_Events_AudioGlitchCountTracelogger.csv" -Header provider,count | Select-Object -Skip 1

    $Metric_CrossProcessAudioGlitch            = [int] ($audiostats | where {$_.provider -eq "Microsoft.Windows.Audio.CrossProcess"}).count
    $Metric_KernelStreamingEndpointAudioGlitch = [int] ($audiostats | where {$_.provider -eq "Microsoft.Windows.Audio.KernelStreamingEndpoint"}).count

    "Metric_CrossProcessAudioGlitch            = $Metric_CrossProcessAudioGlitch"            | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
    "Metric_KernelStreamingEndpointAudioGlitch = $Metric_KernelStreamingEndpointAudioGlitch" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 

    if ( @(([WorkloadEnum]'MOVIESTV'),([WorkloadEnum]'NETFLIX')) -contains ($Playback.Workload) )
    {
        "--------------------------------" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 

        # video stats

        $videostats = import-csv "$AXEResultsPath\Generic_Events_VideoGlitchCount.csv" -Header event,count,average,min,max,sum | Select-Object -Skip 1

        $Metric_SchedGlitchCount = [int] ($videostats | where {$_.event -eq "SCHEDULE_GLITCH"}).count
        $Metric_DroppedFrameCount = [int] ($videostats | where {$_.event -eq "DroppedFrame"}).count
        $Metric_VideoFrameGlitchCount = [int] ($videostats | where {$_.event -eq "VideoFrameGlitch"}).count


        "Metric_SchedGlitchCount = $Metric_SchedGlitchCount" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
        "Metric_DroppedFrameCount = $Metric_DroppedFrameCount" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
        "Metric_VideoFrameGlitchCount = $Metric_VideoFrameGlitchCount" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 

        if ($(($videostats | where {$_.event -eq "VideoFrameGlitch"}))) {

            $average = $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).average)
            $min     = $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).min)
            $max     = $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).max)
            $sum     = $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).sum)
            
            if (     (Test-IsMetric -Metric $average) `
                -and (Test-IsMetric -Metric $min)     `
                -and (Test-IsMetric -Metric $max)     `
                -and (Test-IsMetric -Metric $sum)
            ) {
                "StatsVideoGlitch_LateFrame_Average = $average" | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
                "StatsVideoGlitch_LateFrame_Min     = $min"     | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
                "StatsVideoGlitch_LateFrame_Max     = $max"    | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
                "StatsVideoGlitch_LateFrame_Sum     = $sum"     | out-file "$AXEResultsPath\$TestResultsFile" -append -Force 
            }
            else {
                "WARNING: Missing required video glitch late frame stats in '$videostatsCSV'." | Log-Info
            }

        }
    }

    #*****************************************************************
    #
    #                XML Generation
    #
    #*****************************************************************

    # Create the XML helper object
    #
    $XMLHelper = $Global:axeSupport.CreateResultSnippet()


    $XMLIteration = $XMLHelper.AddIteration()

    # timings
    $XMLIteration.AddMetricValue("StartupDuration", $Metric_StartupDuration)
    $XMLIteration.AddMetricValue("PlaybackDuration", $Metric_PlaybackDuration)
    $XMLIteration.AddMetricValue("TotalPlaybackDuration", $PassDuration)

    # audio
    $XMLIteration.AddMetricValue("CrossProcessAudioGlitch", $Metric_CrossProcessAudioGlitch)
    $XMLIteration.AddMetricValue("KernelStreamingEndpointAudioGlitch", $Metric_KernelStreamingEndpointAudioGlitch)
    $XMLIteration.AddMetricValue("AudioTotal", $Metric_KernelStreamingEndpointAudioGlitch + $Metric_CrossProcessAudioGlitch)

    if ( @(([WorkloadEnum]'MOVIESTV'),([WorkloadEnum]'NETFLIX')) -contains ($Playback.Workload) )
    {
        # video 
        $XMLIteration.AddMetricValue("VideoSchedGlitch", $Metric_SchedGlitchCount)
        $XMLIteration.AddMetricValue("VideoDropped", $Metric_DroppedFrameCount)
        $XMLIteration.AddMetricValue("VideoFrameGlitch", $Metric_VideoFrameGlitchCount)
        $XMLIteration.AddMetricValue("VideoTotal", $Metric_VideoFrameGlitchCount + $Metric_SchedGlitchCount + $Metric_DroppedFrameCount)

        # frames late
        if ($(($videostats | where {$_.event -eq "VideoFrameGlitch"}))) {
            $XMLIteration.AddMetricValue("FrameLateAverage", $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).average))
            $XMLIteration.AddMetricValue("FrameLateMin", $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).min))
            $XMLIteration.AddMetricValue("FrameLateMax", $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).max))
            $XMLIteration.AddMetricValue("FrameLateSum", $(($videostats | where {$_.event -eq "VideoFrameGlitch"}).sum))
        }
    }

    ## Display the generated ETL log on WAC for user to directly open via WPA
    $Activity = $XMLIteration.AddActivity()
    # Copy the WPA Profile file and regions file for use by results
    Copy-Item -Path $WPAPROFILE_FILE -Destination $AXEResultsPath -Force -ErrorAction Ignore
    Copy-Item -Path $MediaRegionsFile -Destination $AXEResultsPath -Force -ErrorAction Ignore

    $WPAProfileFileName = Split-Path $WPAPROFILE_FILE -Leaf
    ## [AssessmentResultsPath] will be resolved by Analysis lib of WPA
    $TraceLink = ("wpa://[AssessmentResultsPath]/{0}?profile={1}" -F $ETWLogfile, $WPAProfileFileName)
    $Activity.SetTrace($ETWLogfile, $TraceLink, "Media Quality", "ETL generated during media quality run")

    # Check if Analysis_MediaQuality.etl exists
    if ( (-not ($Global:axeSupport.Canceled)) -and (-not (Test-Path ("$AXEResultsPath\$ETWLogfileVerbose"))))
    {
        $WarningMessage = "$AXEResultsPath\$ETWLogfileVerbose doesn't exist."
        $XMLHelper.AddWarning($WarningMessage)
    }
    
    $XMLHelper.Save($XMLResultsFile)
}
catch
{
    #*****************************************************************
    #
    #                Handle exceptions
    #
    #*****************************************************************

    if ( $Global:axeLogger ) {
        $Global:axeLogger.LogErrorCode( $_.Exception.Hresult, $_ )
    }

    # Write the exception details to a errors XML file
    $xmlErrorFile = "$AXEResultsPath\ErrorsAndWarnings.xml"
    Log-ToXmlErrorFile -XmlErrorFile $xmlErrorFile -ErrorCode $_.Exception.Hresult -ErrorMessage $_.Exception.Message
    $message = $_.Exception.Message + [Environment]::NewLine + $_.InvocationInfo.PositionMessage
    $message | Log-Info
    
    throw
}
finally {


    #*****************************************************************
    #
    #                Workload cleanup
    #
    #*****************************************************************

    Test-IsOnBattery -LogsPath $LogsPath -Keyword "AFTER"

    # cancel all timer events
    #
    Unregister-Event Timer_FiniteDuration -ErrorAction SilentlyContinue

    # cancel any ETL tracing that might be going on
    if (-not ((wpr -status) -match "not recording")) { wpr -cancel }

    # stop playback handling process
    Stop-Workload

    
    # We should always properly dispose of this guy.
    if ( $Global:axeSupport ) {
        $Global:axeSupport.Dispose()
    }


    if ($debugFlag){
        Read-Host "DEBUG MODE: Press ENTER key to exit"
    }

}
