package com.wasu.cs.module; import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.text.TextUtils; import com.wasu.authsdk.AuthSDK; import com.wasu.authsdk.IAuthInterface; import com.wasu.cs.utils.Base64; import com.wasu.module.http.HttpRequestModule; import com.wasu.module.http.RequestParams; import com.wasu.module.log.WLog; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.TimeZone; import basic.app.TvAppLike; import cn.com.wasu.main.BuildConfig; import cn.com.wasu.main.ChannelFlavor; import cn.com.wasu.main.Common; /** * 顺序异步执行: * getPlayUrl ---> get UTC time ---> getVideUrl ---> getPlayUrl * 外部调用 ---> 获取UTC时间 ---> 获取播放流请求URL ---> 获取并返回播放流(HLS协议) *

* Created by danxingxi on 2016/5/12. * 咪咕认证鉴权和播放流获取模块 */ public class AuthMiguModule { private static final String TAG = "AuthMiguModule"; private static final int CALLBACK = -1; /** * 渠道id **/ private static final String channelid = "0119_61080001-99000-101400000000001"; /** * 合作伙伴id **/ public static final String partnerid = "1002021"; /** * 产品id **/ public static final String productid = "2028593060"; /** * 节目id 由华数WCMS下发,这里为测试用的id **/ public String contentid = "618765126"; /** * 第一步验证权限url **/ public static final String authUrl = "http://aep.cmvideo.cn:1100/streamingapi/getPushUserInfoUrl/v1"; /** * 第二步获取视频流url **/ public static final String videoUrl = "http://aep.cmvideo.cn:1100/pushUserInfo/v1"; private static AuthMiguModule mInstance; /** * 网络请求线程 **/ private HandlerThread requestThread; /** * 网络请求线程的Handler */ private Handler requestThreadHandler; private Context mContext; private AuthMiguCallBack callBack; /*是否停止线程*/ private boolean stopThread = false; /* private的构造函数用于避免外界直接使用new来实例化对象 */ private AuthMiguModule() { } Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == CALLBACK) { if (callBack != null) { if (msg.arg1 == -1) { callBack.result("-1", "获取播放流失败", ""); } else if (msg.arg1 == 0) { callBack.result("0", "SUCCESS", playUrl); } } } } }; /** * 获取单例 * * @return */ public static AuthMiguModule getInstance() { if (null == mInstance) { synchronized (AuthMiguModule.class) { if (null == mInstance) { mInstance = new AuthMiguModule(); } } } return mInstance; } public void init(Context context) { mContext = context; } /** * 获取咪咕视频播放流 对外接口 * * @param mContentid 资产ID * @param authMiguCallBack 获取回调 */ public void getPlayUrl(String mContentid, AuthMiguCallBack authMiguCallBack) { if (mContentid == null || TextUtils.isEmpty(mContentid) || authMiguCallBack == null) { throw new IllegalArgumentException("参数不能为空"); } stopThread = false; this.contentid = mContentid; this.callBack = authMiguCallBack; if (requestThread == null) { requestThread = new HandlerThread("requestThread"); requestThread.start(); } if (requestThreadHandler == null) { requestThreadHandler = new Handler(requestThread.getLooper()); } requestThreadHandler.post(new GetUTCTask()); } /** * 根据contentid请求获取播放流的URL * * @param mContentid 资产id * @param mCreated 实时时间 */ private void getVideoUrl(String mContentid, String mCreated) { /**请求header**/ final Map header = new HashMap(); header.put("User-Agent", "Dalvik/v3.1.31 (Linux; U; Android 1.2.0-R-20140505.1720; WASU_A200f Build/JDQ39)"); header.put("Referer", "http://cms.wasu.tv"); /**app请求时产生的一个随机数**/ String Nonce = "6c2e8a85717641a79c71c13109d6d2e6"; /**随机数生成时间**/ String Created = mCreated; String strTemp = Nonce + Created + Common.miguSecretKey; String passwordDigest = string2SHA2Base64(strTemp); /**header部分Authorization参数**/ String Authorization = "WSSE realm=\"SDP\", profile=\"UsernameToken\", type=\"AppKey\""; /**header部分X-WSSE参数**/ StringBuffer sbXWSSE = new StringBuffer(); sbXWSSE.append(" UsernameToken "); sbXWSSE.append(" Username = " + "\"" + Common.miguAppKey + "\","); sbXWSSE.append(" PasswordDigest=" + "\"" + passwordDigest + "\","); sbXWSSE.append(" Nonce=" + "\"" + Nonce + "\","); sbXWSSE.append(" Created=" + "\"" + Created + "\""); WLog.d(TAG, "X-WSSE-->" + sbXWSSE.toString()); header.put("Host", "aep.sdp.com"); header.put("Authorization", Authorization); header.put("X-WSSE", sbXWSSE.toString()); WLog.d(TAG, "header-->" + header.toString()); /**组装请求body**/ String requestBody = "{\"partnerid\":\"" + partnerid + "\",\"channelid\":\"" + channelid + "\",\"contentid\":\"" + contentid + "\",\"productid\":\"" + productid + "\",\"ratelevel\":\"3\",\"nettype\":\"4\"}"; RequestParams params = new RequestParams(RequestParams.Method.POST, authUrl, header, requestBody.getBytes(), new RequestParams.RequestListener() { @Override public boolean onResponse(int i, String s, int i1, Object o) { if (s != null) { try { JSONObject jsonObject = new JSONObject(s); String resultcode = jsonObject.optString("resultcode"); String desc = jsonObject.optString("desc", "error"); String url = jsonObject.optString("playurl", ""); if (TextUtils.isEmpty(url)) { if (callBack != null) { WLog.e(TAG, "咪咕视频流获取失败,请检查参数后重试!"); callBack.result(resultcode, desc, ""); } } else { requestThreadHandler.post(new GetPlayerUrlTask(url, header)); } } catch (JSONException e) { e.printStackTrace(); } } else { //生成异常并抛出 throw new IllegalArgumentException("请检查请求参数是否正确!"); } return false; } }); HttpRequestModule.getInstance().addTask(params); } /** * 获取视频播放流 */ String playUrl; class GetPlayerUrlTask implements Runnable { String url; Map header; public GetPlayerUrlTask(String mUrl, Map mHeader) { WLog.d(TAG, "IP--->" + TvAppLike.getIpAddress()); String tvid = AuthSDK.getInstance().getValue(IAuthInterface.KEY_TVID); WLog.d(TAG, "TVID--->" + tvid); this.url = mUrl + "&Id_type=2" + "&userid=" + tvid + "&ipaddress=" + TvAppLike.getIpAddress(); this.header = mHeader; } @Override public void run() { if (!stopThread) { try { URL url2 = new URL(url); HttpURLConnection conn = (HttpURLConnection) url2.openConnection();//生成连接对象 conn.setRequestProperty("Host", header.get("Host")); conn.setRequestProperty("Authorization", header.get("Authorization")); conn.setRequestProperty("X-WSSE", header.get("X-WSSE")); conn.setRequestMethod("GET"); /**禁止重定向**/ conn.setInstanceFollowRedirects(false); conn.connect(); //发出连接 playUrl = conn.getHeaderField("Location"); Message msg = new Message(); msg.what = CALLBACK; if (playUrl != null && !TextUtils.isEmpty(playUrl)) { WLog.d(TAG, "Location-->" + playUrl); /*if (callBack != null) { callBack.result("0", "SUCCESS", playUrl); }*/ msg.arg1 = 0; } else { /*callBack.result("-1", "获取播放流失败", "");*/ msg.arg1 = -1; } handler.sendMessage(msg); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 从网络获取UTC标准时间 */ class GetUTCTask implements Runnable { public GetUTCTask() { } @Override public void run() { if (!stopThread) { URL url = null;//取得资源对象 String Created = null; try { url = new URL((BuildConfig.FLAVOR.equalsIgnoreCase(ChannelFlavor.LETV_MARKET) ? "https://letv-ds.wasu.tv" : "http://tang-ds.cs.wasu.tv") + "/now"); HttpURLConnection uc = (HttpURLConnection) url.openConnection();//生成连接对象 uc.connect(); //发出连接 InputStream inStrm = uc.getInputStream(); String result = getEncodedData(inStrm); WLog.d(TAG, "result-->" + result); if (result == null || result.equals("")) { if (callBack != null) { callBack.result("-1", "获取UTC时间失败", ""); } return; } inStrm.close(); String strTime = result.split("\\.")[0] + result.split("\\.")[1]; WLog.d(TAG, "strTime-->" + strTime); long ld = Long.parseLong(strTime.trim()); // long ld = uc.getDate(); //取得网站日期时间 Date date = new Date(ld); //转换为标准时间对象 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); format.setTimeZone(TimeZone.getTimeZone("UTC")); Created = format.format(date); if (Created != null) { WLog.d(TAG, "UTC time:" + Created); getVideoUrl(contentid, Created); } else { if (callBack != null) { callBack.result("-1", "获取UTC时间失败", ""); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 此方法用于从输入流中获取数据 * * @param is 接收来自客户端socket对应的输入流 * @return 返回endodedData * @throws IOException 抛出的异常,统一在run函数处理 */ private String getEncodedData(InputStream is) throws IOException { byte[] maxBuffer = new byte[1024 * 64]; int length = 0; int lengthTemp = 0; while (-1 != (lengthTemp = is.read(maxBuffer))) { length += lengthTemp; if (length >= 1024 * 64) { WLog.d(TAG, "读入的数据超过1024 * 64"); break; } } byte[] endodedData = new byte[length]; System.arraycopy(maxBuffer, 0, endodedData, 0, length); return new String(endodedData); } /** * 请求咪咕视频播放流回调接口 */ public interface AuthMiguCallBack { /** * @param resultcode 结果码,0表示成功,其它表示失败。 * @param desc 结果描述信息。 * @param playUrl 播放流,请求成功后返回播放流,请求失败返回""空字符串 */ public void result(String resultcode, String desc, String playUrl); } /** * 移除回调监听 */ public void removeCallBackLisenter(){ if(callBack!=null){ callBack = null; } } /** * 把一个字符串先进行HA256加密,然后转换为Base64的格式 * * @return */ private String string2SHA2Base64(String str) { if (str != null) { byte[] passwd = str.getBytes(); MessageDigest alga = null; try { alga = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } byte[] strSha = alga.digest(passwd); return Base64.encodeToString(strSha, false); } else { return null; } } /** * app退出后消废线程 */ public void destory() { stopThread = true; if (requestThread != null) { requestThread.quit(); requestThread.interrupt(); } WLog.d(TAG, "destory authmigumodule"); } }