Hikvision cameras API

6.8k views Asked by At

Does anyone have experience with this type of integration? Especially with these types of cameras.

I am currently creating a software for monitoring surveillance devices. Hikvision cameras are installed. The idea is to be able to communicate with these cameras via ISAPI (hikvision API).

The problem is when I run a URL that gives me the alarm status from postman or in my project code, the URL gives status 200 but it doesn't deliver anything and postman keeps "thinking", when the request is made by Google Chrome browser, it downloads a file that never finishes downloading, if I open it, it gives me the data I need, but it would be of great help just making the request from my software and having the devices deliver the data to me.

I have been reading the documentation, I execute the steps that are, but, there has been no success obtaining the data.

endpoint: "GET /ISAPI/Event/notification/alertStream"

Is it possible that someone in this community has had the same experience and was able to solve it?

(It's my first time doing this kind of integrations)

I would greatly appreciate an answer.

3

There are 3 answers

0
Luka T On

There are 3 ways of handling events with Hikvision devices: 1.SDK via arming 2.Using alertStream 3.Alarm Server or sometimes they reffer to it as HTTP Listener mode

SDK is available and you can get demo and solution with documentation. Isapi documentation in detailed version is available only those who sign Licence agreement form at tpp.hikvision.com

Id suggest using HTTP Listener, most if not all cameras do have option to insert IP and port of an listening server.

On your side, do a TCP server listener,but the key is to send reponse 200 OK back to camera once you recieve the event otherwise the same event will loop until you do.

Events are mostly in json format, some can be xml. But also in most cases are multipart data, meaning they contain json values of an event and binary image for you in time of an event. Its up to you to use whatever you need from it.

Take a look at code i did for thermal camera event listener which reads incoming events and parse json data and image data, and it stores to separated folders. I believe better way is to allocate buffer size by actual Content-Length value in received packet, you can edit that yourself.

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Newtonsoft.Json.Linq;

namespace TcpServerExample
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] config = File.ReadAllText("serverconfig.txt").Split(':');
            IPAddress localAddr = IPAddress.Parse(config[0].Trim());
            int port = int.Parse(config[1].Trim());

            TcpListener server = new TcpListener(localAddr, port);
            server.Start();
            Console.WriteLine($"Server is listening on {localAddr}:{port}...");

            // Create directories if they don't exist
            if (!Directory.Exists("images"))
                Directory.CreateDirectory("images");

            if (!Directory.Exists("data"))
                Directory.CreateDirectory("data");

            byte[] lastRequest = null;
            int requestCounter = 0;

            while (true)
            {
                TcpClient client = server.AcceptTcpClient();
                NetworkStream stream = client.GetStream();

                MemoryStream memoryStream = new MemoryStream();
                byte[] buffer = new byte[50024];
                int bytesRead;

                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    memoryStream.Write(buffer, 0, bytesRead);
                }

                byte[] requestData = memoryStream.ToArray();

                if (lastRequest == null || !requestData.SequenceEqual(lastRequest))
                {
                    requestCounter++;
                    Console.WriteLine($"Received request number {requestCounter}.");

                    // Extract Image
                    string boundaryString = "--boundary";
                    byte[] boundaryBytes = Encoding.ASCII.GetBytes(boundaryString);
                    byte[] imageHeaderBytes = Encoding.ASCII.GetBytes("Content-Disposition: form-data; name=\"getUp\"; filename=\"getUp.jpg\";");
                    int imageHeaderStartIndex = FindIndex(requestData, imageHeaderBytes);

                    if (imageHeaderStartIndex != -1)
                    {
                        byte[] doubleLineBreakBytes = Encoding.ASCII.GetBytes("\r\n\r\n");
                        int imageStartIndex = FindIndex(requestData, doubleLineBreakBytes, imageHeaderStartIndex) + doubleLineBreakBytes.Length;
                        int imageEndIndex = FindIndex(requestData, boundaryBytes, imageStartIndex) - 2;  // Exclude preceding "\r\n".

                        if (imageStartIndex != -1 && imageEndIndex != -1)
                        {
                            byte[] imageData = requestData.Skip(imageStartIndex).Take(imageEndIndex - imageStartIndex).ToArray();

                            // Extract JSON
                            string jsonData = Encoding.ASCII.GetString(requestData).Split(new string[] { boundaryString }, StringSplitOptions.None)[1];
                            jsonData = jsonData.Substring(jsonData.IndexOf("{"), jsonData.LastIndexOf("}") - jsonData.IndexOf("{") + 1);
                            JObject obj = JObject.Parse(jsonData);
                            string channelName = obj["channelName"].ToString();
                            string currentDate = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
                            string filenameBase = $"{channelName}_{currentDate}";

                            File.WriteAllBytes($"images/{filenameBase}.jpg", imageData);
                            File.WriteAllText($"data/{filenameBase}.xml", jsonData);
                        }
                    }

                    lastRequest = requestData;
                }

                string responseString = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n";
                byte[] responseBytes = Encoding.ASCII.GetBytes(responseString);
                stream.Write(responseBytes, 0, responseBytes.Length);

                client.Close();
            }
        }

        // Helper method
        static int FindIndex(byte[] data, byte[] sequence, int startIndex = 0)
        {
            int foundIndex = -1;
            for (int i = startIndex; i <= data.Length - sequence.Length; i++)
            {
                if (data.Skip(i).Take(sequence.Length).SequenceEqual(sequence))
                {
                    foundIndex = i;
                    break;
                }
            }
            return foundIndex;
        }
    }
}
0
Daniel On

The reason why the request remains "thinking" is because the endpoint: "GET /ISAPI/Event/notification/alertStream" opens a channel through which a communication is established that sends the data in real time that the camera captures to the device from which the request is made. One option to recover the information that the camera is detecting is to take pieces of data from the request. But first it is necessary to know what the structure of the data that the camera responds is like, according to my experience those cameras responds in XML and JSON, if you want to be sure you can use Wireshark to capture the packages when the request is sended and then check what is the format of the response. Once you know what the format is, you can do some code to take "parts" of the response and extract what you need. Here's an example of this in Python using a camera with ANPR:

import requests
import json

# url de la petición
request_url = 'http://{your_camera_ip}/ISAPI/Event/notification/alertStream'

# información de autenticación
auth = requests.auth.HTTPDigestAuth('admin', 'admin')

# guarda los datos de la respuesta
response = requests.get(request_url,auth=auth, stream=True) 

if response.status_code == 200:
    buffer = b""  # buffer vacío de bytes
    for chunk in response.iter_content(chunk_size=1024): # itera sobre la sección establecida de la respuesta
        if chunk: # si hay algún dato en dicha sección
            # lo añade al buffer
            buffer += chunk

            # lo intenta decodificar en utf-8 y si lo hace correctamente entonces procesa los datos
            try:
                # decodifica los datos que llegan desde el buffer
                decoded_data = buffer.decode("utf-8")
                # teniendo los datos, obtiene una parte de dichos datos según los saltos de línea '\n'
                # y lo almacena en la variable json_data, en la variable separator almacena el salto de línea
                # y el restante lo almacena en la variable reamaining_data
                json_data, separator, remaining_data = decoded_data.partition('\n')

                # mientras haya un salto de línea va a intentar obtener los datos de la información del vehículo
                while separator:
                    if json_data:
                        # si hay algo en la variable va a intentar convertir dichos datos en un json sino
                        # entonces sigue con la ejecución
                        try:
                            # deserializa el json a un objeto de python
                            data = json.loads(json_data)
                            # si los datos que deserializa son de tipo diccionario
                            # y si "VehicleMatchResult" está en los datos
                            # y si "VehicleMatchResult" es de tipo diccionario entonces
                            # recupera los datos que estén en "PlateInfo"
                            if isinstance(data, dict) and "VehicleMatchResult" in data and isinstance(data["VehicleMatchResult"], dict):
                                plate_info = data["VehicleMatchResult"].get("PlateInfo", {})
                                # si "plate_info es un diccionario" y "plate" hace parte de dicho diccionario
                                # obtiene los valores de "plate" y los imprime
                                if isinstance(plate_info, dict) and "plate" in plate_info:
                                    plate_data = plate_info["plate"]
                                    print("Plate:", plate_data)
                        except json.JSONDecodeError:
                            pass
                    
                    # actualiza los datos de cada variable asignando datos nuevos a partir de los datos
                    # restantes de la respuesta
                    json_data, separator, remaining_data = remaining_data.partition('\n')
            except UnicodeDecodeError:
                pass
            # Deja los bytes restantes sin codificar
            buffer = remaining_data.encode("utf-8")
else:
    print("Imposible de conectar al stream. Código de estatus:", response.status_code)
0
susi On

there is a better way to obtain the message: message push mode enables messages to reach the client system in a more real-time manner.  this method only work for those who have opened the message service in https://isgpopen.ezviz.com/

• Request Body JSON format, consistent with the message body structure in message subscription results. See:https://open.ys7.com/doc/zh/book/index/mq_service.html

Request Body Example:

{
    "body": {
        "data": "0",
        "index": 24409
    },
    "header": {
        "channelNo": 1,
        "deviceId": "D98462102",
        "messageId": "5e57f239793f2b007fecb0de",
        "messageTime": 1582821945396,
        "type": "ys.open.isapi"
    }
}