当前位置:   article > 正文

四、JAVA调用海康威视SDK实现摄像头内网推流到阿里云_海康推流给其它平台

海康推流给其它平台

接上一章:三、JAVA调用海康威视SDK实现摄像头本地存储

本章实现摄像头内网推流到阿里云服务器。

环境准备

  1. 开发工具:eclipse
  2. Jdk版本:jdk1.8
  3. 开发语言:java,界面使用swing开发
  4. 摄像头:DS-2CD1221D-I3
  5. 海康威视SDK下载地址:海康威视开放平台
  6. SDK版本:CH-HCNetSDKV6.0.2.35_build20190411_Win64
  7. EasyRTMPLive:EasyRTMPLive

转发流程

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_19,color_FFFFFF,t_70,g_se,x_16

 

账号准备

  1. 海康威视摄像头账号密码及rtsp推拉地址
  2. 阿里云账号,需开通阿里云视频直播相关功能,需获取阿里云推流地址、推流KEY及推流APPNAME,具体参考:快速入门 - 视频直播 - 阿里云

推流域名配置:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

 推流域名设置:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

 推流KEY:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

 

 

 

代码实现

EasyPushProcess

  1. package com.kx.hcws;
  2. import java.util.Date;
  3. import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK;
  4. import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.EASY_FRAME_INFO;
  5. import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.PushLiveInfoCallBack;
  6. import com.kx.hcws.easy.EasyRtspToRtmpLiveSDK.StatusCallBack;
  7. import com.kx.hcws.ui.Player;
  8. /**
  9. *
  10. * 类: EasyPushProcess,描述:
  11. *
  12. * <p style="padding-left:20px">
  13. * 推流处理器
  14. * </p>
  15. *
  16. * @author kxy
  17. *
  18. * @date 2020年5月27日 上午10:45:51
  19. *
  20. * @version V1.0.0
  21. */
  22. public class EasyPushProcess {
  23. // 日志
  24. private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(HCSaveProcess.class);
  25. public int width = 860, height = 480;
  26. public boolean pushing = false;
  27. protected String pushurl = "";
  28. public long errorcount = 0, counttime = 0, alltime = 0, laststarttime = -1, framesize = 0;
  29. private Long deviceId;
  30. private String m_sDeviceIP;
  31. private String username;
  32. private String password;
  33. private int port;
  34. private long iChannelNum;
  35. protected String mediaappname;
  36. private static Player player;
  37. // 状态回调
  38. private static StatusCallBack statusCallBack = new StatusCallBack() {
  39. @Override
  40. public void invoke(int _channelId, int status) {
  41. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_DISCONNECTED) {
  42. logger.info("设备m_sDeviceIPRTMP断开连接 :");
  43. }
  44. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_CONNECT_ABORT) {
  45. logger.info("设备m_sDeviceIPRTMP连接异常中断 :");
  46. }
  47. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_CONNECT_FAILED) {
  48. logger.info("设备m_sDeviceIPRTMP连接失败 :");
  49. }
  50. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_START_ERROR) {
  51. logger.info("设备m_sDeviceIPRTSP启动异常 :");
  52. }
  53. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_ERROR) {
  54. logger.info("设备m_sDeviceIPRTSP链接异常 :");
  55. }
  56. if (status == EasyRtspToRtmpLiveSDK.EASY_RTMP_STATE_TIMEOUT) {// 链接超时或者RTMP创建异常
  57. logger.info("设备m_sDeviceIPRTMP链接超时自动退出:");
  58. }
  59. }
  60. };
  61. // 推流信息回调
  62. private static PushLiveInfoCallBack pushLiveInfoCallBack = new PushLiveInfoCallBack() {
  63. @Override
  64. public void invoke(int _channelId, int _mediatype, EASY_FRAME_INFO frameinfo) {
  65. if (frameinfo != null) {
  66. Double ss = Double.parseDouble(frameinfo.bitrate + "");
  67. logger.info("推流帧率:" + ss.longValue());
  68. player.setPushBitrate(ss.longValue());
  69. }
  70. }
  71. };
  72. // SDK
  73. private EasyRtspToRtmpLiveSDK easyRtspToRtmpLiveSDK;
  74. // 推流帧信息
  75. private EASY_FRAME_INFO frameinfo = null;
  76. // 通道号
  77. private int channelId = -1;
  78. public EasyPushProcess(String m_sDeviceIP, String username, String password, int port, Long deviceId,
  79. String pushkey, String pushurl, String mediaappname, Player player) {
  80. this.deviceId = deviceId;
  81. this.m_sDeviceIP = m_sDeviceIP;
  82. this.username = username;
  83. this.password = password;
  84. this.port = port;
  85. this.mediaappname = mediaappname;
  86. this.player = player;
  87. pushurl = getPushUrl(pushkey, pushurl, mediaappname);
  88. /**
  89. * 海康威视IP摄像头rtsp协议地址如下:
  90. *
  91. * rtsp://[username]:[passwd]@[ip]:[port]/[codec]/[channel]/[subtype]/
  92. * av_stream
  93. *
  94. * 说明:
  95. *
  96. * username:用户名,例如admin
  97. *
  98. * passwd:密码,例如12345
  99. *
  100. * ip:设备的ip地址,例如192.0.0.64
  101. *
  102. * port:端口号默认554,若为默认可以不写
  103. *
  104. * codec:有h264、MPEG-4、mpeg4这几种
  105. *
  106. * channel:通道号,起始为1
  107. *
  108. * subtype:码流类型,主码流为main,子码流为sub
  109. *
  110. * rtsp://admin:GNEHIB@192.168.2.18:554/h265/ch1/sub/av_stream
  111. *
  112. */
  113. String src = "rtsp://" + username + ":" + password + "@" + m_sDeviceIP + ":554/h265/ch1/main/av_stream";// 组织海康威视rtsp拉流地址
  114. easyRtspToRtmpLiveSDK = EasyRtspToRtmpLiveSDK.create();
  115. channelId = easyRtspToRtmpLiveSDK.push(src, pushurl, deviceId.intValue());
  116. easyRtspToRtmpLiveSDK.registerStatusCallBack(channelId, statusCallBack);
  117. easyRtspToRtmpLiveSDK.registerInfoCallBack(channelId, pushLiveInfoCallBack);
  118. }
  119. public void start() {
  120. if (pushing) {
  121. return;
  122. }
  123. errorcount = counttime = alltime = framesize = 0;
  124. laststarttime = System.currentTimeMillis();
  125. pushing = true;
  126. new Thread(new Runnable() {
  127. @Override
  128. public void run() {
  129. if (!easyRtspToRtmpLiveSDK.start(channelId)) {// 返回后会自动停止
  130. stop_();
  131. }
  132. }
  133. }).start();
  134. }
  135. public void stop_() {
  136. pushing = false;
  137. }
  138. /**
  139. *
  140. * 方法: info ,描述:
  141. *
  142. * <p style="padding-left:20px">
  143. * 取得推流信息
  144. * </p>
  145. * kangxiaoyu
  146. *
  147. * @return
  148. */
  149. public EASY_FRAME_INFO info() {
  150. if (channelId >= 0 && isPushing()) {
  151. return frameinfo;
  152. }
  153. return null;
  154. }
  155. /**
  156. * 是否正在推流
  157. *
  158. * @return
  159. */
  160. public boolean isPushing() {
  161. return pushing && easyRtspToRtmpLiveSDK.ispushing(channelId);
  162. }
  163. /**
  164. *
  165. * 方法: ispushing_ ,描述:
  166. *
  167. * <p style="padding-left:20px">
  168. * 判断SDK是否推流
  169. * </p>
  170. * kangxiaoyu
  171. *
  172. * @param channelId
  173. * @return
  174. */
  175. public boolean isSdkPushing(int channelId) {
  176. return easyRtspToRtmpLiveSDK.ispushing(channelId);
  177. }
  178. /**
  179. *
  180. * 方法: isSdkStop ,描述:
  181. *
  182. * <p style="padding-left:20px">
  183. * 调用SDK停止推流
  184. * </p>
  185. * kangxiaoyu
  186. *
  187. * @param channelId
  188. * @return
  189. */
  190. public String isSdkStop(int channelId) {
  191. return easyRtspToRtmpLiveSDK.stop(channelId) + "";
  192. }
  193. public int getChannelId() {
  194. return channelId;
  195. }
  196. /**
  197. * 组织推流地址
  198. *
  199. * @param pushkey
  200. * 阿里云推流KEY
  201. * @param pushurl
  202. * 阿里云推流地址
  203. * @param mediaappname
  204. * 阿里云推流APPNAME
  205. * @return
  206. */
  207. public String getPushUrl(String pushkey, String pushurl, String mediaappname) {
  208. String url = pushurl;
  209. if (url.contains(";")) {
  210. url = url.split(";")[(int) (deviceId % pushurl.split(";").length)];
  211. }
  212. if (pushkey.contains(";")) {
  213. pushkey = pushkey.split(";")[(int) (deviceId % pushkey.split(";").length)];
  214. }
  215. url += deviceId;
  216. Date date = new Date();
  217. String time = date.getTime() + "";
  218. System.out.println("/" + mediaappname + "/" + deviceId + "-" + time + "-0-0-" + pushkey);
  219. String auth_key = MD5Utils.getMd5("/" + mediaappname + "/" + deviceId + "-" + time + "-0-0-" + pushkey);
  220. System.out.println("推流地址:" + url + "?auth_key=" + time + "-0-0-" + auth_key);
  221. // return url + "?auth_key=" + time + "-0-0-" + auth_key;
  222. return url;
  223. }
  224. }

EasyRtspToRtmpLiveSDK

  1. package com.kx.hcws.easy;
  2. import com.sun.jna.Native;
  3. import com.sun.jna.Structure;
  4. import com.sun.jna.win32.StdCallLibrary;
  5. public interface EasyRtspToRtmpLiveSDK extends StdCallLibrary {
  6. // 日志
  7. static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(EasyRtspToRtmpLiveSDK.class);
  8. public static int EASY_RTMP_STATE_CONNECTING = 1; /* 连接中 */
  9. public static int EASY_RTMP_STATE_CONNECTED = 2; /* 连接成功 */
  10. public static int EASY_RTMP_STATE_CONNECT_FAILED = 3; /* 连接失败 */
  11. public static int EASY_RTMP_STATE_CONNECT_ABORT = 4; /* 连接异常中断 */
  12. public static int EASY_RTMP_STATE_PUSHING = 5; /* 推流中 */
  13. public static int EASY_RTMP_STATE_DISCONNECTED = 6; /* 断开连接 */
  14. public static int EASY_RTMP_STATE_ERROR = 7;
  15. public static int EASY_RTMP_STATE_TIMEOUT = -1;
  16. public static int EASY_RTMP_STATE_START_ERROR = -2;
  17. public static int EASY_ACTIVATE_INVALID_KEY = -1; /* 无效Key */
  18. public static int EASY_ACTIVATE_TIME_ERR = -2; /* 时间错误 */
  19. public static int EASY_ACTIVATE_PROCESS_NAME_LEN_ERR = -3; /* 进程名称长度不匹配 */
  20. public static int EASY_ACTIVATE_PROCESS_NAME_ERR = -4; /* 进程名称不匹配 */
  21. public static int EASY_ACTIVATE_VALIDITY_PERIOD_ERR = -5; /* 有效期校验不一致 */
  22. public static int EASY_ACTIVATE_PLATFORM_ERR = -6; /* 平台不匹配 */
  23. public static int EASY_ACTIVATE_COMPANY_ID_LEN_ERR = -7; /* 授权使用商不匹配 */
  24. public static int EASY_ACTIVATE_SUCCESS = 0; /* 激活成功 */
  25. public static int Easy_NoErr = 0;
  26. public static int Easy_RequestFailed = -1;
  27. public static int Easy_Unimplemented = -2;
  28. public static int Easy_RequestArrived = -3;
  29. public static int Easy_OutOfState = -4;
  30. public static int Easy_NotAModule = -5;
  31. public static int Easy_WrongVersion = -6;
  32. public static int Easy_IllegalService = -7;
  33. public static int Easy_BadIndex = -8;
  34. public static int Easy_ValueNotFound = -9;
  35. public static int Easy_BadArgument = -10;
  36. public static int Easy_ReadOnly = -11;
  37. public static int Easy_NotPreemptiveSafe = -12;
  38. public static int Easy_NotEnoughSpace = -13;
  39. public static int Easy_WouldBlock = -14;
  40. public static int Easy_NotConnected = -15;
  41. public static int Easy_FileNotFound = -16;
  42. public static int Easy_NoMoreData = -17;
  43. public static int Easy_AttrDoesntExist = -18;
  44. public static int Easy_AttrNameExists = -19;
  45. public static int Easy_InstanceAttrsNotAllowed = -20;
  46. public static int Easy_InvalidSocket = -21;
  47. public static int Easy_MallocError = -22;
  48. public static int Easy_ConnectError = -23;
  49. public static int Easy_SendError = -24;
  50. // EasyRtspToRtmpLiveSDK推流key,需要从官网获取
  51. public static String RTSPKEY = "6D75724D7A4969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4B56634D5671446A652F34675A57467A65513D3D";
  52. public static String RTMPKEY = "79736C36655969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4931634D5671446A652F34675A57467A65513D3D";
  53. public static LogCallBack logCallBack = new LogCallBack() {
  54. @Override
  55. public void invoke(String logText) {
  56. logger.info(logText);
  57. }
  58. };
  59. public static EasyRtspToRtmpLiveSDK create() {
  60. System.setProperty("jna.debug_load", "true");
  61. System.setProperty("jna.protected", "true");
  62. System.setProperty("jna.encoding", "GBK");
  63. EasyRtspToRtmpLiveSDK easyRtspToRtmpLiveSDK = (EasyRtspToRtmpLiveSDK) Native
  64. .loadLibrary("EasyRtspToRtmpLiveSDK", EasyRtspToRtmpLiveSDK.class);
  65. easyRtspToRtmpLiveSDK.SetDisplayLog(logCallBack);
  66. int res = easyRtspToRtmpLiveSDK.init(RTSPKEY, RTMPKEY);
  67. if (res == 0) {
  68. logger.info("初始化结果:" + (res == 0 ? "初始化成功" : "初始化失败:" + res));
  69. } else {
  70. switch (res) {
  71. case EASY_ACTIVATE_INVALID_KEY:
  72. logger.info("初始化结果:" + "初始化失败,无效Key");
  73. break;
  74. case EASY_ACTIVATE_TIME_ERR:
  75. logger.info("初始化结果:" + "初始化失败,时间错误");
  76. break;
  77. case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
  78. logger.info("初始化结果:" + "初始化失败,进程名称长度不匹配");
  79. break;
  80. case EASY_ACTIVATE_PROCESS_NAME_ERR:
  81. logger.info("初始化结果:" + "初始化失败,进程名称不匹配");
  82. break;
  83. case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
  84. logger.info("初始化结果:" + "初始化失败,有效期校验不一致");
  85. break;
  86. case EASY_ACTIVATE_PLATFORM_ERR:
  87. logger.info("初始化结果:" + "初始化失败,平台不匹配");
  88. break;
  89. case EASY_ACTIVATE_COMPANY_ID_LEN_ERR:
  90. logger.info("初始化结果:" + "初始化失败,授权使用商不匹配");
  91. break;
  92. default:
  93. logger.info("初始化结果:" + "初始化失败,未知异常-" + res);
  94. break;
  95. }
  96. }
  97. return easyRtspToRtmpLiveSDK;
  98. }
  99. public static void init() {
  100. System.setProperty("jna.debug_load", "true");
  101. System.setProperty("jna.protected", "true");
  102. System.setProperty("jna.encoding", "GBK");
  103. if (EasyRtspToRtmpLiveSDKManger.INSTANCE == null) {
  104. try {
  105. // EasyRtspToRtmpLiveSDKManger.INSTANCE =
  106. // (EasyRtspToRtmpLiveSDK) Native
  107. // .loadLibrary("D:\\sdk\\EasyRtspToRtmpLiveSDK",
  108. // EasyRtspToRtmpLiveSDK.class);
  109. EasyRtspToRtmpLiveSDKManger.INSTANCE = (EasyRtspToRtmpLiveSDK) Native
  110. .loadLibrary(".\\EasyRtspToRtmpLiveSDK", EasyRtspToRtmpLiveSDK.class);
  111. } catch (Exception e) {
  112. e.printStackTrace();
  113. } catch (Throwable e) {
  114. e.printStackTrace();
  115. }
  116. }
  117. EasyRtspToRtmpLiveSDKManger.INSTANCE.SetDisplayLog(logCallBack);
  118. int res = EasyRtspToRtmpLiveSDKManger.INSTANCE.init(RTSPKEY, RTMPKEY);
  119. if (res == 0) {
  120. logger.info("初始化结果:" + (res == 0 ? "初始化成功" : "初始化失败:" + res));
  121. } else {
  122. switch (res) {
  123. case EASY_ACTIVATE_INVALID_KEY:
  124. logger.info("初始化结果:" + "初始化失败,无效Key");
  125. break;
  126. case EASY_ACTIVATE_TIME_ERR:
  127. logger.info("初始化结果:" + "初始化失败,时间错误");
  128. break;
  129. case EASY_ACTIVATE_PROCESS_NAME_LEN_ERR:
  130. logger.info("初始化结果:" + "初始化失败,进程名称长度不匹配");
  131. break;
  132. case EASY_ACTIVATE_PROCESS_NAME_ERR:
  133. logger.info("初始化结果:" + "初始化失败,进程名称不匹配");
  134. break;
  135. case EASY_ACTIVATE_VALIDITY_PERIOD_ERR:
  136. logger.info("初始化结果:" + "初始化失败,有效期校验不一致");
  137. break;
  138. case EASY_ACTIVATE_PLATFORM_ERR:
  139. logger.info("初始化结果:" + "初始化失败,平台不匹配");
  140. break;
  141. case EASY_ACTIVATE_COMPANY_ID_LEN_ERR:
  142. logger.info("初始化结果:" + "初始化失败,授权使用商不匹配");
  143. break;
  144. default:
  145. logger.info("初始化结果:" + "初始化失败,未知异常-" + res);
  146. break;
  147. }
  148. }
  149. }
  150. public void SetDisplayLog(LogCallBack DisplayLog);
  151. // 超时时间,单位秒
  152. public void setTimeOut(long timeout_);
  153. // rtmp:79736C36655969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4931634D5671446A652F34675A57467A65513D3D
  154. // rtsp:6D75724D7A4969576B5A75414B304A646F7232332B2B394659584E35556C525455457870646D55755A58686C4B56634D5671446A652F34675A57467A65513D3D
  155. public int init(String rtspkey, String rtmpkey);
  156. public int push(String rtspurl, String rtmpurl, int deviceid);
  157. public boolean start(int channelId);
  158. public boolean close(int channelId);
  159. public boolean stop(int channelId);
  160. public EASY_FRAME_INFO info(int channelId);
  161. public boolean ispushing(int channelId);
  162. public int registerStatusCallBack(int channelId, StatusCallBack _callback);
  163. public int registerInfoCallBack(int channelId, PushLiveInfoCallBack _callback);
  164. public boolean stopAll();
  165. public String fileCreateTime(String filename);
  166. public static class EASY_FRAME_INFO extends Structure {
  167. public int codec; /* 音视频格式 */
  168. public int type; /* 视频帧类型 */
  169. public byte fps; /* 视频帧率 */
  170. public short width; /* 视频宽 */
  171. public short height; /* 视频高 */
  172. public int reserved1; /* 保留参数1 */
  173. public int reserved2; /* 保留参数2 */
  174. public int sample_rate; /* 音频采样率 */
  175. public int channels; /* 音频声道数 */
  176. public int bits_per_sample; /* 音频采样精度 */
  177. public int length; /* 音视频帧大小 */
  178. public int timestamp_usec; /* 时间戳,微妙 */
  179. public int timestamp_sec; /* 时间戳 秒 */
  180. public float bitrate; /* 比特率 */
  181. public float losspacket; /* 丢包率 */
  182. @Override
  183. public String toString() {
  184. return "EASY_FRAME_INFO [codec=" + codec + ", type=" + type + ", fps=" + fps + ", width=" + width
  185. + ", height=" + height + ", reserved1=" + reserved1 + ", reserved2=" + reserved2 + ", sample_rate="
  186. + sample_rate + ", channels=" + channels + ", bits_per_sample=" + bits_per_sample + ", length="
  187. + length + ", timestamp_usec=" + timestamp_usec + ", timestamp_sec=" + timestamp_sec + ", bitrate="
  188. + bitrate + ", losspacket=" + losspacket + "]";
  189. }
  190. }
  191. public static interface PushLiveInfoCallBack extends StdCallCallback {
  192. public void invoke(int _channelId, int _mediatype, EASY_FRAME_INFO frameinfo);
  193. }
  194. public static interface StatusCallBack extends StdCallCallback {
  195. public void invoke(int _channelId, int status);
  196. }
  197. public static interface LogCallBack extends StdCallCallback {
  198. public void invoke(String logText);
  199. }
  200. }

EasyRtspToRtmpLiveSDKManger

  1. package com.kx.hcws.easy;
  2. public class EasyRtspToRtmpLiveSDKManger {
  3. private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
  4. .getLogger(EasyRtspToRtmpLiveSDKManger.class);
  5. public static EasyRtspToRtmpLiveSDK INSTANCE = null;
  6. }

效果预览

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

 本地预览:watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

本地预览:watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16 本地预览并推流:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16 推流成功:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16 推流监控:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16查看在线流: 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16 获取播放地址:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16 VLC播放:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Yab5Yab5ZCbMDE=,size_20,color_FFFFFF,t_70,g_se,x_16

项目源码

源码下载

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/684004
推荐阅读
相关标签
  

闽ICP备14008679号