Hikvision camera picture download Windows ISAPI utility... downloads pictures from SD card ready for timelapse

I'll look into the memory leak issue you mentioned... you can see from my screenshot above, I loaded just over 100,000 images (in one go, several hours) and did not see this... but those files are in the 400 KB range... what size are the files you were downloading?

The camera capture quality is set to "High" which results in picture files that are just shy of 2.5MB each.

Next step I will find a way to shorten and standardize the file naming... first on my list is just what you suggested, there's no need for the camera name since the folder identifies that... and the duplicated channel number, long sequence number, duplicated timestamp... should be easy to fix/standardize.

Agreed. You could argue it's not worth the effort to change but the long files names might be problematic if storing the images within nested folders where the Windows 255 long character limits come into play (if that's still a thing).


Did you notice how the scroll bar works during playback/paused playback?

I overlooked the scroll bar initially. I did not realize until later that I could grab the scroll bar and move back/forth through the snapshots. Particularly liked that I could move the slider to a point of interest and start playback from that location, even adjusting the speed slider to slow down and speed up the transition between pictures. Very nice inclusion.

The only other item I've noticed is that once pictures have finished downloading, I'm presented with only the "Play" or "Cancel" buttons. The remainder are greyed out. It almost feels like all the button selections should be available at this point. If I wanted to download any newer images, I need to hit cancel first, then connect and then download again. Maybe that's as you intended for some reason? But this is being picky. As it currently stands I think it's still a winner.
 
I just reviewed, and I see from your screenshots they are 2 MB files... I'll put an SD card in a DS-2CD2387G2-LU and collect some similar event pictures to try to recreate the memory leak.
 
I overlooked the scroll bar initially.

I figured, there must be a way to make that functionality obvious, but the Windows Forms Toolbox I'm using not good :(

It almost feels like all the button selections should be available at this point. If I wanted to download any newer images, I need to hit cancel first, then connect and then download again. Maybe that's as you intended for some reason?

I did intend it, to make it easier to code/test for now... planning on modifying it just as you described... won't rush though!
 
I just reviewed, and I see from your screenshots they are 2 MB files... I'll put an SD card in a DS-2CD2387G2-LU and collect some similar event pictures to try to recreate the memory leak.

A little more information to help diagnose the memory leak. When playing back the snapshots using the integrated "Play" button, the memory usage quickly increases until all availalbe memory is exhausted.

It took less than 1 minute to completely max out all 32GB on my laptop.

This was during playback of around 1,100 picture files, each of around 2MB each.
 
  • Like
Reactions: johnfitz
Hi @venturis ...

I put a SD card in a DS-2CD2387G2-LU and accumulated about 5,000 2.5MB snapshots to test with.

Memory leak... fixed.
Also, the CPU was pinning at 100 when playback was paused... duh... fixed.
Worse, if you closed the window/form with playback running, the program could continue to run in the background... ugh... fixed.

I found another small bug... a "division by zero" error when there was only 1 picture to download that was timestamped the same minute when trying to download it... I'm not sure that's clear, but anyway, if you had seen that... it's fixed.

I adjusted the small window that shows the zoomed date/time of the pictures, so it has room for these higher resolution images.

Here's version 2.1:

NEWER VERSION AT END OF THREAD
 
Last edited:
Code:
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'                                                        HIK CAMERA PICTURE DOWNLOAD UTILITY
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Option Strict On
Option Explicit On

Imports System.ComponentModel
Imports System.IO
Imports System.Net
Imports System.Net.Http
Imports System.Text

Public Class Form1

    Dim PicName(1000000) As String
    Dim PicAddress(1000000) As String
    Dim PicDTime(1000000) As DateTime
    Dim PicSize(1000000) As String
    Dim PicPlaybackURI(1000000) As String

    Dim picIndex, newStartNum, newEndNum As Integer
    Dim searchFrom As DateTime
    Dim requestCancel As Boolean = False

    Dim cameraCredentials As New NetworkCredential
    Dim cameraImage As Drawing.Image

    Dim iniFile As FileStream

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                               MAIN FORM LOAD
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        If Not File.Exists("HikCameraPictureDownload.ini") Then
            UpdateIniFile()
        End If
        iniFile = File.Open("HikCameraPictureDownload.ini", CType(FileAccess.ReadWrite, FileMode))
        Dim iniReader As New StreamReader(iniFile)
        Username.Text = iniReader.ReadLine()
        DownloadFolder.Text = iniReader.ReadLine()
        Do While Not iniReader.EndOfStream
            IpInfo.Items.Add(iniReader.ReadLine())
        Loop
        iniFile.Close()

        UtcOffset.Value = Convert.ToInt32((DateTime.Now - DateTime.UtcNow).TotalHours)
        cameraCredentials.UserName = Username.Text
        cameraCredentials.Password = Password.Text


    End Sub

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                             CONNECT BUTTON CLICK
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Async Sub ConnectButton_Click(sender As Object, e As EventArgs) Handles ConnectButton.Click

        If Not OkToConnect() Then
            Return
        End If
        EnableConnectInputs(False)
        requestCancel = False
        CancelButton.Enabled = True

        Try                                                                     ' REQUEST CAMERA NAME
            Dim cameraHandler As New HttpClientHandler With {.Credentials = cameraCredentials}
            Dim cameraClient As New HttpClient(cameraHandler) With {.Timeout = TimeSpan.FromSeconds(30)}
            Dim cameraRequest As New HttpRequestMessage With {
                .Method = HttpMethod.Get,
                .RequestUri = New Uri("http://" + IpInfo.Text + "/ISAPI/Streaming/channels/101/capabilities"),
                .Content = New StringContent("", Encoding.UTF8)
            }
            Dim cameraResponse = cameraClient.Send(cameraRequest)
            cameraResponse.EnsureSuccessStatusCode()
            Dim cameraStream = Await cameraResponse.Content.ReadAsStringAsync().ConfigureAwait(True)
            Dim Xml = cameraStream.ToString
            Xml = Replace(Xml, " version=""2.0"" xmlns=""http://www.hikvision.com/ver20/XMLSchema""", "", 1, 1)
            Dim xElement As XElement = XElement.Parse(Xml, options:=LoadOptions.None)
            CameraName.Text = VerifyCameraName(xElement.Descendants("channelName").Value).ToString
            If CameraName.Text = "" Then
                Throw New Exception("Camera has no name.")
            Else
                CameraName.BackColor = Color.Khaki
                If Not IpInfo.Items.Contains(IpInfo.Text) Then
                    IpInfo.Items.Add(IpInfo.Text)
                    UpdateIniFile()
                End If
            End If
        Catch exceptionError As Exception
            MsgBox(String.Format("Error: {0}", exceptionError.Message), vbDefaultButton2, "Error connecting...")
            Disconnect()
            EnableConnectInputs(True)
            CancelButton.Enabled = False
            Return
        End Try

        If SkipPrevious.Checked Then                                      ' CHECK FILES ON DISK PREVIOUSLY
            If Directory.Exists(DownloadFolder.Text + "\" + CameraName.Text) Then
                OldCount.Value = Directory.GetFiles(DownloadFolder.Text + "\" + CameraName.Text).Length

                If OldCount.Value > 0 Then
                    Dim oldestFile = Directory.GetFiles(DownloadFolder.Text + "\" + CameraName.Text).
                                              OrderByDescending(Function(f) New FileInfo(f).LastWriteTime).Last()
                    oldestFile = Replace(oldestFile, DownloadFolder.Text + "\" + CameraName.Text +
                                                                            "\" + CameraName.Text + "_", "")
                    oldestFile = Replace(oldestFile, ".jpg", "")
                    OldStart.Text = StrDateToDT(oldestFile).ToString("yyyy/MM/dd HH:mm:ss")

                    Dim newestFile = Directory.GetFiles(DownloadFolder.Text + "\" + CameraName.Text).
                                               OrderByDescending(Function(f) New FileInfo(f).LastWriteTime).First()
                    CameraMainPicture.Load(newestFile) 'Load the picture of just the newest existing file
                    LoadPicture(CameraMainPicture.Image, False)
                    newestFile = Replace(newestFile, DownloadFolder.Text + "\" + CameraName.Text + "\" +
                                                                                    CameraName.Text + "_", "")
                    newestFile = Replace(newestFile, ".jpg", "")
                    OldEnd.Text = StrDateToDT(newestFile).ToString("yyyy/MM/dd HH:mm:ss")

                    PicDTime(1) = StrDateToDT(newestFile) 'This is needed to keep the progress bar accurate
                    searchFrom = StrDateToDT(newestFile).AddSeconds(1)
                    picIndex = CInt(OldCount.Value + 1)
                End If
            End If
        End If
        SkipPrevious.Enabled = False
        SkipPrevious.Checked = False

        Try                                                               ' REQUEST NEW PICTURE BATCHES
            Dim searchTo = Now
            Dim picBatchAmt = 50
            While picBatchAmt = 50
                Dim cameraHandler As New HttpClientHandler With {.Credentials = cameraCredentials}
                Dim cameraClient As New HttpClient(cameraHandler) With {.Timeout = TimeSpan.FromSeconds(30)}
                Dim cameraRequest As New HttpRequestMessage With {
                    .Method = HttpMethod.Post,
                    .RequestUri = New Uri("http://" + IpInfo.Text + "/ISAPI/contentMgmt/search"),
                    .Content = New StringContent("<CMSearchDescription><searchID>ID</searchID><trackIDList>" +
                                        "<trackID>103</trackID></trackIDList><timeSpanList><timeSpan>" +
                                        "<startTime>" + HikDate(searchFrom.AddHours(-UtcOffset.Value)) + "</startTime>" +
                                        "<endTime>" + HikDate(searchTo.AddHours(-UtcOffset.Value)) + "</endTime>" +
                                        "</timeSpan></timeSpanList><contentTypeList>" +
                                        "<contentType>metadata</contentType></contentTypeList>" +
                                        "<maxResults>50</maxResults><searchResultPostion>0</searchResultPostion>" +
                                        "<metadataList><metadataDescriptor>/recordType.meta.std-cgi.com/CMR" +
                                        "</metadataDescriptor></metadataList></CMSearchDescription>", Encoding.UTF8)
                }
                Dim cameraResponse = cameraClient.Send(cameraRequest)
                Dim cameraStream = Await cameraResponse.Content.ReadAsStringAsync().ConfigureAwait(True)
                Dim Xml = cameraStream.ToString
                Xml = Replace(Xml, " version=""2.0"" xmlns=""http://www.hikvision.com/ver20/XMLSchema""", "", 1, 1)
                Dim xElement As XElement = XElement.Parse(Xml, options:=LoadOptions.None)
                picBatchAmt = CInt(xElement.Descendants("numOfMatches").Value)
                If picBatchAmt = 0 And picIndex = 0 Then
                    Throw New Exception("No pictures on " + CameraName.Text + ".")
                End If

                Dim CMSearchResult = xElement.Descendants("playbackURI")          ' CREATE ARRAY OF PICTURES
                For Each playbackURI As XElement In CMSearchResult
                    picIndex += 1
                    PicPlaybackURI(picIndex) = playbackURI.Value

                    Dim SearchWithinThis = playbackURI.Value
                    Dim SearchForThis = "http://"
                    Dim FirstChar = SearchWithinThis.IndexOf(SearchForThis)
                    SearchForThis = "/ISAPI"
                    Dim LastChar = SearchWithinThis.IndexOf(SearchForThis)
                    If LastChar = -1 Then
                        SearchForThis = "/Streaming"
                        LastChar = SearchWithinThis.IndexOf(SearchForThis)
                    End If
                    PicAddress(picIndex) = Mid(playbackURI.Value, FirstChar + 8, LastChar - FirstChar - 7)

                    SearchWithinThis = playbackURI.Value
                    SearchForThis = "starttime="
                    FirstChar = SearchWithinThis.IndexOf(SearchForThis)
                    PicDTime(picIndex) = StrDateToDT(Mid(playbackURI.Value, FirstChar + 11, 8) +
                                                               Mid(playbackURI.Value, FirstChar + 20, 6))
                    PicDTime(picIndex) = PicDTime(picIndex).AddHours(UtcOffset.Value)

                    SearchWithinThis = playbackURI.Value
                    SearchForThis = "size="
                    FirstChar = SearchWithinThis.IndexOf(SearchForThis)
                    PicSize(picIndex) = Mid(playbackURI.Value, FirstChar + 6, playbackURI.Value.Length - FirstChar - 4)

                    SearchWithinThis = playbackURI.Value
                    SearchForThis = "name="
                    FirstChar = SearchWithinThis.IndexOf(SearchForThis)
                    SearchForThis = "size="
                    LastChar = SearchWithinThis.IndexOf(SearchForThis)
                    PicName(picIndex) = Mid(playbackURI.Value, FirstChar + 6, LastChar - FirstChar - 6)

                    Dim jpgFilename = DownloadFolder.Text + "\" + CameraName.Text + "\" + CameraName.Text + "_" +
                                      PicDTime(picIndex).ToString("yyyyMMddHHmmss") + ".jpg"

                    If File.Exists(jpgFilename) Then
                        OldCount.Value += 1
                        If OldCount.Value = 1 Then
                            OldStart.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                        End If
                        OldEnd.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                        If PreviewOldCheckBox.Checked = True Then
                            CameraMainPicture.Load(jpgFilename)
                            LoadPicture(CameraMainPicture.Image, False)
                        End If
                    Else
                        PreviewOldCheckBox.Enabled = False
                        PreviewOldCheckBox.Checked = False
                        NewCount.Value += 1
                        If newStartNum = 0 Then
                            newStartNum = picIndex
                            NewStart.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                            RequestPicture(picIndex) 'Load just first new pic while connecting
                            LoadPicture(cameraImage, True)
                        End If
                        newEndNum = picIndex
                        NewEnd.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                    End If
                    searchFrom = PicDTime(picIndex).AddSeconds(1)

                    ProgressBar.Value = CInt(100 * (1 - (DateDiff("n", PicDTime(picIndex), searchTo) /
                                                         DateDiff("n", PicDTime(1), searchTo))))
                    My.Application.DoEvents()
                    System.Threading.Thread.Sleep((100 - CpuDelay.Value) * 10)
                    If requestCancel = True Then
                        Disconnect()
                        EnableConnectInputs(True)
                        Return
                    End If
                Next
            End While
        Catch exceptionError As Exception
            MsgBox(String.Format("Error: {0}", exceptionError.Message), vbDefaultButton2, "Error Connecting")
            Disconnect()
            EnableConnectInputs(True)
            CancelButton.Enabled = False
            Return
        End Try

        PreviewOldCheckBox.Enabled = False
        PreviewOldCheckBox.Checked = False
        If NewCount.Value > 0 Then
            DownloadButton.Enabled = True
            PreviewNewCheckBox.Enabled = True
            PreviewNewCheckBox.Checked = True
        Else
            EnableConnectInputs(True)
            SkipPrevious.Enabled = True
            SkipPrevious.Checked = True
        End If

    End Sub

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                          DOWNLOAD BUTTON CLICK
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub DownloadButton_Click(sender As Object, e As EventArgs) Handles DownloadButton.Click

        DownloadButton.Enabled = False
        requestCancel = False
        Try
            If Not Directory.Exists(DownloadFolder.Text + "\" + CameraName.Text) Then
                My.Computer.FileSystem.CreateDirectory(DownloadFolder.Text + "\" + CameraName.Text)
            End If
            For picIndex = newStartNum To newEndNum

                RequestPicture(picIndex)

                Dim jpgFilename = DownloadFolder.Text + "\" + CameraName.Text + "\" + CameraName.Text + "_" +
                                                              StrDate(PicDTime(picIndex)) + ".jpg"
                cameraImage.Save(jpgFilename)
                If PreviewNewCheckBox.Checked = True Or picIndex = newStartNum Then
                    LoadPicture(cameraImage, True)
                End If

                If picIndex = newStartNum And OldStart.Text = "" Then
                    OldStart.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                End If
                OldEnd.Text = PicDTime(picIndex).ToString("yyyy/MM/dd HH:mm:ss")
                OldCount.Value = OldCount.Value + 1
                If picIndex <> newEndNum Then
                    NewStart.Text = PicDTime(picIndex + 1).ToString("yyyy/MM/dd HH:mm:ss")
                    NewCount.Value = NewCount.Value - 1
                    ProgressBar.Value = CInt(100 * (1 - ((newEndNum - picIndex) / (newEndNum - newStartNum))))
                Else
                    NewStart.Text = ""
                    NewEnd.Text = ""
                    NewCount.Value = NewCount.Value - 1
                    newStartNum = 0
                    ProgressBar.Value = 100
                End If

                My.Application.DoEvents()
                Threading.Thread.Sleep((100 - CpuDelay.Value) * 5)
                If requestCancel = True Then
                    Disconnect()
                    EnableConnectInputs(True)
                    Return
                End If
            Next
        Catch exceptionError As Exception
            MsgBox(String.Format("Error: {0}", exceptionError.Message), vbDefaultButton2, "Error Downloading")
            Disconnect()
            EnableConnectInputs(True)
            CancelButton.Enabled = False
            Return
        End Try
        EnableConnectInputs(True)
        CancelButton.Enabled = False

    End Sub

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                         CANCEL BUTTON CLICK
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub CancelButton_Click(sender As Object, e As EventArgs) Handles CancelButton.Click
        requestCancel = True
        DownloadButton.Enabled = False
        Disconnect()
        EnableConnectInputs(True)
    End Sub


    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                         REQUEST PICTURE FROM CAMERA
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Async Sub RequestPicture(picIndex As Integer)
        Dim requestRetry As Integer = 0
        Dim noError As Boolean
        While requestRetry <= 4
            requestRetry += 1
            noError = True
            Try
                Dim cameraHandler As New HttpClientHandler With {.Credentials = cameraCredentials}
                Dim cameraClient As New HttpClient(cameraHandler) With {.Timeout = TimeSpan.FromSeconds(30)}
                Dim cameraRequest As New HttpRequestMessage With {
                .Method = HttpMethod.Get,
                .RequestUri = New Uri("http://" + IpInfo.Text + "/ISAPI/contentMgmt/download"),
                .Content = New StringContent("<?xml version='1.0'?><downloadRequest><playbackURI>rtsp:/" +
                                       PicAddress(picIndex) + "/Streaming/tracks/120?starttime=" +
                                       HikDate(PicDTime(picIndex).AddDays(-UtcOffset.Value)) + "&amp;endtime=" +
                                       HikDate(PicDTime(picIndex).AddDays(-UtcOffset.Value)) + "&amp;name=" +
                                       PicName(picIndex) + "&amp;size=" +
                                       PicSize(picIndex) + "</playbackURI></downloadRequest>", Encoding.UTF8)
                }
                Dim cameraResponse = cameraClient.Send(cameraRequest)
                cameraResponse.EnsureSuccessStatusCode()
                Dim cameraByteStream = Await cameraResponse.Content.ReadAsByteArrayAsync().ConfigureAwait(True)
                Dim cameraMemoryStream = New MemoryStream(cameraByteStream)

                cameraImage = Drawing.Image.FromStream(cameraMemoryStream)

            Catch exceptionError As Exception
                If requestRetry = 4 Then
                    MsgBox(String.Format("Error: {0}", exceptionError.Message), vbDefaultButton2, "Error Requesting Picture")
                    Return
                Else
                    noError = False
                    My.Application.DoEvents()
                    Threading.Thread.Sleep(15000)
                End If
            End Try
            If noError Then Return
        End While
    End Sub

    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    '                                                              FUNCTIONS
    '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
    Private Sub EnableConnectInputs(TurnOn As Boolean)
        ConnectButton.Enabled = TurnOn
        CancelButton.Enabled = Not TurnOn
        Username.Enabled = TurnOn
        Password.Enabled = TurnOn
        IpInfo.Enabled = TurnOn
        DownloadFolder.Enabled = TurnOn
        UtcOffset.Enabled = TurnOn
        If NewCount.Value = 0 Then
            PreviewNewCheckBox.Enabled = False
            PreviewNewCheckBox.Checked = False
        End If
        If TurnOn Then
            Me.AcceptButton = ConnectButton
        Else
            Me.AcceptButton = DownloadButton
        End If
    End Sub

    Private Sub Disconnect()
        CameraName.Text = "DISCONNECTED"
        CameraName.BackColor = Color.Firebrick
        CameraMainPicture.Image = Nothing
        CameraTimePicture.Image = Nothing
        ProgressBar.Value = 0
        picIndex = 0
        searchFrom = New DateTime(2020, 1, 1, 0, 0, 0, 0)
        OldStart.Text = ""
        OldEnd.Text = ""
        OldCount.Value = 0
        NewStart.Text = ""
        NewEnd.Text = ""
        NewCount.Value = 0
        PreviewOldCheckBox.Enabled = False
        PreviewOldCheckBox.Checked = False
        SkipPrevious.Enabled = True
        SkipPrevious.Checked = True
        newStartNum = 0
        newEndNum = 0
    End Sub

    Private Sub SkipPrevious_CheckedChanged(sender As Object, e As EventArgs) Handles SkipPrevious.CheckedChanged
        If SkipPrevious.Checked Then
            PreviewOldCheckBox.Enabled = False
            PreviewOldCheckBox.Checked = False
        Else
            PreviewOldCheckBox.Enabled = True
            PreviewOldCheckBox.Checked = True
        End If
    End Sub

    Private Function OkToConnect() As Boolean
        If Username.Text = "" Then
            MsgBox("Enter Username to connect", vbDefaultButton2, "Username missing")
            Return False
        End If
        If Password.Text = "" Then
            MsgBox("Enter Password to connect", vbDefaultButton2, "Password missing")
            Return False
        End If
        If IpInfo.Text = "" Then
            MsgBox("Enter IP Address:Port to connect", vbDefaultButton2, "IP Address:Port missing")
            Return False
        End If
        If DownloadFolder.Text = "" Then
            MsgBox("Select Download Folder to connect", vbDefaultButton2, "Download Folder missing")
            Return False
        End If
        Return True
    End Function

    Private Sub UpdateIniFile()
        Dim iniFileText As String = Username.Text + Environment.NewLine + DownloadFolder.Text
        For x = 0 To IpInfo.Items.Count - 1
            iniFileText += Environment.NewLine + IpInfo.Items.Item(x).ToString
        Next
        File.WriteAllText("HikCameraPictureDownload.ini", iniFileText)
    End Sub

    Private Sub DownloadFolder_Click(sender As Object, e As EventArgs) Handles DownloadFolder.Click
        FolderBrowser.InitialDirectory = DownloadFolder.Text
        FolderBrowser.ShowDialog()
        DownloadFolder.Text = FolderBrowser.SelectedPath
        Disconnect()
        UpdateIniFile()
    End Sub

    Private Sub Username_Leave(sender As Object, e As EventArgs) Handles Username.Leave
        cameraCredentials.UserName = Username.Text
        Disconnect()
        UpdateIniFile()
    End Sub

    Private Sub Password_Leave(sender As Object, e As EventArgs) Handles Password.Leave
        cameraCredentials.Password = Password.Text
        Disconnect()
    End Sub

    Private Sub IpInfo_SelectedValueChanged(sender As Object, e As EventArgs) Handles IpInfo.SelectedValueChanged
        Disconnect()
    End Sub

    Private Sub GmtOffset_ValueChanged(sender As Object, e As EventArgs) Handles UtcOffset.ValueChanged
        Disconnect()
    End Sub

    Private Function VerifyCameraName(cameraName As String) As String
        cameraName = Replace(cameraName, " ", "_")
        cameraName = Replace(cameraName, "\", "_")
        cameraName = Replace(cameraName, "/", "_")
        cameraName = Replace(cameraName, ":", "_")
        cameraName = Replace(cameraName, "*", "_")
        cameraName = Replace(cameraName, "?", "_")
        cameraName = Replace(cameraName, "<", "_")
        cameraName = Replace(cameraName, ">", "_")
        cameraName = Replace(cameraName, "!", "_")
        Return cameraName
    End Function

    Private Function HikDate(normalDate As DateTime) As String  ' takes datetime returns "yyyy-mm-ddThh:mm:ssZ"
        HikDate = normalDate.ToString("yyyy-MM-dd") + "T" + normalDate.ToString("HH:mm:ss") + "Z"
    End Function

    Private Function StrDateToDT(dateStr As String) As DateTime ' takes "yyyymmddhhmmss" returns datetime
        dateStr = Mid(dateStr, 1, 4) + "/" + Mid(dateStr, 5, 2) + "/" + Mid(dateStr, 7, 2) + " " +
                  Mid(dateStr, 9, 2) + ":" + Mid(dateStr, 11, 2) + ":" + Mid(dateStr, 13, 2)
        StrDateToDT = Convert.ToDateTime(dateStr)
    End Function

    Private Function StrDate(aDate As DateTime) As String       ' takes datetime returns "yyyyMMddHHmmss"
        StrDate = aDate.ToString("yyyyMMddHHmmss")
    End Function

    Private Sub LoadPicture(cameraImage As Image, mainAndTime As Boolean)
        Dim cameraBitmap As System.Drawing.Bitmap
        Dim cameraGraphics As Graphics
        If mainAndTime Then
            CameraMainPicture.Image = cameraImage
        End If
        cameraBitmap = CType(cameraImage, Bitmap)
        CameraTimePicture.Image = New Bitmap(950, 55)
        cameraGraphics = Graphics.FromImage(CameraTimePicture.Image)
        cameraGraphics.DrawImage(image:=cameraBitmap,
                                 destRect:=New Rectangle(x:=0, y:=0, width:=950, height:=55),
                                 srcRect:=New Rectangle(x:=0, y:=0, width:=950, height:=55),
                                 srcUnit:=GraphicsUnit.Pixel)
    End Sub

End Class
yes i need the whole project. but if it is difficult for you, i will be satisfied with the codes you shared. you can also try the following methods. @johnfitz
Hello, it would be great if you can upload the project to GitHub. If you face any issues, I can guide you. As @trempa92 mentioned, you can zip the project by opening its folder from “Solution Explorer” in Visual Studio. or To upload the project to GitHub:
1. Log into your GitHub account and create a New Repository.
2. In Visual Studio, go to the Git section and “Push” the current project to your repository.
3. If you encounter any issues, share the project as a ZIP file, and we'll help you upload it.
or we can follow a different way, it would be useful to upload it to any cloud system and make it available for sharing. we can advance the project better as a community contribution by sharing it on github. but I saw some of the source code in your article, maybe this way I can create a solution project for myself and share it with you. thank you in advance for your help and support. sorry for seeing your articles late, I don't have much time due to the year-end intensity.
 
yes i need the whole project

Hi @xleatz ... I will get the project up on GitHub... but I want to make a few improvements first that @venturis and I discussed above... I should be ready to load it to GitHub in a week or two...

In the meantime, I found another bug (error when loading pictures for the first time, introduced by one of the changes I made to fix the memory leak) ... fixed in this latest release V2.2:
 

Attachments

Merhaba @xleatz ... Projeyi GitHub'a koyacağım... ama önce @venturis ve yukarıda tartıştığımız birkaç iyileştirme yapmak istiyorum... Bir veya iki hafta içinde GitHub'a yüklemeye hazır olmalıyım...

Bu arada, başka bir hata daha buldum (bellek sızıntısını gidermek için yaptığım değişikliklerden birinin neden olduğu, resimleri ilk kez yüklerken oluşan hata) ... bu son sürüm V2.2'de düzeltildi:
teşekkür ederim. github paylaşımınızı bekliyor olacağım. github profilinizi verirseniz takip etmek isterim. teşekkürler
 
  • Like
Reactions: johnfitz