Imports Microsoft.VisualBasic
Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Imports System.Windows.Forms
Imports System.Reflection
Imports System.Diagnostics
Imports System.Text

Imports Stax

Public Class AVIFile
    Public CropLeft, CropTop, CropRight, CropBottom As Integer

    Private AviFile As IntPtr
    Private FrameObject As IntPtr
    Private AviStream As IntPtr
    Private StreamInfo As AVISTREAMINFO
    Private Control As Control
    Private Sourcefile As String

    Private FrameCountValue As Integer

    Public ReadOnly Property FrameCount() As Integer
        Get
            Return FrameCountValue
        End Get
    End Property

    Public ReadOnly Property FrameRate() As Single
        Get
            Return StreamInfo.dwRate / CSng(StreamInfo.dwScale)
        End Get
    End Property

    Private FourCCValue As String

    Public ReadOnly Property FourCC() As String
        Get
            Return FourCCValue
        End Get
    End Property

    Public ReadOnly Property FrameSize() As Size
        Get
            Return New Size(CInt(StreamInfo.rcFrame.right), CInt(StreamInfo.rcFrame.bottom))
        End Get
    End Property

    Private PositionValue As Integer

    Public Property Position() As Integer
        Get
            Return PositionValue
        End Get
        Set(ByVal value As Integer)
            If value < 0 Then
                PositionValue = 0
            ElseIf value > FrameCount - 1 Then
                PositionValue = FrameCount - 1
            Else
                PositionValue = value
            End If
        End Set
    End Property

    Public Sub Open(ByVal fileName As String, ByVal c As Control)
        Open(fileName)
        Control = c
    End Sub

    Private Function GetFourCC(ByVal value As Integer) As String
        Return Encoding.ASCII.GetString(BitConverter.GetBytes(value))
    End Function

    Public Sub Open(ByVal path As String)
        Try
            Sourcefile = path

            AVIFileInit()

            Dim OF_SHARE_DENY_WRITE As Integer = 32

            If AVIFileOpen(AviFile, path, OF_SHARE_DENY_WRITE, IntPtr.Zero) <> 0 Then
                Throw New Exception("AVIFileOpen failed")
            End If

            If AVIFileGetStream(AviFile, AviStream, 1935960438, 0) <> 0 Then 'FourCC for vids
                Throw New Exception("AVIFileGetStream failed")
            End If

            FrameCountValue = AVIStreamLength(AviStream.ToInt32())

            StreamInfo = New AVISTREAMINFO()

            If AVIStreamInfo_(AviStream.ToInt32(), StreamInfo, Marshal.SizeOf(StreamInfo)) <> 0 Then
                Throw New Exception("AVIStreamInfo failed")
            End If

            FourCCValue = GetFourCC(Convert.ToInt32(StreamInfo.fccHandler))

            If FourCC = "YV12" Then
                FrameObject = AVIStreamGetFrameOpen(AviStream, 1)

                If FrameObject = IntPtr.Zero Then
                    Throw New Exception("Failed to decode YV12.")
                End If
            Else
                FrameObject = AVIStreamGetFrameOpen(AviStream, 0)

                If FrameObject = IntPtr.Zero Then
                    Throw New Exception("AVIStreamGetFrameOpen failed")
                End If
            End If
        Catch ex As Exception
            HandleException(ex)
        End Try
    End Sub

    Public Sub HandleException(ByVal ex As Exception)
        Dim sb As New StringBuilder
        Dim yv12 As String = RegistryHelp.LocalMachine.GetString("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32", "VIDC.YV12")

        sb.AppendLine("message: " + ex.Message)
        sb.AppendLine("yv12 decoder: " + yv12)
        sb.AppendLine("FourCC: " + FourCCValue)
        sb.AppendLine("yv12 decoder exists: " + File.Exists(CommonDirs.System + yv12).ToString)
        sb.AppendLine("colordepth: " + Screen.PrimaryScreen.BitsPerPixel.ToString)
        sb.AppendLine("path: " + Sourcefile)

        If Filepath.GetExt(Sourcefile) = ".avs" Then
            If File.Exists(Sourcefile) Then
                sb.AppendLine("script: " + vbCrLf + vbCrLf + StringHelp.ReadFile(Sourcefile))
            Else
                sb.AppendLine("file does not exist!")
            End If
        End If

        Throw New Exception(sb.ToString)
    End Sub

    Public Sub Close()
        If Not FrameObject = IntPtr.Zero Then
            AVIStreamGetFrameClose(FrameObject)
            FrameObject = IntPtr.Zero
        End If

        If Not AviStream = IntPtr.Zero Then
            AVIStreamRelease(AviStream)
            AviStream = IntPtr.Zero
        End If

        If Not AviFile = IntPtr.Zero Then
            AVIFileRelease(AviFile)
            AviFile = IntPtr.Zero
        End If

        AVIFileExit()
    End Sub

    Public Sub Draw()
        If Not Control Is Nothing AndAlso Control.Visible Then
            Dim g As Graphics = Control.CreateGraphics()
            Draw(g)
            g.Dispose()
        End If
    End Sub

    Public Sub Draw(ByVal g As Graphics)
        Try
            If Not Control Is Nothing AndAlso Control.Visible AndAlso Not FrameObject = IntPtr.Zero Then
                Dim img As Image = GetBMPFromDib(New IntPtr(AVIStreamGetFrame(FrameObject, Position)))

                If CropLeft = 0 AndAlso CropTop = 0 AndAlso CropRight = 0 AndAlso CropBottom = 0 Then
                    g.DrawImage(img, Control.ClientRectangle)
                Else
                    Dim factorX As Single = CSng(Control.Width) / img.Width
                    Dim factorY As Single = CSng(Control.Height) / img.Height

                    Dim left As Single = CropLeft * factorX
                    Dim right As Single = CropRight * factorX
                    Dim top As Single = CropTop * factorY
                    Dim bottom As Single = CropBottom * factorY

                    Dim rectDest As RectangleF = New RectangleF()

                    rectDest.X = left
                    rectDest.Y = top
                    rectDest.Width = Control.Width - left - right
                    rectDest.Height = Control.Height - top - bottom

                    Dim rectSrc As Rectangle = New Rectangle()

                    rectSrc.X = CropLeft
                    rectSrc.Y = CropTop
                    rectSrc.Width = img.Width - CropLeft - CropRight
                    rectSrc.Height = img.Height - CropTop - CropBottom

                    g.DrawImage(img, rectDest, rectSrc, GraphicsUnit.Pixel)

                    Dim sb As SolidBrush = New SolidBrush(Color.White)

                    g.FillRectangle(sb, 0, 0, left, Control.Height)
                    g.FillRectangle(sb, 0, 0, Control.Width, top)
                    g.FillRectangle(sb, Control.Width - right, 0, right, Control.Height)
                    g.FillRectangle(sb, 0, Control.Height - bottom, Control.Width, bottom)

                    sb.Dispose()
                End If
            End If
        Catch ex As Exception
            HandleException(ex)
        End Try
    End Sub

    Public Function GetBitmap() As Bitmap
        Return GetBMPFromDib(New IntPtr(AVIStreamGetFrame(FrameObject, Position)))
    End Function

    Public Function GetBMPFromDib(ByVal pDIB As IntPtr) As Bitmap
        Dim pPix As IntPtr = New IntPtr(pDIB.ToInt32() + Marshal.SizeOf(GetType(BITMAPINFOHEADER)))

        Dim mi As MethodInfo = GetType(Bitmap).GetMethod("FromGDIplus", BindingFlags.Static Or BindingFlags.NonPublic)

        Dim pBmp As IntPtr = IntPtr.Zero
        Dim status As Integer = GdipCreateBitmapFromGdiDib(pDIB, pPix, pBmp)

        Return CType(mi.Invoke(Nothing, New Object() {pBmp}), Bitmap)
    End Function

    <DllImport("gdiplus.dll")> _
    Shared Function GdipCreateBitmapFromGdiDib(ByVal bminfo As IntPtr, ByVal pixdat As IntPtr, ByRef image As IntPtr) As Integer
    End Function

    <DllImport("gdiplus.dll")> _
    Shared Function GdipCreateHBITMAPFromBitmap(ByVal image As IntPtr, ByRef hbitmap As IntPtr, ByVal bkg As Integer) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Sub AVIFileInit()
    End Sub

    <DllImport("avifil32.dll")> _
    Shared Function AVIFileOpen(ByRef ppfile As IntPtr, ByVal szFile As String, ByVal uMode As Integer, ByVal pclsidHandler As IntPtr) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIFileGetStream(ByVal pfile As IntPtr, <Out()> ByRef ppavi As IntPtr, ByVal fccType As Integer, ByVal lParam As Integer) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamStart(ByVal pavi As Integer) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamLength(ByVal pavi As Integer) As Integer
    End Function

    <CLSCompliant(False), DllImport("avifil32.dll", EntryPoint:="AVIStreamInfo", CharSet:=CharSet.Auto)> _
    Shared Function AVIStreamInfo_(ByVal pAVIStream As Integer, ByRef psi As AVISTREAMINFO, ByVal lSize As Integer) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamGetFrameOpen(ByVal pAVIStream As IntPtr, ByVal lpbiWanted As Integer) As IntPtr
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamGetFrame(ByVal pGetFrameObj As IntPtr, ByVal lPos As Integer) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamGetFrameClose(ByVal pGetFrameObj As IntPtr) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIStreamRelease(ByVal aviStream As IntPtr) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Function AVIFileRelease(ByVal pfile As IntPtr) As Integer
    End Function

    <DllImport("avifil32.dll")> _
    Shared Sub AVIFileExit()
    End Sub

    Public Structure RECT
        Public left As Int32
        Public top As Int32
        Public right As Int32
        Public bottom As Int32
    End Structure

    Private Structure BITMAPINFOHEADER
        Public biSize As UInt32
        Public biWidth As Int32
        Public biHeight As Int32
        Public biPlanes As Int16
        Public biBitCount As Int16
        Public biCompression As UInt32
        Public biSizeImage As UInt32
        Public biXPelsPerMeter As Int32
        Public biYPelsPerMeter As Int32
        Public biClrUsed As UInt32
        Public biClrImportant As UInt32
    End Structure

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
    Public Structure AVISTREAMINFO
        Public fccType As UInt32
        Public fccHandler As UInt32
        Public dwFlags As UInt32
        Public dwCaps As UInt32
        Public wPriority As UInt16
        Public wLanguage As UInt16
        Public dwScale As UInt32
        Public dwRate As UInt32
        Public dwStart As UInt32
        Public dwLength As UInt32
        Public dwInitialFrames As UInt32
        Public dwSuggestedBufferSize As UInt32
        Public dwQuality As UInt32
        Public dwSampleSize As UInt32
        Public rcFrame As RECT
        Public dwEditCount As UInt32
        Public dwFormatChangeCount As UInt32
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=64)> _
        Public szName As String
    End Structure
End Class