java通过jna调用C++接口介绍

最近在做项目有一个需求是需要实现基于 JS 无插件的视频播放,因此涉及到HPSClientSDK的调用,就需要用java程序对C++接口进行调用。

java调用c++方式

在Java程序中调用C++服务通常有以下4种方式:

  1. JNI(Java Native Interface)
    ①通过JNI可以在Java代码中调用C/C++代码,是最直接的调用方式。
    ②需要用C/C++实现对native方法的实现,使用JNI类型转换把Java数据转换成C/C++可用的数据。
    ③配置好头文件,库文件,然后通过System.loadLibrary加载native库。
  2. JNA(Java Native Access)
    ①JNA可以直接调用C库而不需要JNI写native方法。
    ②使用接口绑定的方式动态链接C库,直接调用C函数和结构体。
    ③需要配置JNA jar包,比JNI使用更简单。
  3. 通过网络调用
    ①Java通过网络请求调用C++开发的服务端程序。
    ②C++服务端可以通过socket,rpc框架开发服务,Java通过网络协议调用服务。
    ③通过网络分离语言层,部署更灵活。
  4. 通过JNI封装网络调用接口
    ①通过JNI封装对C++服务网络调用接口的包装。
    ②Java层调用JNI接口,JNI接口通过网络请求调用C++服务。
    ③结合JNI和网络调用优点,Java层看似直接调用C++服务。
    本项目选择的是JNA调用方式。接下来主要介绍一下JNA调用方式。
    hpsclient的lib依赖库文件下载
  5. 需要在本地resource目录下传hpsclient的lib库文件

下载后解压,在压缩包的bin目录下拷贝所有lib库文件
Linux的lib库文件
Windows的lib库文件
hpsclient的lib依赖库文件导入项目路径以及日志输出配置
1.linux库文件:拷贝后的lib库文件,存放到start》src》main》resources,在resources目录下可以新建一个hpsclient的文件夹,在hpsclient》linux》bin中添加lib库文件
2. 需要在start》src》main》resources》hpsclient》linux》bin下添加log4cxx.properties
3.需要在log4cxx.properties中修改一下日志的输出路径,可以参考如下设置:
进行上述日志输出路径配置后,安装服务器上,hpsclient日志输出路径
3. start》src》assembly下assembly.xml中配置安装组件安装到服务器后,hpsclient的lib库文件存放位置
assembly.xml文件是Maven Assembly Plugin插件的配置文件,它用于配置项目打包相关的信息,主要用于生成项目分发包。assembly.xml的作用主要有:
① 定义项目分发包类型:比如jar、zip包等。
② 指定分发包的名称和存放位置。
③ 指定需要包含在分发包里面的文件。
④ 排除不需要打包的文件。
⑤ 配置分发包的依赖等信息。
⑥ 配置分发包构建时的过滤、替换等操作。
⑦ 指定分发包的格式化、压缩方式等。
⑧ 继承其他assembly配置以复用配置。
4.windows库文件:拷贝后的lib库文件,存放到start》src》main》resources,在resources目录下可以新建一个hpsclient的文件夹,在hpsclient》windows》bin中添加lib库文件

jna调用C++接口
使用JNA(Java Native Access)在Java中调用C++代码的基本步骤:

  1. 引入jna依赖
<!--jna库-->
<dependency>
    <groupId>net.java.dev.jna</groupId>
    <artifactId>jna</artifactId>
    <version>4.2.2</version>
</dependency>
  1. 定义C++接口
    ① 创建一个Java接口,定义需要调用HPSClient中需要调用的C++函数
public interface HPSClientLibrary extends Library {

    /**sdk初始化接口*/
    int HPSClient_InitLib();
    /**sdk反初始化接口*/
    int HPSClient_FiniLib();
    /**创建RTSP会话接口*/
    int HPSClient_CreateSession();
    /**开启实时推流接口*/
    int HPSClient_PushData(int sessionhandle, String url, String useragent, int transmethod, String deviceusername, String devicepasswd, String destip, int destport);
    /**设置消息回调*/
    int HPSClient_SetMsgCallBack(int sessionhandle, HPSClientMsgFunc pMsgFunc, Pointer userdata);
    /**设置会话参数*/
    int HPSClient_SetSessionParams(int sessionhandle, String usrmsg, int len);
    /**强制I帧*/
    int HPSClient_ForceIFrame(int sessionhandle);
    /**停止取流接口*/
    int HPSClient_Stop(int sessionhandle);
    /**销毁RTSP会话接口*/
    int HPSClient_DestroySession(int sessionhandle);
    /**
     * 获取会话取流失败错误码
     * 接口返回失败后需要调用获取接口失败错误码接口
     */
    int HPSClient_GetLastError();
    /**
     * 改变回放/下载速度接口
     */
    int HPSClient_ChangeRate(int sessionhandle, float scale);
    /**
     * 暂停回放接口
     */
    int HPSClient_Pause(int sessionhandle);
    /**
     * 恢复回放接口
     */
    int  HPSClient_Resume(int sessionhandle);
    /**
     * 恢复回放接口
     */
    int  HPSClient_RandomPlayByAbsEx(int sessionhandle, String from, String to);

    int HPSClient_GetLastError(int sessionhandle);

}

② 创建消息回调结构体。

public interface PStreamClientMsgCallback extends Callback {
    int pHPSClientMsgFunc(int session, Pointer userdata, int opt, Pointer param1, Pointer param2, Pointer param3, Pointer param4);
}

@Component
public class HPSClientMsgFunc implements PStreamClientMsgCallback {

    private static final Logger LOGGER = LoggerFactory.getLogger(HPSClientMsgFunc.class);
    protected static final Map<String,Object> IP_PORT_MAP = new ConcurrentHashMap<>();
    @Override
    public int pHPSClientMsgFunc(int session, Pointer userdata, int opt, Pointer param1, Pointer param2,
                                 Pointer param3, Pointer param4) {
        LOGGER.debug("receive message call back, session is = " + session + ", opt is " + opt + ", param1 is " + param1 + ", param2 is " + param2);
        if (session < 0) {
            return 0;
        }
        //HPS_MSGOPT_SERVER_TCP_ADDR==1 表示TCP 推流下 serverIP 和端口。(通知)
        if (opt == 1) {
            short portShort = param1.getShort(0);
            LOGGER.debug("port short:{}",portShort);
            int port = Short.valueOf(portShort).intValue();
            LOGGER.debug("port :{}",port);
            IP_PORT_MAP.put("port", port);
            String ip = param2.getString(0);
            IP_PORT_MAP.put("ip", ip);
        }
        return 0;
    }
}
  1. 实现接口
    ① 不需要实现接口,JNA会动态绑定到C++的实现。
  2. 加载C++库
    ① 使用Native.loadLibrary加载C++的动态库或静态库。
  3. 调用方法
    ①直接调用Java接口定义的方法,JNA会跨语言调用C++实现。

上述四步执行完就完成了java通过jna调用C++接口功能。
具体实现代码可以参考如下HPSClientUtil工具类:

@Component
public class HPSClientUtil {
    protected static AriesJcLogger LOGGER = AriesJcLoggerFactory.getLogger(HPSClientUtil.class);
    static HPSClientLibrary INSTANCE;


    @Autowired
    private HPSClientMsgFunc hpsClientMsgFunc;
    @Autowired
    private RedisTool redisTool;

    @PostConstruct
    public void init(){
        if(OsInfoTool.isLinux()){
            LOGGER.info(System.getProperty("java.library.path"));
            INSTANCE = (HPSClientLibrary) Native.loadLibrary("HPSClient", HPSClientLibrary.class);
            LOGGER.debug("HPSClient init --> os is linux.");
            initLib();
        }else if(OsInfoTool.isWindows()){
            INSTANCE = (HPSClientLibrary) Native.loadLibrary("D:\\媒体服务\\HPSClient_5.11.18.0_windows_vc2008_x86_64\\bin\\HPSClient.dll", HPSClientLibrary.class);
            LOGGER.debug("HPSClient init --> os is windows.");
            initLib();
        }
    }

    /**
     * @param
     * @return void
     * @author hemanman6
     * @description 初始化
     * @date 2023/8/30 10:11
     */
    public void initLib(){
        int initLib = INSTANCE.HPSClient_InitLib();
        LOGGER.debug("HPSClient_InitLib is {}",initLib);
        if(initLib!=0){
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_InitLib获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        
    }

    /**
     * @param
     * @return void
     * @author hemanman6
     * @description 反初始化
     * @date 2023/8/30 10:12
     */
    public void finiLib(){
        int finiLib = INSTANCE.HPSClient_FiniLib();
        LOGGER.debug("HPSClient_FiniLib is {}",finiLib);
        if(finiLib!=0){
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_FiniLib获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
    }

    public void getRecordPlaybackControl(String token,Integer controlType, Integer controlParam){
        try {
            //初始化
//            initLib();
            //创建会话
//            int sessionHandle = INSTANCE.HPSClient_CreateSession();
            //从TokenMapValue获取sessionHanle
            //获取token对应的sessionHandle
            TokenMapValue tokenMapValue = (TokenMapValue)redisTool.get(token);
            LOGGER.debug("getRecordPlaybackControl tokenMapValue:{}",JSON.toJSONString(tokenMapValue));
            if(!Optional.ofNullable(tokenMapValue).isPresent()){
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        "获取getRecordPlaybackControl tokenMapValue为空");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
            }
            Integer sessionHandle = tokenMapValue.getSessionHandle();
            LOGGER.debug("录像回放控制sessionHandle is {}", sessionHandle);
            if(sessionHandle<0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"录像回放控制HPSClient_CreateSession获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());

            }
            if(STOP.equals(controlType)){
                //销毁会话
                int destroySession = INSTANCE.HPSClient_DestroySession(sessionHandle);
                LOGGER.debug("录像回放控制destroySession is {}", destroySession);
                if(destroySession!=0){
                    errorMessage(sessionHandle);
                    LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_DestroySession获取失败");
                    throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                            VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                }
            }else if(PAUSE.equals(controlType)){
                int pasuse = INSTANCE.HPSClient_Pause(sessionHandle);
                LOGGER.debug("录像回放控制pasuse is {}", pasuse);
                if(pasuse!=0){
                    errorMessage(sessionHandle);
                    LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_Pause获取失败");
                    throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                            VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                }
            }else if(RESUME.equals(controlType)){
                int resume = INSTANCE.HPSClient_Resume(sessionHandle);
                LOGGER.debug("录像回放控制resume is {}", resume);
                if(resume!=0){
                    errorMessage(sessionHandle);
                    LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_Resume获取失败");
                    throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                            VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                }
            }else if(RATE_FRONT.equals(controlType)){
                float scale = getPlaybackRate(controlParam);
                LOGGER.debug("速度scale:{}",scale);
                int changeRate = INSTANCE.HPSClient_ChangeRate(sessionHandle, scale);
                LOGGER.debug("录像回放控制changeRate is {}", changeRate);
                if(changeRate!=0){
                    errorMessage(sessionHandle);
                    LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_ChangeRate获取失败");
                    throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                            VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                }
            }else if(RATE_BACK.equals(controlType)){
                float scale = getPlaybackRate(controlParam);
                int changeRate = INSTANCE.HPSClient_ChangeRate(sessionHandle, scale);
                LOGGER.debug("录像回放控制changeRate is {}", changeRate);
                if(changeRate!=0){
                    errorMessage(sessionHandle);
                    LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_ChangeRate获取失败");
                    throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                            VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                }
            }else if(DELAY.equals(controlType)){
                if(redisTool.exist("token_"+token)){
                    PlaybackTime playbackTime = (PlaybackTime)redisTool.get("token_" + token);
                    String kssj = playbackTime.getKssj();
                    //计算开始的偏移时间
                    String from = DateTool.afterDate(kssj, controlParam);
                    String jssj = playbackTime.getJssj();
                    int changeRate = INSTANCE.HPSClient_RandomPlayByAbsEx(sessionHandle, from,jssj);
                    LOGGER.debug("录像回放控制RandomPlayByAbsEx is {}", changeRate);
                    if(changeRate!=0){
                        errorMessage(sessionHandle);
                        LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_RandomPlayByAbsEx获取失败");
                        throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                                VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
                    }
                }
            }
            //反初始化
//            finiLib();
        } catch (Exception e) {
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
    }

    /**
     * @param controlParam
     * @return float
     * @author hemanman6
     * @description 播放控制速率转换
     * @date 2023/9/1 17:16
     */
    public float getPlaybackRate(Integer controlParam){
        float scale = 0.0f;
        if(HALF_RATE.equals(controlParam)){
            scale = 0.5f;
        }else if(QUARTER_RATE.equals(controlParam)){
            scale = 0.25f;
        }else if(EIGHTH_RATE.equals(controlParam)){
            scale = 0.125f;
        }else if(ONE_RATE.equals(controlParam)){
            scale = 1.0f;
        }else if(TWO_RATE.equals(controlParam)){
            scale = 2.0f;
        }else if(FOUR_RATE.equals(controlParam)){
            scale = 4.0f;
        }else if(EIGHT_RATE.equals(controlParam)){
            scale = 8.0f;
        }
        return scale;
    }
    /**
     * @param url
     * @return String
     * @author hemanman6
     * @description push_data获取媒体的ip和port  实时预览
     * @date 2023/8/30 10:15
     */
    public String getRealTimeMgcTcpRawUrl(String token,String url){
        String tcpRawUrl = "";
        try {
            //初始化
//            initLib();
            //实时推流流程
            //从TokenMapValue获取sessionHanle
            //获取token对应的sessionHandle
            TokenMapValue tokenMapValue = (TokenMapValue)redisTool.get(token);
            if(!Optional.ofNullable(tokenMapValue).isPresent()){
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
            }
            Integer sessionHandle = tokenMapValue.getSessionHandle();
            //创建会话
//            int sessionHandle = INSTANCE.HPSClient_CreateSession();
            LOGGER.debug("实时推流sessionHandle is {}", sessionHandle);
            if(sessionHandle<0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"实时推流sessionHandle获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            Map<String, String> map = getIpAndPort(url);
            LOGGER.debug("获取ip、port与restUrl的map:{}", JSON.toJSONString(map));
            if(!Optional.ofNullable(map).isPresent()){
                LOGGER.error("获取ip、port与restUrl的map为空");
            }
            String destip = map.get("destip");
            String portString = map.get("destport");
            String restUrl = map.get("restUrl");
            Integer destport = Integer.valueOf(portString);
            //设置消息回调
            LOGGER.debug("HPSClient_SetMsgCallBack sessionHandle is {}", sessionHandle);
            int msgCallBackResult = INSTANCE.HPSClient_SetMsgCallBack(sessionHandle, hpsClientMsgFunc, null);
            if(msgCallBackResult!=0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_SetMsgCallBack获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            //开启推流请求,并返回媒体ip和端口
            int pushDataResult = INSTANCE.HPSClient_PushData(sessionHandle, url, "HPSClient", 0x9003, null, null,
                    destip, destport);
            LOGGER.debug("HPSClient_PushData 结果 is {}", pushDataResult);
            if(pushDataResult!=0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_PushData获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            //否则获取ip和port
            String ip = (String)IP_PORT_MAP.get("ip");
            Integer port = (Integer) IP_PORT_MAP.get("port");
            LOGGER.debug("返回媒体的ip是:{},port是:{}",ip,port);
            //组装成tcpraw类型的url
            tcpRawUrl = "tcpraw://"+ip+":"+port+restUrl;
            LOGGER.debug("tcpRawUrl:{}",tcpRawUrl);
        } catch (Exception e) {
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        return tcpRawUrl;
    }

    /**
     * @param url
     * @return String
     * @author hemanman6
     * @description 回放取流url
     * @date 2023/9/8 17:21
     */
    public String getPlaybackMgcTcpRawUrl(String token,String url){
        String tcpRawUrl = "";
        try {
            //初始化
//            initLib();
            //实时推流流程
            //创建会话
//            int sessionHandle = INSTANCE.HPSClient_CreateSession();
            //从TokenMapValue获取sessionHanle
            //获取token对应的sessionHandle
            TokenMapValue tokenMapValue = (TokenMapValue)redisTool.get(token);
            if(!Optional.ofNullable(tokenMapValue).isPresent()){
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
            }
            Integer sessionHandle = tokenMapValue.getSessionHandle();
            LOGGER.debug("回放取流sessionHandle is {}", sessionHandle);
            if(sessionHandle<0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"回放取流sessionHandle获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            Map<String, String> map = getIpAndPort(url);
            LOGGER.debug("获取ip、port与restUrl的map:{}", JSON.toJSONString(map));
            if(!Optional.ofNullable(map).isPresent()){
                LOGGER.error("获取ip、port与restUrl的map为空");
            }
            String destip = map.get("destip");
            String portString = map.get("destport");
            String restUrl = map.get("restUrl");
            Integer destport = Integer.valueOf(portString);
            //设置消息回调
            int msgCallBackResult = INSTANCE.HPSClient_SetMsgCallBack(sessionHandle, hpsClientMsgFunc, null);
            if(msgCallBackResult!=0){
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_SetMsgCallBack获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            //开启推流请求,并返回媒体ip和端口
            int pushDataResult = INSTANCE.HPSClient_PushData(sessionHandle, url, "HPSClient", 0x9003, null, null,
                    destip, destport);
            LOGGER.debug("HPSClient_PushData 结果 is {}", pushDataResult);
            if(pushDataResult!=0){
                errorMessage(sessionHandle);
                LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_PushData获取失败");
                throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                        VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
            }
            //否则获取ip和port
            String ip = (String)IP_PORT_MAP.get("ip");
            Integer port = (Integer) IP_PORT_MAP.get("port");
            LOGGER.debug("返回媒体的ip是:{},port是:{}",ip,port);
            //组装成tcpraw类型的url
            tcpRawUrl = "tcpraw://"+ip+":"+port+restUrl;
            LOGGER.debug("tcpRawUrl:{}",tcpRawUrl);
        } catch (Exception e) {
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        return tcpRawUrl;
    }
    /**
     * @author hemanman6
     * @description 停止取流
     * @date 2023/9/1 13:48
     */
    public void realtimeStop(String token){
        //初始化
//        initLib();
        //创建会话
        //从TokenMapValue获取sessionHanle
        //获取token对应的sessionHandle
        TokenMapValue tokenMapValue = (TokenMapValue)redisTool.get(token);
        if(!Optional.ofNullable(tokenMapValue).isPresent()){
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1010.getChMsg());
        }
        Integer sessionHandle = tokenMapValue.getSessionHandle();
//        int sessionHandle = INSTANCE.HPSClient_CreateSession();
        LOGGER.debug("实时停止取流sessionHandle is {}", sessionHandle);
        if(sessionHandle<0){
            errorMessage(sessionHandle);
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"实时停止取流sessionHandle获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        //停止推流
        int hpsClientStop = INSTANCE.HPSClient_Stop(sessionHandle);
        if(hpsClientStop!=0){
            errorMessage(sessionHandle);
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_Stop获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        //销毁会话
        INSTANCE.HPSClient_DestroySession(sessionHandle);
        //反初始化
        finiLib();
    }
    /**
     * 错误回调
     */
    public void errorMessage(Integer sessionHandle) {
        try {
            int err = INSTANCE.HPSClient_GetLastError(sessionHandle);
            String hexString = "0x0"+Integer.toHexString(err);
            LOGGER.info("错误码为:{}", hexString);
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
        }
    }
    /**
     * 销毁会话,销毁当前token的会话对应sessionHandle
     */
    public void destorySession(Integer sessionHandle){
        int destroySession = INSTANCE.HPSClient_DestroySession(sessionHandle);
        if(destroySession<0){
            errorMessage(sessionHandle);
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_DestroySession获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
    }

    /**
     * 销毁会话,销毁当前token的会话对应sessionHandle
     */
    public int createSession(){
        int sessionHandle = INSTANCE.HPSClient_CreateSession();
        if(sessionHandle<0){
            errorMessage(sessionHandle);
            LOGGER.errorWithErrorCode(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(),"HPSClient_CreateSession获取失败");
            throw new BusinessException(VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getErrorCode(),
                    VdmvnpsErrorCodeEnum.VIDEO_ONLINE_ERROR_CODE_1012.getChMsg());
        }
        return sessionHandle;
    }
    public void setMsgCallBack(Integer sessionHandle){
        //设置消息回调
        int msgCallBackResult = INSTANCE.HPSClient_SetMsgCallBack(sessionHandle, hpsClientMsgFunc, null);
        if (msgCallBackResult != 0) {
            errorMessage(sessionHandle);
            throw new BusinessException(VdmvnpsErrorCodeEnum.OTHER_ERROR.getErrorCode(), "HPSClient_SetMsgCallBack获取失败");
        }
    }


    public static void main(String[] args) {
        String url = "rtsp://10.5.160.103:6304/EUrl/qFD77n9";
        String[] urlSplit = url.split("://");
        String[] split = urlSplit[1].split("/")[0].split(":");
        String ip = split[0];
        System.out.println(ip);
        String port = split[1];
        System.out.println(port);
        String s = urlSplit[1].split("/")[1];
        String s1 = urlSplit[1].split("/")[2];
        System.out.println(s);
        System.out.println(s1);
        String hexString = "0x0"+Integer.toHexString(24373874);
        System.out.println("10进制转16进制: "+hexString);
    }

    /**
     * @param url
     * @return null
     * @author hemanman6
     * @description
     * @date 2023/8/29 15:18
     */
    public Map<String, String> getIpAndPort(String url) {
        Map<String, String> map = new HashMap();
        if (StringUtils.isNotBlank(url)) {
            String[] urlSplit = url.split("://");
            if (Optional.ofNullable(urlSplit).isPresent()) {
                String[] split = urlSplit[1].split("/")[0].split(":");
                String restUrl = "/"+urlSplit[1].split("/")[1]+"/"+ urlSplit[1].split("/")[2];
                if (Optional.ofNullable(split).isPresent()) {
                    String ip = split[0];
                    String port = split[1];
                    map.put("destip", ip);
                    map.put("destport", port);
                    map.put("restUrl",restUrl);
                }
            }
        }
        return map;
    }
}

jna调用C++接口调用过程出现的问题
1.vdmvnps安装平台后,调用实时录像接口报错找不到hpsclient的lib库文件
出现该问题是没有在assembly.xml中生成hpsclient生成的jna文件路径,导致在linux服务器上找不到jna调用lib库文件地址
2.本地测试通过windows调用hpsclient推流接口返回tcp协议的端口号小于65535,而安装平台linux环境调用hpsclient推流接口返回tcp协议的端口号就超过65535导致不能获取实时视频流
排查发现导致这种产生的原因是:在Linux下,Pointer param1指向一个小端序的32位int,值是534424。但是在Windows下,同样的指针所指向的int类型可能是16位短整型(short),最大值就是65535。因此在Windows下解析时,获取的值被限制为了16位短整型的范围。
具体修改方法:
在Linux环境下用short类型接收端口号值,这样就不会超过65535

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐