Android compilation ZLMediaKit implementation of NVR function problem points record

NVR function

NVR, the full name of Network Video Recorder, is the storage and forwarding part of the network video surveillance system. NVR works together with the video encoder or network camera to complete the video recording, storage and forwarding functions.

1. git clone project

git clone --recursive https://github.com/ZLMediaKit/ZLMediaKit.git

Note the –recursive parameter. Recursive cloning is used here to avoid downloading problems of associated modules.

image.png

2. Android studio compilation considerations

image.png

image.png
When you see the log output as shown above, the compilation is successful. After the first compilation, you will find that there is nothing on the page. To achieve our functional requirements, we also need to introduce some of our own logic code.

Note that if you do not have the corresponding gradle and cmake versions locally, the loading process of the first import may take a long time.

image.png

3. Functional interface use cases

From the introduction of NVR, we can understand that its main function is not only recording and storage, but also can be achieved without such a heavy framework, such as Camera+MediaRecorder, or just use Camera+MediaCodec to encode and save it yourself. So why use ZLMediaKit? In fact, it is mainly to facilitate later function expansion. Friends who know the video recorder know that it not only has recording, but also has rtmp, rtsp push streaming, rtsp-server, GB28181, onvif and other functions. Here I mainly record my Problems in implementing various functional nodes;

3.1. Record video

What I use here is the restful api interface control method. Friends who are good at C and C++ can try to directly expose the corresponding functional interface through JNI. In theory, it should be possible.

image.png
Seeing the app, stream and other parameters in the interface, those who have never been exposed to them may be a little confused. What actually corresponds here is the address where we push the stream to ZLMediaKit.

rtmp://127.0.0.1/live/1234

The app corresponds to live, and the stream corresponds to 1234. The specific request to enable recording is as follows (the premise is that the application is in ZLMediaKit, of course, it is also possible to use a streaming agent)

scopeNet {
    var result = Get<ResBaseBean>("http://127.0.0.1/index/api/startRecord") {
        param("type", "1")
        param("vhost", "__defaultVhost__")
        param("app", "live")
        param("stream", "1234")
        param("max_second", 60 * 1)
        param("customized_path", path)
        param("secret", "035c73f7-bb6b-4889-a715-d9eb2d1925cc")
    }.await().result
    LocalLog.e(TAG, "开始接口录制返回结果 :$result") 
}

Novices may ask what the ResBaseBean object and the network request framework (Net) are. They are all posted here to avoid such embarrassment (after all, I often do this)

class ResBaseBean (val code:Int, val data: DataBean?, val result: Boolean, val status: Boolean){
    class DataBean(val key: String?, val flag:Boolean ,val originUrl: Boolean) {
    }
}

Of course, if recording is turned on, there must be a way to turn it off. I’ll post it together by the way.

scopeNet {
    var result = Get<ResBaseBean>("http://127.0.0.1/index/api/stopRecord") {
        param("type", "1")
        param("vhost", "__defaultVhost__")
        param("app", "live")
        param("stream", "1234")
        param("secret", "035c73f7-bb6b-4889-a715-d9eb2d1925cc")
    }.await().result
}

3.2. Video streaming

There are two parts of video streaming that are confused here. The first is to push the mobile phone camera encoding to ZLMediaKit (only if this is normal, the recording interface can be used normally), and the second is to forward our stream to other streaming media servers. (such as cloud server or national standard platform, etc.) to facilitate external network access.

3.2.1. Push to ZLMediaKit

There are many open source projects for rtmp and rtsp streaming on Android. Everyone can choose what they like. If it is convenient, you can also directly use the streaming library I packaged. It is also relatively simple to use.

Initialize the push library

InitRtmpCamera.getInstance().init(context, null, null, 1920, 1080, 15, 3000, 0)
parameter illustrate
Context context
SurfaceView Local preview control, null means no preview push
CameraCallBack callback
width Width
height high
fps Frame rate
bitrate Code rate
rotation angle

Start streaming

InitRtmpCamera.getInstance().startPush("rtmp://127.0.0.1/live/1234")

Turn off push streaming

InitRtmpCamera.getInstance().stopPush()

Please note that you must import the aar package before using the push library. If the click-through rate of the article is good, the JitPack will be uploaded later to facilitate your online reference. Here is a download link: rtmp-stream- demo ;

implementation files('libs/rtmp-stream-v1.3.6.aar')

Also don’t forget to apply for permission

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
    android:glEsVersion="0x00020000"
    android:required="true" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
3.2.2. ZLMediaKit pushes streams to other streaming media servers

Start streaming

scopeNet {
   val pushKey = Get<ResBaseBean>("http://127.0.0.1/index/api/addStreamPusherProxy") {
            param("vhost", "__defaultVhost__")
            param("schema", "rtmp")
            param("app", "live")
            param("stream", "1234")
            param("dst_url", "这里是你自己的推流地址,如:rtmp://IP/live/test")
            param("secret", "035c73f7-bb6b-4889-a715-d9eb2d1925cc")
        }.await().data?.key   
   LocalLog.e(TAG, "开始推流 pushKey:$pushKey")
}

Turn off push streaming

var result = Get<ResBaseBean>("http://127.0.0.1/index/api/delStreamPusherProxy") {
        param("key", pushKey)
        param("secret", "035c73f7-bb6b-4889-a715-d9eb2d1925cc")
    }.await().result

3.3. GB28181 retweets the national standard platform

They are all calling interfaces. I won’t post the code here. Just look at the interface documentation of the boss.

image.png

3.4. Introduction to other functions

A closer look at the configuration file of ZLMediaKit will unlock more new skills. I will leave it to you to expand by yourself. Thank you to Mr. Xia Chu for open source such a good project.

#!!!!此配置文件为范例配置文件,意在告诉读者,各个配置项的具体含义和作用,
#!!!!该配置文件在执行cmake时,会拷贝至release/${操作系统类型}/${编译类型}(例如release/linux/Debug) 文件夹。
#!!!!该文件夹(release/${操作系统类型}/${编译类型})同时也是可执行程序生成目标路径,在执行MediaServer进程时,它会默认加载同目录下的config.ini文件作为配置文件,
#!!!!你如果修改此范例配置文件(conf/config.ini),并不会被MediaServer进程加载,因为MediaServer进程默认加载的是release/${操作系统类型}/${编译类型}/config.ini。
#!!!!当然,你每次执行cmake,该文件确实会被拷贝至release/${操作系统类型}/${编译类型}/config.ini,
#!!!!但是一般建议你直接修改release/${操作系统类型}/${编译类型}/config.ini文件,修改此文件一般不起作用,除非你运行MediaServer时使用-c参数指定到此文件。

[api]
#是否调试http api,启用调试后,会打印每次http请求的内容和回复
apiDebug=1
#一些比较敏感的http api在访问时需要提供secret,否则无权限调用
#如果是通过127.0.0.1访问,那么可以不提供secret
secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
#截图保存路径根目录,截图通过http api(/index/api/getSnap)生成和获取
snapRoot=./www/snap/
#默认截图图片,在启动FFmpeg截图后但是截图还未生成时,可以返回默认的预设图片
defaultSnap=./www/logo.png

[ffmpeg]
#FFmpeg可执行程序路径,支持相对路径/绝对路径
bin=/usr/bin/ffmpeg
#FFmpeg拉流再推流的命令模板,通过该模板可以设置再编码的一些参数
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
#FFmpeg生成截图的命令,可以通过修改该配置改变截图分辨率或质量
snap=%s -i %s -y -f mjpeg -t 0.001 %s
#FFmpeg日志的路径,如果置空则不生成FFmpeg日志
#可以为相对(相对于本可执行程序目录)或绝对路径
log=./ffmpeg/ffmpeg.log
# 自动重启的时间(秒), 默认为0, 也就是不自动重启. 主要是为了避免长时间ffmpeg拉流导致的不同步现象
restart_sec=0

#转协议相关开关;如果addStreamProxy api和on_publish hook回复未指定转协议参数,则采用这些配置项
[protocol]
#转协议时,是否开启帧级时间戳覆盖
modify_stamp=0
#转协议是否开启音频
enable_audio=1
#添加acc静音音频,在关闭音频时,此开关无效
add_mute_audio=1
#推流断开后可以在超时时间内重新连接上继续推流,这样播放器会接着播放。
#置0关闭此特性(推流断开会导致立即断开播放器)
#此参数不应大于播放器超时时间;单位毫秒
continue_push_ms=15000

#是否开启转换为hls
enable_hls=1
#是否开启MP4录制
enable_mp4=0
#是否开启转换为rtsp/webrtc
enable_rtsp=1
#是否开启转换为rtmp/flv
enable_rtmp=1
#是否开启转换为http-ts/ws-ts
enable_ts=1
#是否开启转换为http-fmp4/ws-fmp4
enable_fmp4=1

#是否将mp4录制当做观看者
mp4_as_player=0
#mp4切片大小,单位秒
mp4_max_second=3600
#mp4录制保存路径
mp4_save_path=./www

#hls录制保存路径
hls_save_path=./www

###### 以下是按需转协议的开关,在测试ZLMediaKit的接收推流性能时,请把下面开关置1
###### 如果某种协议你用不到,你可以把以下开关置1以便节省资源(但是还是可以播放,只是第一个播放者体验稍微差点),
###### 如果某种协议你想获取最好的用户体验,请置0(第一个播放者可以秒开,且不花屏)
#hls协议是否按需生成,如果hls.segNum配置为0(意味着hls录制),那么hls将一直生成(不管此开关)
hls_demand=0
#rtsp[s]协议是否按需生成
rtsp_demand=0
#rtmp[s]、http[s]-flv、ws[s]-flv协议是否按需生成
rtmp_demand=0
#http[s]-ts协议是否按需生成
ts_demand=0
#http[s]-fmp4、ws[s]-fmp4协议是否按需生成
fmp4_demand=0

[general]
#是否启用虚拟主机
enableVhost=0
#播放器或推流器在断开后会触发hook.on_flow_report事件(使用多少流量事件),
#flowThreshold参数控制触发hook.on_flow_report事件阈值,使用流量超过该阈值后才触发,单位KB
flowThreshold=1024
#播放最多等待时间,单位毫秒
#播放在播放某个流时,如果该流不存在,
#ZLMediaKit会最多让播放器等待maxStreamWaitMS毫秒
#如果在这个时间内,该流注册成功,那么会立即返回播放器播放成功
#否则返回播放器未找到该流,该机制的目的是可以先播放再推流
maxStreamWaitMS=15000
#某个流无人观看时,触发hook.on_stream_none_reader事件的最大等待时间,单位毫秒
#在配合hook.on_stream_none_reader事件时,可以做到无人观看自动停止拉流或停止接收推流
streamNoneReaderDelayMS=20000
#拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始,
#如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写)
resetWhenRePlay=1
#合并写缓存大小(单位毫秒),合并写指服务器缓存一定的数据后才会一次性写入socket,这样能提高性能,但是会提高延时
#开启后会同时关闭TCP_NODELAY并开启MSG_MORE
mergeWriteMS=0
#服务器唯一id,用于触发hook时区别是哪台服务器
mediaServerId=your_server_id

#最多等待未初始化的Track时间,单位毫秒,超时之后会忽略未初始化的Track
wait_track_ready_ms=10000
#如果流只有单Track,最多等待若干毫秒,超时后未收到其他Track的数据,则认为是单Track
#如果协议元数据有声明特定track数,那么无此等待时间
wait_add_track_ms=3000
#如果track未就绪,我们先缓存帧数据,但是有最大个数限制,防止内存溢出
unready_frame_cache=100

[hls]
#hls写文件的buf大小,调整参数可以提高文件io性能
fileBufSize=65536
#hls最大切片时间
segDur=2
#m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个)
#如果设置为0,则不删除切片,而是保存为点播
segNum=3
#HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数
segRetain=5
#是否广播 ts 切片完成通知
broadcastRecordTs=0
#直播hls文件删除延时,单位秒,issue: #913
deleteDelaySec=10
#是否保留hls文件,此功能部分等效于segNum=0的情况
#不同的是这个保留不会在m3u8文件中体现
#0为不保留,不起作用
#1为保留,则不删除hls文件,如果开启此功能,注意磁盘大小,或者定期手动清理hls文件
segKeep=0

[hook]
#在推流时,如果url参数匹对admin_params,那么可以不经过hook鉴权直接推流成功,播放时亦然
#该配置项的目的是为了开发者自己调试测试,该参数暴露后会有泄露隐私的安全隐患
admin_params=secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc
#是否启用hook事件,启用后,推拉流都将进行鉴权
enable=0
#播放器或推流器使用流量事件,置空则关闭
on_flow_report=https://127.0.0.1/index/hook/on_flow_report
#访问http文件鉴权事件,置空则关闭鉴权
on_http_access=https://127.0.0.1/index/hook/on_http_access
#播放鉴权事件,置空则关闭鉴权
on_play=https://127.0.0.1/index/hook/on_play
#推流鉴权事件,置空则关闭鉴权
on_publish=https://127.0.0.1/index/hook/on_publish
#录制mp4切片完成事件
on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4
# 录制 hls ts 切片完成事件
on_record_ts=https://127.0.0.1/index/hook/on_record_ts
#rtsp播放鉴权事件,此事件中比对rtsp的用户名密码
on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth
#rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权
#建议开发者统一采用url参数方式鉴权,rtsp用户名密码鉴权一般在设备上用的比较多
#开启rtsp专属鉴权后,将不再触发on_play鉴权事件
on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm
#远程telnet调试鉴权事件
on_shell_login=https://127.0.0.1/index/hook/on_shell_login
#直播流注册或注销事件
on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed
#无人观看流事件,通过该事件,可以选择是否关闭无人观看的流。配合general.streamNoneReaderDelayMS选项一起使用
on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader
#播放时,未找到流事件,通过配合hook.on_stream_none_reader事件可以完成按需拉流
on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found
#服务器启动报告,可以用于服务器的崩溃重启事件监听
on_server_started=https://127.0.0.1/index/hook/on_server_started
#server保活上报
on_server_keepalive=https://127.0.0.1/index/hook/on_server_keepalive
#发送rtp(startSendRtp)被动关闭时回调
on_send_rtp_stopped=https://127.0.0.1/index/hook/on_send_rtp_stopped
#rtp server 超时未收到数据
on_rtp_server_timeout=https://127.0.0.1/index/hook/on_rtp_server_timeout

#hook api最大等待回复时间,单位秒
timeoutSec=10
#keepalive hook触发间隔,单位秒,float类型
alive_interval=10.0
#hook通知失败重试次数,正整数。为0不重试,1时重试一次,以此类推
retry=1
#hook通知失败重试延时,单位秒,float型
retry_delay=3.0

[cluster]
#设置源站拉流url模板, 格式跟printf类似,第一个%s指定app,第二个%s指定stream_id,
#开启集群模式后,on_stream_not_found和on_stream_none_reader hook将无效.
#溯源模式支持以下类型:
#rtmp方式: rtmp://127.0.0.1:1935/%s/%s
#rtsp方式: rtsp://127.0.0.1:554/%s/%s
#hls方式: http://127.0.0.1:80/%s/%s/hls.m3u8
#http-ts方式: http://127.0.0.1:80/%s/%s.live.ts
#支持多个源站,不同源站通过分号(;)分隔
origin_url=
#溯源总超时时长,单位秒,float型;假如源站有3个,那么单次溯源超时时间为timeout_sec除以3
#单次溯源超时时间不要超过general.maxStreamWaitMS配置
timeout_sec=15
#溯源失败尝试次数,-1时永久尝试
retry_count=3

[http]
#http服务器字符编码,windows上默认gb2312
charSet=utf-8
#http链接超时时间
keepAliveSecond=30
#http请求体最大字节数,如果post的body太大,则不适合缓存body在内存
maxReqSize=40960
#404网页内容,用户可以自定义404网页
#notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit-4.0</center></body></html>
#http服务器监听端口
port=80
#http文件服务器根目录
#可以为相对(相对于本可执行程序目录)或绝对路径
rootPath=./www
#http文件服务器读文件缓存大小,单位BYTE,调整该参数可以优化文件io性能
sendBufSize=65536
#https服务器监听端口
sslport=443
#是否显示文件夹菜单,开启后可以浏览文件夹
dirMenu=1
#虚拟目录, 虚拟目录名和文件路径使用","隔开,多个配置路径间用";"隔开
#例如赋值为 app_a,/path/to/a;app_b,/path/to/b 那么
#访问 http://127.0.0.1/app_a/file_a 对应的文件路径为 /path/to/a/file_a
#访问 http://127.0.0.1/app_b/file_b 对应的文件路径为 /path/to/b/file_b
#访问其他http路径,对应的文件路径还是在rootPath内
virtualPath=
#禁止后缀的文件使用mmap缓存,使用“,”隔开
#例如赋值为 .mp4,.flv
#那么访问后缀为.mp4与.flv 的文件不缓存
forbidCacheSuffix=
#可以把http代理前真实客户端ip放在http头中:https://github.com/ZLMediaKit/ZLMediaKit/issues/1388
#切勿暴露此key,否则可能导致伪造客户端ip
forwarded_ip_header=

[multicast]
#rtp组播截止组播ip地址
addrMax=239.255.255.255
#rtp组播起始组播ip地址
addrMin=239.0.0.0
#组播udp ttl
udpTTL=64

[record]
#mp4录制或mp4点播的应用名,通过限制应用名,可以防止随意点播
#点播的文件必须放置在此文件夹下
appName=record
#mp4录制写文件缓存,单位BYTE,调整参数可以提高文件io性能
fileBufSize=65536
#mp4点播每次流化数据量,单位毫秒,
#减少该值可以让点播数据发送量更平滑,增大该值则更节省cpu资源
sampleMS=500
#mp4录制完成后是否进行二次关键帧索引写入头部
fastStart=0
#MP4点播(rtsp/rtmp/http-flv/ws-flv)是否循环播放文件
fileRepeat=0

[rtmp]
#rtmp必须在此时间内完成握手,否则服务器会断开链接,单位秒
handshakeSecond=15
#rtmp超时时间,如果该时间内未收到客户端的数据,
#或者tcp发送缓存超过这个时间,则会断开连接,单位秒
keepAliveSecond=15
#在接收rtmp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂)
modifyStamp=0
#rtmp服务器监听端口
port=1935
#rtmps服务器监听地址
sslport=0

[rtp]
#音频mtu大小,该参数限制rtp最大字节数,推荐不要超过1400
#加大该值会明显增加直播延时
audioMtuSize=600
#视频mtu大小,该参数限制rtp最大字节数,推荐不要超过1400
videoMtuSize=1400
#rtp包最大长度限制,单位KB,主要用于识别TCP上下文破坏时,获取到错误的rtp
rtpMaxSize=10
# rtp 打包时,低延迟开关,默认关闭(为0),h264存在一帧多个slice(NAL)的情况,在这种情况下,如果开启可能会导致画面花屏
lowLatency=0

[rtp_proxy]
#导出调试数据(包括rtp/ps/h264)至该目录,置空则关闭数据导出
dumpDir=
#udp和tcp代理服务器,支持rtp(必须是ts或ps类型)代理
port=10000
#rtp超时时间,单位秒
timeoutSec=15
#随机端口范围,最少确保36个端口
#该范围同时限制rtsp服务器udp端口范围
port_range=30000-35000
#rtp h264 负载的pt
h264_pt=98
#rtp h265 负载的pt
h265_pt=99
#rtp ps 负载的pt
ps_pt=96
#rtp ts 负载的pt
ts_pt=33
#rtp opus 负载的pt
opus_pt=100
#rtp g711u 负载的pt
g711u_pt=0
#rtp g711a 负载的pt
g711a_pt=8


[rtc]
#rtc播放推流、播放超时时间
timeoutSec=15
#本机对rtc客户端的可见ip,作为服务器时一般为公网ip,可有多个,用','分开,当置空时,会自动获取网卡ip
#同时支持环境变量,以$开头,如"$EXTERN_IP"; 请参考:https://github.com/ZLMediaKit/ZLMediaKit/pull/1786
externIP=
#rtc udp服务器监听端口号,所有rtc客户端将通过该端口传输stun/dtls/srtp/srtcp数据,
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是,如果服务器在nat内,需要做端口映射时,必须确保外网映射端口跟该端口一致
port=8000
#rtc tcp服务器监听端口号,在udp 不通的情况下,会使用tcp传输数据
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
#需要注意的是,如果服务器在nat内,需要做端口映射时,必须确保外网映射端口跟该端口一致
tcpPort = 8000
#设置remb比特率,非0时关闭twcc并开启remb。该设置在rtc推流时有效,可以控制推流画质
#目前已经实现twcc自动调整码率,关闭remb根据真实网络状况调整码率
rembBitRate=0
#rtc支持的音频codec类型,在前面的优先级更高
#以下范例为所有支持的音频codec
preferredCodecA=PCMU,PCMA,opus,mpeg4-generic
#rtc支持的视频codec类型,在前面的优先级更高
#以下范例为所有支持的视频codec
preferredCodecV=H264,H265,AV1,VP9,VP8

[srt]
#srt播放推流、播放超时时间,单位秒
timeoutSec=5
#srt udp服务器监听端口号,所有srt客户端将通过该端口传输srt数据,
#该端口是多线程的,同时支持客户端网络切换导致的连接迁移
port=9000
#srt 协议中延迟缓存的估算参数,在握手阶段估算rtt ,然后latencyMul*rtt 为最大缓存时长,此参数越大,表示等待重传的时长就越大
latencyMul=4
#包缓存的大小
pktBufSize=8192


[rtsp]
#rtsp专有鉴权方式是采用base64还是md5方式
authBasic=0
#rtsp拉流、推流代理是否是直接代理模式
#直接代理后支持任意编码格式,但是会导致GOP缓存无法定位到I帧,可能会导致开播花屏
#并且如果是tcp方式拉流,如果rtp大于mtu会导致无法使用udp方式代理
#假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理
#如果你是rtsp推拉流,但是webrtc播放,也建议关闭直接代理模式,
#因为直接代理时,rtp中可能没有sps pps,会导致webrtc无法播放; 另外webrtc也不支持Single NAL Unit Packets类型rtp
#默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的
directProxy=1
#rtsp必须在此时间内完成握手,否则服务器会断开链接,单位秒
handshakeSecond=15
#rtsp超时时间,如果该时间内未收到客户端的数据,
#或者tcp发送缓存超过这个时间,则会断开连接,单位秒
keepAliveSecond=15
#rtsp服务器监听地址
port=554
#rtsps服务器监听地址
sslport=0
#rtsp 转发是否使用低延迟模式,当开启时,不会缓存rtp包,来提高并发,可以降低一帧的延迟
lowLatency=0
[shell]
#调试telnet服务器接受最大bufffer大小
maxReqSize=1024
#调试telnet服务器监听端口
port=0

4. Secondary packaging aar

I don’t have time at work. If you need it, please @me. If I have time on weekends, I will encapsulate all the api interface functions, mobile phone camera, desktop sharing, and video streaming to ZLMediaKit into Android dependency packages and share them with everyone.

5. Reference materials

ZLMediaKit:https://github.com/ZLMediaKit/ZLMediaKit

rtmp-rtsp-stream-client-java:https://github.com/pedroSG94/rtmp-rtsp-stream-client-java

Net: https://liangjingkanji.github.io/Net/

Guess you like

Origin blog.csdn.net/qq_35350654/article/details/128421401