Tag Archives: C language

[Solved] Hikvision SDK: NET_DVR_GetDVRConfig failed Device does not support this function

Problem:

I have written some code based on Hikvision’s sdk for controlling the camera. One section of the program is mainly used to get NVR channel configuration information

The codes is as below:
I use the function NET_DVR_GetDVRConfig

#include  #include "HCNetSDK.h" int main() { NET_DVR_Init(); //Set connection time and reconnect time NET_DVR_SetConnectTime(2000, 1); NET_DVR_SetReconnect(10000, true); // 注册设备 LONG lUserID; //Login parameters, including device address, login user, password, etc. NET_DVR_USER_LOGIN_INFO struLoginInfo = { 0 }; struLoginInfo.bUseAsynLogin = 0; //同步登录方式 strcpy(struLoginInfo.sDeviceAddress, "192.168.20.106"); //设备IP地址 struLoginInfo.wPort = 8000; //设备服务端口 strcpy(struLoginInfo.sUserName, "admin"); //设备登录用户名 strcpy(struLoginInfo.sPassword, "111111hk"); //设备登录密码 //设备信息, 输出参数 NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = { 0 }; lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40); if (lUserID < 0) { printf("Login failed, error code: %d\n", NET_DVR_GetLastError()); NET_DVR_Cleanup(); return -1; } NET_DVR_IPPARACFG_V40 ipcfg; DWORD bytesReturned = 0; ipcfg.dwSize = sizeof(NET_DVR_IPPARACFG_V40); int iGroupNO = 0; bool resCode = NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_IPPARACFG_V40, iGroupNO, &ipcfg, sizeof(NET_DVR_IPPARACFG_V40), &bytesReturned); if (! resCode) { DWORD code = NET_DVR_GetLastError(); std::cout << "NET_DVR_GetDVRConfig failed " << NET_DVR_GetErrorMsg((LONG*)(&code)) << std::endl; NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return -1; } std::cout << "设备组 " << ipcfg.dwGroupNum << " 数字通道个数 " << ipcfg.dwDChanNum << " 起始通道 " << ipcfg.dwStartDChan << std::endl << std::endl; for (int i = 0; i < ipcfg.dwDChanNum; i++) { NET_DVR_PICCFG_V30 channelInfo; bytesReturned = 0; channelInfo.dwSize = sizeof(NET_DVR_PICCFG_V30); int channelNum = i + ipcfg.dwStartDChan; NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_PICCFG_V30, channelNum, &channelInfo, sizeof(NET_DVR_PICCFG_V30), &bytesReturned); std::cout <<"通道号 "<< channelNum << "\t通道名称 " << channelInfo.sChanName; std::cout << "\t用户名 " << ipcfg.struIPDevInfo[i].sUserName << "\t密码 " << ipcfg.struIPDevInfo[i].sPassword; std::cout << "\t设备ID " << (int)ipcfg.struIPDevInfo[i].szDeviceID; std::cout << "\tip地址 " << ipcfg.struIPDevInfo[i].struIP.sIpV4 << "\t端口 " << ipcfg.struIPDevInfo[i].wDVRPort << std::endl; } //释放SDK资源 NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return 0; } 

The code was running ok, but when migrating to another machine, something went wrong.
Report the error: NET_DVR_GetDVRConfig failed Device does not support this function

Note the phrase:
If the number of IP channels supported by the device is greater than 0, then the remote parameter configuration interface NET_DVR_GetDVRConfig can be used.
This means that to use this function, you need to check the number of IP channels supported by the device first.
Also, the manual gives a sample program (check first, then call)
The sample procedure is as follows.
ps: there is a point I want to spit, the manual on the ET_DVR_GetDVRConfig function explanation, there is no mention of this issue, causing me to look for a long time to find here to write. Since this function is not supported by all devices, then the norm should be written to check first, then call.

#include  #include  #include "Windows.h" #include "string.h" #include "HCNetSDK.h" using namespace std; void main() { int i=0; BYTE byIPID,byIPIDHigh; int iDevInfoIndex, iGroupNO, iIPCh; DWORD dwReturned = 0; //--------------------------------------- // 初始化 NET_DVR_Init(); //设置连接时间与重连时间 NET_DVR_SetConnectTime(2000, 1); NET_DVR_SetReconnect(10000, true); //--------------------------------------- // 注册设备 LONG lUserID; //Login parameters, including device address, login user, password, etc. NET_DVR_USER_LOGIN_INFO struLoginInfo = {0}; struLoginInfo.bUseAsynLogin = 0; //同步登录方式 strcpy(struLoginInfo.sDeviceAddress, "192.0.0.64"); //设备IP地址 struLoginInfo.wPort = 8000; //设备服务端口 strcpy(struLoginInfo.sUserName, "admin"); //设备登录用户名 strcpy(struLoginInfo.sPassword, "abcd1234"); //设备登录密码 //设备信息, 输出参数 NET_DVR_DEVICEINFO_V40 struDeviceInfoV40 = {0}; lUserID = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40); if (lUserID < 0) { printf("Login failed, error code: %d\n", NET_DVR_GetLastError()); NET_DVR_Cleanup(); return; } printf("The max number of analog channels: %d\n",struDeviceInfoV40.struDeviceV30.byChanNum); //模拟通道个数 printf("The max number of IP channels: %d\n", struDeviceInfoV40.struDeviceV30.byIPChanNum + struDeviceInfoV40.struDeviceV30.byHighDChanNum * 256);//IP通道个数 //获取IP通道参数信息 NET_DVR_IPPARACFG_V40 IPAccessCfgV40; memset(&IPAccessCfgV40, 0, sizeof(NET_DVR_IPPARACFG)); iGroupNO=0; if (! NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_IPPARACFG_V40, iGroupNO, &IPAccessCfgV40, sizeof(NET_DVR_IPPARACFG_V40), &dwReturned)) { printf("NET_DVR_GET_IPPARACFG_V40 error, %d\n", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } else { for (i=0;i<IPAccessCfgV40.dwDChanNum;i++) { switch(IPAccessCfgV40.struStreamMode[i].byGetStreamType) { case 0: //直接从设备取流 if (IPAccessCfgV40.struStreamMode[i].uGetStream.struChanInfo.byEnable) { byIPID=IPAccessCfgV40.struStreamMode[i].uGetStream.struChanInfo.byIPID; byIPIDHigh=IPAccessCfgV40.struStreamMode[i].uGetStream.struChanInfo.byIPIDHigh; iDevInfoIndex=byIPIDHigh*256 + byIPID-1-iGroupNO*64; printf("IP channel no.%d is online, IP: %s\n", i+1, IPAccessCfgV40.struIPDevInfo[iDevInfoIndex].struIP.sIpV4); } break; case 1: //从流媒体取流 if (IPAccessCfgV40.struStreamMode[i].uGetStream.struPUStream.struStreamMediaSvrCfg.byValid) { printf("IP channel %d connected with the IP device by stream server.\n", i+1); printf("IP of stream server: %s, IP of IP device: %s\n",IPAccessCfgV40.struStreamMode[i].uGetStream.\ struPUStream.struStreamMediaSvrCfg.struDevIP.sIpV4, IPAccessCfgV40.struStreamMode[i].uGetStream.\ struPUStream.struDevChanInfo.struIP.sIpV4); } break; default: break; } } } //配置IP通道5; iIPCh=4; //支持自定义协议 NET_DVR_CUSTOM_PROTOCAL struCustomPro; if (! NET_DVR_GetDVRConfig(lUserID, NET_DVR_GET_CUSTOM_PRO_CFG, 1, &struCustomPro, sizeof(NET_DVR_CUSTOM_PROTOCAL), &dwReturned)) //获取自定义协议1 { printf("NET_DVR_GET_CUSTOM_PRO_CFG error, %d\n", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } struCustomPro.dwEnabled=1; //启用主码流 struCustomPro.dwEnableSubStream=1; //启用子码流 strcpy((char *)struCustomPro.sProtocalName,"Protocal_RTSP"); //自定义协议名称:Protocal_RTSP,最大16字节 struCustomPro.byMainProType=1; //主码流协议类型: 1- RTSP struCustomPro.byMainTransType=2; //主码流传输协议: 0-Auto, 1-udp, 2-rtp over rtsp struCustomPro.wMainPort=554; //主码流取流端口 strcpy((char *)struCustomPro.sMainPath,"rtsp://192.168.1.65/h264/ch1/main/av_stream");//主码流取流URL struCustomPro.bySubProType=1; //子码流协议类型: 1-RTSP struCustomPro.bySubTransType=2; //子码流传输协议: 0-Auto, 1-udp, 2-rtp over rtsp struCustomPro.wSubPort=554; //子码流取流端口 strcpy((char *)struCustomPro.sSubPath,"rtsp://192.168.1.65/h264/ch1/sub/av_stream");//子码流取流URL if (! NET_DVR_SetDVRConfig(lUserID, NET_DVR_SET_CUSTOM_PRO_CFG, 1, &struCustomPro, sizeof(NET_DVR_CUSTOM_PROTOCAL))) //设置自定义协议1 { printf("NET_DVR_SET_CUSTOM_PRO_CFG error, %d\n", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } printf("Set the custom protocol: %s\n", "Protocal_RTSP"); NET_DVR_IPC_PROTO_LIST m_struProtoList; if (! NET_DVR_GetIPCProtoList(lUserID, &m_struProtoList)) //Get the front-end protocols supported by the device { printf("NET_DVR_GetIPCProtoList error, %d\n", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } IPAccessCfgV40.struIPDevInfo[iIPCh].byEnable=1; //启用 for (i = 0; i<m_struProtoList.dwProtoNum; i++) { if(strcmp((char *)struCustomPro.sProtocalName,(char *)m_struProtoList.struProto[i].byDescribe)==0) { IPAccessCfgV40.struIPDevInfo[iIPCh].byProType=m_struProtoList.struProto[i].dwType; //选择自定义协议 break; } } //IPAccessCfgV40.struIPDevInfo[iIPCh].byProType=0; //厂家私有协议 strcpy((char *)IPAccessCfgV40.struIPDevInfo[iIPCh].struIP.sIpV4,"192.168.1.65"); //前端IP设备的IP地址 IPAccessCfgV40.struIPDevInfo[iIPCh].wDVRPort=8000; //前端IP设备服务端口 strcpy((char *)IPAccessCfgV40.struIPDevInfo[iIPCh].sUserName,"admin"); //前端IP设备登录用户名 strcpy((char *)IPAccessCfgV40.struIPDevInfo[iIPCh].sPassword,"12345"); //前端IP设备登录密码 IPAccessCfgV40.struStreamMode[iIPCh].byGetStreamType=0; IPAccessCfgV40.struStreamMode[iIPCh].uGetStream.struChanInfo.byChannel=1; IPAccessCfgV40.struStreamMode[iIPCh].uGetStream.struChanInfo.byIPID=(iIPCh+1)%256; IPAccessCfgV40.struStreamMode[iIPCh].uGetStream.struChanInfo.byIPIDHigh=(iIPCh+1)/256; //IP通道配置,包括添加、删除、修改IP通道等 if (! NET_DVR_SetDVRConfig(lUserID, NET_DVR_SET_IPPARACFG_V40, iGroupNO, &IPAccessCfgV40, sizeof(NET_DVR_IPPARACFG_V40))) { printf("NET_DVR_SET_IPPARACFG_V40 error, %d\n", NET_DVR_GetLastError()); NET_DVR_Logout(lUserID); NET_DVR_Cleanup(); return; } else { printf("Set IP channel no.%d, IP: %s\n", iIPCh+1, IPAccessCfgV40.struIPDevInfo[iIPCh].struIP.sIpV4); } //注销用户 NET_DVR_Logout(lUserID); //释放SDK资源 NET_DVR_Cleanup(); return; } 

IOS Adds C File Error [How to Solve]

Reason: This is actually the logic of using pch files in Xcode compilation, after introducing c files in the project, pch will mix #include and #import, in order to reduce compiler workload, you need to use precompiled macros to make a slight distinction.

Solution: Check all pch files in the project and put “#import xxx”, which is only used in OC, in “#indef __OBJC__” and “#endif”

#ifndef PrefixHeader_pch
#define PrefixHeader_pch

#ifdef __OBJC__

#import "Masonry.h"

#endif


#endif /* PrefixHeader_pch */

[Solved] KEIL Error: keil Error: failed to execute ‘C:\Keil\ARM\ARMCC‘

 

Question:

keil Error: failed to execute ‘C:\Keil\ARM\ARMCC’

Cause of problem:

route:

.\MDK_ARM\ARM

Kiel ARM compiler is missing:

Because the latest version of Keil installation directory does not support ARMCC, that is, version 5 cannot be used

Solution:

1. Download the old version of keil

2. Right click to unzip the file, and put the file into the folder where the new version of ARM is installed

3. Add this folder

C++ Compile Error: error: invalid conversion from ‘void*‘ to ‘char*‘ [-fpermissive]

error: invalid conversion from ‘void*‘ to ‘char*‘ [-fpermissive]

#include <stdio.h>
#include<malloc.h>
#define IN
#define OUT

// Get file size
int FileSize(IN char *file)
{
	FILE *fil;
	fil = fopen(file,"rb");
	fseek(fil,0L,SEEK_END);
	int filesize = ftell(fil);
	fseek(fil,0,0);
	return filesize;
}

// read the file
int ReadFileData(IN char *fileName, OUT char *filedata)
{
	FILE *fpIN;
	int fileSizes = FileSize(fileName);
	fpIN = fopen(fileName,"rb");
	fread(filedata,1,fileSizes,fpIN);
	fclose(fpIN);
}

// write the file
int WriteToFile(char *filedata, int size, OUT char *outFileName)
{
	FILE *fpOUT;
	fpOUT = fopen(outFileName,"w+");
	fwrite(filedata,1,size,fpOUT);
	fclose(fpOUT);
}

int main()
{
	char *origin_file = "test.cpp";
	int orgfilesize = FileSize(origin_file);  // Get file size



	char *file_data=  malloc(orgfilesize);      // Allocate file size memory
    if (file_data == NULL)
        return NULL;
	ReadFileData(origin_file, file_data);     // read the file
	char *outFile = "test.txt";
	WriteToFile(file_data,orgfilesize,outFile);  // write the file

	return 0;
}

The following line of code

char *file_data=  malloc(orgfilesize);

Malloc function is used to allocate space in C language. The return type is void*. Void* indicates a pointer of undetermined type. C. C++ specifies that the void* type can cast any other type of pointer.

The malloc() function actually finds a space of a specified size in memory and ranges the first address of that space to a pointer variable.
Here the pointer variable can be a single pointer or the first address of an array.
It depends on the size of the malloc() function.

Use GCC compilation to directly pass and print out the following results

Original String: testing.

When compiling with g++, an error and warning will appear, as follows

error: invalid conversion from ‘void*’ to ‘char*’ [-fpermissive]
warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]

The reason for the error is that c++ is designed to be more secure than C, and it cannot automatically convert void * to other pointer types.

The reason for warning is that the program attempts to convert the string literal (const char [] in c++ and char [] in C language) to char * type,,

char *file_data= (char*) malloc(orgfilesize); 
# The return value of the malloc function is a void*, which is assigned to a variable by adding a forced conversion in front of malloc

Introduction to malloc function
malloc function is often used in C language and c++ to dynamically allocate memory space for variables. Malloc requests the system to allocate memory space of the specified size bytes

function void malloc(int size)

explain:

Malloc requests the system to allocate memory space of the specified size bytes. If the allocation is successful, a pointer to the allocated memory is returned; otherwise, a null pointer is returned
this function is included in the header file: \include < malloc.h> you should import the header file *< malloc.h>  or  < stdlib.h>** when you are using

Note: when the memory is no longer used, the free() function should be used to free the memory block
Common usage

1. When you do not know the definite memory required by a variable

For example, when defining an array, the size of the array is not known until the program is compiled. In this case, you can use the malloc function

int main()
{
	int n;
	scanf("%d",&n);
	int *m=(int *)malloc(sizeof(int)*n);  //Defining a pointer variable that points to n int is equivalent to opening an array of n int elements.
	// If n is very large, more than 1000000, then opening an int array of this size will cause a stack overflow.
	int m[1000000]; //Stack overflow will occur.
	return 0;
}

2. Allocate space for structural variables
define a common variable of structure type. You can dynamically apply for memory without malloc. The CPU will allocate memory for structure variables.

typedef struct
{
    int n;
    char *p;
}node;

int  main()
{
	node a;  //The definition is a structured ordinary variable, you can request memory without using malloc, the CPU will allocate memory for this structured variable
    a.n=4;
    printf("%d",a->n); //can output successfully
    node *b; //defines a structure pointer variable, the CPU will open up memory for this pointer with a size of 4 bytes. But to store the data members of the structure this space is not enough, it will raise a segment error, at this time you must malloc request a structure type size of dynamic memory to store the data members.
    //b=(node *)malloc(sizeof(node));
    printf("%d",sizeof(b)); // use sizeof(b) to see the size of b is 4
    char p[]="abcd";
    printf("%d",b->n);
    (a->p)=p;
    printf("%c",a->p[0]);
    return 0;
}

If malloc is not used to allocate space for structure pointer variable B, warning: ‘B’ is used uninitialized in this function [-wuninitialized]|.

3. When defining a structure, you need to pay attention to allocating space for its members in turn
in normal use, after allocating space for a structure with malloc function, operate on its member variable (pointer type).

For example, when the pointer p=null, it will always report “program received signal SIGSEGV, segmentation fault.”
use malloc function

[Solved] mosquitto log Error: SSL routines:SSL3_READ_BYTES:tlsv1 alert decrypt error

Problem description

The following errors occurred in the TLS/SSL of mosquitto during the test today, indicating that the error occurred in the TLS version:

I know that the default tls version of mosquitto_pub and mosquitto_sub is tlsv1.2, however, the version of openssl in my virtual machine is 1.1.1, and the tls_version tlsv1.1 is configured in mosquitto.conf (one-way authentication is configured here, two-way authentication is also (here the configuration is one-way authentication, two-way authentication is also applicable), so there will be tls version error.


Solution:

For C programs of mosquitto library
Use the API mosquitto_tls_set() before attaching mosquitto_tls_opts_set(), this API can set the corresponding tls version of the client, note that the tls version defaults to tlsv1.2.

The code is as follows:

int    rc = -1;

rc = mosquitto_tls_opts_set(mosq, 1, "tlsv1.1", NULL);
if( rc != MOSQ_ERR_SUCCESS )
{
    printf("mosquitto_tls_opts_set failure.\n");
    exit(1);
}

rc = mosquitto_tls_set(mosq, CAFILE, CAPATH, CERTFILE, KEYFILE, NULL);
if( rc != MOSQ_ERR_SUCCESS )
{
    printf("mosquitto_tls_set failure.\n");
    exit(1);
}

On the mosquitto command line

The following is the test of one-way authentication (in case of two-way authentication, add the certificate and key by yourself)

mosquitto_ pub -h 192.168.222.130 -p 8884 -t “hello” -m “hi sub” –tls-version tlsv1. 1 –cafile ./ ca/ca.crt

mosquitto_ sub -h 192.168.222.130 -p 8884 -t “hello” –tls-version tlsv1. 1 –cafile ./ ca/ca.crt

If the problem is solved successfully, it can be connected normally:

[Solved] Keil Error: ST-LINK USB communication error

Keil Error: ST-LINK USB communication error

Today, when downloading the program with stlink-v2, there was st-link USB communication error. This problem suddenly appeared. I found many solutions on the Internet, but they can’t be used. My solutions are given below for reference only.

If there is a problem as shown in the figure below:

We can solve this problem by re brushing the firmware. you can download the stlink online.

It is very easy to upgrade the firmware of ST-Link. If the ST-Link can be used normally, please do not come to upgrade easily. Here you can open our ST-Link package, there is a zip package inside: ST-Link firmware upgrade software.zip, you can see after decompression.

For windows computers, we go directly to the windows folder and click ST-LinkUpgrade.exe:

The opened interface is as follows:

At this time, we need to connect st link to the computer through USB. After connecting, click the device connect button on the interface,
if the connection is successful, the prompt message in the following interface will appear:

Note: if an error is reported when brushing firmware: Communication error with ST-Link .Try to start it. We can solve this problem by unplugging stlink and then continuing to update the firmware

[Solved] error converting to execution character set illegal byte sequence

Today, after writing a program, I found that the compiler always reports an error: error converting to execution character set illegal byte sequence. When compiling by default, it is parsed according to UTF-8, and when the character set is not specified, it is always treated as UTF-8. So you have to add the following in settings->compiler->Global compiler settings->Other options:

-fexec-charset=GBK
-finput-charset=UTF-8

The former represents the encoding interpretation format of the input file during compilation, and the latter represents the encoding format used for the display of the generated execution file during execution.

At the same time. In settings -> Editor-> gernal settings-> Other settings, set the file encoding format saved by default to UTF-8, and keep the encoding formats of both sides the same.

But after I did this, I found it useless… Later, I found that my program didn’t know when it was changed to ANSI format, so it had been either compiled incorrectly or Chinese garbled.

To solve this problem, you can open the code file with notepad and select the file – & gt; How to select the encoding format of UTF-8 to save as.

[Solved] leetcode Common Error: :runtime error: member access within misaligned address 0xbebebebebebebebe for type ‘str

Common mistakes of brush force buckle:

runtime error: member access within misaligned address 0xbebebebebebebebe for type ‘struct TreeNode’, which requires 8 byte alignment [TreeNode.c]
0xbebebebebebebebe: note: pointer points here

 


cause:
when we access a variable, it contains an unassigned pointer. Pointers that are defined but not assigned are called wild pointers. The direction of the wild pointer is unknown, which has unknown consequences for the program, and the citation is a big problem. Therefore, C language strictly opposes the wild pointer.

In this topic, use

TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));

The root->left and root->right pointers are not assigned initial values or set to NULL, resulting in an error when assigning values to left and right later.

This can be solved by adding two statements.

TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
root->left = NULL;	// Here's the problem!!! A null pointer without an assignment must be set to NULL
root->right = NULL;	// 

This is the correct code:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

typedef struct TreeNode TreeNode;


TreeNode* CreatTree(int* preorder,int* inorder,int l1,int r1,int l2,int r2){
    if(l1 > r1 || l2 > r2){      // Return NULL
        printf("%d",1);
        return NULL;
    }
    TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
    root->left = NULL;	// Here's the problem!!! A null pointer without an assignment must be set to NULL
    root->right = NULL;	// 
    int i;
    root->val = preorder[l1];
    
    for(i=l2;inorder[i]!=root->val;i++);
    int lLen = i - l2;
    int rLen = r2 - i;
    if(lLen > 0){
        root->left = CreatTree(preorder,inorder,l1+1,l1+lLen,l2,l2+lLen-1);
    }
    
    if(rLen > 0){
        root->right = CreatTree(preorder,inorder,r1-rLen+1,r1,r2-rLen+1,r2);
    }
    return root;

}


struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize){
    TreeNode* root = CreatTree(preorder,inorder,0,preorderSize-1,0,inorderSize-1);
    return root;
}

[Solved] import cv2 Error: ImportError: libGL.so.1: cannot open shared object file: No such file or directory

Execute the following command:

    import cv2
  File "/appletree/miniconda3/envs/yyp_pytorch/lib/python3.7/site-packages/cv2/__init__.py", line 5, in <module>
    from .cv2 import *
ImportError: libGL.so.1: cannot open shared object file: No such file or directory

Reason: libGL.so.1 is missing.
Solution:
execute the following command after pip install opencv-python

pip install opencv-python-headless