C#调用EasyPusher推送到EasyDarwin流媒体服务器直播方案及示例代码整理
博客一:转自:http://blog.csdn.net/u011039529/article/details/70832857
大家好,本人刚毕业程序猿一枚。受人所托,第一次写博客,如有错误之处敬请谅解。本文主要讲解:如何在C#中封装以及调用C++编写的函数,通过对EasyDarwin开源流媒体的EasyPusher为例讲解。
首先,我个人不喜欢复杂的调用,比如很多C#中Marshal类相关的操作。我更愿意把c++代码封装成对象,以对象void*到IntPtr的相互转换来实现。 当然,这种方法对于需要传递复杂的数据结构来说,显然还是有其缺点的,比如:在opencv中,我就不知道如何传递mat图片 。
。
本文将用极少的代码来说明。有源码哦!
//----------------------------------------------------华丽的分割线---------------------------------------------------
用过EasyPusher的人都知道,在一个main函数中,实现了推流。调用十分简单!!!而我们要做的:
第一步:将EasyPusher的main函数构成封装成一个类,暴露可供调用的函数即可,如RTSPPusher类,实现函数见源码。
- <span style="font-size:14px;">class RTSPPusher
- {
- private:
- void* rtspHandle;//rtsp源拉流句柄
- void* pusherHandle;//推流句柄
- char* PusherServerIP;//推送的服务器IP
- int PusherServerPort;//推送的服务器端口
- char* PusherTailName;//推送的rtsp的后缀名
- char* srcRtspAddr; //源rtsp流地址
- EASY_MEDIA_INFO_T* fSourceMediaInfo;//媒体信息
- public:
- RTSPPusher(const char* pushServerIP, int pushServerPort, const char* tailName, const char* sourceRtsp);
- virtual ~RTSPPusher();
- bool OnCreatePusher();
- bool OnClosePusher();
- public:
- //rtsp对象拉流回调
- void OnRtspSrcCall( int _mediatype, char *pbuf, RTSP_FRAME_INFO *frameinfo);
- //推送器回馈消息
- void OnPusherCall(int _id, EASY_PUSH_STATE_T _state, EASY_AV_Frame *_frame);
- };</span>
第二步:建立一个c++的管理方法,其实就是导出函数,用来让外部调用(c++、c#调用)。此处只贴头文件,实现见源码。
- <span style="font-size:14px;">#pragma once
- #include <Windows.h>
- extern "C"
- {
- #define LIB_PUSH_API __declspec(dllexport)
- ///创建并且开启推流
- LIB_PUSH_API void* CreateStartPush(const char* pushServerIP, int pushServerPort, const char* tailName, const char* sourceRtsp);
- //停止推流
- LIB_PUSH_API bool ClosePush(void* senderObjPtr);
- }
- </span>
由此,我们只要调用void* CreateStartPush(const char* pushServerIP, int pushServerPort, const char* tailName, const char* sourceRtsp);函数就能创建一个推流对象,实现推流,并返回此对象的地址void*。在c#中用Intptr保存。至此,c++对EasyPusher的封装就算完成了。当然,如果觉得封装太简单,那就是仁者见仁了,自由修改就行啦。
第三步:如何把第二步的c++函数定义代码转换成C#的呢?在此提供一个工具点击打开链接
)[XNLWLVUBHSA37.png)
拷贝代码到C#中,把调用的dll名字改下,如下:(注意:函数我添加了一句:CallingConvention = CallingConvention.Cdecl),自己百度一下吧。
- <span style="font-size:14px;">using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Text;
- namespace ConsolePusher
- {
- public class PusherSDK
- {
- const string dllName = "libPushCplus.dll";
- /// Return Type: void*
- ///pushServerIP: char*
- ///pushServerPort: int
- ///tailName: char*
- ///sourceRtsp: char*
- [System.Runtime.InteropServices.DllImportAttribute(dllName, EntryPoint = "CreateStartPush", CallingConvention = CallingConvention.Cdecl)]
- public static extern System.IntPtr CreateStartPush([System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string pushServerIP, int pushServerPort, [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string tailName, [System.Runtime.InteropServices.InAttribute()] [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.LPStr)] string sourceRtsp);
- /// Return Type: boolean
- ///senderObjPtr: void*
- [System.Runtime.InteropServices.DllImportAttribute(dllName, EntryPoint = "ClosePush", CallingConvention = CallingConvention.Cdecl)]
- [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.I1)]
- public static extern bool ClosePush(System.IntPtr senderObjPtr);
- }
- }
- </span>
第四步:到此,我们封装好了c++,并且得到了c#掉用c++导出函数的方法。我们只需编程就好啦。具体见c#源码的main函数。实现源码点击打开链接
总结:第一次写博客,大家别笑话哈。如过大家觉得还行,我也试试博客,偶尔写写一些平时的积累。这篇博客比较简单,主要点在EasyPusher的使用;导出函数的声明方法;c++函数如何转换为c#。
注:本博客中的代码仅供学习交流使用,如有侵权,请及时删除。还有就是由于工作原因,如有疑问可能无法回复。
博客二:转自:http://www.cnblogs.com/wipphj/p/6114762.html
EasyPusher推流类库的.NET调用说明
以下内容基于在使用EasyPusher过程中遇到的问题,以及相应的注意事项。
本文主要是基于对C++类库的二次封装(便于调试发现问题)以供C#调用以及对一些方法使用.NET实现。
1. C++类库的二次封装
较少接触C+ +在直接调用C+ +类库的情况下发生错误会容易出现不好定位错误的情况,在部门同事的提醒下使用C+ +对原有的类库进行了二次封装,这样就可以使用C+ +调用C+ +也就可以方便的调试(eg:查看.NET传递的参数是否符合预期)
具体的对C++类库的封装及调试可参考博客:C+ +创建DLL并用C#调用且同时实现对DLL的调试
注意事项
项目的VC编译选项要设置为”多线程(/MT )”,不然可能会出现服务器上运行时找不到DLL的问题 
参考链接用VS2010编写的C++程序,在其他电脑上无法运行,提示缺少mfc100.dll的解决办法
2. 使用说明
由于二次封装仅仅是便于调试方便,未对原有类库的方法进行新的整合,故而使用方法同原生类库的使用方法是一致的。
该推流模块主要适用于已经存在音视频数据流的情况下对音视频数据流进行推送。
以海康设备为例
- 使用海康SDK获取音视频数据
- 使用工具函数对每一帧数据进行处理[判断数据帧类型/数据转换]
- 使用EasyPusher_PushFrame逐帧推送数据到远程服务器
代码附录
- DLL C#调用
<code class="cs" data-origin=""/// <summary>" id="PreCode" style="margin: 0px 2px; padding: 0px 5px; border: 1px solid rgb(204, 204, 204); display: block; font-family: Consolas, Inconsolata, Courier, monospace; font-weight: bold; white-space: pre; border-radius: 3px; word-wrap: break-word; font-size: 1em; letter-spacing: -1px;"> /// <summary> /// 推流SDK方法封装 /// </summary> public class EasyPusherSDK { public EasyPusherSDK() { } [StructLayoutAttribute(LayoutKind.Sequential)] public struct EASY_AV_Frame { public uint u32AVFrameFlag; /* 帧标志 视频 or 音频 */ public uint u32AVFrameLen; /* 帧的长度 */ public uint u32VFrameType; /* 视频的类型,I帧或P帧 */ public IntPtr pBuffer; /* 数据 */ public uint u32TimestampSec; /* 时间戳(秒)*/ public uint u32TimestampUsec; /* 时间戳(微秒) */ } public enum EASY_PUSH_STATE_T { EASY_PUSH_STATE_CONNECTING = 1, /* 连接中 */ EASY_PUSH_STATE_CONNECTED, /* 连接成功 */ EASY_PUSH_STATE_CONNECT_FAILED, /* 连接失败 */ EASY_PUSH_STATE_CONNECT_ABORT, /* 连接异常中断 */ EASY_PUSH_STATE_PUSHING, /* 推流中 */ EASY_PUSH_STATE_DISCONNECTED, /* 断开连接 */ EASY_PUSH_STATE_ERROR } [StructLayoutAttribute(LayoutKind.Sequential)] public struct EASY_MEDIA_INFO_T { /// <summary> /// 视频编码类型 /// </summary> public uint u32VideoCodec; /// <summary> /// 视频帧率 /// </summary> public uint u32VideoFps; /// <summary> /// 音频编码类型 /// </summary> public uint u32AudioCodec; /// <summary> /// 音频采样率 /// </summary> public uint u32AudioSamplerate; /// <summary> /// 音频通道数 /// </summary> public uint u32AudioChannel; /// <summary> /// 音频采样精度 /// </summary> public uint u32AudioBitsPerSample; /// <summary> /// 视频sps帧长度 /// </summary> public uint u32H264SpsLength; /// <summary> /// 视频pps帧长度 /// </summary> public uint u32H264PpsLength; /// <summary> /// 视频sps帧内容 /// </summary> [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)] public char[] u8H264Sps; /// <summary> /// 视频sps帧内容 /// </summary> [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 36)] public char[] u8H264Pps; } [DllImport(@"Lib\RTPusher.dll", CallingConvention = CallingConvention.Cdecl)] // -1, /* 无效Key */ // -2, /* 时间错误 */ // -3, /* 进程名称长度不匹配 */ // -4, /* 进程名称不匹配 */ // -5, /* 有效期校验不一致 */ //-6, /* 平台不匹配 */ // -7, /* 授权使用商不匹配 */ // 0, /* 激活成功 */ public static extern int RTPusher_Activate(string license); [DllImport(@"Lib\RTPusher.dll")] /* 创建推送句柄 返回为句柄值 */ public static extern IntPtr RTPusher_Create(); [DllImport(@"Lib\RTPusher.dll", CallingConvention = CallingConvention.Cdecl)] /* 释放推送句柄 */ public static extern uint RTPusher_Release(IntPtr pushPtr); [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int RTPusher_Callback(int _id, EASY_PUSH_STATE_T _state, ref EASY_AV_Frame _frame, IntPtr _userptr); [DllImport(@"Lib\RTPusher.dll" , CallingConvention = CallingConvention.Cdecl)] /* 设置流传输事件回调 userptr传输自定义对象指针*/ public static extern uint RTPusher_SetEventCallback(IntPtr handle, RTPusher_Callback callback, int id, IntPtr userptr); /* 开始流传输 serverAddr:流媒体服务器地址、port:流媒体端口、streamName:流名称<xxx.sdp>、username/password:推送携带的用户名密码、pstruStreamInfo:推送的媒体定义、bufferKSize:以k为单位的缓冲区大小<512~2048之间,默认512> bool createlogfile:创建日志文件*/ [DllImport(@"Lib\RTPusher.dll", CallingConvention = CallingConvention.Cdecl)] public static extern uint RTPusher_StartStream(IntPtr handle, string serverAddr, uint port, string streamName, string username, string password, ref EASY_MEDIA_INFO_T pstruStreamInfo, uint bufferKSize, bool createlogfile); /// <summary> /// 关闭推流,并释放资源. /// </summary> /// <param name="pushPtr">The push PTR.</param> /// <returns>System.UInt32.</returns> [DllImport(@"Lib\RTPusher.dll", CallingConvention = CallingConvention.Cdecl)] /* 停止流传输 */ public static extern uint RTPusher_StopStream(IntPtr pushPtr); [DllImport(@"Lib\RTPusher.dll", CallingConvention = CallingConvention.Cdecl)] /* 推流 frame:具体推送的流媒体帧 */ public static extern uint RTPusher_PushFrame(IntPtr pushPtr, ref EASY_AV_Frame frame); }
- <code class="cs" data-origin=""
工具方法 
<code class="cs" data-origin=""<code class="cs" data-origin=""/// <summary>" id="PreCode" style="margin: 0px 2px; padding: 0px 5px; border: 1px solid rgb(204, 204, 204); display: block; font-family: Consolas, Inconsolata, Courier, monospace; font-weight: bold; white-space: pre; border-radius: 3px; word-wrap: break-word; font-size: 1em; letter-spacing: -1px;"> /// <summary> /// Determines whether [is i frame] [the specified buf]. /// </summary> /// <param name="buf">The buf.</param> /// <returns><c>true</c> if [is i frame] [the specified buf]; otherwise, <c>false</c>.</returns> public static bool IsIFrame(byte[] buf) { int naltype = (buf[4] & 0x1F); switch (naltype) { case 7: //sps case 8: // pps case 6: // i case 5: //idr return true; case 1: // slice case 9: // unknown ??? default: return false; } } /// <summary> /// Gets the H246 from ps. /// </summary> /// <param name="pBuffer">PS 流数据</param> /// <param name="pH264">转换后的H264流数据(音视频)</param> /// <param name="bVideo">if set to <c>true</c> [b video].</param> /// <param name="bAudio">if set to <c>true</c> [b audio].</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> public static bool GetH246FromPS(byte[] pBuffer, ref byte[] pH264, out bool bVideo, out bool bAudio) { var _nBufLenth = (int)pBuffer.Length; if (pBuffer == null || _nBufLenth <= 0) { bVideo = bAudio = false; return false; } int nHerderLen = 0; if (pBuffer != null && pBuffer[0] == 0x00 && pBuffer[1] == 0x00 && pBuffer[2] == 0x01 && pBuffer[3] == 0xE0)//E==视频数据(此处E0标识为视频) { bVideo = true; bAudio = false; nHerderLen = 9 + (int)pBuffer[8];//9个为固定的数据包头长度,pBuffer[8]为填充头部分的长度 var nH264Lenth = _nBufLenth - nHerderLen; if (pH264 == null) { pH264 = new byte[nH264Lenth]; } if (pH264 != null && nH264Lenth > 0) { pH264 = pBuffer.Skip(nHerderLen).Take(nH264Lenth).ToArray(); } return true; } else if (pBuffer != null && pBuffer[0] == 0x00 && pBuffer[1] == 0x00 && pBuffer[2] == 0x01 && pBuffer[3] == 0xC0) //C==音频数据? { pH264 = null; bVideo = false; bAudio = true; var nH264Lenth = _nBufLenth - nHerderLen; nHerderLen = 9 + (int)pBuffer[8];//9个为固定的数据包头长度,pBuffer[8]为填充头部分的长度 if (pH264 == null) { pH264 = new byte[nH264Lenth]; } if (pH264 != null && nH264Lenth > 0) { pH264 = pBuffer.Skip(nHerderLen).Take(nH264Lenth).ToArray(); } return true; } else if (pBuffer != null && pBuffer[0] == 0x00 && pBuffer[1] == 0x00 && pBuffer[2] == 0x01 && pBuffer[3] == 0xBA)//视频流数据包 包头 { bVideo = true; bAudio = false; pH264 = null; return false; } bVideo = bAudio = false; return false; }
参考链接
获取更多信息
EasyPusher直播推送技术交流群:465901074(相关代码见群共享文件)
Copyright © EasyDarwin.org 2012-2017

C#调用EasyPusher推送到EasyDarwin流媒体服务器直播方案及示例代码整理的更多相关文章
- C#调用EasyPusher推送到EasyDarwin实现视频流中转
		本文转自:http://www.cnblogs.com/kangkey/p/6772863.html 最近在公司项目中,遇到需要将内网的监控视频信息,在外网进行查看,最终通过查阅资料,发现EasyDa ... 
- RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播
		方案介绍 目前互联网直播的CDN和标准RTMP流媒体服务器通常只能接收RTMP格式的音视频推流.目前市场上有一些自带RTMP推流的摄像机和编码器,可以直接在其rtmp推流配置里面配置推送到RTMP流媒 ... 
- EasyDarwin开源手机直播方案:EasyPusher手机直播推送,EasyDarwin流媒体服务器,EasyPlayer手机播放器
		在不断进行EasyDarwin开源流媒体服务器的功能和性能完善的同时,我们也配套实现了目前在安防和移动互联网行业比较火热的移动端手机直播方案,主要就是我们的 EasyPusher直播推送项目 和 Ea ... 
- EasyDarwin开源手机直播方案:EasyPusher手机直播推送,EasyDarwin流媒体server,EasyPlayer手机播放器
		在不断进行EasyDarwin开源流媒体server的功能和性能完好的同一时候,我们也配套实现了眼下在安防和移动互联网行业比較火热的移动端手机直播方案,主要就是我们的 EasyPusher直播推送项目 ... 
- C#调用IOS推送
		C#调用IOS推送 使用的是 PushSharp 开源库 源码代码如下 点我 
- 用live555将内网摄像机视频推送到外网服务器,附源码
		最近很多人问,如何将内网的摄像机流媒体数据发布到公网,如果用公网与局域网间的端口映射方式太过麻烦,一个摄像机要做一组映射,而且不是每一个局域网都是有固定ip地址,即使外网主机配置好了每一个摄像机的映射 ... 
- EasyPusher安卓Android手机直播推送之RTSP流媒体协议流程
		EasyPusher移动端推送同我们平时用的RTSP直播推送流程一样,都是采用标准RTSP/RTP推送流程:ANNOUNCE->SETUP->PLAY->RTP/RTCP->T ... 
- iOS 后台调用apns推送
		1.java调用apns推送 2.php 调用apns 推送,可借助终端 
- EasyDarwin流媒体服务器RTSP拉模式流媒体转发模块设计
		拉模式转发 拉模式转发,顾名思义就是服务器主动从源端(IPCamera.NVR.或者其他流媒体服务器)通过RTSP/RTP协议将流媒体音视频数据拉取到流媒体转发服务器,再通过内部分发调度机制,分发给请 ... 
随机推荐
- NHibernate 配置增加代码感知
			Adding the Schema Include the schema in your Project, Solution, or Visual Studios XML Schemas folder ... 
- Mysql数据表字段设置了默认值,插入数据后默认字段的值却为null,不是默认值
			我将mysql的数据表的某个字段设置了默认值为1,当向该表插入数据的时候该字段的值不是默认值,而是null. 我的错误原因: 对数据库的操作我使用了持久化工具mybatis,插入数据的时候插入的是整个 ... 
- git---控制面板提交
			比如我修改了一个项目的代码.需要提交代码. 1.打开项目所在目录,右键>Git Bash Here 2.打开交互模式.git会列出所有untracked的文件,然后你可以用各种形式加入.git ... 
- HDU - 2819 Swap (二分图匹配-匈牙利算法)
			题意:一个N*N的01矩阵,行与行.列与列之间可以互换.要求变换出一个对角线元素全为1的矩阵,给出互换的行号或列号. 分析:首先一个矩阵若能构成对角线元素全为1,那么矩阵的秩为N,秩小于N的情况无解. ... 
- python进阶——进程/线程/协程
			1 python线程 python中Threading模块用于提供线程相关的操作,线程是应用程序中执行的最小单元. #!/usr/bin/env python # -*- coding:utf-8 - ... 
- 【c++ primer, 5e】访问控制与封装
			练习 7.16 无,类的接口定义在public说明符之后,类的实现细节定义在private说明符之后. 7.17 有.类成员的默认访问权限不同.class的类成员默认为private,struct的则 ... 
- nc之netcat端口测试与nmap工具
			nc介绍: nc是netcat的简写,有着网络界的瑞士军刀美誉.因为它短小精悍.功能实用,被设计为一个简单.可靠的网络工具,其有Windows和Linux的版本,可通过TCP或UDP协议传输读写数据. ... 
- NOIP 统计单词个数
			描述 给出一个长度不超过200的由小写英文字母组成的字母串(约定;该字串以每行20个字母的方式输入,且保证每行一定为20个).要求将此字母串分成k份(1<k<=40),且每份中包含的单词个 ... 
- 初识 Zookeeper
			云计算越来越流行的今天,单一机器处理能力已经不能满足我们的需求,不得不采用大量的服务集群.服务集群对外提供服务的过程中,有很多的配置需要随时更新,服务间需要协调工作,这些信息如何推送到各个节点?并且保 ... 
- LINUX系统运行查看
			1.查看内存使用情况 free -m 2.查看内存,cpu等使用情况排序,使用ps -aux命令 ps -aux --sort=+rss :按内存升序排列 ps -aux --sort=-rss :按 ... 
