基于CyberGarage库的dlna开发(android)

关于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库的DMS实现(android)

基于Platinum库的DMR实现(android)
 
more brilliant,Please pay attention to my CSDN blog -->http://blog.csdn.net/geniuseoe2012

posted @ 2013-01-07 16:25  蓝斯老师  阅读(2015)  评论(0编辑  收藏  举报