关于dlna的介绍就不多说了,具体可以看这篇博文http://blog.csdn.net/geniuseoe2012/article/details/8230877
或者问百度谷歌
Dlna通讯采用upnp协议,详看这篇博文:http://blog.csdn.net/geniuseoe2012/article/details/8132719
或者问百度谷歌
更多开发资料稍后再末尾附上
我们在做DLNA开发的时候都是用现有的upnp开源框架,upnp官网地址是:
http://upnp.org/
相关SDK地址为http://upnp.org/sdcps-and-certification/resources/sdks/
本文阐述的是基于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; } }
最后远程播放音视频流,对于图片先下载到本地再显示
具体详细看demo吧
apk下载地址:
https://raw.githubusercontent.com/geniusgithub/MediaPlayer/master/storage/mediaPlayer.apk
附上工程链接:
http://download.csdn.net/detail/geniuseoe2012/4970066(旧版)
github下载链接:
https://github.com/geniusgithub/MediaPlayer
PS:建议大家上github下载,这样可以更新到最新的代码
文档链接:
http://download.csdn.net/detail/geniuseoe2012/4969961
关于DMR和DMS的实现请参考这两篇博文:
基于Platinum库的DMR实现(android)
more brilliant,Please pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012