即构科技集成直播后,需求中需要拉流端(观众)切换到阿里云的流地址,而不是用即构返回的即构流地址,原因是即构的流比较贵,阿里云的收费非常的便宜,差距大概是阿里云的四倍吧,所以考虑到用前端进行流地址的切换,直播推流决定着观众的拉流,也就是说,主播直播的时候推的是什么流,观众拉的就是什么流了,所以在推流的时候进行切换流地址,(没有让即构在返回列表里返回多个流地址,据说即构那边是可以配置的),我是直接与后台对接,把后台拿到的即构流、阿里云流等都通过一个接口返回给我,我自己截取替换流地址;对于阿里云的流地址的使用,除了替换还有进行鉴权,下面看看具体的文档:

阿里云鉴权的说明:https://www.alibabacloud.com/help/zh/doc-detail/97943.htm?spm=a2c63.p38356.b99.29.37182eb8uQa4rL

代码示例:https://www.alibabacloud.com/help/zh/doc-detail/57388.htm?spm=a2c63.p38356.879954.22.117d7e67Z9OUO1#concept-b2p-mfd-2fb

https://www.alibabacloud.com/help/zh/doc-detail/66016.htm?spm=a2c63.p38356.b99.72.3e31zGVxzGVxCj

https://help.aliyun.com/knowledge_list/49528.html

即构相关链接:

https://doc.zego.im/API/ZegoLiveRoom/Android/html/com/zego/zegoliveroom/ZegoLiveRoom.html#startPublishing-java.lang.String-java.lang.String-int-java.lang.String-

https://doc.zego.im/API/ZegoLiveRoom/Android/html/com/zego/zegoliveroom/ZegoLiveRoom.html#startPublishing2-java.lang.String-java.lang.String-int-int-

https://doc.zego.im/API/ZegoLiveRoom/Android/html/com/zego/zegoliveroom/ZegoLiveRoom.html#startPlayingStream-java.lang.String-java.lang.Object-

https://doc.zego.im/CN/303.html
startPublishing 开始推流 
startPublishing2 开始推流,可推两路流 
startPlayingStream 开始拉流

https://doc.zego.im/CN/491.html#3
如何自定义指定的CDN拉流

如何获取转码推流地址

  • 推流地址规则

    直播只支持RTMP格式推流。

    推流地址格式为RTMP://推流域名/AppName/StreamName?鉴权串

  • 示例

    例如,推流域名是push.aliyunlive.com,AppName为app,StreamName为stream,鉴权key是123,则推流地址为RTMP://push.aliyunlive.com/app/stream?auth_key=timestamp-rand-uid-md5hash

  • 示例

    例如,播流域名是play.aliyunlive.com,AppName为app,StreamName为stream,鉴权key是456,使用sd模板,拼接地址如下所示:

    • RTMP协议地址rtmp://play.aliyunlive.com/app/stream_sd?auth_key=timestamp-rand-uid-md5hash
    • FLV协议地址http://play.aliyunlive.com/app/stream_sd.flv?auth_key=timestamp-rand-uid-md5hash
    • HLS协议地址http://play.aliyunlive.com/app/stream_sd.m3u8?auth_key=timestamp-rand-uid-md5hash

鉴权串字段对照,参见鉴权地址Democode。

字段 描述
timestamp 失效时间,整形正数,固定长度 10,1970 年 1 月 1 日以来的秒数。用来控制失效时间,10 位整数,有效时间 1800s
rand 随机数,建议使用UUID(不能包含中划线“-”,例如: 477b3bbc253f467b8def6711128c7bec 格式)
uid 暂未使用(设置成 0 即可)
md5hash 通过 md5 算法计算出的验证串,数字和小写英文字母混合(0-9和a-z),固定长度 32

鉴权算法

未鉴权URL: http://DomainName/AppName/StreamName.flv

  • 鉴权后的URL:http://DomainName/Filename?auth_key=timestamp-rand-0-md5hash

参数说明

  • timestamp:失效时间,整形正数,固定长度 10,1970 年 1 月 1 日以来的秒数。用来控制失效时间。

  • rand:随机数,一般设成 0。

  • md5hash:通过md5算法计算出的验证串,算法(假设用户的密钥为 123456:md5hash=md5(timestamp-rand-0-123456)。

鉴权示例

用户给 a.com 这个域名配置了一个鉴权密钥:xyz,希望鉴权有效时间为:2017-07-28 05:43:20,准备用rtmp://video-center.alivecdn.com/live/abc?vhost=a.com进行推流,相应的鉴权计算逻辑为:

推流

  • 获取推流uri:/live/abc

  • 获取 timestamp:过期时间 2017-07-28 05:43:200 的unixtimestamp为:1501191800。

  • 组装加密字符串:/live/abc-1501191800-0-0-xyz

  • 计算加密后的字符串:md5 (“/live/abc-1501191800-0-0-xyz”) = 7856896499b661b1123dca82d7406aa3。

  • 拼接加密后的推流URL:rtmp://video- center.alivecdn.com/live/abc?vhost=a.com?auth_key=1501191800-0-0-7856896499b661b1123dca82d7406aa3

播放

播放的计算方法与推流类似,以 httpflv 播放地址为例。

  • 未鉴权的 URL 为:http:// a.com/live/abc.flv

  • 获取播放URL:/live/abc.flv

  • 获取 timestamp:过期时间 2017-07-28 05:43:200 的 unixtimestamp 为:1501191800。

  • 组装加密字符串:/live/abc.flv-1501191800-0-0-xyz

  • 计算加密后的字符串:md5(“/live/abc.flv-1501191800-0-0-xyz”) = b022238fd0cd1c8efae2ba84fc0c9119。

  • 拼接加密后的推流URL:http://a.com/live/abc.flv?auth_key=1501191800-0-0-b022238fd0cd1c8efae2ba84fc0c9119

  • 设置方法:可以在直播控制台进行设置,控制台中鉴权计算器,可以方便的生成鉴权 URL。详情参考 直播鉴权。

关键说明上面已经贴出,下面来看看代码部分:

1.在BasePublishActivity中设置流地址的切换:

(替换前)

 /*** 推流成功.*/protected void handlePublishSucc(String streamID, HashMap<String, Object> info) {//        ViewLive viewLivePublish = getViewLiveByStreamID(streamID);
//        List<String> listUrls = getShareUrlList(info);
//        if (viewLivePublish != null && listUrls.size() >= 2) {
//            // 设置分享连接
//            viewLivePublish.setListShareUrls(listUrls);
//
//            // 将流地址发送给房间观众
//            Map<String, String> mapUrls = new HashMap<>();
//            mapUrls.put(Constants.FIRST_ANCHOR, String.valueOf(true));
//            mapUrls.put(Constants.KEY_HLS, listUrls.get(0));
//            mapUrls.put(Constants.KEY_RTMP, listUrls.get(1));
//            Gson gson = new Gson();
//            String json = gson.toJson(mapUrls);
//            mZegoLiveRoom.updateStreamExtraInfo(json);
//        }super.handlePublishSucc(streamID);}

上述代码注释取消就可以了,可以看到

mapUrls.put(Constants.KEY_RTMP, url1);

就是RTMP直播时候提供给观众的流地址,在这里进行替换。

(替换后)

 /*** 推流成功.*/protected void handlePublishSucc(String streamID, HashMap<String, Object> info) {getRtmpUrl(streamID,info);super.handlePublishSucc(streamID);}
/*** 获取直播推流的流地址*/private void getRtmpUrl(final String streamId, final HashMap<String, Object> info) {HttpParam param = new HttpParam();param.put("streamId", streamId);HttpLoader.getInstance(this).post(UrlConstant.RTMPURL, param, UrlConstant.USER_EDIT_RECORDS_REQUEST_CODE, new HttpLoader.HttpListener() {@Overridepublic void onSuccess(int resultCode, String result) {RtmpUrlBean entity = JsonUtils.toEntity(result, RtmpUrlBean.class);if (entity != null) {if(!TextUtils.isEmpty(entity.getData())){//截取流地址String rtmpUrl = entity.getData();if(rtmpUrl.indexOf(",") != -1)//是否包含,{
//                            System.out.println("包含该字符串");String[] splitUrl = rtmpUrl.split(",");String url0 = splitUrl[0];url1 = splitUrl[1];String url2 = splitUrl[2];}//}ViewLive viewLivePublish = getViewLiveByStreamID(streamId);List<String> listUrls = getShareUrlList(info);if (viewLivePublish != null && listUrls.size() >= 2) {// 设置分享连接viewLivePublish.setListShareUrls(listUrls);// 将流地址发送给房间观众Map<String, String> mapUrls = new HashMap<>();mapUrls.put(Constants.FIRST_ANCHOR, String.valueOf(true));mapUrls.put(Constants.KEY_HLS, listUrls.get(0));if(TextUtils.isEmpty(entity.getData())){mapUrls.put(Constants.KEY_RTMP, listUrls.get(1));}else {mapUrls.put(Constants.KEY_RTMP, url1);}Gson gson = new Gson();String json = gson.toJson(mapUrls);mZegoLiveRoom.updateStreamExtraInfo(json);}}}@Overridepublic void onfailed(int resultCode, String error) {}});}

可以看到,后台返回的字符串通过逗号截取后进行替换地址,这样就可以,接着就是要进行阿里云流地址的鉴权了:

2.在BaseLiveActivity中设置阿里云鉴权:

protected void publishStream(){}方法中修改,将原来的startPublishing修改成startPublishing2,把相应的参数加上:

 protected void publishStream() {if (TextUtils.isEmpty(mPublishStreamID)) {return;}if (isStreamExisted(mPublishStreamID)) {Toast.makeText(this, "流已存在", Toast.LENGTH_LONG).show();return;}ViewLive freeViewLive = getFreeViewLive();if (freeViewLive == null) {return;}// 设置流信息freeViewLive.setStreamID(mPublishStreamID);freeViewLive.setPublishView(true);// 初始化配置信息, 混流模式使用initPublishConfigs();// 输出发布状态recordLog(MY_SELF + ": start publishing(" + mPublishStreamID + ")");// 设置水印
//        mZegoLiveRoom.setWaterMarkImagePath("asset:watermark.png");
//        Rect rect = new Rect();
//        rect.left = 50;
//        rect.top = 20;
//        rect.right = 200;
//        rect.bottom = 170;
//        mZegoLiveRoom.setPreviewWaterMarkRect(rect);
//        mZegoLiveRoom.setPublishWaterMarkRect(rect);// 开启流量自动控制int properties = ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_FPS| ZegoConstants.ZegoTrafficControlProperty.ZEGOAPI_TRAFFIC_RESOLUTION;mZegoLiveRoom.enableTrafficControl(properties, true);//开启双声道mZegoLiveRoom.setAudioChannelCount(2);// 开始播放
//        if(PreferenceUtil.getInstance().getUseExternalRender(false)){
//          if(videoRenderer == null){
//              videoRenderer = new VideoRenderer();
//          }
//         开启外部渲染
//         videoRenderer.init();
//         videoRenderer.setRendererView(freeViewLive.getTextureView());
//         mZegoLiveRoom.setZegoExternalRenderCallback(videoRenderer);
//        }else{
//         }mZegoLiveRoom.setPreviewView(freeViewLive.getTextureView());mZegoLiveRoom.startPreview();if (PreferenceUtil.getInstance().getVideoFilter(false)) {mZegoLiveRoom.setFilter(ZegoFilter.Wine, ZegoConstants.PublishChannelIndex.MAIN);}mZegoLiveRoom.enableMic(mEnableMic);mZegoLiveRoom.enableCamera(mEnableCamera);// 阿里云主播推流未鉴权
//        mZegoLiveRoom.startPublishing(mPublishStreamID, mPublishTitle, mPublishFlag);//阿里云主播推流鉴权//传入自定义参数,即传入应用名称和流名称String yuMing = "ceshipushs_a.a.sallsmeanalysispxy.xyzk";//时间戳,有效时间
//        long time = (System.currentTimeMillis() + 1800) / 1000;long time = (System.currentTimeMillis() / 1000) + 1800;//加密key,即直播后台鉴权里面自行设置String key = "AldNSFA6h9";String strpush = "/" + "live" + "/" + mPublishStreamID + "-" + time + "-0-0-" + key;// 里面的直播推流中心服务器域名、vhost域名可根据自身实际情况进行设置String pushurl = "rtmp://ceshi.a.sallysispxy.xyzk/" + "lives" + "/" + mPublishStreamID + "?auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush);String rtmpurl = "rtmp://" + yuMing + "/" + "live" + "/" + mPublishStreamID + "?auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush);LogUtils.e("KING: " + pushurl);LogUtils.e("KING: " + rtmpurl);LogUtils.e("KING: " + strpush);mZegoLiveRoom.startPublishing2(mPublishStreamID, mPublishTitle, mPublishFlag,"1024","auth_key=" + time + "-0-0-" + MD5Util.MD5(strpush),ZegoConstants.PublishChannelIndex.MAIN);//阿里云主播推流鉴权mZegoLiveRoom.setPreviewViewMode(ZegoVideoViewMode.ScaleAspectFill);}

上述代码中涉及到设置时间戳:(设置十位数的时间戳)

a.十位数的UNIX时间戳:

Long newTime = System.currentTimeMillis();
String time = newTime.toString().substring(0, 10); 

或:

long time = (System.currentTimeMillis() / 1000) + 1800;

或:

long timeStampSec = System.currentTimeMillis()/1000;String timestamp = String.format("%010d", timeStampSec);

或自定义工具类,写在方法中:

 /*** 将时间转换成时间戳* 设置即构、阿里云需要的时间戳(秒)* @return counter*/public static String dataToStamp() {long time = System.currentTimeMillis();time += 30*1000*60;//在当前系统时间加30分钟,有效时间30分钟String res = "";SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");try {Date data = simpleDateFormat.parse(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time));long ts = data.getTime();//得出的是毫秒,阿里云需要的是秒,所以除以1000ts = ts/1000;res = String.valueOf(ts);} catch (ParseException e) {e.printStackTrace();}return res;}

//        long time = (System.currentTimeMillis() + 1800) / 1000;默认是13位的。

3.MD5工具类:(最下面有视频直播Url鉴权工具类)

package com.southsummer.goddessplan.utils;import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;public class MD5Util {public final static String MD5(String s) {char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9','a', 'b', 'c', 'd', 'e', 'f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};try {byte[] btInput = s.getBytes();// 获得MD5摘要算法的 MessageDigest 对象MessageDigest mdInst = MessageDigest.getInstance("MD5");// 使用指定的字节更新摘要mdInst.update(btInput);// 获得密文byte[] md = mdInst.digest();// 把密文转换成十六进制的字符串形式int j = md.length;char str[] = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];str[k++] = hexDigits[byte0 >>> 4 & 0xf];str[k++] = hexDigits[byte0 & 0xf];}return new String(str);} catch (Exception e) {return null;}}/*** @Description: 32位小写MD5*/public static String parseStrToMd5L32(String str) {String reStr = null;try {MessageDigest md5 = MessageDigest.getInstance("MD5");byte[] bytes = md5.digest(str.getBytes());StringBuffer stringBuffer = new StringBuffer();for (byte b : bytes) {int bt = b & 0xff;if (bt < 16) {stringBuffer.append(0);}stringBuffer.append(Integer.toHexString(bt));}reStr = stringBuffer.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return reStr;}/*** @param str* @Description: 32位大写MD5*/public static String parseStrToMd5U32(String str) {String reStr = parseStrToMd5L32(str);if (reStr != null) {reStr = reStr.toUpperCase();}return reStr;}/*** @param str* @Description: 16位小写MD5*/public static String parseStrToMd5U16(String str) {String reStr = parseStrToMd5L32(str);if (reStr != null) {reStr = reStr.toUpperCase().substring(8, 24);}return reStr;}/*** @param str* @Description: 16位大写MD5*/public static String parseStrToMd5L16(String str) {String reStr = parseStrToMd5L32(str);if (reStr != null) {reStr = reStr.substring(8, 24);}return reStr;}
}

4.接下来就是在拉流的时候鉴权了,需求是拉流和推流都需要鉴权的

在BasePlayActivity中设置:

    /*** 观众登录房间成功.*/protected void handleAudienceLoginRoomSuccess(ZegoStreamInfo[] zegoStreamInfos) {// 播放房间的流if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {for (int i = 0; i < zegoStreamInfos.length; i++) {String streamId = zegoStreamInfos[i].streamID;LogUtils.e("流附加信息:  " + zegoStreamInfos[i].extraInfo);LogUtils.e("流附加信息: 长度: " + zegoStreamInfos.length);ZegoStreamExtraPlayInfo info = JsonUtils.toEntity(zegoStreamInfos[i].extraInfo, ZegoStreamExtraPlayInfo.class);LogUtils.w("流附加信息:" + info.rtmpUrls+"------"+info.flvUrls+"------"+mOldSavedStreamList);if (mOldSavedStreamList != null && mOldSavedStreamList.contains(streamId)) {LogUtils.w("BasePlayAct" + "Has quick start, ignore");mOldSavedStreamList.remove(streamId);} else {startPlay(streamId, info);}}if (mOldSavedStreamList != null && mOldSavedStreamList.size() > 0) {for (String streamId : mOldSavedStreamList) {LogUtils.w("BasePlayAct" + "Remove timeout stream id: " + streamId);stopPlay(streamId);}mOldSavedStreamList.clear();}}// 分享主播视频if (zegoStreamInfos != null && zegoStreamInfos.length > 0) {for (ZegoStreamInfo info : zegoStreamInfos) {ViewLive viewLive = getViewLiveByStreamID(info.streamID);final HashMap<String, String> mapInfo =(new Gson()).fromJson(info.extraInfo, new TypeToken<HashMap<String, String>>() {}.getType());if (viewLive != null && mapInfo != null) {boolean firstAnchor = Boolean.valueOf(mapInfo.get(Constants.FIRST_ANCHOR));if (firstAnchor) {List<String> listUrls = new ArrayList<>();listUrls.add(mapInfo.get(Constants.KEY_HLS));listUrls.add(mapInfo.get(Constants.KEY_RTMP));viewLive.setListShareUrls(listUrls);break;}}}}// 打印logrecordLog(MY_SELF + ": onLoginRoom success(" + mRoomID + "), streamCounts:" + zegoStreamInfos.length);}

根据https://doc.zego.im/CN/491.html#3  可以看出,在包房房间流的时候进行流地址的设置,调用startPlayingStream的接口可以传入完整的自定义拉流地址,参考如下:

ZegoStreamExtraPlayInfo info = new ZegoStreamExtraPlayInfo();info.params = "";//以下rtmp或flv的拉流方式,两个选择一个就可以info.rtmpUrls = new String[]{"xxx", "yyy"}; //数组中的每个值都是完整的rtmp拉流,如果有鉴权参数的,将鉴权参数带上info.flvUrls = new String[]{"xxx", "yyy"}; //数组中的每个值都是完整的flv拉流地址,如果有鉴权参数的,将鉴权参数带上mZegoLiveRoom.startPlayingStream("streamID",inview, info);//这里的streamID一定不能为空,可以自定义一个,inview是外部指定的渲染视图

这里的鉴权和推流的鉴权是否一致,还要等验证完才可以下结论。

鉴权注意推流、拉流的细节:

推流

  • 获取推流uri:/live/abc

  • 获取 timestamp:过期时间 2017-07-28 05:43:200 的unixtimestamp为:1501191800。

  • 组装加密字符串:/live/abc-1501191800-0-0-xyz

  • 计算加密后的字符串:md5 (“/live/abc-1501191800-0-0-xyz”) = 7856896499b661b1123dca82d7406aa3。

  • 拼接加密后的推流URL:rtmp://video- center.alivecdn.com/live/abc?vhost=a.com?auth_key=1501191800-0-0-7856896499b661b1123dca82d7406aa3

播放

播放的计算方法与推流类似,以 httpflv 播放地址为例。

  • 未鉴权的 URL 为:http:// a.com/live/abc.flv

  • 获取播放URL:/live/abc.flv

  • 获取 timestamp:过期时间 2017-07-28 05:43:200 的 unixtimestamp 为:1501191800。

  • 组装加密字符串:/live/abc.flv-1501191800-0-0-xyz

  • 计算加密后的字符串:md5(“/live/abc.flv-1501191800-0-0-xyz”) = b022238fd0cd1c8efae2ba84fc0c9119。

  • 拼接加密后的推流URL:http://a.com/live/abc.flv?auth_key=1501191800-0-0-b022238fd0cd1c8efae2ba84fc0c9119

  • 设置方法:可以在直播控制台进行设置,控制台中鉴权计算器,可以方便的生成鉴权 URL。详情参考 直播鉴权。

视频直播Url鉴权工具类:

import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;@Component
@SuppressWarnings({"FieldCanBeLocal", "SameParameterValue", "WeakerAccess"})
class LivingUrlUtil {//app名称 自己定义private final String appName = "dkcj";//房间名 自己定义 随便取private final String streamName = "room1";//鉴权失效的超时时间(分)private final Integer overTime = 30;//推流域名 阿里云配置的推流域名private final String pushDomain = "zbvip.push.haic666.com";//拉流域名 阿里云配置的拉流域名private final String pullDomain = "zbvip.pull.haic666.com";//uid   暂时没用,设置为0private final String uid = "0";//密钥 阿里配置的鉴权keyprivate final String privateKey = "qihan888";//加密随机字段,也可以设置为0private final String randomNumber = RandomStringUtils.random(5, "1234567890");String getPushUrl() {String pathModel = "/%s/%s";return getUrl(pathModel, pushDomain, "rtmp://");}String getPullUrl() {String pathModel = "/%s/%s";return getUrl(pathModel, pullDomain, "rtmp://");}String getPullUrlFlv() {String pathModel = "/%s/%s.flv";return getUrl(pathModel, pullDomain, "http://");}String getPullUrlM3u8() {String pathModel = "/%s/%s.m3u8";return getUrl(pathModel, pullDomain, "http://");}private String getUrl(String pathModel, String domain, String origin) {//当前时间戳+超时时间 ,用来生成鉴权字串Long timestampSecond = Instant.now().getEpochSecond() + overTime * 60;System.out.println(timestampSecond);//路径原始地址 用于生成鉴权字串String pathUrl = String.format(pathModel, appName, streamName);//拉流原始地址meu8版  http://{pullDomain}/{AppName}/{StreamName}String urlBase = String.format("%s%s%s", origin, domain, pathUrl);//获取推流鉴权字段String pushMd5Hash = getSString(pathUrl, timestampSecond);//格式化推流最终地址  最终推流地址模板 pushUrlBase?auth_key=timestamp-rand-uid-md5hashreturn String.format("%s?auth_key=%s-%s-%s-%s", urlBase, timestampSecond, randomNumber, uid, pushMd5Hash);}private String getSString(String pushUrl, Long timestampSecond) {//格式化鉴权字符串 鉴权原始字符串 URI-timestamp-rand-uid-PrivateKeyString pushSstring = String.format("%s-%s-%s-%s-%s", pushUrl, timestampSecond, randomNumber, uid, privateKey);//鉴权字符串md5return getMd5(pushSstring).toLowerCase();}private static String getMd5(String s) {try {MessageDigest messageDigest = MessageDigest.getInstance("MD5");messageDigest.update(s.getBytes());BigInteger HashInt = new BigInteger(1, messageDigest.digest());return String.format("%1$032X", HashInt);} catch (NoSuchAlgorithmException lException) {throw new RuntimeException(lException);}}
}

随机数工具类:

package com.southsummer.goddessplan.utils;import android.graphics.Color;import java.util.HashSet;import java.util.Iterator;import java.util.Random;import java.util.Set;/*** 工具类-随机数*/public class RandomUtil {private static final String ALL_CHAR = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final String LETTER_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";private static final String NUMBER_CHAR = "0123456789";/*** 获取从a至z,长度为length随机数** @return*/public static String getRandomStr(int length) {String base = "abcdefghijklmnopqrstuvwxyz";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 获取范围内int值** @param* @return*/public static int getRandomRange(int max, int min) {return (int) (Math.random() * (max - min) + min);}/*** 获取随机长度随机字符** @param length base* @return*/public static String getRandomString(int length, String base) { // length表示生成字符串的长度Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 获取随机长度随机字符** @param length* @return*/public static String getRandomString(int length) { // length表示生成字符串的长度String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 获取随机长度随机数字** @param length* @return*/public static String getRandomNumString(int length) { // length表示生成字符串的长度String base = "0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}/*** 返回随机数组** @param start 开始值* @param end   结束值* @return*/public static int[] getRangRandom(int start, int end) {return getRangRandom(start, end, end - start + 1);}/*** 返回指定范围指定个数的不重复随机数。** @param start* @param end* @param num* @return*/public static int[] getRangRandom(int start, int end, int num) {int length = end - start + 1;// 参数不合法if (length < 1 || num > length) {return null;} else {int[] numbers = new int[length];int[] result = new int[num];// 循环赋初始值for (int i = 0; i < length; i++) {numbers[i] = i + start;}Random random = new Random();// 取randomMax次数for (int i = 0; i < num; i++) {// 随机获取取数的位置int m = random.nextInt(length - i) + i;result[i] = numbers[m];// 交换位置int temp = numbers[m];numbers[m] = numbers[i];numbers[i] = temp;}return result;}}/** 生成6位随机数验证码*/public static String code() {Set<Integer> set = GetRandomNumber();Iterator<Integer> iterator = set.iterator();String temp = "";while (iterator.hasNext()) {temp += iterator.next();}return temp;}public static Set<Integer> GetRandomNumber() {Set<Integer> set = new HashSet<Integer>();Random random = new Random();while (set.size() < 6) {set.add(random.nextInt(10));}return set;}/*** 获取定长的随机数,只包含数字** @param length 随机数长度* @return*/public static String generateNumberString(int length) {StringBuffer sb = new StringBuffer();Random random = new Random();random.for (int i = 0; i < length; i++) {sb.append(NUMBER_CHAR.charAt(random.nextInt(NUMBER_CHAR.length())));}return sb.toString();}/*** 获取定长的随机数,包含大小写字母** @param length 随机数长度* @return*/public static String generateMixString(int length) {StringBuffer sb = new StringBuffer();Random random = new Random();for (int i = 0; i < length; i++) {sb.append(LETTER_CHAR.charAt(random.nextInt(LETTER_CHAR.length())));}return sb.toString();}/*** 获取定长的随机数,只包含小写字母** @param length 随机数长度* @return*/public static String generateLowerString(int length) {return generateMixString(length).toLowerCase();}/*** 获取定长的随机数,只包含大写字母** @param length 随机数长度* @return*/public static String generateUpperString(int length) {return generateMixString(length).toUpperCase();}static Random rd = new Random();/*** 产生1-9之间随机数*/public static int gtRdNum() {return rd.nextInt(9) + 1; //(0-9内)+1 变相的变成了 1-10内}/*** 产生(+、-、*、/)之间随机运算符*/public static String gtRdEx() {int ex = rd.nextInt(4); // 0+、1-、2*、3/if (ex == 0) {return "+";}if (ex == 1) {return "-";}if (ex == 2) {return "*";}if (ex == 3) {return "/";}return "";}/*** 递归获取整数*/public static String gtZs() {String str = gtEx();if (".0".equals(str.substring(str.length() - 2))) {return str;} else {System.out.println("[递归] " + str);return gtZs();}}/*** 获取3个随机数(1-9),进行随机加减乘数运行(+、-、*、/),返回整数运算结果的表达式*/public static String gtEx() {String str = gtRdNum() + gtRdEx() + gtRdNum() + gtRdEx() + gtRdNum();double ts = 0.00;//优先计算乘除(第一个运算符)if ("*".equals(str.substring(1, 2)) || "/".equals(str.substring(1, 2))) {if ("*".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) * Integer.parseInt(str.substring(2, 3));} else {ts = Integer.parseInt(str.substring(0, 1)) / Integer.parseInt(str.substring(2, 3));}//第二个运算符if ("+".equals(str.substring(3, 4))) {ts = ts + Integer.parseInt(str.substring(4, 5));} else if ("-".equals(str.substring(3, 4))) {ts = ts - Integer.parseInt(str.substring(4, 5));} else if ("*".equals(str.substring(3, 4))) {ts = ts * Integer.parseInt(str.substring(4, 5));} else if ("/".equals(str.substring(3, 4))) {ts = ts / Integer.parseInt(str.substring(4, 5));}} else {//优先计算乘除(第二个运算符)if ("*".equals(str.substring(3, 4)) || "/".equals(str.substring(3, 4))) {if ("*".equals(str.substring(3, 4))) {ts = Integer.parseInt(str.substring(2, 3)) * Integer.parseInt(str.substring(4, 5));} else {ts = Integer.parseInt(str.substring(2, 3)) / Integer.parseInt(str.substring(4, 5));}//第二个运算符if ("+".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) + ts;} else if ("-".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) - ts;} else if ("*".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) * ts;} else if ("/".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) / ts;}} else {//第一个运算符if ("+".equals(str.substring(1, 2))) {ts = Integer.parseInt(str.substring(0, 1)) + Integer.parseInt(str.substring(2, 3));} else {ts = Integer.parseInt(str.substring(0, 1)) - Integer.parseInt(str.substring(2, 3));}//第二个运算符if ("+".equals(str.substring(3, 4))) {ts = ts + Integer.parseInt(str.substring(4, 5));} else {ts = ts - Integer.parseInt(str.substring(4, 5));}}}return str + "=" + ts;}/*** 获取指定长度的16进制字符串.*/public static String randomHexStr(int len) {try {StringBuffer result = new StringBuffer();for (int i = 0; i < len; i++) {//随机生成0-15的数值并转换成16进制result.append(Integer.toHexString(new Random().nextInt(16)));}return result.toString().toUpperCase();} catch (Exception e) {System.out.println("获取16进制字符串异常,返回默认...");return "00CCCC";}}// 随机颜色// Random random = new Random();// int ranColor = 0xff000000 | random.nextInt(0x00ffffff);// holder.iv_dynamic_pic.setBackgroundColor(ranColor);// 随机颜色// Random random = new Random();// int []ranColor ={0x00FFFFCC,0xCCFFFF,0xff000000,0x00ffff00,0x00FFFFCC,0x00ff0000};// 0xff000000 | random.nextInt(0x00ffffff);// int randomcolor =random.nextInt(ranColor.length);// int color = Color.argb(1, random.nextInt(255) / 255, random.nextInt(255) / 255, random.nextInt(255) / 255);// holder.iv_dynamic_pic.setBackgroundColor(color);// 随机颜色// Random random = new Random();// #FFFFF0//int ranColor = 0xff000000 | random.nextInt(0x00ffffff);// int []ranColor ={0xffFFFFF0,0xffF5FFFA, 0xffFFE7B, 0xffF0F8FF, 0xffEECFA1, 0xffF5FFFA,0xffFFF0F5,0xffEEE9E9};// 0xff000000 | random.nextInt(0x00ffffff);   int randomcolor =random.nextInt(ranColor.length);// int color = Color.argb(1, random.nextInt(255) / 255, random.nextInt(255) / 255, random.nextInt(255) / 255);// holder.iv_dynamic_pic.setBackgroundColor(ranColor[randomcolor]);// 随机颜色
//    Random myRandom=new Random();
//    int ranColor = 0xff000000 | mRandom.nextInt(0x00ffffff);
//    Random random = new Random();
//    int[] ranColor = {0xff000000, 0x00ff00ff, 0xff000000, 0x00ffff00, 0x00ffffff, 0x00ff0000};//0xff000000 | random.nextInt(0x00ffffff);
//     for(int i = 0;i<ranColor.length;i++)
//    {
//        holder.iv_dynamic_pic.setBackgroundColor(ranColor[i]);
//    }//    Random random = new Random();
//    int r = random.nextInt(256);
//    int g = random.nextInt(256);
//    int b = random.nextInt(256);
//    textView.setTextColor(Color.rgb(r,g,b));/*** 获取十六进制的颜色代码.例如  "#5A6677"* 分别取R、G、B的随机值,然后加起来即可** @return String*/public static String getRandColor() {String R, G, B;Random random = new Random();R = Integer.toHexString(random.nextInt(256)).toUpperCase();G = Integer.toHexString(random.nextInt(256)).toUpperCase();B = Integer.toHexString(random.nextInt(256)).toUpperCase();R = R.length() == 1 ? "0" + R : R;G = G.length() == 1 ? "0" + G : G;B = B.length() == 1 ? "0" + B : B;return R + G + B;}/*** 获取十六进制的颜色代码.例如  "#6E36B4" , For HTML ,** @return String*/public static String getRandColorCode() {String r, g, b;Random random = new Random();r = Integer.toHexString(random.nextInt(256)).toUpperCase();g = Integer.toHexString(random.nextInt(256)).toUpperCase();b = Integer.toHexString(random.nextInt(256)).toUpperCase();r = r.length() == 1 ? "0" + r : r;g = g.length() == 1 ? "0" + g : g;b = b.length() == 1 ? "0" + b : b;return r + g + b;}private static final int[] prefix = {1, 2, 3, 4, 5, 6, 7, 8, 9};
//    /**
//     * 随机产生最大为18位的long型数据(long型数据的最大值是9223372036854775807,共有19位)   *   * @param digit     *            用户指定随机数据的位数
//     */
//    public static long randomLong(int digit) {
//        if (digit >= 19 || digit <= 0)
//            throw new IllegalArgumentException("digit should between 1 and 18(1<=digit<=18)");
//        String s = RandomUtil.randomNumeric(digit - 1);
//        return Long.parseLong(getPrefix() + s);
//    }private static int getDigit(int max) {return rd.nextInt(max + 1);}/*** 保证第一位不是零  *   * @return*/private static String getPrefix() {return prefix[rd.nextInt(9)] + "";}}

如有错误之处,请多多指教

安卓 即构科技直播使用阿里云鉴权切换流地址相关推荐

  1. 【Android RTMP】RTMP 直播推流 ( 阿里云服务器购买 | 远程服务器控制 | 搭建 RTMP 服务器 | 服务器配置 | 推流软件配置 | 直播软件配置 | 推流直播效果展示 )

    文章目录 安卓直播推流专栏博客总结 一. 阿里云服务器购买 二. 远程服务器控制软件 三. 配置 Ubuntu 服务器 1 . 更新 apt 源 2 . 安装 pcre.OpenSSL.zlib 库 ...

  2. 全民直播牵手阿里云,技术升级触发直播新体验

    免费开通大数据服务:https://www.aliyun.com/product/odps [猎云网(微信:ilieyun)北京]1 月 21 日报道(文 / 王艺多) " 建设一家大型的直 ...

  3. Android(安卓)上传文件到阿里云点播,阿里云点播转码

    Android(安卓)上传文件到阿里云点播,阿里云点播转码 文章目录 Android(安卓)上传文件到阿里云点播,阿里云点播转码 一:登录阿里云点播平台配置添加转码模板组 1:需要什么参数,可自行填写 ...

  4. 阿里云发布首个流式存储与播放解决方案

    本文讲的是阿里云发布首个流式存储与播放解决方案[IT168资讯]9月11日,首个面向视频监控行业的流式存储与播放一体化方案在阿里云官网上线.借助这份技术方案,摄像头接入云端的时间将从一两个月缩短到一周 ...

  5. 使用阿里云的国内镜像仓库地址

    使用阿里云的国内镜像仓库地址,就可以快速的下载需要的文件 修改项目根目录下的文件 build.gradle : buildscript {repositories {maven{ url 'http: ...

  6. 阿里云学生服务器官网地址入口

    1.百度搜索阿里云服务器进入官网注册一个账号,然后实名认证,再学生认证 阿里云学生机官网地址(不用百度搜索-点击直达) 2.官网内搜索"学生机" 3.购买之后登上自己账户,在账户名 ...

  7. 阿里云盘网页版登录地址

    阿里云盘网页版登录地址 https://www.aliyundrive.com/sign/in

  8. 在腾讯云买的域名解析阿里云的服务器的ip地址

    最近有这样一个需求,之前一直在使用腾讯云服务器,在上面买了域名和服务器,服务器到期了但是域名没有到期,现在想转战阿里云,想使用之前在腾讯云购买的域名解析阿里云服务器的IP地址. 这个怎么搞,其实特别简 ...

  9. RTMP推流直播流媒体平台LiveQing鉴权直播拉转直播开放直播支持推送总流量和播放总流量统计

    RTMP推流直播流媒体平台LiveQing鉴权直播拉转直播开放直播支持推送总流量和播放总流量统计 1.鉴权直播中的相关统计 2.拉转直播中的相关统计 3.RTMP推流视频直播和点播流媒体服务 1.鉴权 ...

最新文章

  1. 企业 SOA 设计(1)–ESB 设计
  2. C++11: chrono
  3. layui对json数据的格式要求
  4. 来自Riot 的一份游戏美术教程(四):环境美术
  5. 前端:CSS/14/综合案例:传智首页
  6. python np数组中括号里面‘:n‘与‘n:‘什么意思
  7. C++网络编程学习指南
  8. V-by-One基础知识
  9. 在线制作html个人简历,HTML制作个人简历的简单实现
  10. php redis超卖,PHP用redis解决超卖的问题
  11. srsLTE学习笔记(一)——eNB配置文件enb.conf解析
  12. 乐MAX2 乐视X820_官方线刷包_救砖包_解账户锁
  13. [bzoj 2038 OR 清橙A1206 小Z的袜子]莫队算法
  14. WordPress独立下载页面插件
  15. c 中服务器多次接受消息,c/s模拟高并发服务器端线程池接收问题
  16. 增量式光栅尺与绝对式光栅尺的区别及发展趋势
  17. Ruby on Rails 之旅(七)—— Ruby on Rails 入门(5)
  18. 毕业设计-基于SpringBoot小区物业管理系统
  19. win10任务栏的音量图标变透明且无法点击
  20. DDoS攻击详解ufonet、Mirai分布式拒绝服务攻击工具的介绍

热门文章

  1. java为什么要实例化?
  2. 使用socks5实现简易代理服务器
  3. 排列组合 n个球放入m个盒子m问题 总结
  4. 表哥表妹们,Excel2019 给我们带来好东西了-新函数帮你来了!
  5. iCore-3588Q 8K人工智能核心板
  6. ssm课堂考勤管理毕业设计-附源码191617
  7. js限制Promise“并发”的数量——React项目实践
  8. 阿里云新BGP高防IP详解
  9. html超链接qq临时会话,【一个小功能】点击图标/链接发起QQ临时会话
  10. chrome不走proxifier