基於CyberGarage庫的dlna開發(android)
阿新 • • 發佈:2019-01-30
或者問百度谷歌
或者問百度谷歌
更多開發資料稍後再末尾附上
我們在做DLNA開發的時候都是用現有的upnp開源框架,upnp官網地址是:
http://upnp.org/
本文闡述的是基於CyberGarage庫的DMP開發,ControlPoint是核心類
關鍵方法有search();start(),stop(),addDeviceChangeListener(DeviceChangeListener listener)
見名知意
先上幾張效果圖:
程式碼裡將ControlPoint委託給service,外部通過與service的互動來執行相關操作
public class DlnaService extends Service implements IBaseEngine, DeviceChangeListener, ControlCenterWorkThread.ISearchDeviceListener{ private static final CommonLog log = LogFactory.createLog(); public static final String SEARCH_DEVICES = "com.geniusgithub.allshare.search_device"; public static final String RESET_SEARCH_DEVICES = "com.geniusgithub.allshare.reset_search_device"; private static final int NETWORK_CHANGE = 0x0001; private boolean firstReceiveNetworkChangeBR = true; private NetworkStatusChangeBR mNetworkStatusChangeBR; private ControlPoint mControlPoint; private ControlCenterWorkThread mCenterWorkThread; private AllShareProxy mAllShareProxy; private Handler mHandler; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); log.e("DlnaService onCreate"); init(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null && intent.getAction() != null){ String action = intent.getAction(); if (DlnaService.SEARCH_DEVICES.equals(action)) { startEngine(); }else if (DlnaService.RESET_SEARCH_DEVICES.equals(action)){ restartEngine(); } }else{ log.e("intent = " + intent); } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { log.e("DlnaService onDestroy"); unInit(); super.onDestroy(); } private void init(){ mAllShareProxy = AllShareProxy.getInstance(this); mControlPoint = new ControlPoint(); AllShareApplication.getInstance().setControlPoint(mControlPoint); mControlPoint.addDeviceChangeListener(this); mControlPoint.addSearchResponseListener(new SearchResponseListener() { public void deviceSearchResponseReceived(SSDPPacket ssdpPacket) { } }); mCenterWorkThread = new ControlCenterWorkThread(this, mControlPoint); mCenterWorkThread.setSearchListener(this); mHandler = new Handler(){ public void handleMessage(Message msg) { switch(msg.what){ case NETWORK_CHANGE: mAllShareProxy.resetSearch(); break; } } }; registerNetworkStatusBR(); } private void unInit(){ unRegisterNetworkStatusBR(); AllShareApplication.getInstance().setControlPoint(null); mCenterWorkThread.setSearchListener(null); mCenterWorkThread.exit(); } @Override public boolean startEngine() { awakeWorkThread(); return true; } @Override public boolean stopEngine() { exitWorkThread(); return true; } @Override public boolean restartEngine() { mCenterWorkThread.reset(); return true; } @Override public void deviceAdded(Device dev) { mAllShareProxy.addDevice(dev); } @Override public void deviceRemoved(Device dev) { mAllShareProxy.removeDevice(dev); } private void awakeWorkThread(){ if (mCenterWorkThread.isAlive()){ mCenterWorkThread.awakeThread(); }else{ mCenterWorkThread.start(); } } private void exitWorkThread(){ if (mCenterWorkThread != null && mCenterWorkThread.isAlive()){ mCenterWorkThread.exit(); long time1 = System.currentTimeMillis(); while(mCenterWorkThread.isAlive()){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } long time2 = System.currentTimeMillis(); log.e("exitCenterWorkThread cost time:" + (time2 - time1)); mCenterWorkThread = null; } } @Override public void onSearchComplete(boolean searchSuccess) { if (!searchSuccess){ sendSearchDeviceFailBrocast(this); } } public static final String SEARCH_DEVICES_FAIL = "com.geniusgithub.allshare.search_devices_fail"; public static void sendSearchDeviceFailBrocast(Context context){ log.e("sendSearchDeviceFailBrocast"); Intent intent = new Intent(SEARCH_DEVICES_FAIL); context.sendBroadcast(intent); } private class NetworkStatusChangeBR extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { if (intent != null){ String action = intent.getAction(); if (action != null){ if (action.equalsIgnoreCase(ConnectivityManager.CONNECTIVITY_ACTION)){ sendNetworkChangeMessage(); } } } } } private void registerNetworkStatusBR(){ if (mNetworkStatusChangeBR == null){ mNetworkStatusChangeBR = new NetworkStatusChangeBR(); registerReceiver(mNetworkStatusChangeBR, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); } } private void unRegisterNetworkStatusBR(){ if (mNetworkStatusChangeBR != null){ unregisterReceiver(mNetworkStatusChangeBR); } } private void sendNetworkChangeMessage(){ if (firstReceiveNetworkChangeBR){ log.e("first receive the NetworkChangeMessage, so drop it..."); firstReceiveNetworkChangeBR = false; return ; } mHandler.removeMessages(NETWORK_CHANGE); mHandler.sendEmptyMessageDelayed(NETWORK_CHANGE, 500); } }
Service內部開啟一個執行緒來執行控制點的搜尋,停止等命令
public class ControlCenterWorkThread extends Thread{ private static final CommonLog log = LogFactory.createLog(); private static final int REFRESH_DEVICES_INTERVAL = 30 * 1000; public static interface ISearchDeviceListener{ public void onSearchComplete(boolean searchSuccess); } private ControlPoint mCP = null; private Context mContext = null; private boolean mStartComplete = false; private boolean mIsExit = false; private ISearchDeviceListener mSearchDeviceListener; public ControlCenterWorkThread(Context context, ControlPoint controlPoint){ mContext = context; mCP = controlPoint; } public void setCompleteFlag(boolean flag){ mStartComplete = flag; } public void setSearchListener(ISearchDeviceListener listener){ mSearchDeviceListener = listener; } public void awakeThread(){ synchronized (this) { notifyAll(); } } public void reset(){ setCompleteFlag(false); awakeThread(); } public void exit(){ mIsExit = true; awakeThread(); } @Override public void run() { log.e("ControlCenterWorkThread run..."); while(true) { if (mIsExit){ mCP.stop(); break; } refreshDevices(); synchronized(this) { try { wait(REFRESH_DEVICES_INTERVAL); } catch(Exception e) { e.printStackTrace(); } } } log.e("ControlCenterWorkThread over..."); } private void refreshDevices(){ log.e("refreshDevices..."); if (!CommonUtil.checkNetworkState(mContext)){ return ; } try { if (mStartComplete){ boolean searchRet = mCP.search(); log.e("mCP.search() ret = " + searchRet); if (mSearchDeviceListener != null){ mSearchDeviceListener.onSearchComplete(searchRet); } }else{ boolean startRet = mCP.start(); log.e("mCP.start() ret = " + startRet); if (startRet){ mStartComplete = true; } } } catch (Exception e) { e.printStackTrace(); } } }
外部類過濾DLNA裝置資訊保留DMS裝置列表,併發出更新廣播通知UI
public class AllShareProxy implements IDeviceOperator, IDeviceOperator.IDMSDeviceOperator{ private static final CommonLog log = LogFactory.createLog(); private static AllShareProxy instance; private Context mContext; private AbstractMediaMng dmsMediaMng; private AllShareProxy(Context context) { mContext = context; dmsMediaMng = new MediaServerMng(context); } public static synchronized AllShareProxy getInstance(Context context) { if (instance == null){ instance = new AllShareProxy(context); } return instance; } public void startSearch(){ mContext.startService(new Intent(DlnaService.SEARCH_DEVICES)); } public void resetSearch(){ mContext.startService(new Intent(DlnaService.RESET_SEARCH_DEVICES)); clearDevice(); } public void exitSearch(){ mContext.stopService(new Intent(mContext, DlnaService.class)); clearDevice(); } @Override public void addDevice(Device d) { if (UpnpUtil.isMediaServerDevice(d)){ dmsMediaMng.addDevice(d); } } @Override public void removeDevice(Device d) { if (UpnpUtil.isMediaServerDevice(d)){ dmsMediaMng.removeDevice(d); } } @Override public void clearDevice() { dmsMediaMng.clear(); } @Override public List<Device> getDMSDeviceList() { return dmsMediaMng.getDeviceList(); } @Override public void setDMSSelectedDevice(Device selectedDevice) { dmsMediaMng.setSelectedDevice(selectedDevice); } @Override public Device getDMSSelectedDevice() { return dmsMediaMng.getSelectedDevice(); } }
選中瀏覽裝置後通過代理放送控制命令獲取xml檔案描述並解析得到多媒體檔案資訊
public class BrowseDMSProxy {
public static interface BrowseRequestCallback
{
public void onGetItems(final List<MediaItem> list);
}
private static final CommonLog log = LogFactory.createLog();
public static void syncGetDirectory(final Context context, final BrowseRequestCallback callback) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
List<MediaItem> list = null;
try {
list = getDirectory(context);
} catch (Exception e) {
e.printStackTrace();
}
if (callback != null){
callback.onGetItems(list);
}
}
});
thread.start();
}
public static void syncGetItems(final Context context, final String id,final BrowseRequestCallback callback) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
List<MediaItem> list = null;
try {
list = getItems(context, id);
} catch (Exception e) {
e.printStackTrace();
}
if (callback != null){
callback.onGetItems(list);
}
}
});
thread.start();
}
public static List<MediaItem> getDirectory(Context context) throws Exception {
Device selDevice = AllShareProxy.getInstance(context).getDMSSelectedDevice();
if (selDevice == null) {
log.e("no selDevice!!!");
return null;
}
// Node selDevNode = selDevice.getDeviceNode();
// if (selDevNode != null){
// selDevNode.print();
// }
org.cybergarage.upnp.Service service = selDevice
.getService("urn:schemas-upnp-org:service:ContentDirectory:1");
if (service == null)
{
log.e("no service for ContentDirectory!!!");
return null;
}
// Node serverNode = service.getServiceNode();
// if (serverNode != null){
// serverNode.print();
// }
Action action = service.getAction("Browse");
if(action == null)
{
log.e("action for Browse is null!!!");
return null;
}
ArgumentList argumentList = action.getArgumentList();
argumentList.getArgument("ObjectID").setValue(0);
argumentList.getArgument("BrowseFlag").setValue("BrowseDirectChildren");
argumentList.getArgument("StartingIndex").setValue("0");
argumentList.getArgument("RequestedCount").setValue("0");
argumentList.getArgument("Filter").setValue("*");
argumentList.getArgument("SortCriteria").setValue("");
ArgumentList actionInputArgList = action.getInputArgumentList();
// int size = actionInputArgList.size();
// for(int i = 0; i < size; i++){
// Argument argument = (Argument) (actionInputArgList.get(i));
// argument.getArgumentNode().print();
// }
if (action.postControlAction()) {
ArgumentList outArgList = action.getOutputArgumentList();
Argument result = outArgList.getArgument("Result");
log.d("result value = \n" + result.getValue());
List<MediaItem> items = ParseUtil.parseResult(result);
return items;
} else {
UPnPStatus err = action.getControlStatus();
log.e("Error Code = " + err.getCode());
log.e("Error Desc = " + err.getDescription());
}
return null;
}
public static List<MediaItem> getItems(Context context, String id) throws Exception{
Device selDevice = AllShareProxy.getInstance(context).getDMSSelectedDevice();
if (selDevice == null) {
log.e("no selDevice!!!");
return null;
}
org.cybergarage.upnp.Service service = selDevice
.getService("urn:schemas-upnp-org:service:ContentDirectory:1");
if (selDevice == null)
{
log.e("no service for ContentDirectory!!!");
return null;
}
Action action = service.getAction("Browse");
if(action == null)
{
log.e("action for Browse is null");
return null;
}
// action.getActionNode().print();
ArgumentList argumentList = action.getArgumentList();
argumentList.getArgument("ObjectID").setValue(id);
argumentList.getArgument("BrowseFlag").setValue("BrowseDirectChildren");
argumentList.getArgument("StartingIndex").setValue("0");
argumentList.getArgument("RequestedCount").setValue("0");
argumentList.getArgument("Filter").setValue("*");
argumentList.getArgument("SortCriteria").setValue("");
if (action.postControlAction()) {
ArgumentList outArgList = action.getOutputArgumentList();
Argument result = outArgList.getArgument("Result");
log.d("result value = \n" + result.getValue());
List<MediaItem> items = ParseUtil.parseResult(result);
return items;
} else {
UPnPStatus err = action.getControlStatus();
System.out.println("Error Code = " + err.getCode());
System.out.println("Error Desc = " + err.getDescription());
}
return null;
}
}
最後遠端播放音視訊流,對於圖片先下載到本地再顯示
鑑於CyberGarage庫有些許bug,工程裡依賴的jar包dlna_framework.jar是經過修改過的
原版cyber庫下載地址:https://github.com/cybergarage/CyberLink4Java
dlna_framework下載地址: https://github.com/geniusgithub/dlna_framework
具體詳細看demo吧
附上工程連結:
http://download.csdn.net/detail/geniuseoe2012/4970066(舊版)
github下載連結:
https://github.com/geniusgithub/MediaPlayer
PS:建議大家上github下載,這樣可以更新到最新的程式碼
文件連結:
http://download.csdn.net/detail/geniuseoe2012/4969961
關於DMR和DMS的實現請參考這兩篇博文: