EasyPusher推流服务接口的.NET导出

本文是在使用由 EasyDarwin 团队开发的EasyPusher时导出的C++接口的.NET实现

public class EasyPushSDK
    {
        public EasyPushSDK() { }

        [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
        {
            public uint u32VideoCodec;             /* 视频编码类型 */
            public uint u32VideoFps;               /* 视频帧率 */

            public uint u32AudioCodec;             /* 音频编码类型 */
            public uint u32AudioSamplerate;        /* 音频采样率 */
            public uint u32AudioChannel;           /* 音频通道数 */
            public uint u32AudioBitsPerSample;     /* 音频采样精度 */

            public uint u32H264SpsLength;          /* 视频sps帧长度 */
            public uint u32H264PpsLength;          /* 视频pps帧长度 */
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 128)]
            public char[] u8H264Sps;                /* 视频sps帧内容 */

            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 36)]
            public char[] u8H264Pps;                /* 视频sps帧内容 */
        }

        /// <summary>
        /// Easies the pusher_ activate.
        /// </summary>
        /// <param name="license">The license.</param>
        /// <returns>System.Int32.</returns>
        /// <remarks>
        ///      <para>创建:***</para>
        ///      <para>日期:2016/11/22</para>
        /// </remarks>
        [DllImport(@"..\bin\libEasyPusher.dll")]

        // -1,             /* 无效Key */
        // -2,            /* 时间错误 */
        // -3,            /* 进程名称长度不匹配 */
        // -4,            /* 进程名称不匹配 */
        // -5,          /* 有效期校验不一致 */
        //-6,            /* 平台不匹配 */
        // -7,          /* 授权使用商不匹配 */
        // 0,          /* 激活成功 */
        public static extern int EasyPusher_Activate(string license);

        [DllImport(@"..\bin\libEasyPusher.dll")]
        /* 创建推送句柄  返回为句柄值 */
        public static extern IntPtr EasyPusher_Create();

        [DllImport(@"..\bin\libEasyPusher.dll")]
        /* 释放推送句柄 */
        public static extern uint EasyPusher_Release(IntPtr pushPtr);

        [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        public delegate int EasyPusher_Callback(int _id, EASY_PUSH_STATE_T _state, ref EASY_AV_Frame _frame, IntPtr _userptr);

        [DllImport(@"..\bin\libEasyPusher.dll", CallingConvention = CallingConvention.Cdecl)]
        /* 设置流传输事件回调 userptr传输自定义对象指针*/
        public static extern uint EasyPusher_SetEventCallback(IntPtr handle, EasyPusher_Callback callback, int id, IntPtr userptr);

        /* 开始流传输 serverAddr:流媒体服务器地址、port:流媒体端口、streamName:流名称<xxx.sdp>、username/password:推送携带的用户名密码、pstruStreamInfo:推送的媒体定义、bufferKSize:以k为单位的缓冲区大小<512~2048之间,默认512> bool createlogfile:创建日志文件*/
        [DllImport(@"..\bin\libEasyPusher.dll")]
        public static extern uint EasyPusher_StartStream(IntPtr handle, string serverAddr, uint port,
            string streamName, string username, string password, ref EASY_MEDIA_INFO_T pstruStreamInfo, uint bufferKSize, bool createlogfile);

        [DllImport(@"..\bin\libEasyPusher.dll")]
        /* 停止流传输 */
        public static extern uint EasyPusher_StopStream(IntPtr pushPtr);

        [DllImport(@"..\bin\libEasyPusher.dll")]
        /* 推流 frame:具体推送的流媒体帧 */
        public static extern uint EasyPusher_PushFrame(IntPtr pushPtr, ref EASY_AV_Frame frame);
    }

附加将海康的PS流转换为H264流的.NET实现GetH246FromPS

public static class Utils
    {
        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>
        /// <remarks>
        ///      <para>创建:***</para>
        ///      <para>日期:2016/11/25</para>
        /// </remarks>
        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;
        }
    }

TIPS:

  把.NET 数组数据转为指针的方法

Byte[] _h264Buf = new Byte[1000];
                            //申请内存并返回指针
                            IntPtr fStreamAudioHandle = Marshal.AllocHGlobal(_h264Buf.Length);
                            Marshal.Copy(_h264Buf, 0, fStreamAudioHandle, _h264Buf.Length);

                            Marshal.FreeHGlobal(fStreamAudioHandle);//使用过后释放内存

  取出指针处的数据到.NET数组

//取出指针处数据
                        //pBuffer 指针
                        //dwBufSize 指针缓存区大小
                        byte[] byx = new byte[(int)dwBufSize];
                        Marshal.Copy(pBuffer, byx, 0, (int)dwBufSize);

小建议:在导出C++接口的时候,最好自己使用C++再一次封装原有接口。这样导出自己实现的C++接口的时候就可以打断点,会比较方便的知道导出的.NET方法/传入的参数是否正确,总之可以自己有调试的手段了

参考:http://www.voidcn.com/blog/bigpudding24/article/p-5036339.html

时间: 2024-08-10 02:10:45

EasyPusher推流服务接口的.NET导出的相关文章

筋斗云框架:REST-RPC风格服务接口实例分析

无论设计原生手机App,或是前面文章曾提及过的"变脸式应用"(一种无网页刷新的多页面Web应用),都需要后端应用服务器提供业务支持.于是,如何设计后端服务接口是开发前必须考虑清楚的一件事. 谈及接口设计,我们需要从两个维度来考虑:协议(Protocol)及原型(Prototype),简称2P维度. 原型定义了一个调用的抽象形式.假定要做上门送餐业务,每个"商户"是个对象,取名为"Store",那么一个"查询商户列表"接口,可以

Spring Cloud微服务接口这么多怎么调试?

前言 今天和大家聊一下Spring Cloud微服务下服务接口调试及管理的话题!我们知道在微服务架构下,软件系统会被拆分成很多个独立运行的服务,而这些服务间需要交互通信,就需要定义各种各样的服务接口.具体来说,在基于Spring Cloud的微服务模式中,各个微服务会基于Spring MVC的Controller定义多个该微服务需要向外部发布的接口. 根据各个微服务功能边界定义的不同,有些微服务会提供与具体业务相关的接口,如支付接口.账户接口等:而有些微服务则会提供一些公共性质的服务接口,如短信

《连载 | 物联网框架ServerSuperIO教程》- 12.服务接口的开发,以及与云端双向交互

1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架ServerSuperIO教程>2.服务实例的配置参数说明 <连载 | 物联网框架ServerSuperIO教程>- 3.设备驱动介绍 <连载 | 物联网框架ServerSuperIO教程>-4.如开发一套设备驱动,同时支持串口和网络通讯. <连载 | 物联网框架ServerSupe

服务接口API限流 Rate Limit

一.场景描述 很多做服务接口的人或多或少的遇到这样的场景,由于业务应用系统的负载能力有限,为了防止非预期的请求对系统压力过大而拖垮业务应用系统,便在对服务接口做了许多策略:服务接口降级.限流.引流等.本文讨论下限流策略,虽然降低了服务接口的访问频率和并发量,却换取服务接口和业务应用系统的可用性. 二.常用的限流算法 常用的限流算法由:楼桶算法和令牌桶算法.本文不具体的详细说明两种算法的原理,原理会在接下来的文章中做说明. 1.漏桶算法 漏桶(Leaky Bucket)算法思路很简单,水(请求)先

Thrift-0.9.3的多服务接口实现

上篇文章讲了单服务接口的实现,如果有多个接口怎么办? 还好thrift的后续版本提供了,下面就来说下怎么实现, 这里参考了文章: http://blog.csdn.net/hivon/article/details/11681977 服务端 package service.server; import org.apache.thrift.TMultiplexedProcessor; import org.apache.thrift.TProcessor; import org.apache.th

WCF服务接口多,客户端在引用时出错!报WCF The maximum nametable character count quota (16384) has been exceeded while reading XML data错误

原文:WCF服务接口多,客户端在引用时出错!报WCF The maximum nametable character count quota (16384) has been exceeded while reading XML data错误 在服务端中定义接口太多时,在客户端的服务引用时,报错误: 元数据包含无法解析的引用:“net.tcp://localhost:8081/BaseData/mex”.    XML 文档中有错误.    读取 XML 数据时,超出最大名称表字符计数配额 (1

标准服务接口示例代码

第一步:定有接口 标准的服务接口通常包含 添加或更新单条记录: ServiceResult AddOrUpdate(Entity model) 批量添加或更新单条记录: ServiceResult AddOrUpdate(IEnumerable<Entity> soucre) 获取单条记录: Entity GetSingle(long? id) 获取列表记录:List<Entity> GetList(Expression<Func<Entity, bool>>

.Net 与 Java 的服务接口相互调用

本文介绍.Net 与 Java 相互调用的例子.下面的介绍主要包括三方面:一是通过常用Web服务进行相互调用,二是使用TCP/IP套接字进行相互调用,三是使用Remote实现远程对象相互调用. 首先说一下Web服务的来源,Web服务是一种新的Web应用程序分支,可以执行从简单的请求到复杂商务处理等任何功能.一旦部署以后,其他Web服务应用程序可以发现并调用它部署的服务. Web Service是一种应用程序,它可以使用标准的互联网协议,像超文件传输协议(HTTP).简单对象访问协议(SOAP).

fastdfs分布式文件系统之与dubbo整合实现分布式服务接口

fastdfs是开源的轻量级分布式文件系统,它提供了java版本的client api.通过client API可以实现对文件的上传.追加.下载.删除等功能. 为了避免每个应用都配置fasdtfs参数.读取配置文件.调用client api获取trackerServer和StorageServer进行上传.下载.删除等操作及返回结果的 处理.所以采用与dubbo整合,提供分布式服务接口,来简化其它服务和应用的文件操作处理,同时提高代码的复用性. 1.总体结构 如图,总共分为fastdfs-api