FFmpeg支持Cronet(Chromium网络库)

2024-01-23 07:08

本文主要是介绍FFmpeg支持Cronet(Chromium网络库),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

FFmpeg支持Cronet

  • 1 背景
  • 2 代码
  • 3 Cronet使用介绍
    • 3.1 接口
    • 3.2 组件及工作流程
    • 3.3 Native开发
      • 3.3.1 创建并配置Cronet_Engine
      • 3.3.2 创建Cronet_UrlRequestCallback
      • 3.3.3 创建Cronet_Executor
      • 3.3.4 创建并发起Cronet_UrlRequest请求
      • 3.3.5 结束、销毁请求
  • 4 FFmpeg集成Cronet
  • 5 编译Cronet
    • 5.1 Windows
      • 5.1.1 下载Chromium
      • 5.1.2 生成工程
      • 5.1.3 编译
    • 5.2 Android
      • 5.2.1 下载Chromium
      • 5.2.2 生成工程
      • 5.2.3 编译
    • 5.3 iOS
      • 5.3.1 下载Chromium
      • 5.3.2 生成工程
      • 5.3.3 编译
  • 6 编译FFmpeg
    • 6.1 获取支持Cronet的FFmpeg代码
    • 6.2 编译
      • 6.2.1 Windows
        • 6.2.1.1 安装依赖
        • 6.2.1.2 处理cronet库的符号
        • 6.2.1.3 编译
      • 6.2.2 Android
        • 6.2.2.1 编译环境
        • 6.2.2.2 编译
      • 6.2.3 iOS
        • 6.2.3.1 编译环境
        • 6.2.3.2 安装gas-preprocessor和yasm
        • 6.2.3.3 编译
  • 7 测试
  • 8 关于QUIC服务端

1 背景

之前在博文《FFmpeg支持QUIC》中介绍了FFmpeg支持谷歌QUIC协议的方法,简单说就是在Chromium中自定义一个模块,把QUIC的接口封装成FFmpeg Protocol插件需要的接口形式。但是这种办法归根结底只是实验性质,实际用到生产环境中,往往QUIC(基于UDP)有可能不通,需要一定的回退策略。

本文将介绍FFmpeg集成Chromium自带网络库Cronet的方法,在Cronet中有使用QUIC的完整策略,可以参考我的博文《Chromium QUIC逻辑》。当然使用Cronet并不仅仅可以使用QUIC,它还支持HTTP/HTTP2,甚至可以支持WebSocket,如果要防止DNS劫持,可以启用其基于异步DNS的DOH(DNS Over HTTP)功能。FFmpeg内部实现的HTTP协议仅支持HTTP/1.1,如果考虑升级FFmpeg的HTTP协议,跨平台的Cronet是一个比较好的选择。

目前Cronet已经在Google的YouTube、Google App等产品中被大量使用,此外国内的各大厂商如腾讯、新浪微博、百度、哔哩哔哩等都陆续使用了Cronet。这段时间主要就是在折腾这个库,我们的产品也已经全面上线Cronet。

2 代码

FFmepg代码:https://github.com/sonysuqin/FFmpeg-Quic-Cronet

3 Cronet使用介绍

3.1 接口

在这里插入图片描述
上图描述了Cronet网络库的基本层次结构,在Chromium内核的基础上封装了Cronet Native层,针对不同的平台分别封装了C层、Android、iOS的接口。具体可以参考谷歌官方Cronet接口参考。

3.2 组件及工作流程

在这里插入图片描述
上图是Cronet网络库接口的基本工作流程,适用于所有平台。

接口基本组件包括:

序号组件功能
1CronetEngineCronet引擎,存储Cronet的一些全局数据,例如代理配置、HTTP缓存、DNS缓存等。每个HTTP请求都基于CronetEngine这个上下文,不同的HTTP请求可以通过同一个CronetEngine共享各种缓存。一个APP最好只创建一个CronetEngine。
2CronetRequest一次Cronet请求,封装该请求的方法、数据、状态等。
3CronetExecutorCronetRequest的运行环境,由APP实现,是Cronet底层与APP交互的通道,通常是一个线程。
4CronetCallbackCronet异步接口的回调对象,每个CronetRequest必须绑定一个CronetCallback对象才能获得通知。

工作流程:

  • APP创建一个全局的CronetEngine;
  • APP创建一个全局的CronetExecutor,用于执行各种Task;
  • APP为一次请求创建一个CronetCallback;
  • APP从CronetEngine、CronetExecutor、CronetCallback创建一个CronetRequest;
  • APP发起CronetRequest请求,CronetRequest请求内部的所有Task都在CronetExecutor中执行;
  • CronetRequest的结果通过绑定的CronetCallback回调上报到APP。

3.3 Native开发

基于C层的接口,适用于在所有平台上开发C/C++的程序,Android和iOS版本的开发本文不涉及。

3.3.1 创建并配置Cronet_Engine

Cronet_EnginePtr CreateCronetEngine() {// 创建Cronet_Engine对象, 每个APP最好只创建一个Cronet_Engine对象.Cronet_EnginePtr cronet_engine = Cronet_Engine_Create();// 创建Cronet_EngineParams对象.Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create();// 设置User agent.Cronet_EngineParams_user_agent_set(engine_params, "CronetTest/1");// 使能HTTP2.Cronet_EngineParams_enable_http2_set(engine_params, true);// 使能HTTP3/QUIC.Cronet_EngineParams_enable_quic_set(engine_params, true);// 设置QUIC的空闲超时,在已经协商成功的QUIC通道上请求数据,如果// 超过5秒未收到响应,则超时并自动回滚到HTTP,如果在接收QUIC数// 据的过程中UDP不可用,则5秒后上报超时失败。注意QUIC的参数通过// JSON串设置,对全平台适用。Cronet_EngineParams_experimental_options_set(engine_params,"{\"QUIC\":{\"idle_connection_timeout_seconds\":5}}");// 启动Cronet_Engine.Cronet_Engine_StartWithParams(cronet_engine, engine_params);// 销毁Cronet_EngineParams对象.Cronet_EngineParams_Destroy(engine_params);return cronet_engine;
}

3.3.2 创建Cronet_UrlRequestCallback

Cronet_UrlRequestCallback是Cronet所有数据、结果上报的唯一方式,因为Cronet Native层只提供了异步接口,每个Cronet_UrlRequest请求必须绑定一个Cronet_UrlRequestCallback对象才能获得通知。
原型:

CRONET_EXPORT Cronet_UrlRequestCallbackPtr Cronet_UrlRequestCallback_CreateWith(Cronet_UrlRequestCallback_OnRedirectReceivedFunc OnRedirectReceivedFunc,Cronet_UrlRequestCallback_OnResponseStartedFunc OnResponseStartedFunc,Cronet_UrlRequestCallback_OnReadCompletedFunc OnReadCompletedFunc,Cronet_UrlRequestCallback_OnSucceededFunc OnSucceededFunc,Cronet_UrlRequestCallback_OnFailedFunc OnFailedFunc,Cronet_UrlRequestCallback_OnCanceledFunc OnCanceledFunc,Cronet_UrlRequestCallback_OnMetricsCollectedFunc OnMetricsCollectedFunc);
回调作用
Cronet_UrlRequestCallback_OnRedirectReceivedFunc接收到重定向的通知,APP可以决定进行重定向或者取消请求。
Cronet_UrlRequestCallback_OnResponseStartedFunc开始接收数据的通知,APP可以在此回调中获取HTTP响应头,并开始读取响应数据。
Cronet_UrlRequestCallback_OnReadCompletedFunc一次读取结束的通知,APP可以获取一次读取的数据,并发起下一次读取操作。
Cronet_UrlRequestCallback_OnSucceededFunc一次请求成功结束的通知,但是不能代表一次业务请求的成功,APP需要判断响应码并处理响应的数据。
Cronet_UrlRequestCallback_OnFailedFunc一次请求失败的通知,可能原因是网络错误,并不会因为服务端返回400、500错误调用。
Cronet_UrlRequestCallback_OnCanceledFunc一次请求被成功取消的通知。
Cronet_UrlRequestCallback_OnMetricsCollectedFunc一次请求的度量通知,以JSON格式上报HTTP请求的各个阶段:包括DNS请求、连接、握手、收发数据等阶段消耗的时间。

该方法需要传入若干回调,为了与某个APP的对象建立关系,可以调用下面的方法:

CRONET_EXPORT void Cronet_UrlRequestCallback_SetClientContext(Cronet_UrlRequestCallbackPtr self, Cronet_ClientContext client_context);

可以看到上述每个回调都会携带Cronet_UrlRequestCallbackPtr self参数,APP可以从回调本身获得绑定的APP的对象,调用下面的方法:

CRONET_EXPORT Cronet_ClientContext Cronet_UrlRequestCallback_GetClientContext(Cronet_UrlRequestCallbackPtr self);

当然从每个回调携带的Cronet_UrlRequestPtr request参数也获得了每个绑定的HTTP请求。
注意:这些回调都不是直接在底层的线程直接调用到APP,而是通过Cronet_Executor来调用。

3.3.3 创建Cronet_Executor

Cronet_Executor是Cronet底层与APP交互的通道,Cronet底层的某些任务(例如回调)会通过Cronet_Executor调用,Cronet_Executor可以选择在自己的线程中调用这些任务,达到与底层线程的隔离。

CRONET_EXPORT Cronet_ExecutorPtr Cronet_Executor_CreateWith(Cronet_Executor_ExecuteFunc ExecuteFunc);

上面的方法创建一个Cronet_Executor,需要传入一个回调:

typedef void (*Cronet_Executor_ExecuteFunc)(Cronet_ExecutorPtr self, Cronet_RunnablePtr command);

该回调会在底层线程调用,通知APP有一个Cronet_RunnablePtr任务需要调度执行,通常APP需要将任务Cronet_RunnablePtr放到独立的线程执行,最后APP负责删除该Cronet_RunnablePtr 任务。

可以通过以下两个方法设置、获取Cronet_Executor绑定的某个APP的自定义对象。

CRONET_EXPORT void Cronet_Executor_SetClientContext(Cronet_ExecutorPtr self, Cronet_ClientContext client_context);
CRONET_EXPORT Cronet_ClientContext Cronet_Executor_GetClientContext(Cronet_ExecutorPtr self);

不需要每个请求都创建一个Cronet_Executor,例如,可以只创建一个Cronet_Executor,所有请求都共享一个Cronet_Executor。

3.3.4 创建并发起Cronet_UrlRequest请求

Cronet_UrlRequestPtr PerformRequest(const std::string& url,                          // url.Cronet_EnginePtr cronet_engine,                  // Cronet_Engine对象.Cronet_ExecutorPtr executor,                     // Cronet_Executor对象.Cronet_UrlRequestCallbackPtr callback) {         // Cronet_UrlRequestCallback对象.// 创建Cronet_UrlRequest对象.Cronet_UrlRequestPtr request = Cronet_UrlRequest_Create();// 创建Cronet_UrlRequestParams对象.Cronet_UrlRequestParamsPtr request_params = Cronet_UrlRequestParams_Create();// 设置GET方法.Cronet_UrlRequestParams_http_method_set(request_params, "GET");// 用上述参数初始化请求.Cronet_RESULT ret = Cronet_UrlRequest_InitWithParams(request,            // Cronet_UrlRequest对象.cronet_engine,      // Cronet_Engine对象.url.c_str(),        // url.request_params,     // Cronet_UrlRequestParams对象.callback,           // Cronet_UrlRequestCallback对象.executor);          // Cronet_Executor对象.// 销毁Cronet_UrlRequestParams对象.Cronet_UrlRequestParams_Destroy(request_params);// 判断Cronet_UrlRequest_InitWithParams结果.if (ret != Cronet_RESULT_SUCCESS) {std::cout << "Cronet_UrlRequest_InitWithParams error:" << ret;return NULL;}// 启动Cronet_UrlRequest请求.ret = Cronet_UrlRequest_Start(request);// 判断Cronet_UrlRequest_Start结果.if (ret != Cronet_RESULT_SUCCESS) {std::cout << "Cronet_UrlRequest_Start error:" << ret;return NULL;}return request;
}

3.3.5 结束、销毁请求

APP必须保证在Cronet_UrlRequestCallback_OnSucceededFunc、Cronet_UrlRequestCallback_OnFailedFunc、Cronet_UrlRequestCallback_OnCanceledFunc这些回调调用之后才调用以下方法销毁请求对象以保证安全。

CRONET_EXPORT void Cronet_UrlRequest_Destroy(Cronet_UrlRequestPtr self);

在请求还没有结束前的任何时刻,都可以调用Cronet_UrlRequest_Cancel方法来取消一个请求,然后在Cronet_UrlRequestCallback_OnCanceledFunc回调中调用Cronet_UrlRequest_Destroy释放请求。

将所有调用都POST到Cronet_Executor线程中执行可以保证线程安全性。

4 FFmpeg集成Cronet

跟《FFmpeg支持QUIC》中提到的方法一样,这里在FFmpeg内部增加了一个协议cronet,实现FFmpeg协议要求的基本方法:

const URLProtocol ff_cronet_protocol = {.name                = "cronet",.url_open            = cronet_open,.url_close           = cronet_close,.url_read            = cronet_read,.url_write           = cronet_write,.url_seek            = cronet_seek,.priv_data_size      = sizeof(CronetContext),.priv_data_class     = &cronet_context_class,.flags               = URL_PROTOCOL_FLAG_NETWORK,.default_whitelist   = "cronet,cronets"
};

调用上一节介绍的C层接口,并将Cronet异步接口转成FFmpeg要求的同步接口。

在Android下面,APP层(Java)和Native层FFmpeg依赖同一个Cronet动态库,在iOS下,APP层(OC)和Native层FFmpeg依赖同一个Cronet.framework。

具体细节请直接获取、阅读、测试代码:

git clone https://github.com/sonysuqin/FFmpeg-Quic-Cronet.git
git checkout -b 4.1.cronet remotes/origin/4.1.cronet

5 编译Cronet

5.1 Windows

5.1.1 下载Chromium

按照chromium的官方编译文档,配置环境并下载chromium代码。注意checkout到较新的稳定版tag。

5.1.2 生成工程

在chromium/src下执行:

gn gen out/Debug --args="is_debug=true is_component_build=false target_cpu=\"x86\""

这里可以决定是产生Debug版还是Release版。

5.1.3 编译

在chromium/src下执行:

ninja -C out\Debug cronet

在chromium/src/out/Debug目录下会生成:

  • cronet.73.0.3683.75.dll
  • cronet.73.0.3683.75.dll.lib
  • cronet.73.0.3683.75.dll.pdb

5.2 Android

5.2.1 下载Chromium

按照chromium的官方编译文档,配置环境并下载chromium代码。注意checkout到较新的稳定版tag。

5.2.2 生成工程

在chromium/src下执行:

./components/cronet/tools/cr_cronet.py gn --release --out_dir=out/Release

这里可以决定是产生Debug版还是Release版。

5.2.3 编译

在chromium/src下执行:

ninja -C out\Release cronet_package

在chromium/src/out/Release目录下会生成cronet目录:

cronet
|-- api_version.txt
|-- AUTHORS
|-- cronet_api.jar
|-- cronet_api-src.jar
|-- cronet_impl_common_java.jar
|-- cronet_impl_common_java-src.jar
|-- cronet_impl_common_proguard.cfg
|-- cronet_impl_native_java.jar
|-- cronet_impl_native_java-src.jar
|-- cronet_impl_native_proguard.cfg
|-- cronet_impl_platform_java.jar
|-- cronet_impl_platform_java-src.jar
|-- cronet_impl_platform_proguard.cfg
|-- cronet-sample-src.jar
|-- javadoc
|-- libs
|-- LICENSE
|-- README.md.html
|-- res
|-- symbols
|-- test
`-- VERSION

jar包为Android使用的库文件,lib目录下为Native动态库,symbols目录下为Native动态库对应的符号文件。

5.3 iOS

5.3.1 下载Chromium

按照chromium的官方编译文档,配置环境并下载chromium代码。注意checkout到较新的稳定版tag。

5.3.2 生成工程

在chromium/src下执行:

./components/cronet/tools/cr_cronet.py -i gn --release --out_dir=out/Release

这里可以决定是产生Debug版还是Release版。

5.3.3 编译

在chromium/src下执行:

ninja -C out\Release cronet_package

在chromium/src/out/Release目录下会生成:

  • Cronet.framework,动态库;
  • Static/Cronet.framework,静态库;
  • Cronet.dSYM,用于线上崩溃问题的堆栈解析。

6 编译FFmpeg

6.1 获取支持Cronet的FFmpeg代码

git clone https://github.com/sonysuqin/FFmpeg-Quic-Cronet.git
git checkout -b 4.1.cronet remotes/origin/4.1.cronet

6.2 编译

6.2.1 Windows

6.2.1.1 安装依赖

需要安装mingw32/msys2环境,然后安装GCC、SDL2等FFmpeg通常需要依赖的工具,主要参考了以下这些网页:
《msys2和SDL2环境搭建》
《windows下编译FFMPEG篇》
《Windows10平台编译ffmpeg 4.0.2,生成ffplay》

6.2.1.2 处理cronet库的符号

使用mingw32,在chromium/src/out/Debug目录下,执行:

gendef cronet.73.0.3683.75.dll
dlltool --kill-at -d cronet.73.0.3683.75.def --dllname cronet.73.0.3683.75.dll -l cronet.73.0.3683.75.a

注意自己修改cronet的版本号。

6.2.1.3 编译

在FFmpeg目录下,执行:

mkdir build_debug
cd build_debug
../configure --prefix=/d/log/ffmpeg_debug --disable-static --enable-shared \--enable-debug=3 --disable-optimizations --disable-stripping \--enable-gpl --enable-version3 --enable-sdl --disable-mmx \--arch=x86 --enable-libcronet \--extra-cflags="-I/d/work/google/chromium/src/components/cronet/native/include -I/d/work/google/chromium/src/components/cronet/native/generated" \--extra-ldflags=-L/d/work/google/chromium/src/out/Debug \--extra-libs=-lcronet.73.0.3683.75
make && make install

6.2.2 Android

6.2.2.1 编译环境
软件版本
Ubuntu16.04
NDKr17c
6.2.2.2 编译

在FFmpeg目录下,执行:

./build_android.sh

可以在脚本中自行指定输出目录,默认在当前目录的android目录下。

6.2.3 iOS

6.2.3.1 编译环境
软件版本
macos10.14
xcode10.3
6.2.3.2 安装gas-preprocessor和yasm

参考FFmpeg-iOS-build-script,安装gas-preprocessor和yasm。

6.2.3.3 编译

在FFmpeg目录下,执行:

./build_ios.sh

可以在脚本中自行指定输出目录,默认在当前上级目录的thin目录下。

7 测试

Windows端可以使用ffplay直接进行测试,用mingw32进入FFmpeg的输出目录,执行:

./ffplay cronets://roblin.cn/video/mao.mp4

Debug版需要同时拷贝mingw环境的依赖库到FFmpeg输出目录下,例如/mingw32/bin下的dll。

bin
|-- avcodec-58.dll
|-- avcodec.lib
|-- avdevice-58.dll
|-- avdevice.lib
|-- avfilter-7.dll
|-- avfilter.lib
|-- avformat-58.dll
|-- avformat.lib
|-- avutil-56.dll
|-- avutil.lib
|-- cronet.73.0.3683.75.dll
|-- ffmpeg.exe
|-- ffplay.exe
|-- ffprobe.exe
|-- libatomic-1.dll
|-- libbz2-1.dll
|-- libcharset-1.dll
|-- libgcc_s_dw2-1.dll
|-- libgmp-10.dll
|-- libgmpxx-4.dll
|-- libgomp-1.dll
|-- libiconv-2.dll
|-- liblsmash-2.dll
|-- liblzma-5.dll
|-- libminizip-1.dll
|-- libopenal-1.dll
|-- libquadmath-0.dll
|-- libssp-0.dll
|-- libstdc++-6.dll
|-- libvulkan-1.dll
|-- libwinpthread-1.dll
|-- libx264-157.dll
|-- postproc-55.dll
|-- postproc.lib
|-- SDL2.dll
|-- swresample-3.dll
|-- swresample.lib
|-- swscale-5.dll
|-- swscale.lib
`-- zlib1.dll

在这里插入图片描述
其他平台的测试需要编写程序,调用FFmpeg的avformat API,传入URL:cronets://roblin.cn/video/mao.mp4。
如果想用浏览器测试QUIC可以直接点播放。
在这里插入图片描述

8 关于QUIC服务端

目前在roblin.cn上部署了一个我们自己开发的支持QUIC的Nginx,代码地址:https://github.com/evansun922/nginx-quic。

这篇关于FFmpeg支持Cronet(Chromium网络库)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/635596

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

SpringKafka消息发布之KafkaTemplate与事务支持功能

《SpringKafka消息发布之KafkaTemplate与事务支持功能》通过本文介绍的基本用法、序列化选项、事务支持、错误处理和性能优化技术,开发者可以构建高效可靠的Kafka消息发布系统,事务支... 目录引言一、KafkaTemplate基础二、消息序列化三、事务支持机制四、错误处理与重试五、性能优

SpringBoot使用OkHttp完成高效网络请求详解

《SpringBoot使用OkHttp完成高效网络请求详解》OkHttp是一个高效的HTTP客户端,支持同步和异步请求,且具备自动处理cookie、缓存和连接池等高级功能,下面我们来看看SpringB... 目录一、OkHttp 简介二、在 Spring Boot 中集成 OkHttp三、封装 OkHttp

Linux系统之主机网络配置方式

《Linux系统之主机网络配置方式》:本文主要介绍Linux系统之主机网络配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、查看主机的网络参数1、查看主机名2、查看IP地址3、查看网关4、查看DNS二、配置网卡1、修改网卡配置文件2、nmcli工具【通用

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

一文教你解决Python不支持中文路径的问题

《一文教你解决Python不支持中文路径的问题》Python是一种广泛使用的高级编程语言,然而在处理包含中文字符的文件路径时,Python有时会表现出一些不友好的行为,下面小编就来为大家介绍一下具体的... 目录问题背景解决方案1. 设置正确的文件编码2. 使用pathlib模块3. 转换路径为Unicod

定价129元!支持双频 Wi-Fi 5的华为AX1路由器发布

《定价129元!支持双频Wi-Fi5的华为AX1路由器发布》华为上周推出了其最新的入门级Wi-Fi5路由器——华为路由AX1,建议零售价129元,这款路由器配置如何?详细请看下文介... 华为 Wi-Fi 5 路由 AX1 已正式开售,新品支持双频 1200 兆、配有四个千兆网口、提供可视化智能诊断功能,建

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为