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;
};
这里可以参考另一篇文章,如何实现相机掉线重连。
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环境下使用,可以试试。
七、程序逻辑介绍
未完待续,。。。