免费高清特黄a大片,九一h片在线免费看,a免费国产一级特黄aa大,国产精品国产主播在线观看,成人精品一区久久久久,一级特黄aa大片,俄罗斯无遮挡一级毛片

分享

minidlna源碼初探(一)

 昵稱7324690 2014-08-07

前言

minidlna是一種優(yōu)秀的DLNA解決方案。本文將涉及minidlna的upnp以及目錄管理的代碼。minidlna的下載鏈接如下:

  1. wget http://netcologne.dl./project/minidlna/minidlna/1.1.0/minidlna-1.1.0.tar.gz  

控制點(diǎn)使用VLC Media Player,下載鏈接如下:

  1. http://www./vlc/index.zh.html#download  

關(guān)于minidlna的配置,網(wǎng)上已有很多介紹,在這里就不復(fù)述了。

本文中一些關(guān)于UPNP的理論問題參考了IBM的相關(guān)介紹:

UPnP協(xié)議編程實(shí)踐(1)

UPnP協(xié)議編程實(shí)踐(2)


正文

在minidlna,本文描述的主要內(nèi)容分布在minidlna.c(主程序),inotify.c(目錄管理),upnphttp.c(upnp通信),minissdp.c(ssdp設(shè)備發(fā)現(xiàn)相關(guān)),upnpsoap.c(soap設(shè)備控制相關(guān))等。

照例從main函數(shù)進(jìn)入,這個在~/minidlna.c下。程序首先執(zhí)行了init,open_db等方法:

  1. ret = init(argc, argv);   //這里主要分析配置文件以及命令中的選項(xiàng)  
  2. //......  
  3. LIST_INIT(&upnphttphead); //初始化upnphttphead  
  4. ret = open_db(NULL);  //新建sqlite3 db  
  5. //......  
  6. check_db(db, ret, &scanner_pid);  

新建連接用socket:

  1. sudp = OpenAndConfSSDPReceiveSocket();   //新建一個socket,執(zhí)行setsockopt并且bind之, sudp就是返回的socket , 端口號SSDP_PORT(1900), 用于接受控制點(diǎn)信息  
  2. if (sudp < 0)  
  3. {  
  4.     DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n");  
  5.     if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0)  
  6.         DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING");  
  7. }  
  8. /* open socket for HTTP connections. Listen on the 1st LAN address */  
  9. shttpl = OpenAndConfHTTPSocket(runtime_vars.port);  //新建一個socket,執(zhí)行setsockopt并且bind之, shttpl就是返回的socket , 端口號runtime_vars.port = 8200 , 它來自minidlna.conf  
  10. if (shttpl < 0)  
  11.     DPRINTF(E_FATAL, L_GENERAL, "Failed to open socket for HTTP. EXITING\n");  
  12. DPRINTF(E_WARN, L_GENERAL, "HTTP listening on port %d\n", runtime_vars.port);  
  13. /* open socket for sending notifications */  
  14. if (OpenAndConfSSDPNotifySockets(snotify) < 0)  //初始化n_lan_addr個廣播用socket  
  15.     DPRINTF(E_FATAL, L_GENERAL, "Failed to open sockets for sending SSDP notify "  
  16.             "messages. EXITING\n");  
進(jìn)入一個標(biāo)準(zhǔn)的select模型:
  1. while (!quitting) //init quitting = 0  
  2. {  
  3.     /* Check if we need to send SSDP NOTIFY messages and do it if 
  4.     * needed */  
  5.     if (gettimeofday(&timeofday, 0) < 0)  
  6.     {  
  7.         DPRINTF(E_ERROR, L_GENERAL, "gettimeofday(): %s\n", strerror(errno));  
  8.         timeout.tv_sec = runtime_vars.notify_interval;  
  9.         timeout.tv_usec = 0;  
  10.     }  
  11.     else  
  12.     {  
  13.         /* the comparison is not very precise but who cares ? */  
  14.         if (timeofday.tv_sec >= (lastnotifytime.tv_sec + runtime_vars.notify_interval)) //如果超時  
  15.         {  
  16.             SendSSDPNotifies2(snotify,  
  17.                         (unsigned short)runtime_vars.port,  
  18.                         (runtime_vars.notify_interval << 1)+10);  //心跳廣播ssdp:alive消息,通知其他接入點(diǎn)自己就緒  
  19.             memcpy(&lastnotifytime, &timeofday, sizeof(struct timeval));  
  20.             timeout.tv_sec = runtime_vars.notify_interval;  
  21.             timeout.tv_usec = 0;  
  22.         }  
  23.         else  
  24.         {  
  25.             timeout.tv_sec = lastnotifytime.tv_sec + runtime_vars.notify_interval  
  26.                             - timeofday.tv_sec;  
  27.             if (timeofday.tv_usec > lastnotifytime.tv_usec)  
  28.             {  
  29.                 timeout.tv_usec = 1000000 + lastnotifytime.tv_usec  
  30.                                     - timeofday.tv_usec;  
  31.                 timeout.tv_sec--;  
  32.             }  
  33.             else  
  34.                 timeout.tv_usec = lastnotifytime.tv_usec - timeofday.tv_usec;  
  35.   
  36.         //..............  
  37.           
  38.         FD_ZERO(&readset);  
  39.           
  40.         if (sudp >= 0)   
  41.         {  
  42.             FD_SET(sudp, &readset);  //將sudp加入readset  
  43.             max_fd = MAX(max_fd, sudp);  
  44.         }  
  45.           
  46.         if (shttpl >= 0)  
  47.         {  
  48.             FD_SET(shttpl, &readset); //將shttpl加入readset  
  49.             max_fd = MAX(max_fd, shttpl);  
  50.         }  
  51.           
  52.         //......  
  53.         i = 0;  /* active HTTP connections count */  
  54.         // struct upnphttp *e  
  55.         for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)  
  56.         {  
  57.             if ((e->socket >= 0) && (e->state <= 2))  
  58.             {  
  59.                 FD_SET(e->socket, &readset); //添加記錄的socket進(jìn)入readset  
  60.                 max_fd = MAX(max_fd, e->socket);  
  61.                 i++;  
  62.             }  
  63.         }  
  64.           
  65.         //.......  
  66.           
  67.         FD_ZERO(&writeset);  
  68.         upnpevents_selectfds(&readset, &writeset, &max_fd);  
  69.         ret = select(max_fd+1, &readset, &writeset, 0, &timeout);   
  70.         if (ret < 0)  
  71.         {  
  72.             if(quitting) goto shutdown;  
  73.             if(errno == EINTR) continue;  
  74.             DPRINTF(E_ERROR, L_GENERAL, "select(all): %s\n", strerror(errno));  
  75.             DPRINTF(E_FATAL, L_GENERAL, "Failed to select open sockets. EXITING\n");  
  76.         }  
  77.         upnpevents_processfds(&readset, &writeset);  
  78.   
  79.         /* process SSDP packets */  
  80.         if (sudp >= 0 && FD_ISSET(sudp, &readset))  
  81.         {  
  82.             /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/  
  83.             ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port); //接受控制點(diǎn)傳來的ssdp信息,并回傳給控制點(diǎn)設(shè)備描述信息  
  84.         }  
  85.           
  86.         //......  
  87.           
  88.         for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)  
  89.         {  
  90.             if ((e->socket >= 0) && (e->state <= 2) && (FD_ISSET(e->socket, &readset)))  
  91.             {  
  92.                 Process_upnphttp(e);  //這里會回送消息給控制點(diǎn)( 設(shè)備信息xml或遠(yuǎn)程目錄信息等), port:8200  
  93.             }  
  94.         }  
  95.         /* process incoming HTTP connections */  
  96.         if (shttpl >= 0 && FD_ISSET(shttpl, &readset))  
  97.         {  
  98.             int shttp;  
  99.             socklen_t clientnamelen;  
  100.             struct sockaddr_in clientname;  
  101.             clientnamelen = sizeof(struct sockaddr_in);  
  102.             shttp = accept(shttpl, (struct sockaddr *)&clientname, &clientnamelen);  //獲取遠(yuǎn)程socket shttp  
  103.             if (shttp<0)  
  104.             {  
  105.                 DPRINTF(E_ERROR, L_GENERAL, "accept(http): %s\n", strerror(errno));  
  106.             }  
  107.             else  
  108.             {  
  109.                 struct upnphttp * tmp = 0;  
  110.                 DPRINTF(E_DEBUG, L_GENERAL, "HTTP connection from %s:%d\n",  
  111.                 inet_ntoa(clientname.sin_addr),  
  112.                 ntohs(clientname.sin_port) );  
  113.                 /*if (fcntl(shttp, F_SETFL, O_NONBLOCK) < 0) { 
  114.                 DPRINTF(E_ERROR, L_GENERAL, "fcntl F_SETFL, O_NONBLOCK\n"); 
  115.                 }*/  
  116.                 /* Create a new upnphttp object and add it to 
  117.                 * the active upnphttp object list */  
  118.                 tmp = New_upnphttp(shttp);   //初始化 struct upnphttp ,并且將shttp賦予其socket字段  
  119.                 if (tmp)  
  120.                 {  
  121.                     tmp->clientaddr = clientname.sin_addr;  
  122.                     LIST_INSERT_HEAD(&upnphttphead, tmp, entries); //將tmp插入鏈表upnphttphead中  
  123.                 }  
  124.                 else  
  125.                 {  
  126.                     DPRINTF(E_ERROR, L_GENERAL, "New_upnphttp() failed\n");  
  127.                     close(shttp);  
  128.                 }  
  129.             }  
  130.         }  
  131.           
  132.         //......  
  133.           
  134. }  
設(shè)備發(fā)現(xiàn)是UPnP網(wǎng)絡(luò)實(shí)現(xiàn)的第一步。在這里,minidlna啟動后,本機(jī)作為一個設(shè)備加入到網(wǎng)絡(luò)中,設(shè)備發(fā)現(xiàn)過程允許設(shè)備向網(wǎng)絡(luò)上的控制點(diǎn)告知它提供的服務(wù)(ssdp:alive)。當(dāng)一個控制點(diǎn)加入到網(wǎng)絡(luò)中時,設(shè)備發(fā)現(xiàn)過程允許控制點(diǎn)尋找網(wǎng)絡(luò)上感興趣的設(shè)備(ssdp:discover)。在這兩種情況下,基本的交換信息就是發(fā)現(xiàn)消息。發(fā)現(xiàn)消息包括設(shè)備的一些特定信息或者某項(xiàng)服務(wù)的信息,例如它的類型、標(biāo)識符、和指向XML設(shè)備描述文檔的指針。簡單發(fā)現(xiàn)協(xié)議(SSDP)定義了在網(wǎng)絡(luò)中發(fā)現(xiàn)網(wǎng)絡(luò)服務(wù),控制點(diǎn)定位網(wǎng)絡(luò)上相關(guān)資源和設(shè)備在網(wǎng)絡(luò)上聲明其可用性的方法。

在上面的select模型中,程序通過定時執(zhí)行SendSSDPNotifies2方法,廣播設(shè)備就緒消息(心跳包),它的實(shí)現(xiàn)如下:

  1. void                                       
  2. SendSSDPNotifies2(int *sockets,  
  3.                   unsigned short port,  
  4.                   unsigned int lifetime)  
  5. {                          
  6.     int i;  
  7.     DPRINTF(E_DEBUG, L_SSDP, "Sending SSDP notifies\n");  
  8.     for (i = 0; i < n_lan_addr; i++) //向本地的網(wǎng)絡(luò)接口循環(huán)發(fā)送ssdp:alive消息  
  9.     {  
  10.         SendSSDPNotifies(sockets[i], lan_addr[i].str, port, lifetime); //發(fā)送ssdp:alive  
  11.     }  
  12. }  
發(fā)送的ssdp:alive消息格式如下:
  1. NOTIFY * HTTP/1.1  
  2. HOST:239.255.255.250:1900    #協(xié)議保留多播地址和端口,必須是239.255.255.250:1900  
  3. CACHE-CONTROL:max-age=1810   #max-age指定通知消息存活時間,如果超過此時間間隔,控制點(diǎn)可以認(rèn)為設(shè)備不存在  
  4. LOCATION:http://192.168.1.20:8200/rootDesc.xml     #包含根設(shè)備描述得URL地址  
  5. SERVER: 3.4.72-rt89 DLNADOC/1.50 UPnP/1.0 MiniDLNA/1.1.0  
  6. NT:upnp:rootdevice   #在此消息中,NT頭必須為服務(wù)的服務(wù)類型  
  7. USN:uuid:4d696e69-444c-164e-9d41-001ec92f0378::upnp:rootdevice   #表示不同服務(wù)的統(tǒng)一服務(wù)名,它提供了一種標(biāo)識出相同類型服務(wù)的能力  
  8. NTS:ssdp:alive   #表示通知消息的子類型,必須為ssdp:alive  

UPnP網(wǎng)絡(luò)結(jié)構(gòu)的第二步是設(shè)備描述。在控制點(diǎn)發(fā)現(xiàn)了一個設(shè)備之后,控制點(diǎn)仍然對設(shè)備知之甚少,控制點(diǎn)可能僅僅知道設(shè)備或服務(wù)的UPnP類型,設(shè)備的UUID和設(shè)備描述的URL地址。為了讓控制點(diǎn)更多的了解設(shè)備和它的功能或者與設(shè)備交互,控制點(diǎn)必須從發(fā)現(xiàn)消息中得到設(shè)備描述的URL,通過URL取回設(shè)備描述。
在程序中,我們發(fā)送完ssdp:alive廣播后,網(wǎng)絡(luò)上的控制點(diǎn)就會發(fā)送相應(yīng)的消息到程序,在上邊的select模型中,我們會通過以下程序接收控制點(diǎn)傳來的ssdp消息:

  1. if (sudp >= 0 && FD_ISSET(sudp, &readset))  
  2. {  
  3.     /*DPRINTF(E_DEBUG, L_GENERAL, "Received UDP Packet\n");*/  
  4.     ProcessSSDPRequest(sudp, (unsigned short)runtime_vars.port); //接受控制點(diǎn)傳來的ssdp信息,并回傳給控制點(diǎn)設(shè)備描述信息  
  5. }  
在ProcessSSDPRequest中實(shí)現(xiàn)了接收控制點(diǎn)傳來的消息,以及回傳給控制點(diǎn)的信息(設(shè)備描述URL),接收的控制點(diǎn)消息格式如下(ssdp:discover):
  1. M-SEARCH * HTTP/1.1  
  2. Host: 239.255.255.250:1900  #設(shè)置為協(xié)議保留多播地址和端口,必須是239.255.255.250:1900。  
  3. Man: "ssdp:discover"   #設(shè)置協(xié)議查詢的類型,必須是"ssdp:discover"。  
  4. MX: 5   #設(shè)置設(shè)備響應(yīng)最長等待時間,設(shè)備響應(yīng)在0和這個值之間隨機(jī)選擇響應(yīng)延遲的值。這樣可以為控制點(diǎn)響應(yīng)平衡網(wǎng)絡(luò)負(fù)載。  
  5. ST: upnp:rootdevice  #設(shè)置服務(wù)查詢的目標(biāo)  
回傳給控制點(diǎn)的消息格式如下:
  1. HTTP/1.1 200 OK  
  2. CACHE-CONTROL: max-age=1810  #max-age指定通知消息存活時間,如果超過此時間間隔,控制點(diǎn)可以認(rèn)為設(shè)備不存在  
  3. DATE: Tue, 11 Feb 2014 08:16:14 GMT  #指定響應(yīng)生成的時間  
  4. ST: upnp:rootdevice  #內(nèi)容和意義與查詢請求的相應(yīng)字段相同  
  5. USN: uuid:4d696e69-444c-164e-9d41-001ec92f0378::upnp:rootdevice  #表示不同服務(wù)的統(tǒng)一服務(wù)名,它提供了一種標(biāo)識出相同類型服務(wù)的能力。  
  6. EXT:     #向控制點(diǎn)確認(rèn)MAN頭域已經(jīng)被設(shè)備理解  
  7. SERVER: 3.4.72-rt89 DLNADOC/1.50 UPnP/1.0 MiniDLNA/1.1.0  
  8. LOCATION: http://192.168.1.20:8200/rootDesc.xml    #包含根設(shè)備描述得URL地址  
  9. Content-Length: 0  

設(shè)備控制是UPnP網(wǎng)絡(luò)的第三步。在接收設(shè)備和服務(wù)描述之后,控制點(diǎn)可以向這些服務(wù)發(fā)出動作,同時控制點(diǎn)也可以輪詢服務(wù)的狀態(tài)變量值。發(fā)出動作實(shí)質(zhì)上是一種遠(yuǎn)程過程調(diào)用,控制點(diǎn)將動作送到設(shè)備服務(wù),在動作完成之后,服務(wù)返回相應(yīng)的結(jié)果。在這里,我們利用minidlna的基本功能——遠(yuǎn)程目錄瀏覽,來說明。當(dāng)我們在控制點(diǎn)VLC Media Player中點(diǎn)擊“通用即插即播”,它會自動完成前面描述的設(shè)備發(fā)現(xiàn)設(shè)備描述,顯示可用的設(shè)備信息列表(在這里,可用設(shè)備就是minidlna服務(wù))


點(diǎn)擊這里的Jane,就會顯示minidlna設(shè)備指定的目錄下的目錄信息。當(dāng)我們做這些操作的時候,控制點(diǎn)正在向minidlna設(shè)備發(fā)送請求消息。這個請求的格式如下:

  1. POST /ctl/ContentDir HTTP/1.1  
  2. HOST: 192.168.1.20:8200  
  3. CONTENT-LENGTH: 488  
  4. CONTENT-TYPE: text/xml; charset="utf-8"  
  5. SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"  
  6. USER-AGENT: 6.1.7600 2/, UPnP/1.0, Portable SDK for UPnP devices/1.6.18  
  7.   
  8. <s:Envelope xmlns:s="http://schemas./soap/envelope/" s:encodingStyle="http://schemas./soap/encoding/">  
  9. <s:Body><u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">  
  10. <ObjectID>64$4</ObjectID>  
  11. <BrowseFlag>BrowseDirectChildren</BrowseFlag>  
  12. <Filter>id,dc:title,res,sec:CaptionInfo,sec:CaptionInfoEx</Filter>  
  13. <StartingIndex>0</StartingIndex>  
  14. <RequestedCount>0</RequestedCount>  
  15. <SortCriteria></SortCriteria>  
  16. </u:Browse>  
  17. </s:Body>  
  18. </s:Envelope>  
注意這里SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse",Browse將決定我們遠(yuǎn)程執(zhí)行何種方法(有點(diǎn)類似信令)。在上邊的select模型中,我們收到該請求:
  1. for (e = upnphttphead.lh_first; e != NULL; e = e->entries.le_next)  
  2. {  
  3.     if ((e->socket >= 0) && (e->state <= 2) && (FD_ISSET(e->socket, &readset)))  
  4.     {  
  5.         Process_upnphttp(e);  //這里會回送消息給控制點(diǎn)( 設(shè)備信息xml或遠(yuǎn)程目錄信息等), port:8200  
  6.     }  
  7. }  
Process_upnphttp會在底層調(diào)用upnpsoap.c中的ExecuteSoapAction方法,在upnpsoap.c定義了相關(guān)信令和它們對應(yīng)的方法,如下:
  1. static const struct  
  2. {  
  3.     const char * methodName;  
  4.     void (*methodImpl)(struct upnphttp *, const char *);  
  5. }  
  6. soapMethods[] =  
  7. {  
  8.     { "QueryStateVariable", QueryStateVariable},  
  9.     { "Browse", BrowseContentDirectory},  
  10.     { "Search", SearchContentDirectory},  
  11.     { "GetSearchCapabilities", GetSearchCapabilities},  
  12.     { "GetSortCapabilities", GetSortCapabilities},  
  13.     { "GetSystemUpdateID", GetSystemUpdateID},  
  14.     { "GetProtocolInfo", GetProtocolInfo},  
  15.     { "GetCurrentConnectionIDs", GetCurrentConnectionIDs},  
  16.     { "GetCurrentConnectionInfo", GetCurrentConnectionInfo},  
  17.     { "IsAuthorized", IsAuthorizedValidated},  
  18.     { "IsValidated", IsAuthorizedValidated},  
  19.     { "X_GetFeatureList", SamsungGetFeatureList},  
  20.     { "X_SetBookmark", SamsungSetBookmark},  
  21.     { 0, 0 }  
  22. };   

更具對應(yīng)關(guān)系,ExecuteSoapAction會再調(diào)用BrowseContentDirectory方法。BrowseContentDirectory中會搜索sqlite中的目錄信息,將信息拼接出xml字符串,代碼如下:

  1. static void  
  2. BrowseContentDirectory(struct upnphttp * h, const char * action)  
  3. {  
  4.     static const char resp0[] =  
  5.         "<u:BrowseResponse "  
  6.         "xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"  
  7.         "<Result>"  
  8.         "<DIDL-Lite"  
  9.       
  10.     //......  
  11.    
  12.     sql = sqlite3_mprintf( SELECT_COLUMNS  
  13.                                    "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"  
  14.                                    " where PARENT_ID = '%q' %s limit %d, %d;",  
  15.                                    ObjectID, orderBy, StartingIndex, RequestedCount);     
  16.     DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql);  
  17.     /* 
  18.      *  SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS, d.SIZE, d.TITLE, d.DURATION,         
  19.      *  d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM, d.GENRE, d.COMMENT, d.CHANNELS, d.TRACK, d.DATE,  
  20.      *  d.RESOLUTION, d.THUMBNAIL, d.CREATOR, d.DLNA_PN, d.MIME, d.ALBUM_ART, d.DISC from OBJECTS o  
  21.      *  left join DETAILS d on (d.ID = o.DETAIL_ID) where PARENT_ID = '0'  limit 0, -1; 
  22.      */  
  23.     ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);  //查詢目錄信息  
  24.     // ......  
  25.       
  26.     ret = strcatf(&str, "</DIDL-Lite></Result>\n"  
  27.                       "<NumberReturned>%u</NumberReturned>\n"  
  28.                       "<TotalMatches>%u</TotalMatches>\n"  
  29.                       "<UpdateID>%u</UpdateID>"  
  30.                       "</u:BrowseResponse>",  
  31.                       args.returned, totalMatches, updateID); //拼接xml字符串  
  32.       
  33.     BuildSendAndCloseSoapResp(h, str.data, str.off); //回送給控制點(diǎn)xml字符串消息  
  34.           
  35.     //......  
  36. }  
通過BuildSendAndCloseSoapResp回傳給控制點(diǎn),這個xml字符串格式如下:

  1. <u:BrowseResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">  
  2. <Result><DIDL-Lite xmlns:dc="http:///dc/elements/1.1/"   
  3.                       xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"   
  4.                       xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><  
  5.                       container id="64$0" parentID="64" restricted="1" ><dc:title>android-14</dc:title><upnp:class>object.container.storageFolder</upnp:class></container><  
  6.                       container id="64$1" parentID="64" restricted="1" ><dc:title>armeabi-v7a</dc:title><upnp:class>object.container.storageFolder</upnp:class></container><  
  7.                       container id="64$2" parentID="64" restricted="1" ><dc:title>libwnck-2.22.0</dc:title><upnp:class>object.container.storageFolder</upnp:class></container><  
  8.                       container id="64$3" parentID="64" restricted="1" ><dc:title>voice-client-example</dc:title><upnp:class>object.container.storageFolder</upnp:class></container></DIDL-Lite>  
  9. </Result>  
  10. <NumberReturned>6</NumberReturned>  
  11. <TotalMatches>6</TotalMatches>  
  12. <UpdateID>10</UpdateID></u:BrowseResponse>               
這個xml字符串,說明minidlna指定的目錄下有android-14,armeabi-v7a,libwnck-2.22.0和voice-client-example等4個目錄??刂泣c(diǎn)通過這一信息獲取minidlna服務(wù)。







    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多