C#在linux中开发

Posted by BY Blog on December 6, 2019

C#在Linux中开发

Mono is a software platform designed to allow developers to easily create cross platform applications part of the .NET Foundation.

首先介绍Mono是什么,官网上给出的定义为 “ Mono是一个软件平台,旨在使开发人员可以轻松创建.NET Foundation一部分的跨平台应用程序。”简单点来说,它是一个跨平台的、开源的、基于.Net的编译环境。你可以在各个平台上使用它来编写C#应用程序,包括Linux、Windows、macOS。以下从几个部分进行Mono的介绍。

一、安装

Mono的安装并不复杂,在官网上有着详细的步骤可以参考,https://www.mono-project.com/download/stable/

在Linux下安装Mono有以下步骤:

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
sudo apt install apt-transport-https ca-certificates
echo "deb https://download.mono-project.com/repo/ubuntu stable-xenial main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update
sudo apt install mono-devel

上述命令实现了Mono环境的安装,你可以用以下命令检测是否安装成功。

mozroots --import --sync

如果没有安装成功的话,建议卸载重装。当然这是后话了。

安装完 mono-devel后你会发现你找不到这个东西,因为你只安装了编译器,并没有安装IDE,下面将继续安装MonoDevelop IDE。

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
sudo apt install apt-transport-https
echo "deb https://download.mono-project.com/repo/ubuntu vs-xenial main" | sudo tee /etc/apt/sources.list.d/mono-official-vs.list
sudo apt update
sudo apt-get install monodevelop

上述命令完成了Mono IDE的安装,如何判断是否安装成功?很简单,打开Mono创建hello world项目,运行成功就安装成功。这里就不进行演示了。

二、确立开发任务

Mono的安装显然不是摆设,它需要实际的应用上。我们这边遇到的需求是连接Basler相机,对图像取帧处理后输出数据和推送视频流到流服务器。

这里可以分为几个问题:

1、如何使用C#在Linux下连接Basler相机?

2、如何使用C#实现RTMP推流?

3、如何搭建可靠稳定的RTMP流服务器?

4、如何发送数据到客户端?

5、如何使用Mono IDE编写程序逻辑

由于以上的问题还都是挺麻烦的,一两句肯定讲不清楚,下面继续分章节讲述。

三、在Linux下使用C#连接相机

说实话想要用C#在Linux下连接相机是一件非常操蛋的事,在basler官网上给出的Linux下的SDK接口并没有C#的选项,也就是说,你无法直接调用SDK连接相机。但没有条件创造条件也得上呀,继续将问题拆成三部分。

1、根据硬件处理器下载对应Basler相机驱动并安装。

2、使用C++实现相机的成功调用并编写接口。

3、使用C#调用C++接口并测试。

1、Linux下Basler相机的安装与调试

这里参考宇神的博客,他对于怎么在Linux下使用basler相机做出了详尽的描述。从虚拟机的安装到网络环境的配置到最后的读取测试。地址为:

https://blog.csdn.net/qq_36024364/article/details/95470718

2、Linux下C++编写相机接口

首先确定我们的思路:

先利用basler给出的SDK,使用C++编写程序成功连接相机,再使用C++生产Linux下的动态连接库SO文件。使用C#调用SO文件中的函数,实现C#连接相机。

1)C++调用相机

官方给出了C++调用相机的案例,可以参考并进行实现。但总结来说有三步。

① 寻找相机设备

② 打开相机更改相机设置并设立回调函数

③ 关闭相机并断开接口以免相机持续占用

对着实现很简单,这里直接给出代码。

//Camera.cpp
#include "Camera.h"
#include <stdexcept>
#include <pylon/PylonIncludes.h>

using namespace Pylon;
using namespace std;
using namespace GenApi;
using namespace std;

String_t ID_t;

MyCamera::MyCamera() {
	
}
MyCamera::~MyCamera() {
	Dispose();
}
void MyCamera::OpenCamera() {
	PylonInitialize();
	//camera = new CInstantCamera();//CTlFactory::GetInstance().CreateFirstDevice()
	camera.Attach(CTlFactory::GetInstance().CreateFirstDevice());

	cout << camera.GetDeviceInfo().GetFullName() << endl;
	camera.RegisterImageEventHandler(new CImageEventPrinter, RegistrationMode_Append, Cleanup_Delete);
	// For demonstration purposes only, register another configuration event handler that handles device removal.
	camera.RegisterConfiguration(new CSampleConfigurationEventHandler, RegistrationMode_Append, Cleanup_Delete);
	camera.Open();
	CHeartbeatHelper heartbeatHelper(camera);
	heartbeatHelper.SetValue(1000);
}
void MyCamera::OpenCameraByID(String_t ID) {
	ID_t = ID;
	PylonInitialize();
	CDeviceInfo cInfo;
	cInfo.SetSerialNumber(ID);
	camera.Attach(CTlFactory::GetInstance().CreateDevice(cInfo));
	cout << camera.GetDeviceInfo().GetFullName() << endl;
	camera.RegisterImageEventHandler(new CImageEventPrinter, RegistrationMode_Append, Cleanup_Delete);
	// For demonstration purposes only, register another configuration event handler that handles device removal.
	camera.RegisterConfiguration(new CSampleConfigurationEventHandler, RegistrationMode_Append, Cleanup_Delete);
	camera.Open();
	CHeartbeatHelper heartbeatHelper(camera);
	heartbeatHelper.SetValue(1000);
}
void MyCamera::CloseCamera() {
	camera.Close();
}
void MyCamera::StartGrabing() {
	camera.StartGrabbing(GrabStrategy_OneByOne, GrabLoop_ProvidedByInstantCamera);
	camera.ExecuteSoftwareTrigger();
	std::cout << "Using device 11" << camera.GetDeviceInfo().GetModelName() << endl;
	
}
void MyCamera::StopGrabing() {
	camera.StopGrabbing();
}

void MyCamera::Dispose() {
	camera.DestroyDevice();
}

bool MyCamera::SetPixelForamt(char* PixelForamt) {
	GenApi::INodeMap& nodemap = camera.GetNodeMap();
	// Set the pixel data format.
	CEnumerationPtr(nodemap.GetNode("PixelFormat"))->FromString(PixelForamt);
	return true;
}

bool MyCamera::SetGain(double newGain) {

	INodeMap& nodemap = camera.GetNodeMap();
	CEnumerationPtr gainAuto(nodemap.GetNode("GainAuto"));
	if (IsWritable(gainAuto))
	{
		gainAuto->FromString("Off");
		// Check to see which Standard Feature Naming Convention (SFNC) is used by the camera device.
		if (camera.GetSfncVersion() >= Sfnc_2_0_0)
		{
			// Access the Gain float type node. This node is available for USB camera devices.
			// USB camera devices are compliant to SFNC version 2.0.
			CFloatPtr gain(nodemap.GetNode("Gain"));
			gain->SetValue(newGain);
			cout << "Gain (50%)       : " << gain->GetValue() << " (Min: " << gain->GetMin() << "; Max: " << gain->GetMax() << ")" << endl;
		}
		else
		{
			// Access the GainRaw integer type node. This node is available for IIDC 1394 and GigE camera devices.
			CIntegerPtr gainRaw(nodemap.GetNode("GainRaw"));
			gainRaw->SetValue(newGain);
			cout << "Gain (50%)       : " << gainRaw->GetValue() << " (Min: " << gainRaw->GetMin() << "; Max: " << gainRaw->GetMax() << "; Inc: " << gainRaw->GetInc() << ")" << endl;
		}

		return true;
	}
	return false;
}
int  MyCamera::GetGain() {
	return (int)GetCamera(Type_Basler_GainRaw);
}
bool MyCamera::SetFps(double fps) {
	SetCamera(Type_Basler_AcquisitionFrameRateAbs, fps);
	return true;
}

int MyCamera::GetFps() {
	return (int)GetCamera(Type_Basler_AcquisitionFrameRateAbs);
}
bool MyCamera::SetExposure(double time) {

	SetCamera(Type_Basler_ExposureTimeAbs, time);
	return true;
}
int MyCamera::getExposureTime()
{
	return (int)GetCamera(Type_Basler_ExposureTimeAbs);
}
int MyCamera::getExposureTimeMin()
{
	return DOUBLE_MIN;
}
int MyCamera::getExposureTimeMax()
{
	return DOUBLE_MAX;
}

bool MyCamera::SetROI(int W, int H, int X, int Y) {
	GenApi::INodeMap& nodemap = camera.GetNodeMap();
	//获取相机成像宽度和高度
	const CIntegerPtr width = nodemap.GetNode("Width");
	const CIntegerPtr height = nodemap.GetNode("Height");
	const CIntegerPtr offsetX = nodemap.GetNode("OffsetX");
	const CIntegerPtr offsetY = nodemap.GetNode("OffsetY");
	width->SetValue(W);
	height->SetValue(H);
	offsetX->SetValue(X);
	offsetY->SetValue(Y);
	cout << W << H << X << Y << endl;
	return true;
}
int MyCamera::GetOffsetX() {
	INodeMap& control = camera.GetNodeMap();
	const CIntegerPtr offsetX = control.GetNode("OffsetX");
	return offsetX->GetValue();
}
int MyCamera::GetOffsetY() {
	INodeMap& control = camera.GetNodeMap();
	const CIntegerPtr offsetY = control.GetNode("OffsetY");
	return offsetY->GetValue();
}
int MyCamera::GetWidth() {
	INodeMap& control = camera.GetNodeMap();

	const CIntegerPtr width = control.GetNode("Width");
	return width->GetValue();
}
int MyCamera::GetHeight() {
	INodeMap& control = camera.GetNodeMap();

	const CIntegerPtr height = control.GetNode("Height");
	return height->GetValue();
}


double MyCamera::GetCamera(MyCamera::MyCamera_Type index)
{
	INodeMap& cameraNodeMap = camera.GetNodeMap();
	switch (index) {
	case Type_Basler_ExposureTimeAbs: {
		const CFloatPtr exposureTime = cameraNodeMap.GetNode("ExposureTimeAbs");
		return exposureTime->GetValue();
	} break;
	case Type_Basler_GainRaw: {
		const CIntegerPtr cameraGen = cameraNodeMap.GetNode("GainRaw");
		return cameraGen->GetValue();
	} break;
	case Type_Basler_AcquisitionFrameRateAbs: {
		const CBooleanPtr frameRate = cameraNodeMap.GetNode("AcquisitionFrameRateEnable");
		frameRate->SetValue(TRUE);
		const CFloatPtr frameRateABS = cameraNodeMap.GetNode("AcquisitionFrameRateAbs");
		return frameRateABS->GetValue();
	} break;
	case Type_Basler_Width: {
		const CIntegerPtr widthPic = cameraNodeMap.GetNode("Width");
		return widthPic->GetValue();
	} break;
	case Type_Basler_Height: {
		const CIntegerPtr heightPic = cameraNodeMap.GetNode("Height");
		return heightPic->GetValue();
	} break;
	default:
		return -1;
		break;
	}
}
void MyCamera::SetCamera(MyCamera::MyCamera_Type index, double tmpValue)
{
	INodeMap& cameraNodeMap = camera.GetNodeMap();
	switch (index) {
	case Type_Basler_Freerun: {
		CEnumerationPtr  ptrTriggerSel = cameraNodeMap.GetNode("TriggerSelector");
		ptrTriggerSel->FromString("FrameStart");
		CEnumerationPtr  ptrTrigger = cameraNodeMap.GetNode("TriggerMode");
#ifdef Real_Freerun
		ptrTrigger->SetIntValue(0);
#else //Software
		ptrTrigger->SetIntValue(1);
		CEnumerationPtr  ptrTriggerSource = cameraNodeMap.GetNode("TriggerSource");
		ptrTriggerSource->FromString("Software");
#endif
	} break;
	case Type_Basler_Line1: {
		CEnumerationPtr  ptrTriggerSel = cameraNodeMap.GetNode("TriggerSelector");
		ptrTriggerSel->FromString("FrameStart");
		CEnumerationPtr  ptrTrigger = cameraNodeMap.GetNode("TriggerMode");
		ptrTrigger->SetIntValue(1);
		CEnumerationPtr  ptrTriggerSource = cameraNodeMap.GetNode("TriggerSource");
		ptrTriggerSource->FromString("Line1");
	} break;
	case Type_Basler_ExposureTimeAbs: {
		const CFloatPtr exposureTime = cameraNodeMap.GetNode("ExposureTimeAbs");
		exposureTime->SetValue(tmpValue);
	} break;
	case Type_Basler_GainRaw: {
		const CIntegerPtr cameraGen = cameraNodeMap.GetNode("GainRaw");
		cameraGen->SetValue(tmpValue);
	} break;
	case Type_Basler_AcquisitionFrameRateAbs: {
		const CBooleanPtr frameRate = cameraNodeMap.GetNode("AcquisitionFrameRateEnable");
		frameRate->SetValue(TRUE);
		const CFloatPtr frameRateABS = cameraNodeMap.GetNode("AcquisitionFrameRateAbs");
		frameRateABS->SetValue(tmpValue);
	} break;
	case Type_Basler_Width: {
		const CIntegerPtr widthPic = cameraNodeMap.GetNode("Width");
		widthPic->SetValue(tmpValue);
	} break;
	case Type_Basler_Height: {
		const CIntegerPtr heightPic = cameraNodeMap.GetNode("Height");
		heightPic->SetValue(tmpValue);
	} break;
	case Type_Basler_LineSource: {
		CEnumerationPtr  ptrLineSource = cameraNodeMap.GetNode("LineSource");
		ptrLineSource->SetIntValue(2);
	} break;
	default:
		break;
	}
}

void MyCamera::setConvertImageHandler(ConvertImageHandler handler) {
	m_hanlder = handler;
}

void MyCamera::DetectCamera() {

	if (camera.IsCameraDeviceRemoved()) {
		CTlFactory& tlFactory = CTlFactory::GetInstance();
		CDeviceInfo info;
		// Remember the camera properties that allow detecting the same camera again.
		info.SetDeviceClass(camera.GetDeviceInfo().GetDeviceClass());
		info.SetSerialNumber(camera.GetDeviceInfo().GetSerialNumber());

		// Create a filter containing the CDeviceInfo object info which describes the properties of the device we are looking for.
		DeviceInfoList_t filter;
		filter.push_back(info);
		DeviceInfoList_t devices;
		cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<" << endl;
		if (tlFactory.EnumerateDevices(devices, filter) > 0)
		{
			cout << ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
			// Destroy the Pylon Device representing the detached camera device.
			// It cannot be used anymore.
			camera.DestroyDevice();
			// The camera has been found. Create and attach it to the Instant Camera object.
			camera.Attach(tlFactory.CreateDevice(devices[0]));
			camera.Open();
			CHeartbeatHelper heartbeatHelper(camera);
			//Reset heartbeat 1000
			heartbeatHelper.SetValue(1000);
			StartGrabing();
		}
	}

}

//Camera.h
#pragma once
#include <stdexcept>
#include <pylon/PylonIncludes.h>

using namespace Pylon;
using namespace std;
using namespace GenApi;
#define DOUBLE_MAX 100000
#define DOUBLE_MIN 0

//定义抓取的图像数
static const uint32_t c_countOfImagesToGrab = 10;
using namespace std;
typedef void (*ConvertImageHandler)(uint8_t* cameraData);
static ConvertImageHandler m_hanlder;
class CImageEventPrinter : public CImageEventHandler
{
public:
	virtual void OnImageGrabbed(CInstantCamera& camera, const CGrabResultPtr& ptrGrabResult)
	{
		if (ptrGrabResult->GrabSucceeded())
		{
			uint8_t* pImageBuffer = (uint8_t*)ptrGrabResult->GetBuffer();
			m_hanlder(pImageBuffer);
			//std::cout << "SizeX: " << ptrGrabResult->GetWidth() << std::endl;
			//std::cout << "SizeY: " << ptrGrabResult->GetHeight() << std::endl;
			//const uint8_t* pImageBuffer = (uint8_t*)ptrGrabResult->GetBuffer();
			//std::cout << "Gray value of first pixel: " << (uint32_t)pImageBuffer[0] << std::endl;
			//std::cout << std::endl;
		}
		else
		{
			std::cout << "Error: " << ptrGrabResult->GetErrorCode() << " " << ptrGrabResult->GetErrorDescription() << std::endl;
		}
	}
};
// Simple helper class to set the HeartbeatTimeout safely.
class CHeartbeatHelper
{
public:
	explicit CHeartbeatHelper(CInstantCamera& camera)
		: m_pHeartbeatTimeout(NULL)
	{
		// m_pHeartbeatTimeout may be NULL
		m_pHeartbeatTimeout = camera.GetTLNodeMap().GetNode("HeartbeatTimeout");
	}

	bool SetValue(int64_t NewValue)
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;

		// Apply the increment and cut off invalid values if neccessary.
		int64_t correctedValue = NewValue - (NewValue % m_pHeartbeatTimeout->GetInc());
		m_pHeartbeatTimeout->SetValue(correctedValue);
		cout << "设置相机心跳成功--CHeartbeat:"<< NewValue << std::endl;
		return true;
	}

	bool SetMax()
	{
		// Do nothing if no heartbeat feature is available.
		if (!m_pHeartbeatTimeout.IsValid())
			return false;

		int64_t maxVal = m_pHeartbeatTimeout->GetMax();
		return SetValue(maxVal);
	}

protected:
	GenApi::CIntegerPtr m_pHeartbeatTimeout; 
};

class CSampleConfigurationEventHandler : public Pylon::CConfigurationEventHandler
{
public:
	void OnCameraDeviceRemoved(CInstantCamera& /*camera*/)
	{
		cout << "相机已掉线。" << std::endl;
	}
};
class MyCamera
{
public:
	MyCamera();
	~MyCamera();
	enum MyCamera_Type {
		Type_Basler_Freerun,
		Type_Basler_Line1, 
		Type_Basler_ExposureTimeAbs, 
		Type_Basler_GainRaw, 
		Type_Basler_AcquisitionFrameRateAbs, 
		Type_Basler_Width, 
		Type_Basler_Height, 
		Type_Basler_LineSource, 
	};
	void OpenCamera();
	void OpenCameraByID(String_t ID); 
	void CloseCamera();
	void StartGrabing();
	void StopGrabing();
	void Dispose();
	void DetectCamera();
	bool SetPixelForamt(char* PixelForamt);
	bool SetROI(int W, int H, int X, int Y);
	bool SetExposure(double exposure);
	bool SetGain(double newGain);
	bool SetFps(double fps);
	double GetCamera(MyCamera::MyCamera_Type index);
	int getExposureTime(); 
	int getExposureTimeMin(); 
	int getExposureTimeMax();
	int GetWidth();
	int GetHeight();
	int GetOffsetX();
	int GetOffsetY();
	int GetFps();
	int GetGain();
	void SetCamera(MyCamera::MyCamera_Type index, double tmpValue);
	void setConvertImageHandler(ConvertImageHandler handler);
private:
	CInstantCamera camera;
	
};

这里可以参考另一篇文章,如何实现相机掉线重连。

http://xianyupro.top/2019/11/26/Basler%E7%9B%B8%E6%9C%BA%E6%8E%89%E7%BA%BF%E9%87%8D%E8%BF%9E-C++%E7%89%88/

2)C++生成接口

关于这一部分,在Windows系统下使用C++生成dll给C#调用还是挺常见的,毕竟都需要给微软老大哥一个面子,但是在Linux下so文件的调用在网络上的资料少之又少,仔细研究一下发现其实并不是很难,只是很少人告诉我们方法而已。

不需要构建词啥的,只需要写出接口函数就万事大吉了,下面给出代码,供参考:

// UseCamera.h

#pragma once

#include "Camera.h"

MyCamera*  InitCamera();

void  OpenCamera(MyCamera* PCamera);

void  OpenCameraByID(MyCamera* PCamera,char* ID);

void  CloseCamera(MyCamera* PCamera);

void  StartGrabing(MyCamera* PCamera);

void  StopGrabing(MyCamera* PCamera);

void  Dispose(MyCamera* PCamera);

bool  SetPixelForamt(MyCamera* PCamera,char* PixelForamt);

bool  SetROI(MyCamera* PCamera,int W, int H, int X, int Y);

void  setConvertImageHandler(MyCamera* PCamera,ConvertImageHandler handler);

bool  SetExposure(MyCamera* PCamera, double exposure);

bool  SetGain(MyCamera* PCamera, double newGain);

bool  SetFps(MyCamera* PCamera, double fps);

int  getExposureTime(MyCamera* PCamera);

int  getExposureTimeMin(MyCamera* PCamera);

int  getExposureTimeMax(MyCamera* PCamera);

int  GetWidth(MyCamera* PCamera);

int  GetHeight(MyCamera* PCamera);

int  GetOffsetX(MyCamera* PCamera);

int  GetOffsetY(MyCamera* PCamera);

int  GetFps(MyCamera* PCamera);

int  GetGain(MyCamera* PCamera);
// UseCamera.cpp

#include "UseCamera.h"
#include<thread>
using namespace std;
bool Csignal = true;

void CameraLostDector(MyCamera* PCamera) {
	while (Csignal) {
		Sleep(500);
		//cout << "dect XXXXX,,," << endl;
		if (Csignal)
			PCamera->DetectCamera();
	}
}

 MyCamera* InitCamera()
{
	 MyCamera* PCamera = new MyCamera();
	return PCamera;
}

 void  OpenCamera(MyCamera* PCamera)
{
	if (PCamera != NULL)
	{
		PCamera->OpenCamera();
		Csignal = true;
		thread T1(CameraLostDector, PCamera);
		T1.detach();
	}
}

 void  OpenCameraByID(MyCamera* PCamera, char* ID)
 {
	 if (PCamera != NULL)
	 {
		 PCamera->OpenCameraByID(ID);
		 Csignal = true;
		 thread T1(CameraLostDector, PCamera);
		 T1.detach();
	 }
 }

 void  CloseCamera(MyCamera* PCamera)
{
	 if (PCamera != NULL)
	 {
		 PCamera->CloseCamera();
		 Csignal = false;
	 }
}

 void  StartGrabing(MyCamera* PCamera)
{
	 if (PCamera != NULL)
	 {
		 PCamera->StartGrabing();
	 }
}

 void  StopGrabing(MyCamera* PCamera)
{
	 if (PCamera != NULL)
	 {
		 PCamera->StopGrabing();
		 Csignal = false;
	 }
}

 void  Dispose(MyCamera* PCamera)
{
	 if (PCamera != NULL)
	 {
		 PCamera->Dispose();
	 }
}

bool  SetPixelForamt(MyCamera* PCamera, char* PixelForamt)
{
	if (PCamera != NULL)
	{
		PCamera->SetPixelForamt(PixelForamt);
	}
	return true;
}

bool  SetROI(MyCamera* PCamera, int W, int H, int X, int Y)
{
	if (PCamera != NULL)
	{
		PCamera->SetROI( W,  H,  X,  Y);
	}
	return true;
}

void  setConvertImageHandler(MyCamera* PCamera, ConvertImageHandler handler)
{
	if (PCamera != NULL)
	{
		PCamera->setConvertImageHandler(handler);
	}
}

bool SetExposure(MyCamera* PCamera, double exposure)
{
	if (PCamera != NULL)
	{
		return PCamera->SetExposure(exposure);
	}
	return false;
}

bool SetGain(MyCamera* PCamera, double newGain)
{
	if (PCamera != NULL)
	{
		return PCamera->SetGain(newGain);
	}
	return false;
}

bool SetFps(MyCamera* PCamera, double fps)
{
	if (PCamera != NULL)
	{
		return PCamera->SetFps(fps);
	}
	return false;
}

int getExposureTime(MyCamera* PCamera)
{
	return PCamera->getExposureTime();
}

int getExposureTimeMin(MyCamera* PCamera)
{
	return PCamera->getExposureTimeMin();
}

int getExposureTimeMax(MyCamera* PCamera)
{
	return PCamera->getExposureTimeMax();
}

int GetWidth(MyCamera* PCamera)
{
	return PCamera->GetWidth();
}

int GetHeight(MyCamera* PCamera)
{
	return PCamera->GetHeight();
}

int GetOffsetX(MyCamera* PCamera)
{
	return PCamera->GetOffsetX();
}

int GetOffsetY(MyCamera* PCamera)
{
	return PCamera->GetOffsetY();
}

int GetFps(MyCamera* PCamera)
{
	return PCamera->GetFps();
}

int GetGain(MyCamera* PCamera)
{
	return PCamera->GetGain();
}

3、Linux下C#调用SO相机接口

对于调用非托管文件来说,C#在Windows下和在Linux下并没有太大区别,可能唯一的区别在于文件名字的不同【笑cry】,这里也是直接给出实现好的代码供参考。可以提醒的是,如果你不知道函数的入口名称的话,可以使用nm命令进行查看,如:

$ nm libBaslerDLL.so
public class BSCamera
	{
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z10InitCamerav")]
		public extern static IntPtr InitCamera();
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z10OpenCameraP13Basler_Camera")]
		public extern static void OpenCamera(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z14OpenCameraByIDP13Basler_CameraPc")]
		public extern static void OpenCameraByID(IntPtr a, string CameraID);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z11CloseCameraP13Basler_Camera")]
		public extern static void CloseCamera(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z12StartGrabingP13Basler_Camera")]
		public extern static void StartGrabing(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z11StopGrabingP13Basler_Camera")]
		public extern static void StopGrabing(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z7DisposeP13Basler_Camera")]
		public extern static void Dispose(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z14SetPixelForamtP13Basler_CameraPc")]
		public extern static bool SetPixelForamt(IntPtr a, string PixelForamt);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z6SetROIP13Basler_Cameraiiii")]
		public extern static bool SetROI(IntPtr a, int W, int H, int X, int Y);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z22setConvertImageHandlerP13Basler_CameraPFvPhE")]
		public extern static void setConvertImageHandler(IntPtr a, [MarshalAs(UnmanagedType.FunctionPtr)]ConvertImageHandler h);

		[DllImport("libBaslerDLL.so", EntryPoint = "_Z11SetExposureP13Basler_Camerad")]
		public extern static bool SetExposure(IntPtr a, double exposure);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z7SetGainP13Basler_Camerad")]
		public extern static bool SetGain(IntPtr a, double newGain);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z6SetFpsP13Basler_Camerad")]
		public extern static bool SetFps(IntPtr a, double fps);

		[DllImport("libBaslerDLL.so", EntryPoint = "_Z15getExposureTimeP13Basler_Camera")]
		public extern static bool getExposureTime(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z18getExposureTimeMinP13Basler_Camera")]
		public extern static bool getExposureTimeMin(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z18getExposureTimeMaxP13Basler_Camera")]
		public extern static bool getExposureTimeMax(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z8GetWidthP13Basler_Camera")]
		public extern static bool GetWidth(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z9GetHeightP13Basler_Camera")]
		public extern static bool GetHeight(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z10GetOffsetXP13Basler_Camera")]
		public extern static bool GetOffsetX(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z10GetOffsetYP13Basler_Camera")]
		public extern static bool GetOffsetY(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z6GetFpsP13Basler_Camera")]
		public extern static bool GetFps(IntPtr a);
		[DllImport("libBaslerDLL.so", EntryPoint = "_Z7GetGainP13Basler_Camera")]
		public extern static bool GetGain(IntPtr a);


		[System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.Cdecl)]
		public delegate void ConvertImageHandler([MarshalAs(UnmanagedType.LPArray, SizeConst = 800 * 600)] byte[] src);


		//private readonly ConvertImageHandler handerFrame;
		static void handle(byte[] data)
		{
			//Console.WriteLine("reback success----------byte:" + data[0]);
			CameraByteEvent(data);

		}

		IntPtr intPtrCamera;
		public delegate void CameraByte(byte[] bitmap);
		public static event CameraByte CameraByteEvent;
		private int _frameHeight = 600;
		private int _frameWidth = 800;
		private int _frameRate = 50;

		public int frameHeight
		{
			get { return _frameHeight; }
		}
		public int frameWidth
		{
			get { return _frameWidth; }
		}
		public int frameRate
		{
			get { return _frameRate; }
		}
		public BSCamera(string CameraID)
		{
			intPtrCamera = InitCamera();
			OpenCameraByID(intPtrCamera, CameraID);
			SetROI(intPtrCamera, 800, 600, 0, 0);
			SetFps(intPtrCamera, 25);
			SetExposure(intPtrCamera, 5211);
			SetPixelForamt(intPtrCamera, "BayerBG8");
			setConvertImageHandler(intPtrCamera, handle);
		}
		public void OpenCamera()
		{
			StartGrabing(intPtrCamera);
		}
		public void CloseCamera()
		{
			StopGrabing(intPtrCamera);
			CloseCamera(intPtrCamera);
		}
		
		public void RestartCamera()
		{
			StopGrabing(intPtrCamera);
			Thread.Sleep(500);
			StartGrabing(intPtrCamera);
		}

		public int GetExposureTime()
		{
			return 5000;
		}

		public int GetFps()
		{
			return 25;
		}
	}

总结:通过以上命令,便可以实现在Linux系统下C#调用相机。

四、使用C#实现RTMP推流

现有的推流等操作都是大厂家在做,而且使用的都是C++,很少有基本没有看到C#的调用方法,面对的对象一般是摄像头、录屏等。很少有处理后的图像推送。当然这里面涉及到大量的视频音频的编解码,与视频协议等都有很大关系,这里不做细致的展开,等有时间了考虑另写一篇描述,如何在Linux实现rtmp推流。当然,可以先确定的是,这里依旧使用的ffmpeg开源库。所以需要搭建ffmpeg环境。

1、安装X264编码器。

2、编译并配置环境。

关于环境的搭建参考另一篇文章:

http://xianyupro.top/2019/11/28/FFmpeg%E7%BC%96%E8%AF%91-Linux/

这里不做过多的介绍。

置于推流,下篇再写。

五、RTMP流服务器的搭建

在网上百度流服务器,可以发现商用的服务器还是挺多的,比较出名的有EasyDarwin,但很显然一年几万的授权费不适合我。这里整理两款开源的很好的服务器做介绍:

1、SRS服务器

SRS在之前的一篇文章内做出来单独的介绍,从安装到调试,下面为链接:

http://xianyupro.top/2019/12/02/%E6%B5%81%E6%9C%8D%E5%8A%A1%E5%99%A8SRS%E5%88%9D%E6%8E%A2/

2、Nginx服务器

Nginx属于现在使用最广泛的服务器,有着良好的性能与体验,兼容各个操作系统,本来想着写一篇详细的介绍,结果百度一下,大佬们写的比我们好多了,这里也就不写了,给出大佬的链接。

【搭建RTMP服务器】https://www.jianshu.com/p/1cbff1431590

3、ffserver服务器

不考虑使用rtmp使用http或者rtsp等服务器的话,ffserver服务器也是很值得考虑的,因为网络上有大量的教程,所以依旧给出链接。

https://blog.csdn.net/cug_heshun2013/article/details/79518632

六、发送数据到客户端

作为服务端来说,发送数据到客户端属于必须的事情,这个过程中涉及到大量的TCP、UDP协议,作为使用者的话当然没必要了解那么多东西,只需要知道怎么用就行了。这里推荐别人封装好的dll。

地址为:https://github.com/fengma312/socket.framework

优点是这个dll可以在mono环境下使用,可以试试。

七、程序逻辑介绍

未完待续,。。。