今日來公司的項目要求接入視頻,如是根據(jù)需求對網(wǎng)易云視頻袒炉,anychat旁理,騰訊云視頻進行了技術(shù)調(diào)研,因為基于響應(yīng)時間我磁,降噪等諸多效果的測試孽文,考慮,最終選擇了騰訊云視頻夺艰。因為騰訊云視頻剛剛推出不久芋哭,demo,文檔寫的不是太集中郁副,故在集成時出現(xiàn)多個坑】簦現(xiàn)對集成步驟做一下簡單歸納:
1.集成播放列表:
直播播放列表需要開通查詢統(tǒng)計信息(Beta)
該接口的相關(guān)文檔地址:https://www.qcloud.com/document/product/267/6110。
這個接口需要提交工單才會開通霞势,默認是不開通的烹植。
開通后會產(chǎn)生appid和推流鑒權(quán)key (這個東西會在生成sign時使用,sign生成的規(guī)則是推流鑒權(quán)key直接拼接過期時間)
String playUrl ="http://statcgi.video.qcloud.com/common_access?"+
"cmd=1251175924&interface=Get_LiveStat"+
"&Param.n.page_no=1"+"&Param.n.page_size=20"+
"&t="+time+"&sign="+signValue;
cmd后面需要根據(jù)你自己的需要更換成自己的appid愕贡,我的核心代碼如下:
sign是推流鑒權(quán)key直接拼接過期時間進行md5生成:
md5工具類:
public class MD5Utils {
static String MD5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
result = buf.toString();
System.out.println("MD5(" + sourceStr + ",32) = " + result);
System.out.println("MD5(" + sourceStr + ",16) = " + buf.toString().substring(8, 24));
} catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
return result;
}
}
過期時間戳轉(zhuǎn)化工具類:
public class TimeUtils {
//日期轉(zhuǎn)化為時間戳
public static String dateToStamp(String s) throws Exception{
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(s);
long ts = (date.getTime()+300000)/1000;
res = String.valueOf(ts);
return res;
}
public static String stampToDate(String s){
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long lt = new Long(s);
Date date = new Date(lt);
res = simpleDateFormat.format(date);
return res;
}
}
查詢時間列表的核心邏輯:
public static void main(String[] args) throws Exception {
OkHttpClient client = new OkHttpClient();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time="";
try {
//獲取當前日期的時間戳
time = TimeUtils.dateToStamp(sdf.format(new Date()));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(time);
//推流鑒權(quán)key
String key="7407d5829e270dd92661ffcf06169bcf";
String signValue=MD5Utils.MD5(key+time);
String playUrl ="http://statcgi.video.qcloud.com/common_access?"+
"cmd=1251175924&interface=Get_LiveStat"+
"&Param.n.page_no=1"+"&Param.n.page_size=20"+
"&t="+time+"&sign="+signValue;
System.out.println(playUrl);
Request request = new Request.Builder().url(playUrl).build();
Response response = client.newCall(request).execute();
if (response.isSuccessful()) {
System.out.println(response.body().string());
} else {
throw new IOException("Unexpected code " + response);
}
獲取成功會如下顯示:
{"errmsg":"has no stream is living","message":"has no stream is living","output":null,"ret":0,"retcode":0}
如果有推流的話是:
{"errmsg":"","message":"","output":{"stream_count":1,"stream_info":[{"bandwidth":0.0,"client_ip":"125.46.216.125","flr":1.0,"fps":0,"online":0,"server_ip":"123.138.162.79","speed":0,"stream_name":"8818_b262bb2709","time":"2017-04-13 11:04:13"}],"total_bandwidth":0.0,"total_online":0},"ret":0,"retcode":0}
返回的是json這個由你自己的業(yè)務(wù)需求定
2.登錄用戶體系:
騰訊提供了兩種賬戶體系事秀,一種是托管的浓恶,一種是獨立的省核,托管的是需要注冊登錄騰訊tls體系豌熄,賬戶管理交由騰訊,獨立的就是不對現(xiàn)有用戶體系有影響憨琳,而是在需要調(diào)用騰訊的時候诫钓,去到騰訊上驗證一下,就像融云的token一樣篙螟。
2.1托管體系注冊
android:
accountHelper.TLSStrAccReg("xtfgq", "12345678", new TLSStrAccRegListener() {
@Override
public void OnStrAccRegSuccess(TLSUserInfo tlsUserInfo) {
// tlsUserInfo.
Log.e("vv","111");
}
@Override
public void OnStrAccRegFail(TLSErrInfo tlsErrInfo) {
Log.e("vv","222");
}
@Override
public void OnStrAccRegTimeout(TLSErrInfo tlsErrInfo) {
Log.e("vv","333");
}
});
需要在oncreate中完成初始化菌湃,這里面的appid與前面的不同,(特別注意)遍略,這個是開通騰訊云云通信里面的那個千萬不要搞錯惧所。
accountHelper=TLSAccountHelper.getInstance().init(SettingActivity.this,1251175924,12001,"1.0.1");
托管登錄:
loginHelper = TLSLoginHelper.getInstance()
.init(getApplicationContext(), 11270, 12001, "1.0.1");
loginHelper.TLSPwdLogin("用戶帳號", "用戶密碼".getBytes(), new TLSPwdLoginListener(){
@Override
public void OnPwdLoginSuccess(TLSUserInfo tlsUserInfo) {
final TIMUser user = new TIMUser();
user.setIdentifier("用戶帳號");
user.setAccountType("12001");
user.setAppIdAt3rd(String.valueOf(Constants.sdkAppId));
TIMManager.getInstance().login(
Constants.sdkAppId, //sdkAppId骤坐,由騰訊分配
user,
loginHelper.getUserSig("用戶帳號"), //用戶帳號簽名,由私鑰加密獲得下愈,具體請參考文檔
new TIMCallBack() {//回調(diào)接口
@Override
public void onSuccess() {//登錄成功
Log.e("vvv","succuss");
//根據(jù)邏輯跳到不同界面
}
@Override
public void onError(int code, String desc) {//登錄失敗
//錯誤碼code和錯誤描述desc纽绍,可用于定位請求失敗原因
//錯誤碼code含義請參見錯誤碼表
Log.e("eer","eeror");
}
});
}
@Override
public void OnPwdLoginReaskImgcodeSuccess(byte[] bytes) {
}
@Override
public void OnPwdLoginNeedImgcode(byte[] bytes, TLSErrInfo tlsErrInfo) {
}
@Override
public void OnPwdLoginFail(TLSErrInfo tlsErrInfo) {
}
@Override
public void OnPwdLoginTimeout(TLSErrInfo tlsErrInfo) {
}
});
2.2 獨立模式,即usersinger需從自己的服務(wù)器獲得势似,可參考usersinger生成文檔:https://www.qcloud.com/document/product/269/1510拌夏。
客戶端不需要像剛才那樣先登陸tls,然后在成功回調(diào)里調(diào)用 TIMManager.getInstance().login履因,而是直接從自己的服務(wù)取得usersinger障簿,
代碼如下:
final TIMUser user = new TIMUser();
user.setIdentifier(userid);
user.setAccountType("12001");
user.setAppIdAt3rd(String.valueOf(Constants.sdkAppId));
TIMManager.getInstance().login(
Constants.sdkAppId, //sdkAppId,由騰訊分配
user,
sig, //用戶帳號簽名搓逾,由私鑰加密獲得卷谈,具體請參考文檔
new TIMCallBack() {//回調(diào)接口
@Override
public void onSuccess() {//登錄成功
Log.e("vvv","succuss");
}
@Override
public void onError(int code, String desc) {//登錄失敗
//錯誤碼code和錯誤描述desc杯拐,可用于定位請求失敗原因
//錯誤碼code含義請參見錯誤碼表
Log.e("eer","eeror");
}
});
最后霞篡,不管哪中模式,都需要調(diào)用修改昵稱的騰訊接口端逼,否則會出現(xiàn)發(fā)彈幕上用戶名變成數(shù)字方面的問題朗兵。
public void setMyNickName(String nickName){
TIMFriendshipManager.getInstance().setNickName(nickName, new TIMCallBack() {
@Override
public void onError(int i, String s) {
}
@Override
public void onSuccess() {
}
});
}
3.加入聊天室:
private void joinIMChatRoom(final String chatRoomId) {
mGroupConversation = TIMManager.getInstance().getConversation(TIMConversationType.Group, chatRoomId);
TIMGroupManager.getInstance().applyJoinGroup(chatRoomId, Constants.APPLY_CHATROOM + chatRoomId, new TIMCallBack() {
@Override
public void onError(int i, String s) {
//已經(jīng)在是成員了
if (i == Constants.IS_ALREADY_MEMBER) {
initTIMListener(chatRoomId);
}
}
@Override
public void onSuccess() {
initTIMListener(chatRoomId);
}
});
}
消息監(jiān)聽:
public void initTIMListener(String chatRoomId) {
mGroupConversation = TIMManager.getInstance().getConversation(TIMConversationType.Group, chatRoomId);
TIMManager.getInstance().addMessageListener(msgListener);
}
/**
* 群消息回調(diào)
*/
private TIMMessageListener msgListener = new TIMMessageListener() {
@Override
public boolean onNewMessages(List<TIMMessage> list) {
//SxbLog.d(TAG, "onNewMessages readMessage " + list.size());
//解析TIM推送消息
parseIMMessage(list);
return false;
}
};
/**
* 解析消息回調(diào)
*
* @param list 消息列表
*/
private void parseIMMessage(List<TIMMessage> list) {
List<TIMMessage> tlist = list;
if (tlist.size() > 0) {
if (mGroupConversation != null)
mGroupConversation.setReadMessage(tlist.get(0));
}
for (int i = tlist.size() - 1; i >= 0; i--) {
TIMMessage currMsg = tlist.get(i);
for (int j = 0; j < currMsg.getElementCount(); j++) {
if (currMsg.getElement(j) == null)
continue;
TIMElem elem = currMsg.getElement(j);
TIMElemType type = elem.getType();
//系統(tǒng)消息
if (type == TIMElemType.GroupSystem) {
if (TIMGroupSystemElemType.TIM_GROUP_SYSTEM_DELETE_GROUP_TYPE == ((TIMGroupSystemElem) elem).getSubtype()) {
}
}
//最后處理文本消息
if (type == TIMElemType.Text) {
if (currMsg.isSelf()) {
handleTextMessage(elem, name);
} else {
TIMUserProfile sendUser = currMsg.getSenderProfile();
String nick="";
if(sendUser!=null){
nick=sendUser.getNickName();
}else{
nick=currMsg.getSender();
}
handleTextMessage(elem,nick);
}
}
}
}
}
發(fā)送消息:
mGroupConversation = TIMManager.getInstance().getConversation(TIMConversationType.Group, groupId);
mGroupConversation.sendMessage(msg, new TIMValueCallBack<TIMMessage>() {//發(fā)送消息回調(diào)
@Override
public void onError(int code, String desc) {//發(fā)送消息失敗
//錯誤碼code和錯誤描述desc,可用于定位請求失敗原因
//錯誤碼code含義請參見錯誤碼表
if (code == 85) { //消息體太長
if (null != getActivity()) {
Toast.makeText(getActivity(), "輸入內(nèi)容太長", Toast.LENGTH_SHORT).show();
}
} else if (code == 6011) {//群主不存在
if (null != getActivity()) {
Toast.makeText(getActivity(), "群主不存在", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onSuccess(TIMMessage timMessage) {//發(fā)送消息成功
mBoolRefreshLock=false;
for (int j = 0; j < timMessage.getElementCount(); j++) {
TIMElem elem = (TIMElem) timMessage.getElement(0);
if (timMessage.isSelf()) {
handleTextMessage(elem, name);
} else {
TIMUserProfile sendUser = timMessage.getSenderProfile();
String name;
if (sendUser != null) {
name = sendUser.getNickName();
}
else {
name = timMessage.getSender();
}
//String sendId = timMessage.getSender();
handleTextMessage(elem,name);
}
}
}
});
}
處理消息文本:
/**
* 處理文本消息解析
*
* @param elem
* @param name
*/
private void handleTextMessage(TIMElem elem, String name) {
TIMTextElem textElem = (TIMTextElem) elem;
if (textElem.getText() != null) {
refreshTextListView(name, textElem.getText(), Constants.TEXT_TYPE);
}
}
/**
* 消息刷新顯示
*
* @param name 發(fā)送者
* @param context 內(nèi)容
* @param type 類型 (上線線消息和 聊天消息)
*/
public void refreshTextListView(String name, String context, int type) {
ChatEntity entity = new ChatEntity();
entity.setSenderName(name);
entity.setContext(context);
entity.setType(type);
notifyRefreshListView(entity);
mListViewMsgItems.setVisibility(View.VISIBLE);
if (mListViewMsgItems.getCount() > 1) {
if (true)
mListViewMsgItems.setSelection(0);
else
mListViewMsgItems.setSelection(mListViewMsgItems.getCount() - 1);
}
}
最后實現(xiàn)效果如圖: