接上一章:三、JAVA调用海康威视SDK实现摄像头本地存储
本章实现摄像头内网推流到阿里云服务器。
环境准备
- 开发工具:eclipse
- Jdk版本:jdk1.8
- 开发语言:java,界面使用swing开发
- 摄像头:DS-2CD1221D-I3
- 海康威视SDK下载地址:海康威视开放平台
- SDK版本:CH-HCNetSDKV6.0.2.35_build20190411_Win64
- EasyRTMPLive:EasyRTMPLive
转发流程
账号准备
- 海康威视摄像头账号密码及rtsp推拉地址
- 阿里云账号,需开通阿里云视频直播相关功能,需获取阿里云推流地址、推流KEY及推流APPNAME,具体参考:快速入门 - 视频直播 - 阿里云
推流域名配置:
推流域名设置:
推流KEY:
代码实现
EasyPushProcess
package com.kx.hcws;
import java.util.Date;
import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK;
import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.EASY_FRAME_INFO;
import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.PushLiveInfoCallBack;
import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.StatusCallBack;
import com.kx.hcws.ui.Player;
/**
*
* 类: EasyPushProcess,描述:
*
* <p style="padding-left:20px">
* 推流处理器
* </p>
*
* @author kxy
*
* @date 2020年5月27日 上午10:45:51
*
* @version V1.0.0
*/
public class EasyPushProcess {
// 日志
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(HCSaveProcess.class);
public int width = 860, height = 480;
public boolean pushing = false;
protected String pushurl = "";
public long errorcount = 0, counttime = 0, alltime = 0, laststarttime = -1, framesize = 0;
private Long deviceId;
private String m_sDeviceIP;
private String username;
private String password;
private int port;
private long iChannelNum;
protected String mediaappname;
private static Player player;
// 状态回调
private static StatusCallBack statusCallBack = new StatusCallBack() {
@Override
public void invoke(int _channelId, int status) {
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_DISCONNECTED) {
logger.info("设备m_sDeviceIPRTMP断开连接 :");
}
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_CONNECT_ABORT) {
logger.info("设备m_sDeviceIPRTMP连接异常中断 :");
}
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_CONNECT_FAILED) {
logger.info("设备m_sDeviceIPRTMP连接失败 :");
}
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_START_ERROR) {
logger.info("设备m_sDeviceIPRTSP启动异常 :");
}
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_ERROR) {
logger.info("设备m_sDeviceIPRTSP链接异常 :");
}
if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_TIMEOUT) {// 链接超时或者RTMP创建异常
logger.info("设备m_sDeviceIPRTMP链接超时自动退出:");
}
}
};
// 推流信息回调
private static PushLiveInfoCallBack pushLiveInfoCallBack = new PushLiveInfoCallBack() {
@Override
public void invoke(int _channelId, int _mediatype, EASY_FRAME_INFO frameinfo) {
if (frameinfo != null) {
Double ss = Double.parseDouble(frameinfo.bitrate + "");
logger.info("推流帧率:" + ss.longValue());
player.setPushBitrate(ss.longValue());
}
}
};
// SDK
private EasyRtspToRtmpLiveSDK easyRtspToRtmpLiveSDK;
// 推流帧信息
private EASY_FRAME_INFO frameinfo = null;
// 通道号
private int channelId = -1;
public EasyPushProcess(String m_sDeviceIP, String username, String password, int port, Long deviceId,
String pushkey, String pushurl, String mediaappname, Player player) {
this.deviceId = deviceId;
this.m_sDeviceIP = m_sDeviceIP;
this.username = username;
this.password = password;
this.port = port;
this.mediaappname = mediaappname;
this.player = player;
pushurl = getPushUrl(pushkey, pushurl, mediaappname);
/**
* 海康威视IP摄像头rtsp协议地址如下:
*
* rtsp://[username]:[passwd]@[ip]:[port]/[codec]/[channel]/[subtype]/
* av_stream
*
* 说明:
*
* username:用户名,例如admin
*
* passwd:密码,例如12345
*
* ip:设备的ip地址,例如192.0.0.64
*
* port:端口号默认554,若为默认可以不写
*
* codec:有h264、MPEG-4、mpeg4这几种
*
* channel:通道号,起始为1
*
* subtype:码流类型,主码流为main,子码流为sub
*
* rtsp://admin:GNEHIB@192.168.2.18:554/h265/ch1/sub/av_stream
*
*/
String src = "rtsp://" + username + ":" + password + "@" + m_sDeviceIP + ":554/h265/ch1/main/av_stream";// 组织海康威视rtsp拉流地址
easyRtspToRtmpLiveSDK = EasyRtspToRtmpLiveSDK.create();
channelId = easyRtspToRtmpLiveSDK.push(src, pushurl, deviceId.intValue());
easyRtspToRtmpLiveSDK.registerStatusCallBack(channelId, statusCallBack);
easyRtspToRtmpLiveSDK.registerInfoCallBack(channelId, pushLiveInfoCallBack);
}
public void start() {
if (pushing) {
return;
}
errorcount = counttime = alltime = framesize = 0;
laststarttime = System.currentTimeMillis();
pushing = true;
new Thread(new Runnable() {
@Override
public void run() {
if (!easyRtspToRtmpLiveSDK.start(channelId)) {// 返回后会自动停止
stop_();
}
}
}).start();
}
public void stop_() {
pushing = false;
}
/**
*
* 方法: info ,描述:
*
* <p style="padding-left:20px">
* 取得推流信息
* </p>
* kangxiaoyu
*
* @return
*/
public EASY_FRAME_INFO info() {
if (channelId >= 0 && isPushing()) {
return frameinfo;
}
return null;
}
/**
* 是否正在推流
*
* @return
*/
public boolean isPushing() {
return pushing && easyRtspToRtmpLiveSDK.ispushing(channelId);
}
/**
*
* 方法: ispushing_ ,描述:
*
* <p style="padding-left:20px">
* 判断SDK是否推流
* </p>
* kangxiaoyu
*
* @param channelId
* @return
*/
public boolean isSdkPushing(int channelId) {
return easyRtspToRtmpLiveSDK.ispushing(channelId);
}
/**
*
* 方法: isSdkStop ,描述:
*
* <p style="padding-left:20px">
* 调用SDK停止推流
* </p>
* kangxiaoyu
*
* @param channelId
* @return
*/
public String isSdkStop(int channelId) {
return easyRtspToRtmpLiveSDK.stop(channelId) + "";
}
public int getChannelId() {
return channelId;
}
/**
* 组织推流地址
*
* @param pushkey
* 阿里云推流KEY
* @param pushurl
* 阿里云推流地址
* @param mediaappname
* 阿里云推流APPNAME
* @return
*/
public String getPushUrl(String pushkey, String pushurl, String mediaappname) {
String url = pushurl;
if (url.contains(";")) {
url = url.split(";")[(int) (deviceId % pushurl.split(";").length)];
}
if (pushkey.contains(";")) {
pushkey = pushkey.split(";")[(int) (deviceId % pushkey.split(";").length)];
}
url += deviceId;
Date date = new Date();
String time = date.getTime() + "";
System.out.println("/" + mediaappname + "/" + deviceId + "-" + time + "-0-0-" + pushkey);
String auth_key = MD5Utils.getMd5("/" + mediaappname + "/" + deviceId + "-" + time + "-0-0-" + pushkey);
System.out.println("推流地址:" + url + "?auth_key=" + time + "-0-0-" + auth_key);
// return url + "?auth_key=" + time + "-0-0-" + auth_key;
return url;
}
}
EasyRtspToRtmpLiveSDK
package com.kx.hcws.easy;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;
public interface EasyRtspToRtmpLiveSDK extends StdCallLibrary {
// 日志
static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(EasyRtspToRtmpLiveSDK.class);
public static int EASY_RTMP_STATE_CONNECTING = 1; /* 连接中 */
public static int EASY_RTMP_STATE_CONNECTED = 2; /* 连接成功 */
public static int EASY_RTMP_STATE_CONNECT_FAILED = 3; /* 连接失败 */
public static int EASY_RTMP_STATE_CONNECT_ABORT = 4; /* 连接异常中断 */
public static int EASY_RTMP_STATE_PUSHING = 5; /* 推流中 */
public static int EASY_RTMP_STATE_DISCONNECTED = 6; /* 断开连接 */
public static int EASY_RTMP_STATE_ERROR = 7;
public static int EASY_RTMP_STATE_TIMEOUT = -1;
public static int EASY_RTMP_STATE_START_ERROR = -2;
public static int EASY_ACTIVATE_INVALID_KEY = -1; /* 无效Key */
public static int EASY_ACTIVATE_TIME_ERR = -2; /* 时间错误 */
public static int EASY_ACTIVATE_PROCESS_NAME_LEN_ERR = -3; /* 进程名称长度不匹配 */
public static int EASY_ACTIVATE_PROCESS_NAME_ERR = -4; /* 进程名称不匹配 */
public static int EASY_ACTIVATE_VALIDITY_PERIOD_ERR = -5; /* 有效期校验不一致 */
public static int EASY_ACTIVATE_PLATFORM_ERR = -6; /* 平台不匹配 */
public static int EASY_ACTIVATE_COMPANY_ID_LEN_ERR = -7; /* 授权使用商不匹配 */
public static int EASY_ACTIVATE_SUCCESS = 0; /* 激活成功 */
public static int Easy_NoErr = 0;
public static int Easy_RequestFailed = -1;
public static int Easy_Unimplemented = -2;
public static int Easy_RequestArrived = -3;
public static int Easy_OutOfState = -4;
public static int Easy_NotAModule = -5;
public static int Easy_WrongVersion = -6;
public static int Easy_IllegalService = -7;
public static int Easy_BadIndex = -8;
public static int Easy_ValueNotFound = -9;
public static int Easy_BadArgument = -10;
public static int Easy_ReadOnly = -11;
public static int Easy_NotPreemptiveSafe = -12;
public static int Easy_NotEnoughSpace = -13;
public static int Easy_WouldBlock = -14;
public static int Easy_NotConnected = -15;
public static int Easy_FileNotFound = -16;
public static int Easy_NoMoreData = -17;
public static int Easy_AttrDoesntExist = -18;
public static int Easy_AttrNameExists = -19;
public static int Easy_InstanceAttrsNotAllowed = -20;
public static int Easy_InvalidSocket = -21;
public static int Easy_MallocError = -22;
public static int Easy_ConnectError = -23;
public static int Easy_SendError = -24;
// EasyRtspToRtmpLiveSDK推流key,需要从官网获取
public static String RTSPKEY = "6D75724D7A4969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4B56634D5671446A652F34675A57467A65513D3D";
public static String RTMPKEY = "79736C36655969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4931634D5671446A652F34675A57467A65513D3D";
public static LogCallBack logCallBack = new LogCallBack() {
@Override
public void invoke(String logText) {
logger.info(logText);
}
};
public static EasyRtspToRtmpLiveSDK create() {
System.setProperty("jna.debug_load", "true");
System.setProperty("jna.protected", "true");
System.setProperty("jna.encoding", "GBK");
EasyRtspToRtmpLiveSDK easyRtspToRtmpLiveSDK = (EasyRtspToRtmpLiveSDK) Native
.loadLibrary("EasyRtspToRtmpLiveSDK", EasyRtspToRtmpLiveSDK.class);
easyRtspToRtmpLiveSDK.SetDisplayLog(logCallBack);
int res = easyRtspToRtmpLiveSDK.init(RTSPKEY, RTMPKEY);
if (res == 0) {
logger.info("初始化结果:" + (res == 0 ? "初始化成功" : "初始化失败:" + res));
} else {
switch (res) {
case EASY_ACTIVATE_INVALID_KEY:
logger.info("初始化结果:" + "初始化失败,无效Key");
break;
case EASY_ACTIVATE_TIME_ERR:
logger.info("初始化结果:" + "初始化失败,时间错误");
break;
case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
logger.info("初始化结果:" + "初始化失败,进程名称长度不匹配");
break;
case EASY_ACTIVATE_PROCESS_NAME_ERR:
logger.info("初始化结果:" + "初始化失败,进程名称不匹配");
break;
case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
logger.info("初始化结果:" + "初始化失败,有效期校验不一致");
break;
case EASY_ACTIVATE_PLATFORM_ERR:
logger.info("初始化结果:" + "初始化失败,平台不匹配");
break;
case EASY_ACTIVATE_COMPANY_ID_LEN_ERR:
logger.info("初始化结果:" + "初始化失败,授权使用商不匹配");
break;
default:
logger.info("初始化结果:" + "初始化失败,未知异常-" + res);
break;
}
}
return easyRtspToRtmpLiveSDK;
}
public static void init() {
System.setProperty("jna.debug_load", "true");
System.setProperty("jna.protected", "true");
System.setProperty("jna.encoding", "GBK");
if (EasyRtspToRtmpLiveSDKManger.INSTANCE == null) {
try {
// EasyRtspToRtmpLiveSDKManger.INSTANCE =
// (EasyRtspToRtmpLiveSDK) Native
// .loadLibrary("D:\\sdk\\EasyRtspToRtmpLiveSDK",
// EasyRtspToRtmpLiveSDK.class);
EasyRtspToRtmpLiveSDKManger.INSTANCE = (EasyRtspToRtmpLiveSDK) Native
.loadLibrary(".\\EasyRtspToRtmpLiveSDK", EasyRtspToRtmpLiveSDK.class);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}
EasyRtspToRtmpLiveSDKManger.INSTANCE.SetDisplayLog(logCallBack);
int res = EasyRtspToRtmpLiveSDKManger.INSTANCE.init(RTSPKEY, RTMPKEY);
if (res == 0) {
logger.info("初始化结果:" + (res == 0 ? "初始化成功" : "初始化失败:" + res));
} else {
switch (res) {
case EASY_ACTIVATE_INVALID_KEY:
logger.info("初始化结果:" + "初始化失败,无效Key");
break;
case EASY_ACTIVATE_TIME_ERR:
logger.info("初始化结果:" + "初始化失败,时间错误");
break;
case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
logger.info("初始化结果:" + "初始化失败,进程名称长度不匹配");
break;
case EASY_ACTIVATE_PROCESS_NAME_ERR:
logger.info("初始化结果:" + "初始化失败,进程名称不匹配");
break;
case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
logger.info("初始化结果:" + "初始化失败,有效期校验不一致");
break;
case EASY_ACTIVATE_PLATFORM_ERR:
logger.info("初始化结果:" + "初始化失败,平台不匹配");
break;
case EASY_ACTIVATE_COMPANY_ID_LEN_ERR:
logger.info("初始化结果:" + "初始化失败,授权使用商不匹配");
break;
default:
logger.info("初始化结果:" + "初始化失败,未知异常-" + res);
break;
}
}
}
public void SetDisplayLog(LogCallBack DisplayLog);
// 超时时间,单位秒
public void setTimeOut(long timeout_);
// rtmp:79736C36655969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4931634D5671446A652F34675A57467A65513D3D
// rtsp:6D75724D7A4969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4B56634D5671446A652F34675A57467A65513D3D
public int init(String rtspkey, String rtmpkey);
public int push(String rtspurl, String rtmpurl, int deviceid);
public boolean start(int channelId);
public boolean close(int channelId);
public boolean stop(int channelId);
public EASY_FRAME_INFO info(int channelId);
public boolean ispushing(int channelId);
public int registerStatusCallBack(int channelId, StatusCallBack _callback);
public int registerInfoCallBack(int channelId, PushLiveInfoCallBack _callback);
public boolean stopAll();
public String fileCreateTime(String filename);
public static class EASY_FRAME_INFO extends Structure {
public int codec; /* 音视频格式 */
public int type; /* 视频帧类型 */
public byte fps; /* 视频帧率 */
public short width; /* 视频宽 */
public short height; /* 视频高 */
public int reserved1; /* 保留参数1 */
public int reserved2; /* 保留参数2 */
public int sample_rate; /* 音频采样率 */
public int channels; /* 音频声道数 */
public int bits_per_sample; /* 音频采样精度 */
public int length; /* 音视频帧大小 */
public int timestamp_usec; /* 时间戳,微妙 */
public int timestamp_sec; /* 时间戳 秒 */
public float bitrate; /* 比特率 */
public float losspacket; /* 丢包率 */
@Override
public String toString() {
return "EASY_FRAME_INFO [codec=" + codec + ", type=" + type + ", fps=" + fps + ", width=" + width
+ ", height=" + height + ", reserved1=" + reserved1 + ", reserved2=" + reserved2 + ", sample_rate="
+ sample_rate + ", channels=" + channels + ", bits_per_sample=" + bits_per_sample + ", length="
+ length + ", timestamp_usec=" + timestamp_usec + ", timestamp_sec=" + timestamp_sec + ", bitrate="
+ bitrate + ", losspacket=" + losspacket + "]";
}
}
public static interface PushLiveInfoCallBack extends StdCallCallback {
public void invoke(int _channelId, int _mediatype, EASY_FRAME_INFO frameinfo);
}
public static interface StatusCallBack extends StdCallCallback {
public void invoke(int _channelId, int status);
}
public static interface LogCallBack extends StdCallCallback {
public void invoke(String logText);
}
}
EasyRtspToRtmpLiveSDKManger
package com.kx.hcws.easy;
public class EasyRtspToRtmpLiveSDKManger {
private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
.getLogger(EasyRtspToRtmpLiveSDKManger.class);
public static EasyRtspToRtmpLiveSDK INSTANCE = null;
}
效果预览
本地预览:
本地预览:
本地预览并推流:
推流成功:
推流监控:
查看在线流:
获取播放地址:
VLC播放: