关于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;
}
}
最后远程播放音视频流,对于图片先下载到本地再显示
鉴于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的实现请参考这两篇博文: