Full Code of ClaudeLi/DLNA_UPnP for AI

master 41fcb826bbea cached
85 files
708.9 KB
196.7k tokens
3 symbols
1 requests
Download .txt
Showing preview only (747K chars total). Download the full file or copy to clipboard to get everything.
Repository: ClaudeLi/DLNA_UPnP
Branch: master
Commit: 41fcb826bbea
Files: 85
Total size: 708.9 KB

Directory structure:
gitextract_m2svozsz/

├── .gitignore
├── .travis.yml
├── DLNA_UPnP/
│   ├── Assets/
│   │   └── .gitkeep
│   └── Classes/
│       ├── GData/
│       │   ├── GDataXMLNode.h
│       │   └── GDataXMLNode.m
│       └── UPnP/
│           ├── CLUPnP.h
│           ├── CLUPnPAVPositionInfo.h
│           ├── CLUPnPAVPositionInfo.m
│           ├── CLUPnPAction.h
│           ├── CLUPnPAction.m
│           ├── CLUPnPDevice.h
│           ├── CLUPnPDevice.m
│           ├── CLUPnPRenderer.h
│           ├── CLUPnPRenderer.m
│           ├── CLUPnPResponseDelegate.h
│           ├── CLUPnPServer.h
│           └── CLUPnPServer.m
├── DLNA_UPnP.podspec
├── Example/
│   ├── DLNA_UPnP/
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── CLAppDelegate.h
│   │   ├── CLAppDelegate.m
│   │   ├── CLControlViewController.h
│   │   ├── CLControlViewController.m
│   │   ├── CLControlViewController.xib
│   │   ├── CLSearchDeviceController.h
│   │   ├── CLSearchDeviceController.m
│   │   ├── CLViewController.h
│   │   ├── CLViewController.m
│   │   ├── DLNA_UPnP-Info.plist
│   │   ├── DLNA_UPnP-Prefix.pch
│   │   ├── Device.xml
│   │   ├── Images.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── SetUrl.xml
│   │   ├── en.lproj/
│   │   │   └── InfoPlist.strings
│   │   └── main.m
│   ├── DLNA_UPnP.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   └── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── DLNA_UPnP-Example.xcscheme
│   ├── DLNA_UPnP.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   ├── Podfile
│   ├── Pods/
│   │   ├── CocoaAsyncSocket/
│   │   │   ├── LICENSE.txt
│   │   │   ├── README.markdown
│   │   │   └── Source/
│   │   │       └── GCD/
│   │   │           ├── GCDAsyncSocket.h
│   │   │           ├── GCDAsyncSocket.m
│   │   │           ├── GCDAsyncUdpSocket.h
│   │   │           └── GCDAsyncUdpSocket.m
│   │   ├── Local Podspecs/
│   │   │   └── DLNA_UPnP.podspec.json
│   │   ├── Pods.xcodeproj/
│   │   │   └── project.pbxproj
│   │   └── Target Support Files/
│   │       ├── CocoaAsyncSocket/
│   │       │   ├── CocoaAsyncSocket-Info.plist
│   │       │   ├── CocoaAsyncSocket-dummy.m
│   │       │   ├── CocoaAsyncSocket-prefix.pch
│   │       │   ├── CocoaAsyncSocket-umbrella.h
│   │       │   ├── CocoaAsyncSocket.modulemap
│   │       │   └── CocoaAsyncSocket.xcconfig
│   │       ├── DLNA_UPnP/
│   │       │   ├── DLNA_UPnP-Info.plist
│   │       │   ├── DLNA_UPnP-dummy.m
│   │       │   ├── DLNA_UPnP-prefix.pch
│   │       │   ├── DLNA_UPnP-umbrella.h
│   │       │   ├── DLNA_UPnP.modulemap
│   │       │   └── DLNA_UPnP.xcconfig
│   │       ├── Pods-DLNA_UPnP_Example/
│   │       │   ├── Pods-DLNA_UPnP_Example-Info.plist
│   │       │   ├── Pods-DLNA_UPnP_Example-acknowledgements.markdown
│   │       │   ├── Pods-DLNA_UPnP_Example-acknowledgements.plist
│   │       │   ├── Pods-DLNA_UPnP_Example-dummy.m
│   │       │   ├── Pods-DLNA_UPnP_Example-frameworks.sh
│   │       │   ├── Pods-DLNA_UPnP_Example-umbrella.h
│   │       │   ├── Pods-DLNA_UPnP_Example.debug.xcconfig
│   │       │   ├── Pods-DLNA_UPnP_Example.modulemap
│   │       │   └── Pods-DLNA_UPnP_Example.release.xcconfig
│   │       └── Pods-DLNA_UPnP_Tests/
│   │           ├── Pods-DLNA_UPnP_Tests-Info.plist
│   │           ├── Pods-DLNA_UPnP_Tests-acknowledgements.markdown
│   │           ├── Pods-DLNA_UPnP_Tests-acknowledgements.plist
│   │           ├── Pods-DLNA_UPnP_Tests-dummy.m
│   │           ├── Pods-DLNA_UPnP_Tests-umbrella.h
│   │           ├── Pods-DLNA_UPnP_Tests.debug.xcconfig
│   │           ├── Pods-DLNA_UPnP_Tests.modulemap
│   │           └── Pods-DLNA_UPnP_Tests.release.xcconfig
│   └── Tests/
│       ├── Tests-Info.plist
│       ├── Tests-Prefix.pch
│       ├── Tests.m
│       └── en.lproj/
│           └── InfoPlist.strings
├── LICENSE
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# OS X
.DS_Store

# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa

# Bundler
.bundle

# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
# 
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/


================================================
FILE: .travis.yml
================================================
# references:
# * https://www.objc.io/issues/6-build-tools/travis-ci/
# * https://github.com/supermarin/xcpretty#usage

osx_image: xcode7.3
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
# - gem install cocoapods # Since Travis is not always on latest version
# - pod install --project-directory=Example
script:
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/DLNA_UPnP.xcworkspace -scheme DLNA_UPnP-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
- pod lib lint


================================================
FILE: DLNA_UPnP/Assets/.gitkeep
================================================


================================================
FILE: DLNA_UPnP/Classes/GData/GDataXMLNode.h
================================================
/* Copyright (c) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// These node, element, and document classes implement a subset of the methods
// provided by NSXML.  While NSXML behavior is mimicked as much as possible,
// there are important differences.
//
// The biggest difference is that, since this is based on libxml2, there
// is no retain model for the underlying node data.  Rather than copy every
// node obtained from a parse tree (which would have a substantial memory
// impact), we rely on weak references, and it is up to the code that
// created a document to retain it for as long as any
// references rely on nodes inside that document tree.


#import <Foundation/Foundation.h>

// libxml includes require that the target Header Search Paths contain
//
//   /usr/include/libxml2
//
// and Other Linker Flags contain
//
//   -lxml2

// <>引头文件其实是libxml是一个文件夹,tree.h是文件夹中的文件
// 第一步:添加系统或者第三方库文件
// 第二步:找文件夹的路径
// 第三步:添加头文件路径,在BuildSetting->header search path,添加/usr/include/libxml2 路径
// header search path 代表的是添加的库文件的.h文件所在的位置,以后的第三方中会经常使用到,比如 百度地图
#import <libxml/tree.h>
#import <libxml/parser.h>
#import <libxml/xmlstring.h>
#import <libxml/xpath.h>
#import <libxml/xpathInternals.h>


#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4) || defined(GDATA_TARGET_NAMESPACE)
  // we need NSInteger for the 10.4 SDK, or we're using target namespace macros
  #import "GDataDefines.h"
#endif

#undef _EXTERN
#undef _INITIALIZE_AS
#ifdef GDATAXMLNODE_DEFINE_GLOBALS
#define _EXTERN
#define _INITIALIZE_AS(x) =x
#else
#define _EXTERN extern
#define _INITIALIZE_AS(x)
#endif

// when no namespace dictionary is supplied for XPath, the default namespace
// for the evaluated tree is registered with the prefix _def_ns
_EXTERN const char* kGDataXMLXPathDefaultNamespacePrefix _INITIALIZE_AS("_def_ns");

// Nomenclature for method names:
//
// Node = GData node
// XMLNode = xmlNodePtr
//
// So, for example:
//  + (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode;

@class NSArray, NSDictionary, NSError, NSString, NSURL;
@class GDataXMLElement, GDataXMLDocument;

enum {
  GDataXMLInvalidKind = 0,
  GDataXMLDocumentKind,
  GDataXMLElementKind,
  GDataXMLAttributeKind,
  GDataXMLNamespaceKind,
  GDataXMLProcessingInstructionKind,
  GDataXMLCommentKind,
  GDataXMLTextKind,
  GDataXMLDTDKind,
  GDataXMLEntityDeclarationKind,
  GDataXMLAttributeDeclarationKind,
  GDataXMLElementDeclarationKind,
  GDataXMLNotationDeclarationKind
};

typedef NSUInteger GDataXMLNodeKind;

@interface GDataXMLNode : NSObject {
@protected
  // NSXMLNodes can have a namespace URI or prefix even if not part
  // of a tree; xmlNodes cannot.  When we create nodes apart from
  // a tree, we'll store the dangling prefix or URI in the xmlNode's name,
  // like
  //   "prefix:name"
  // or
  //   "{http://uri}:name"
  //
  // We will fix up the node's namespace and name (and those of any children)
  // later when adding the node to a tree with addChild: or addAttribute:.
  // See fixUpNamespacesForNode:.

  xmlNodePtr xmlNode_; // may also be an xmlAttrPtr or xmlNsPtr
  BOOL shouldFreeXMLNode_; // if yes, xmlNode_ will be free'd in dealloc

  // cached values
  NSString *cachedName_;
  NSArray *cachedChildren_;
  NSArray *cachedAttributes_;
}

+ (GDataXMLElement *)elementWithName:(NSString *)name;
+ (GDataXMLElement *)elementWithName:(NSString *)name stringValue:(NSString *)value;
+ (GDataXMLElement *)elementWithName:(NSString *)name URI:(NSString *)value;

+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)value;
+ (id)attributeWithName:(NSString *)name URI:(NSString *)attributeURI stringValue:(NSString *)value;

+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)value;

+ (id)textWithStringValue:(NSString *)value;

- (NSString *)stringValue;
- (void)setStringValue:(NSString *)str;

- (NSUInteger)childCount;
- (NSArray *)children;
- (GDataXMLNode *)childAtIndex:(unsigned)index;

- (NSString *)localName;
- (NSString *)name;
- (NSString *)prefix;
- (NSString *)URI;

- (GDataXMLNodeKind)kind;

- (NSString *)XMLString;

+ (NSString *)localNameForName:(NSString *)name;
+ (NSString *)prefixForName:(NSString *)name;

// This is the preferred entry point for nodesForXPath.  This takes an explicit
// namespace dictionary (keys are prefixes, values are URIs).
- (NSArray *)nodesForXPath:(NSString *)xpath namespaces:(NSDictionary *)namespaces error:(NSError **)error;

// This implementation of nodesForXPath registers namespaces only from the
// document's root node.  _def_ns may be used as a prefix for the default
// namespace, though there's no guarantee that the default namespace will
// be consistenly the same namespace in server responses.
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;

// access to the underlying libxml node; be sure to release the cached values
// if you change the underlying tree at all
- (xmlNodePtr)XMLNode;
- (void)releaseCachedValues;

@end


@interface GDataXMLElement : GDataXMLNode

- (id)initWithXMLString:(NSString *)str error:(NSError **)error;

- (NSArray *)namespaces;
- (void)setNamespaces:(NSArray *)namespaces;
- (void)addNamespace:(GDataXMLNode *)aNamespace;

- (void)addChild:(GDataXMLNode *)child;
- (void)removeChild:(GDataXMLNode *)child;

- (NSArray *)elementsForName:(NSString *)name;
- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI;

- (NSArray *)attributes;
- (GDataXMLNode *)attributeForName:(NSString *)name;
- (GDataXMLNode *)attributeForLocalName:(NSString *)name URI:(NSString *)attributeURI;
- (void)addAttribute:(GDataXMLNode *)attribute;

- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI;

@end

@interface GDataXMLDocument : NSObject {
@protected
  xmlDoc* xmlDoc_; // strong; always free'd in dealloc
}

- (id)initWithXMLString:(NSString *)str options:(unsigned int)mask error:(NSError **)error;
- (id)initWithData:(NSData *)data options:(unsigned int)mask error:(NSError **)error;
- (id)initWithRootElement:(GDataXMLElement *)element;

- (GDataXMLElement *)rootElement;

- (NSData *)XMLData;

- (void)setVersion:(NSString *)version;
- (void)setCharacterEncoding:(NSString *)encoding;

// This is the preferred entry point for nodesForXPath.  This takes an explicit
// namespace dictionary (keys are prefixes, values are URIs).
- (NSArray *)nodesForXPath:(NSString *)xpath namespaces:(NSDictionary *)namespaces error:(NSError **)error;

// This implementation of nodesForXPath registers namespaces only from the
// document's root node.  _def_ns may be used as a prefix for the default
// namespace, though there's no guarantee that the default namespace will
// be consistenly the same namespace in server responses.
- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error;

- (NSString *)description;
@end


================================================
FILE: DLNA_UPnP/Classes/GData/GDataXMLNode.m
================================================
/* Copyright (c) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define GDATAXMLNODE_DEFINE_GLOBALS 1
#import "GDataXMLNode.h"

@class NSArray, NSDictionary, NSError, NSString, NSURL;
@class GDataXMLElement, GDataXMLDocument;


static const int kGDataXMLParseOptions = (XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS);

// dictionary key callbacks for string cache
static const void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator, const void *str);
static void StringCacheKeyReleaseCallBack(CFAllocatorRef allocator, const void *str);
static CFStringRef StringCacheKeyCopyDescriptionCallBack(const void *str);
static Boolean StringCacheKeyEqualCallBack(const void *str1, const void *str2);
static CFHashCode StringCacheKeyHashCallBack(const void *str);

// isEqual: has the fatal flaw that it doesn't deal well with the received
// being nil. We'll use this utility instead.

// Static copy of AreEqualOrBothNil from GDataObject.m, so that using
// GDataXMLNode does not require pulling in all of GData.
static BOOL AreEqualOrBothNilPrivate(id obj1, id obj2) {
  if (obj1 == obj2) {
    return YES;
  }
  if (obj1 && obj2) {
    return [obj1 isEqual:obj2];
  }
  return NO;
}


// convert NSString* to xmlChar*
//
// the "Get" part implies that ownership remains with str

static xmlChar* GDataGetXMLString(NSString *str) {
  xmlChar* result = (xmlChar *)[str UTF8String];
  return result;
}

// Make a fake qualified name we use as local name internally in libxml
// data structures when there's no actual namespace node available to point to
// from an element or attribute node
//
// Returns an autoreleased NSString*

static NSString *GDataFakeQNameForURIAndName(NSString *theURI, NSString *name) {

  NSString *localName = [GDataXMLNode localNameForName:name];
  NSString *fakeQName = [NSString stringWithFormat:@"{%@}:%@",
                         theURI, localName];
  return fakeQName;
}


// libxml2 offers xmlSplitQName2, but that searches forwards. Since we may
// be searching for a whole URI shoved in as a prefix, like
//   {http://foo}:name
// we'll search for the prefix in backwards from the end of the qualified name
//
// returns a copy of qname as the local name if there's no prefix
static xmlChar *SplitQNameReverse(const xmlChar *qname, xmlChar **prefix) {

  // search backwards for a colon
  int qnameLen = xmlStrlen(qname);
  for (int idx = qnameLen - 1; idx >= 0; idx--) {

    if (qname[idx] == ':') {

      // found the prefix; copy the prefix, if requested
      if (prefix != NULL) {
        if (idx > 0) {
          *prefix = xmlStrsub(qname, 0, idx);
        } else {
          *prefix = NULL;
        }
      }

      if (idx < qnameLen - 1) {
        // return a copy of the local name
        xmlChar *localName = xmlStrsub(qname, idx + 1, qnameLen - idx - 1);
        return localName;
      } else {
        return NULL;
      }
    }
  }

  // no colon found, so the qualified name is the local name
  xmlChar *qnameCopy = xmlStrdup(qname);
  return qnameCopy;
}

@interface GDataXMLNode (PrivateMethods)

// consuming a node implies it will later be freed when the instance is
// dealloc'd; borrowing it implies that ownership and disposal remain the
// job of the supplier of the node

+ (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode;
- (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode;

+ (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode;
- (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode;

// getters of the underlying node
- (xmlNodePtr)XMLNode;
- (xmlNodePtr)XMLNodeCopy;

// search for an underlying attribute
- (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode;

// return an NSString for an xmlChar*, using our strings cache in the
// document
- (NSString *)stringFromXMLString:(const xmlChar *)chars;

// setter/getter of the dealloc flag for the underlying node
- (BOOL)shouldFreeXMLNode;
- (void)setShouldFreeXMLNode:(BOOL)flag;

@end

@interface GDataXMLElement (PrivateMethods)

+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
            graftingToTreeNode:(xmlNodePtr)graftPointNode;
@end

@implementation GDataXMLNode

+ (void)load {
  xmlInitParser();
}

// Note on convenience methods for making stand-alone element and
// attribute nodes:
//
// Since we're making a node from scratch, we don't
// have any namespace info.  So the namespace prefix, if
// any, will just be slammed into the node name.
// We'll rely on the -addChild method below to remove
// the namespace prefix and replace it with a proper ns
// pointer.

+ (GDataXMLElement *)elementWithName:(NSString *)name {

  xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
                                     GDataGetXMLString(name));
  if (theNewNode) {
    // succeeded
    return [self nodeConsumingXMLNode:theNewNode];
  }
  return nil;
}

+ (GDataXMLElement *)elementWithName:(NSString *)name stringValue:(NSString *)value {

  xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
                                     GDataGetXMLString(name));
  if (theNewNode) {

    xmlNodePtr textNode = xmlNewText(GDataGetXMLString(value));
    if (textNode) {

      xmlNodePtr temp = xmlAddChild(theNewNode, textNode);
      if (temp) {
        // succeeded
        return [self nodeConsumingXMLNode:theNewNode];
      }
    }

    // failed; free the node and any children
    xmlFreeNode(theNewNode);
  }
  return nil;
}

+ (GDataXMLElement *)elementWithName:(NSString *)name URI:(NSString *)theURI {

  // since we don't know a prefix yet, shove in the whole URI; we'll look for
  // a proper namespace ptr later when addChild calls fixUpNamespacesForNode

  NSString *fakeQName = GDataFakeQNameForURIAndName(theURI, name);

  xmlNodePtr theNewNode = xmlNewNode(NULL, // namespace
                                     GDataGetXMLString(fakeQName));
  if (theNewNode) {
      return [self nodeConsumingXMLNode:theNewNode];
  }
  return nil;
}

+ (id)attributeWithName:(NSString *)name stringValue:(NSString *)value {

  xmlChar *xmlName = GDataGetXMLString(name);
  xmlChar *xmlValue = GDataGetXMLString(value);

  xmlAttrPtr theNewAttr = xmlNewProp(NULL, // parent node for the attr
                                     xmlName, xmlValue);
  if (theNewAttr) {
    return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];
  }

  return nil;
}

+ (id)attributeWithName:(NSString *)name URI:(NSString *)attributeURI stringValue:(NSString *)value {

  // since we don't know a prefix yet, shove in the whole URI; we'll look for
  // a proper namespace ptr later when addChild calls fixUpNamespacesForNode

  NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, name);

  xmlChar *xmlName = GDataGetXMLString(fakeQName);
  xmlChar *xmlValue = GDataGetXMLString(value);

  xmlAttrPtr theNewAttr = xmlNewProp(NULL, // parent node for the attr
                                     xmlName, xmlValue);
  if (theNewAttr) {
    return [self nodeConsumingXMLNode:(xmlNodePtr) theNewAttr];
  }

  return nil;
}

+ (id)textWithStringValue:(NSString *)value {

  xmlNodePtr theNewText = xmlNewText(GDataGetXMLString(value));
  if (theNewText) {
    return [self nodeConsumingXMLNode:theNewText];
  }
  return nil;
}

+ (id)namespaceWithName:(NSString *)name stringValue:(NSString *)value {

  xmlChar *href = GDataGetXMLString(value);
  xmlChar *prefix;

  if ([name length] > 0) {
    prefix = GDataGetXMLString(name);
  } else {
    // default namespace is represented by a nil prefix
    prefix = nil;
  }

  xmlNsPtr theNewNs = xmlNewNs(NULL, // parent node
                               href, prefix);
  if (theNewNs) {
    return [self nodeConsumingXMLNode:(xmlNodePtr) theNewNs];
  }
  return nil;
}

+ (id)nodeConsumingXMLNode:(xmlNodePtr)theXMLNode {
  Class theClass;

  if (theXMLNode->type == XML_ELEMENT_NODE) {
    theClass = [GDataXMLElement class];
  } else {
    theClass = [GDataXMLNode class];
  }
  return [[[theClass alloc] initConsumingXMLNode:theXMLNode] autorelease];
}

- (id)initConsumingXMLNode:(xmlNodePtr)theXMLNode {
  self = [super init];
  if (self) {
    xmlNode_ = theXMLNode;
    shouldFreeXMLNode_ = YES;
  }
  return self;
}

+ (id)nodeBorrowingXMLNode:(xmlNodePtr)theXMLNode {
  Class theClass;
  if (theXMLNode->type == XML_ELEMENT_NODE) {
    theClass = [GDataXMLElement class];
  } else {
    theClass = [GDataXMLNode class];
  }

  return [[[theClass alloc] initBorrowingXMLNode:theXMLNode] autorelease];
}

- (id)initBorrowingXMLNode:(xmlNodePtr)theXMLNode {
  self = [super init];
  if (self) {
    xmlNode_ = theXMLNode;
    shouldFreeXMLNode_ = NO;
  }
  return self;
}

- (void)releaseCachedValues {

  [cachedName_ release];
  cachedName_ = nil;

  [cachedChildren_ release];
  cachedChildren_ = nil;

  [cachedAttributes_ release];
  cachedAttributes_ = nil;
}


// convert xmlChar* to NSString*
//
// returns an autoreleased NSString*, from the current node's document strings
// cache if possible
- (NSString *)stringFromXMLString:(const xmlChar *)chars {

#if DEBUG
  NSCAssert(chars != NULL, @"GDataXMLNode sees an unexpected empty string");
#endif
  if (chars == NULL) return nil;

  CFMutableDictionaryRef cacheDict = NULL;

  NSString *result = nil;

  if (xmlNode_ != NULL
    && (xmlNode_->type == XML_ELEMENT_NODE
        || xmlNode_->type == XML_ATTRIBUTE_NODE
        || xmlNode_->type == XML_TEXT_NODE)) {
    // there is no xmlDocPtr in XML_NAMESPACE_DECL nodes,
    // so we can't cache the text of those

    // look for a strings cache in the document
    //
    // the cache is in the document's user-defined _private field

    if (xmlNode_->doc != NULL) {

      cacheDict = xmlNode_->doc->_private;

      if (cacheDict) {

        // this document has a strings cache
        result = (NSString *) CFDictionaryGetValue(cacheDict, chars);
        if (result) {
          // we found the xmlChar string in the cache; return the previously
          // allocated NSString, rather than allocate a new one
          return result;
        }
      }
    }
  }

  // allocate a new NSString for this xmlChar*
  result = [NSString stringWithUTF8String:(const char *) chars];
  if (cacheDict) {
    // save the string in the document's string cache
    CFDictionarySetValue(cacheDict, chars, result);
  }

  return result;
}

- (void)dealloc {

  if (xmlNode_ && shouldFreeXMLNode_) {
    xmlFreeNode(xmlNode_);
  }

  [self releaseCachedValues];
  [super dealloc];
}

#pragma mark -

- (void)setStringValue:(NSString *)str {
  if (xmlNode_ != NULL && str != nil) {

    if (xmlNode_->type == XML_NAMESPACE_DECL) {

      // for a namespace node, the value is the namespace URI
      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;

      if (nsNode->href != NULL) xmlFree((char *)nsNode->href);

      nsNode->href = xmlStrdup(GDataGetXMLString(str));

    } else {

      // attribute or element node

      // do we need to call xmlEncodeSpecialChars?
      xmlNodeSetContent(xmlNode_, GDataGetXMLString(str));
    }
  }
}

- (NSString *)stringValue {

  NSString *str = nil;

  if (xmlNode_ != NULL) {

    if (xmlNode_->type == XML_NAMESPACE_DECL) {

      // for a namespace node, the value is the namespace URI
      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;

      str = [self stringFromXMLString:(nsNode->href)];

    } else {

      // attribute or element node
      xmlChar* chars = xmlNodeGetContent(xmlNode_);
      if (chars) {

        str = [self stringFromXMLString:chars];

        xmlFree(chars);
      }
    }
  }
  return [str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

- (NSString *)XMLString {

  NSString *str = nil;

  if (xmlNode_ != NULL) {

    xmlBufferPtr buff = xmlBufferCreate();
    if (buff) {

      xmlDocPtr doc = NULL;
      int level = 0;
      int format = 0;

      int result = xmlNodeDump(buff, doc, xmlNode_, level, format);

      if (result > -1) {
        str = [[[NSString alloc] initWithBytes:(xmlBufferContent(buff))
                                        length:(xmlBufferLength(buff))
                                      encoding:NSUTF8StringEncoding] autorelease];
      }
      xmlBufferFree(buff);
    }
  }

  // remove leading and trailing whitespace
  NSCharacterSet *ws = [NSCharacterSet whitespaceAndNewlineCharacterSet];
  NSString *trimmed = [str stringByTrimmingCharactersInSet:ws];
  return trimmed;
}

- (NSString *)localName {
  NSString *str = nil;

  if (xmlNode_ != NULL) {

    str = [self stringFromXMLString:(xmlNode_->name)];

    // if this is part of a detached subtree, str may have a prefix in it
    str = [[self class] localNameForName:str];
  }
  return str;
}

- (NSString *)prefix {

  NSString *str = nil;

  if (xmlNode_ != NULL) {

    // the default namespace's prefix is an empty string, though libxml
    // represents it as NULL for ns->prefix
    str = @"";

    if (xmlNode_->ns != NULL && xmlNode_->ns->prefix != NULL) {
      str = [self stringFromXMLString:(xmlNode_->ns->prefix)];
    }
  }
  return str;
}

- (NSString *)URI {

  NSString *str = nil;

  if (xmlNode_ != NULL) {

    if (xmlNode_->ns != NULL && xmlNode_->ns->href != NULL) {
      str = [self stringFromXMLString:(xmlNode_->ns->href)];
    }
  }
  return str;
}

- (NSString *)qualifiedName {
  // internal utility

  NSString *str = nil;

  if (xmlNode_ != NULL) {
    if (xmlNode_->type == XML_NAMESPACE_DECL) {

      // name of a namespace node
      xmlNsPtr nsNode = (xmlNsPtr)xmlNode_;

      // null is the default namespace; one is the loneliest number
      if (nsNode->prefix == NULL) {
        str = @"";
      }
      else {
        str = [self stringFromXMLString:(nsNode->prefix)];
      }

    } else if (xmlNode_->ns != NULL && xmlNode_->ns->prefix != NULL) {

      // name of a non-namespace node

      // has a prefix
      char *qname;
      if (asprintf(&qname, "%s:%s", (const char *)xmlNode_->ns->prefix,
                   xmlNode_->name) != -1) {
        str = [self stringFromXMLString:(const xmlChar *)qname];
        free(qname);
      }
    } else {
      // lacks a prefix
      str = [self stringFromXMLString:(xmlNode_->name)];
    }
  }

  return str;
}

- (NSString *)name {

  if (cachedName_ != nil) {
    return cachedName_;
  }

  NSString *str = [self qualifiedName];

  cachedName_ = [str retain];

  return str;
}

+ (NSString *)localNameForName:(NSString *)name {
  if (name != nil) {

    NSRange range = [name rangeOfString:@":"];
    if (range.location != NSNotFound) {

      // found a colon
      if (range.location + 1 < [name length]) {
        NSString *localName = [name substringFromIndex:(range.location + 1)];
        return localName;
      }
    }
  }
  return name;
}

+ (NSString *)prefixForName:(NSString *)name {
  if (name != nil) {

    NSRange range = [name rangeOfString:@":"];
    if (range.location != NSNotFound) {

      NSString *prefix = [name substringToIndex:(range.location)];
      return prefix;
    }
  }
  return nil;
}

- (NSUInteger)childCount {

  if (cachedChildren_ != nil) {
    return [cachedChildren_ count];
  }

  if (xmlNode_ != NULL) {

    unsigned int count = 0;

    xmlNodePtr currChild = xmlNode_->children;

    while (currChild != NULL) {
      ++count;
      currChild = currChild->next;
    }
    return count;
  }
  return 0;
}

- (NSArray *)children {

  if (cachedChildren_ != nil) {
    return cachedChildren_;
  }

  NSMutableArray *array = nil;

  if (xmlNode_ != NULL) {

    xmlNodePtr currChild = xmlNode_->children;

    while (currChild != NULL) {
      GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currChild];

      if (array == nil) {
        array = [NSMutableArray arrayWithObject:node];
      } else {
        [array addObject:node];
      }

      currChild = currChild->next;
    }

    cachedChildren_ = [array retain];
  }
  return array;
}

- (GDataXMLNode *)childAtIndex:(unsigned)index {

  NSArray *children = [self children];

  if ([children count] > index) {

    return [children objectAtIndex:index];
  }
  return nil;
}

- (GDataXMLNodeKind)kind {
  if (xmlNode_ != NULL) {
    xmlElementType nodeType = xmlNode_->type;
    switch (nodeType) {
      case XML_ELEMENT_NODE:         return GDataXMLElementKind;
      case XML_ATTRIBUTE_NODE:       return GDataXMLAttributeKind;
      case XML_TEXT_NODE:            return GDataXMLTextKind;
      case XML_CDATA_SECTION_NODE:   return GDataXMLTextKind;
      case XML_ENTITY_REF_NODE:      return GDataXMLEntityDeclarationKind;
      case XML_ENTITY_NODE:          return GDataXMLEntityDeclarationKind;
      case XML_PI_NODE:              return GDataXMLProcessingInstructionKind;
      case XML_COMMENT_NODE:         return GDataXMLCommentKind;
      case XML_DOCUMENT_NODE:        return GDataXMLDocumentKind;
      case XML_DOCUMENT_TYPE_NODE:   return GDataXMLDocumentKind;
      case XML_DOCUMENT_FRAG_NODE:   return GDataXMLDocumentKind;
      case XML_NOTATION_NODE:        return GDataXMLNotationDeclarationKind;
      case XML_HTML_DOCUMENT_NODE:   return GDataXMLDocumentKind;
      case XML_DTD_NODE:             return GDataXMLDTDKind;
      case XML_ELEMENT_DECL:         return GDataXMLElementDeclarationKind;
      case XML_ATTRIBUTE_DECL:       return GDataXMLAttributeDeclarationKind;
      case XML_ENTITY_DECL:          return GDataXMLEntityDeclarationKind;
      case XML_NAMESPACE_DECL:       return GDataXMLNamespaceKind;
      case XML_XINCLUDE_START:       return GDataXMLProcessingInstructionKind;
      case XML_XINCLUDE_END:         return GDataXMLProcessingInstructionKind;
      case XML_DOCB_DOCUMENT_NODE:   return GDataXMLDocumentKind;
    }
  }
  return GDataXMLInvalidKind;
}

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
  // call through with no explicit namespace dictionary; that will register the
  // root node's namespaces
  return [self nodesForXPath:xpath namespaces:nil error:error];
}

- (NSArray *)nodesForXPath:(NSString *)xpath
                namespaces:(NSDictionary *)namespaces
                     error:(NSError **)error {

  NSMutableArray *array = nil;
  NSInteger errorCode = -1;
  NSDictionary *errorInfo = nil;

  // xmlXPathNewContext requires a doc for its context, but if our elements
  // are created from GDataXMLElement's initWithXMLString there may not be
  // a document. (We may later decide that we want to stuff the doc used
  // there into a GDataXMLDocument and retain it, but we don't do that now.)
  //
  // We'll temporarily make a document to use for the xpath context.

  xmlDocPtr tempDoc = NULL;
  xmlNodePtr topParent = NULL;

  if (xmlNode_->doc == NULL) {
    tempDoc = xmlNewDoc(NULL);
    if (tempDoc) {
      // find the topmost node of the current tree to make the root of
      // our temporary document
      topParent = xmlNode_;
      while (topParent->parent != NULL) {
        topParent = topParent->parent;
      }
      xmlDocSetRootElement(tempDoc, topParent);
    }
  }

  if (xmlNode_ != NULL && xmlNode_->doc != NULL) {

    xmlXPathContextPtr xpathCtx = xmlXPathNewContext(xmlNode_->doc);
    if (xpathCtx) {
      // anchor at our current node
      xpathCtx->node = xmlNode_;

      // if a namespace dictionary was provided, register its contents
      if (namespaces) {
        // the dictionary keys are prefixes; the values are URIs
        for (NSString *prefix in namespaces) {
          NSString *uri = [namespaces objectForKey:prefix];

          xmlChar *prefixChars = (xmlChar *) [prefix UTF8String];
          xmlChar *uriChars = (xmlChar *) [uri UTF8String];
          int result = xmlXPathRegisterNs(xpathCtx, prefixChars, uriChars);
          if (result != 0) {
#if DEBUG
            NSCAssert1(result == 0, @"GDataXMLNode XPath namespace %@ issue",
                      prefix);
#endif
          }
        }
      } else {
        // no namespace dictionary was provided
        //
        // register the namespaces of this node, if it's an element, or of
        // this node's root element, if it's a document
        xmlNodePtr nsNodePtr = xmlNode_;
        if (xmlNode_->type == XML_DOCUMENT_NODE) {
          nsNodePtr = xmlDocGetRootElement((xmlDocPtr) xmlNode_);
        }

        // step through the namespaces, if any, and register each with the
        // xpath context
        if (nsNodePtr != NULL) {
          for (xmlNsPtr nsPtr = nsNodePtr->ns; nsPtr != NULL; nsPtr = nsPtr->next) {

            // default namespace is nil in the tree, but there's no way to
            // register a default namespace, so we'll register a fake one,
            // _def_ns
            const xmlChar* prefix = nsPtr->prefix;
            if (prefix == NULL) {
              prefix = (xmlChar*) kGDataXMLXPathDefaultNamespacePrefix;
            }

            int result = xmlXPathRegisterNs(xpathCtx, prefix, nsPtr->href);
            if (result != 0) {
#if DEBUG
              NSCAssert1(result == 0, @"GDataXMLNode XPath namespace %s issue",
                        prefix);
#endif
            }
          }
        }
      }

      // now evaluate the path
      xmlXPathObjectPtr xpathObj;
      xpathObj = xmlXPathEval(GDataGetXMLString(xpath), xpathCtx);
      if (xpathObj) {

        // we have some result from the search
        array = [NSMutableArray array];

        xmlNodeSetPtr nodeSet = xpathObj->nodesetval;
        if (nodeSet) {

          // add each node in the result set to our array
          for (int index = 0; index < nodeSet->nodeNr; index++) {

            xmlNodePtr currNode = nodeSet->nodeTab[index];

            GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:currNode];
            if (node) {
              [array addObject:node];
            }
          }
        }
        xmlXPathFreeObject(xpathObj);
      } else {
        // provide an error for failed evaluation
        const char *msg = xpathCtx->lastError.str1;
        errorCode = xpathCtx->lastError.code;
        if (msg) {
          NSString *errStr = [NSString stringWithUTF8String:msg];
          errorInfo = [NSDictionary dictionaryWithObject:errStr
                                                  forKey:@"error"];
        }
      }

      xmlXPathFreeContext(xpathCtx);
    }
  } else {
    // not a valid node for using XPath
    errorInfo = [NSDictionary dictionaryWithObject:@"invalid node"
                                            forKey:@"error"];
  }

  if (array == nil && error != nil) {
    *error = [NSError errorWithDomain:@"com.google.GDataXML"
                                 code:errorCode
                             userInfo:errorInfo];
  }

  if (tempDoc != NULL) {
    xmlUnlinkNode(topParent);
    xmlSetTreeDoc(topParent, NULL);
    xmlFreeDoc(tempDoc);
  }
  return array;
}

- (NSString *)description {
  int nodeType = (xmlNode_ ? (int)xmlNode_->type : -1);

  return [NSString stringWithFormat:@"%@ %p: {type:%d name:%@ xml:\"%@\"}",
          [self class], self, nodeType, [self name], [self XMLString]];
}

- (id)copyWithZone:(NSZone *)zone {

  xmlNodePtr nodeCopy = [self XMLNodeCopy];

  if (nodeCopy != NULL) {
    return [[[self class] alloc] initConsumingXMLNode:nodeCopy];
  }
  return nil;
}

- (BOOL)isEqual:(GDataXMLNode *)other {
  if (self == other) return YES;
  if (![other isKindOfClass:[GDataXMLNode class]]) return NO;

  return [self XMLNode] == [other XMLNode]
  || ([self kind] == [other kind]
      && AreEqualOrBothNilPrivate([self name], [other name])
      && [[self children] count] == [[other children] count]);

}

- (NSUInteger)hash {
  return (NSUInteger) (void *) [GDataXMLNode class];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
  return [super methodSignatureForSelector:selector];
}

#pragma mark -

- (xmlNodePtr)XMLNodeCopy {
  if (xmlNode_ != NULL) {

    // Note: libxml will create a new copy of namespace nodes (xmlNs records)
    // and attach them to this copy in order to keep namespaces within this
    // node subtree copy value.

    xmlNodePtr nodeCopy = xmlCopyNode(xmlNode_, 1); // 1 = recursive
    return nodeCopy;
  }
  return NULL;
}

- (xmlNodePtr)XMLNode {
  return xmlNode_;
}

- (BOOL)shouldFreeXMLNode {
  return shouldFreeXMLNode_;
}

- (void)setShouldFreeXMLNode:(BOOL)flag {
  shouldFreeXMLNode_ = flag;
}

@end



@implementation GDataXMLElement

- (id)initWithXMLString:(NSString *)str error:(NSError **)error {
  self = [super init];
  if (self) {

    const char *utf8Str = [str UTF8String];
    // NOTE: We are assuming a string length that fits into an int
    xmlDocPtr doc = xmlReadMemory(utf8Str, (int)strlen(utf8Str), NULL, // URL
                                  NULL, // encoding
                                  kGDataXMLParseOptions);
    if (doc == NULL) {
      if (error) {
        // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
      }
    } else {
      // copy the root node from the doc
      xmlNodePtr root = xmlDocGetRootElement(doc);
      if (root) {
        xmlNode_ = xmlCopyNode(root, 1); // 1: recursive
      }
      xmlFreeDoc(doc);
    }


    if (xmlNode_ == NULL) {
      // failure
      if (error) {
        *error = [NSError errorWithDomain:@"com.google.GDataXML"
                                     code:-1
                                 userInfo:nil];
      }
      [self release];
      return nil;
    }
  }
  return self;
}

- (NSArray *)namespaces {

  NSMutableArray *array = nil;

  if (xmlNode_ != NULL && xmlNode_->nsDef != NULL) {

    xmlNsPtr currNS = xmlNode_->nsDef;
    while (currNS != NULL) {

      // add this prefix/URI to the list, unless it's the implicit xml prefix
      if (!xmlStrEqual(currNS->prefix, (const xmlChar *) "xml")) {
        GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:(xmlNodePtr) currNS];

        if (array == nil) {
          array = [NSMutableArray arrayWithObject:node];
        } else {
          [array addObject:node];
        }
      }

      currNS = currNS->next;
    }
  }
  return array;
}

- (void)setNamespaces:(NSArray *)namespaces {

  if (xmlNode_ != NULL) {

    [self releaseCachedValues];

    // remove previous namespaces
    if (xmlNode_->nsDef) {
      xmlFreeNsList(xmlNode_->nsDef);
      xmlNode_->nsDef = NULL;
    }

    // add a namespace for each object in the array
    NSEnumerator *enumerator = [namespaces objectEnumerator];
    GDataXMLNode *namespace;
    while ((namespace = [enumerator nextObject]) != nil) {

      xmlNsPtr ns = (xmlNsPtr) [namespace XMLNode];
      if (ns) {
        (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);
      }
    }

    // we may need to fix this node's own name; the graft point is where
    // the namespace search starts, so that points to this node too
    [[self class] fixUpNamespacesForNode:xmlNode_
                      graftingToTreeNode:xmlNode_];
  }
}

- (void)addNamespace:(GDataXMLNode *)aNamespace {

  if (xmlNode_ != NULL) {

    [self releaseCachedValues];

    xmlNsPtr ns = (xmlNsPtr) [aNamespace XMLNode];
    if (ns) {
      (void)xmlNewNs(xmlNode_, ns->href, ns->prefix);

      // we may need to fix this node's own name; the graft point is where
      // the namespace search starts, so that points to this node too
      [[self class] fixUpNamespacesForNode:xmlNode_
                        graftingToTreeNode:xmlNode_];
    }
  }
}

- (void)addChild:(GDataXMLNode *)child {
  if ([child kind] == GDataXMLAttributeKind) {
    [self addAttribute:child];
    return;
  }

  if (xmlNode_ != NULL) {

    [self releaseCachedValues];

    xmlNodePtr childNodeCopy = [child XMLNodeCopy];
    if (childNodeCopy) {

      xmlNodePtr resultNode = xmlAddChild(xmlNode_, childNodeCopy);
      if (resultNode == NULL) {

        // failed to add
        xmlFreeNode(childNodeCopy);

      } else {
        // added this child subtree successfully; see if it has
        // previously-unresolved namespace prefixes that can now be fixed up
        [[self class] fixUpNamespacesForNode:childNodeCopy
                          graftingToTreeNode:xmlNode_];
      }
    }
  }
}

- (void)removeChild:(GDataXMLNode *)child {
  // this is safe for attributes too
  if (xmlNode_ != NULL) {

    [self releaseCachedValues];

    xmlNodePtr node = [child XMLNode];

    xmlUnlinkNode(node);

    // if the child node was borrowing its xmlNodePtr, then we need to
    // explicitly free it, since there is probably no owning object that will
    // free it on dealloc
    if (![child shouldFreeXMLNode]) {
      xmlFreeNode(node);
    }
  }
}

- (NSArray *)elementsForName:(NSString *)name {

  NSString *desiredName = name;

  if (xmlNode_ != NULL) {

    NSString *prefix = [[self class] prefixForName:desiredName];
    if (prefix) {

      xmlChar* desiredPrefix = GDataGetXMLString(prefix);

      xmlNsPtr foundNS = xmlSearchNs(xmlNode_->doc, xmlNode_, desiredPrefix);
      if (foundNS) {

        // we found a namespace; fall back on elementsForLocalName:URI:
        // to get the elements
        NSString *desiredURI = [self stringFromXMLString:(foundNS->href)];
        NSString *localName = [[self class] localNameForName:desiredName];

        NSArray *nsArray = [self elementsForLocalName:localName URI:desiredURI];
        return nsArray;
      }
    }

    // no namespace found for the node's prefix; try an exact match
    // for the name argument, including any prefix
    NSMutableArray *array = nil;

    // walk our list of cached child nodes
    NSArray *children = [self children];

    for (GDataXMLNode *child in children) {

      xmlNodePtr currNode = [child XMLNode];

      // find all children which are elements with the desired name
      if (currNode->type == XML_ELEMENT_NODE) {

        NSString *qName = [child name];
        if ([qName isEqual:name]) {

          if (array == nil) {
            array = [NSMutableArray arrayWithObject:child];
          } else {
            [array addObject:child];
          }
        }
      }
    }
    return array;
  }
  return nil;
}

- (NSArray *)elementsForLocalName:(NSString *)localName URI:(NSString *)URI {

  NSMutableArray *array = nil;

  if (xmlNode_ != NULL && xmlNode_->children != NULL) {

    xmlChar* desiredNSHref = GDataGetXMLString(URI);
    xmlChar* requestedLocalName = GDataGetXMLString(localName);
    xmlChar* expectedLocalName = requestedLocalName;

    // resolve the URI at the parent level, since usually children won't
    // have their own namespace definitions, and we don't want to try to
    // resolve it once for every child
    xmlNsPtr foundParentNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, desiredNSHref);
    if (foundParentNS == NULL) {
      NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);
      expectedLocalName = GDataGetXMLString(fakeQName);
    }

    NSArray *children = [self children];

    for (GDataXMLNode *child in children) {

      xmlNodePtr currChildPtr = [child XMLNode];

      // find all children which are elements with the desired name and
      // namespace, or with the prefixed name and a null namespace
      if (currChildPtr->type == XML_ELEMENT_NODE) {

        // normally, we can assume the resolution done for the parent will apply
        // to the child, as most children do not define their own namespaces
        xmlNsPtr childLocalNS = foundParentNS;
        xmlChar* childDesiredLocalName = expectedLocalName;

        if (currChildPtr->nsDef != NULL) {
          // this child has its own namespace definitons; do a fresh resolve
          // of the namespace starting from the child, and see if it differs
          // from the resolve done starting from the parent.  If the resolve
          // finds a different namespace, then override the desired local
          // name just for this child.
          childLocalNS = xmlSearchNsByHref(xmlNode_->doc, currChildPtr, desiredNSHref);
          if (childLocalNS != foundParentNS) {

            // this child does indeed have a different namespace resolution
            // result than was found for its parent
            if (childLocalNS == NULL) {
              // no namespace found
              NSString *fakeQName = GDataFakeQNameForURIAndName(URI, localName);
              childDesiredLocalName = GDataGetXMLString(fakeQName);
            } else {
              // a namespace was found; use the original local name requested,
              // not a faked one expected from resolving the parent
              childDesiredLocalName = requestedLocalName;
            }
          }
        }

        // check if this child's namespace and local name are what we're
        // seeking
        if (currChildPtr->ns == childLocalNS
            && currChildPtr->name != NULL
            && xmlStrEqual(currChildPtr->name, childDesiredLocalName)) {

          if (array == nil) {
            array = [NSMutableArray arrayWithObject:child];
          } else {
            [array addObject:child];
          }
        }
      }
    }
    // we return nil, not an empty array, according to docs
  }
  return array;
}

- (NSArray *)attributes {

  if (cachedAttributes_ != nil) {
    return cachedAttributes_;
  }

  NSMutableArray *array = nil;

  if (xmlNode_ != NULL && xmlNode_->properties != NULL) {

    xmlAttrPtr prop = xmlNode_->properties;
    while (prop != NULL) {

      GDataXMLNode *node = [GDataXMLNode nodeBorrowingXMLNode:(xmlNodePtr) prop];
      if (array == nil) {
        array = [NSMutableArray arrayWithObject:node];
      } else {
        [array addObject:node];
      }

      prop = prop->next;
    }

    cachedAttributes_ = [array retain];
  }
  return array;
}

- (void)addAttribute:(GDataXMLNode *)attribute {

  if (xmlNode_ != NULL) {

    [self releaseCachedValues];

    xmlAttrPtr attrPtr = (xmlAttrPtr) [attribute XMLNode];
    if (attrPtr) {

      // ignore this if an attribute with the name is already present,
      // similar to NSXMLNode's addAttribute
      xmlAttrPtr oldAttr;

      if (attrPtr->ns == NULL) {
        oldAttr = xmlHasProp(xmlNode_, attrPtr->name);
      } else {
        oldAttr = xmlHasNsProp(xmlNode_, attrPtr->name, attrPtr->ns->href);
      }

      if (oldAttr == NULL) {

        xmlNsPtr newPropNS = NULL;

        // if this attribute has a namespace, search for a matching namespace
        // on the node we're adding to
        if (attrPtr->ns != NULL) {

          newPropNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, attrPtr->ns->href);
          if (newPropNS == NULL) {
            // make a new namespace on the parent node, and use that for the
            // new attribute
            newPropNS = xmlNewNs(xmlNode_, attrPtr->ns->href, attrPtr->ns->prefix);
          }
        }

        // copy the attribute onto this node
        xmlChar *value = xmlNodeGetContent((xmlNodePtr) attrPtr);
        xmlAttrPtr newProp = xmlNewNsProp(xmlNode_, newPropNS, attrPtr->name, value);
        if (newProp != NULL) {
          // we made the property, so clean up the property's namespace

          [[self class] fixUpNamespacesForNode:(xmlNodePtr)newProp
                            graftingToTreeNode:xmlNode_];
        }

        if (value != NULL) {
          xmlFree(value);
        }
      }
    }
  }
}

- (GDataXMLNode *)attributeForXMLNode:(xmlAttrPtr)theXMLNode {
  // search the cached attributes list for the GDataXMLNode with
  // the underlying xmlAttrPtr
  NSArray *attributes = [self attributes];

  for (GDataXMLNode *attr in attributes) {

    if (theXMLNode == (xmlAttrPtr) [attr XMLNode]) {
      return attr;
    }
  }

  return nil;
}

- (GDataXMLNode *)attributeForName:(NSString *)name {

  if (xmlNode_ != NULL) {

    xmlAttrPtr attrPtr = xmlHasProp(xmlNode_, GDataGetXMLString(name));
    if (attrPtr == NULL) {

      // can we guarantee that xmlAttrPtrs always have the ns ptr and never
      // a namespace as part of the actual attribute name?

      // split the name and its prefix, if any
      xmlNsPtr ns = NULL;
      NSString *prefix = [[self class] prefixForName:name];
      if (prefix) {

        // find the namespace for this prefix, and search on its URI to find
        // the xmlNsPtr
        name = [[self class] localNameForName:name];
        ns = xmlSearchNs(xmlNode_->doc, xmlNode_, GDataGetXMLString(prefix));
      }

      const xmlChar* nsURI = ((ns != NULL) ? ns->href : NULL);
      attrPtr = xmlHasNsProp(xmlNode_, GDataGetXMLString(name), nsURI);
    }

    if (attrPtr) {
      GDataXMLNode *attr = [self attributeForXMLNode:attrPtr];
      return attr;
    }
  }
  return nil;
}

- (GDataXMLNode *)attributeForLocalName:(NSString *)localName
                                    URI:(NSString *)attributeURI {

  if (xmlNode_ != NULL) {

    const xmlChar* name = GDataGetXMLString(localName);
    const xmlChar* nsURI = GDataGetXMLString(attributeURI);

    xmlAttrPtr attrPtr = xmlHasNsProp(xmlNode_, name, nsURI);

    if (attrPtr == NULL) {
      // if the attribute is in a tree lacking the proper namespace,
      // the local name may include the full URI as a prefix
      NSString *fakeQName = GDataFakeQNameForURIAndName(attributeURI, localName);
      const xmlChar* xmlFakeQName = GDataGetXMLString(fakeQName);

      attrPtr = xmlHasProp(xmlNode_, xmlFakeQName);
    }

    if (attrPtr) {
      GDataXMLNode *attr = [self attributeForXMLNode:attrPtr];
      return attr;
    }
  }
  return nil;
}

- (NSString *)resolvePrefixForNamespaceURI:(NSString *)namespaceURI {

  if (xmlNode_ != NULL) {

    xmlChar* desiredNSHref = GDataGetXMLString(namespaceURI);

    xmlNsPtr foundNS = xmlSearchNsByHref(xmlNode_->doc, xmlNode_, desiredNSHref);
    if (foundNS) {

      // we found the namespace
      if (foundNS->prefix != NULL) {
        NSString *prefix = [self stringFromXMLString:(foundNS->prefix)];
        return prefix;
      } else {
        // empty prefix is default namespace
        return @"";
      }
    }
  }
  return nil;
}

#pragma mark Namespace fixup routines

+ (void)deleteNamespacePtr:(xmlNsPtr)namespaceToDelete
               fromXMLNode:(xmlNodePtr)node {

  // utilty routine to remove a namespace pointer from an element's
  // namespace definition list.  This is just removing the nsPtr
  // from the singly-linked list, the node's namespace definitions.
  xmlNsPtr currNS = node->nsDef;
  xmlNsPtr prevNS = NULL;

  while (currNS != NULL) {
    xmlNsPtr nextNS = currNS->next;

    if (namespaceToDelete == currNS) {

      // found it; delete it from the head of the node's ns definition list
      // or from the next field of the previous namespace

      if (prevNS != NULL) prevNS->next = nextNS;
      else node->nsDef = nextNS;

      xmlFreeNs(currNS);
      return;
    }
    prevNS = currNS;
    currNS = nextNS;
  }
}

+ (void)fixQualifiedNamesForNode:(xmlNodePtr)nodeToFix
              graftingToTreeNode:(xmlNodePtr)graftPointNode {

  // Replace prefix-in-name with proper namespace pointers
  //
  // This is an inner routine for fixUpNamespacesForNode:
  //
  // see if this node's name lacks a namespace and is qualified, and if so,
  // see if we can resolve the prefix against the parent
  //
  // The prefix may either be normal, "gd:foo", or a URI
  // "{http://blah.com/}:foo"

  if (nodeToFix->ns == NULL) {
    xmlNsPtr foundNS = NULL;

    xmlChar* prefix = NULL;
    xmlChar* localName = SplitQNameReverse(nodeToFix->name, &prefix);
    if (localName != NULL) {
      if (prefix != NULL) {

        // if the prefix is wrapped by { and } then it's a URI
        int prefixLen = xmlStrlen(prefix);
        if (prefixLen > 2
            && prefix[0] == '{'
            && prefix[prefixLen - 1] == '}') {

          // search for the namespace by URI
          xmlChar* uri = xmlStrsub(prefix, 1, prefixLen - 2);

          if (uri != NULL) {
            foundNS = xmlSearchNsByHref(graftPointNode->doc, graftPointNode, uri);

            xmlFree(uri);
          }
        }
      }

      if (foundNS == NULL) {
        // search for the namespace by prefix, even if the prefix is nil
        // (nil prefix means to search for the default namespace)
        foundNS = xmlSearchNs(graftPointNode->doc, graftPointNode, prefix);
      }

      if (foundNS != NULL) {
        // we found a namespace, so fix the ns pointer and the local name
        xmlSetNs(nodeToFix, foundNS);
        xmlNodeSetName(nodeToFix, localName);
      }

      if (prefix != NULL) {
        xmlFree(prefix);
        prefix = NULL;
      }

      xmlFree(localName);
    }
  }
}

+ (void)fixDuplicateNamespacesForNode:(xmlNodePtr)nodeToFix
                   graftingToTreeNode:(xmlNodePtr)graftPointNode
             namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {

  // Duplicate namespace removal
  //
  // This is an inner routine for fixUpNamespacesForNode:
  //
  // If any of this node's namespaces are already defined at the graft point
  // level, add that namespace to the map of namespace substitutions
  // so it will be replaced in the children below the nodeToFix, and
  // delete the namespace record

  if (nodeToFix->type == XML_ELEMENT_NODE) {

    // step through the namespaces defined on this node
    xmlNsPtr definedNS = nodeToFix->nsDef;
    while (definedNS != NULL) {

      // see if this namespace is already defined higher in the tree,
      // with both the same URI and the same prefix; if so, add a mapping for
      // it
      xmlNsPtr foundNS = xmlSearchNsByHref(graftPointNode->doc, graftPointNode,
                                           definedNS->href);
      if (foundNS != NULL
          && foundNS != definedNS
          && xmlStrEqual(definedNS->prefix, foundNS->prefix)) {

        // store a mapping from this defined nsPtr to the one found higher
        // in the tree
        [nsMap setObject:[NSValue valueWithPointer:foundNS]
                  forKey:[NSValue valueWithPointer:definedNS]];

        // remove this namespace from the ns definition list of this node;
        // all child elements and attributes referencing this namespace
        // now have a dangling pointer and must be updated (that is done later
        // in this method)
        //
        // before we delete this namespace, move our pointer to the
        // next one
        xmlNsPtr nsToDelete = definedNS;
        definedNS = definedNS->next;

        [self deleteNamespacePtr:nsToDelete fromXMLNode:nodeToFix];

      } else {
        // this namespace wasn't a duplicate; move to the next
        definedNS = definedNS->next;
      }
    }
  }

  // if this node's namespace is one we deleted, update it to point
  // to someplace better
  if (nodeToFix->ns != NULL) {

    NSValue *currNS = [NSValue valueWithPointer:nodeToFix->ns];
    NSValue *replacementNS = [nsMap objectForKey:currNS];

    if (replacementNS != nil) {
      xmlNsPtr replaceNSPtr = [replacementNS pointerValue];

      xmlSetNs(nodeToFix, replaceNSPtr);
    }
  }
}



+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
            graftingToTreeNode:(xmlNodePtr)graftPointNode
      namespaceSubstitutionMap:(NSMutableDictionary *)nsMap {

  // This is the inner routine for fixUpNamespacesForNode:graftingToTreeNode:
  //
  // This routine fixes two issues:
  //
  // Because we can create nodes with qualified names before adding
  // them to the tree that declares the namespace for the prefix,
  // we need to set the node namespaces after adding them to the tree.
  //
  // Because libxml adds namespaces to nodes when it copies them,
  // we want to remove redundant namespaces after adding them to
  // a tree.
  //
  // If only the Mac's libxml had xmlDOMWrapReconcileNamespaces, it could do
  // namespace cleanup for us

  // We only care about fixing names of elements and attributes
  if (nodeToFix->type != XML_ELEMENT_NODE
      && nodeToFix->type != XML_ATTRIBUTE_NODE) return;

  // Do the fixes
  [self fixQualifiedNamesForNode:nodeToFix
              graftingToTreeNode:graftPointNode];

  [self fixDuplicateNamespacesForNode:nodeToFix
                   graftingToTreeNode:graftPointNode
             namespaceSubstitutionMap:nsMap];

  if (nodeToFix->type == XML_ELEMENT_NODE) {

    // when fixing element nodes, recurse for each child element and
    // for each attribute
    xmlNodePtr currChild = nodeToFix->children;
    while (currChild != NULL) {
      [self fixUpNamespacesForNode:currChild
                graftingToTreeNode:graftPointNode
          namespaceSubstitutionMap:nsMap];
      currChild = currChild->next;
    }

    xmlAttrPtr currProp = nodeToFix->properties;
    while (currProp != NULL) {
      [self fixUpNamespacesForNode:(xmlNodePtr)currProp
                graftingToTreeNode:graftPointNode
          namespaceSubstitutionMap:nsMap];
      currProp = currProp->next;
    }
  }
}

+ (void)fixUpNamespacesForNode:(xmlNodePtr)nodeToFix
            graftingToTreeNode:(xmlNodePtr)graftPointNode {

  // allocate the namespace map that will be passed
  // down on recursive calls
  NSMutableDictionary *nsMap = [NSMutableDictionary dictionary];

  [self fixUpNamespacesForNode:nodeToFix
            graftingToTreeNode:graftPointNode
      namespaceSubstitutionMap:nsMap];
}

@end


@interface GDataXMLDocument (PrivateMethods)
- (void)addStringsCacheToDoc;
@end

@implementation GDataXMLDocument

- (id)initWithXMLString:(NSString *)str options:(unsigned int)mask error:(NSError **)error {

  NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
  GDataXMLDocument *doc = [self initWithData:data options:mask error:error];
  return doc;
}

- (id)initWithData:(NSData *)data options:(unsigned int)mask error:(NSError **)error {

  self = [super init];
  if (self) {

    const char *baseURL = NULL;
    const char *encoding = NULL;

    // NOTE: We are assuming [data length] fits into an int.
    xmlDoc_ = xmlReadMemory([data bytes], (int)[data length], baseURL, encoding,
                            kGDataXMLParseOptions); // TODO(grobbins) map option values
    if (xmlDoc_ == NULL) {
      if (error) {
       *error = [NSError errorWithDomain:@"com.google.GDataXML"
                                    code:-1
                                userInfo:nil];
        // TODO(grobbins) use xmlSetGenericErrorFunc to capture error
        [self release];
      }
      return nil;
    } else {
      if (error) *error = NULL;

      [self addStringsCacheToDoc];
    }
  }

  return self;
}

- (id)initWithRootElement:(GDataXMLElement *)element {

  self = [super init];
  if (self) {

    xmlDoc_ = xmlNewDoc(NULL);

    (void) xmlDocSetRootElement(xmlDoc_, [element XMLNodeCopy]);

    [self addStringsCacheToDoc];
  }

  return self;
}

- (void)addStringsCacheToDoc {
  // utility routine for init methods

#if DEBUG
  NSCAssert(xmlDoc_ != NULL && xmlDoc_->_private == NULL,
            @"GDataXMLDocument cache creation problem");
#endif

  // add a strings cache as private data for the document
  //
  // we'll use plain C pointers (xmlChar*) as the keys, and NSStrings
  // as the values
  CFIndex capacity = 0; // no limit

  CFDictionaryKeyCallBacks keyCallBacks = {
    0, // version
    StringCacheKeyRetainCallBack,
    StringCacheKeyReleaseCallBack,
    StringCacheKeyCopyDescriptionCallBack,
    StringCacheKeyEqualCallBack,
    StringCacheKeyHashCallBack
  };

  CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
    kCFAllocatorDefault, capacity,
    &keyCallBacks, &kCFTypeDictionaryValueCallBacks);

  // we'll use the user-defined _private field for our cache
  xmlDoc_->_private = dict;
}

- (NSString *)description {
  return [NSString stringWithFormat:@"%@ %p", [self class], self];
}

- (void)dealloc {
  if (xmlDoc_ != NULL) {
    // release the strings cache
    //
    // since it's a CF object, were anyone to use this in a GC environment,
    // this would need to be released in a finalize method, too
    if (xmlDoc_->_private != NULL) {
      CFRelease(xmlDoc_->_private);
    }

    xmlFreeDoc(xmlDoc_);
  }
  [super dealloc];
}

#pragma mark -

- (GDataXMLElement *)rootElement {
  GDataXMLElement *element = nil;

  if (xmlDoc_ != NULL) {
    xmlNodePtr rootNode = xmlDocGetRootElement(xmlDoc_);
    if (rootNode) {
      element = [GDataXMLElement nodeBorrowingXMLNode:rootNode];
    }
  }
  return element;
}

- (NSData *)XMLData {

  if (xmlDoc_ != NULL) {
    xmlChar *buffer = NULL;
    int bufferSize = 0;

    xmlDocDumpMemory(xmlDoc_, &buffer, &bufferSize);

    if (buffer) {
      NSData *data = [NSData dataWithBytes:buffer
                                    length:bufferSize];
      xmlFree(buffer);
      return data;
    }
  }
  return nil;
}

- (void)setVersion:(NSString *)version {

  if (xmlDoc_ != NULL) {
    if (xmlDoc_->version != NULL) {
      // version is a const char* so we must cast
      xmlFree((char *) xmlDoc_->version);
      xmlDoc_->version = NULL;
    }

    if (version != nil) {
      xmlDoc_->version = xmlStrdup(GDataGetXMLString(version));
    }
  }
}

- (void)setCharacterEncoding:(NSString *)encoding {

  if (xmlDoc_ != NULL) {
    if (xmlDoc_->encoding != NULL) {
      // version is a const char* so we must cast
      xmlFree((char *) xmlDoc_->encoding);
      xmlDoc_->encoding = NULL;
    }

    if (encoding != nil) {
      xmlDoc_->encoding = xmlStrdup(GDataGetXMLString(encoding));
    }
  }
}

- (NSArray *)nodesForXPath:(NSString *)xpath error:(NSError **)error {
  return [self nodesForXPath:xpath namespaces:nil error:error];
}

- (NSArray *)nodesForXPath:(NSString *)xpath
                namespaces:(NSDictionary *)namespaces
                     error:(NSError **)error {
  if (xmlDoc_ != NULL) {
    GDataXMLNode *docNode = [GDataXMLElement nodeBorrowingXMLNode:(xmlNodePtr)xmlDoc_];
    NSArray *array = [docNode nodesForXPath:xpath
                                 namespaces:namespaces
                                      error:error];
    return array;
  }
  return nil;
}

@end

//
// Dictionary key callbacks for our C-string to NSString cache dictionary
//
static const void *StringCacheKeyRetainCallBack(CFAllocatorRef allocator, const void *str) {
  // copy the key
  xmlChar* key = xmlStrdup(str);
  return key;
}

static void StringCacheKeyReleaseCallBack(CFAllocatorRef allocator, const void *str) {
  // free the key
  char *chars = (char *)str;
  xmlFree((char *) chars);
}

static CFStringRef StringCacheKeyCopyDescriptionCallBack(const void *str) {
  // make a CFString from the key
  CFStringRef cfStr = CFStringCreateWithCString(kCFAllocatorDefault,
                                                (const char *)str,
                                                kCFStringEncodingUTF8);
  return cfStr;
}

static Boolean StringCacheKeyEqualCallBack(const void *str1, const void *str2) {
  // compare the key strings
  if (str1 == str2) return true;

  int result = xmlStrcmp(str1, str2);
  return (result == 0);
}

static CFHashCode StringCacheKeyHashCallBack(const void *str) {

  // dhb hash, per http://www.cse.yorku.ca/~oz/hash.html
  CFHashCode hash = 5381;
  int c;
  const char *chars = (const char *)str;

  while ((c = *chars++) != 0) {
    hash = ((hash << 5) + hash) + c;
  }
  return hash;
}


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnP.h
================================================
//
//  CLUPnP.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/9/29.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#ifndef CLUPnP_h
#define CLUPnP_h

#import "CLUPnPServer.h"
#import "CLUPnPRenderer.h"
#import "CLUPnPDevice.h"
#import "CLUPnPAVPositionInfo.h"

#ifdef DEBUG
#define CLLog(s, ... ) NSLog( @"[%@ in line %d] => %@", [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#else
#define CLLog(s, ... )
#endif

static NSString *ssdpAddres = @"239.255.255.250";
static UInt16   ssdpPort = 1900;

static NSString *serviceType_AVTransport        = @"urn:schemas-upnp-org:service:AVTransport:1";
static NSString *serviceType_RenderingControl   = @"urn:schemas-upnp-org:service:RenderingControl:1";

static NSString *serviceId_AVTransport          = @"urn:upnp-org:serviceId:AVTransport";
static NSString *serviceId_RenderingControl     = @"urn:upnp-org:serviceId:RenderingControl";


static NSString *unitREL_TIME = @"REL_TIME";
static NSString *unitTRACK_NR = @"TRACK_NR";

#endif /* CLUPnP_h */


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPAVPositionInfo.h
================================================
//
//  CLUPnPAVPositionInfo.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/10.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface CLUPnPAVPositionInfo : NSObject

@property (nonatomic, assign) float trackDuration;
@property (nonatomic, assign) float absTime;
@property (nonatomic, assign) float relTime;

- (void)setArray:(NSArray *)array;

@end


@interface CLUPnPTransportInfo : NSObject

@property (nonatomic, copy) NSString *currentTransportState;
@property (nonatomic, copy) NSString *currentTransportStatus;
@property (nonatomic, copy) NSString *currentSpeed;

- (void)setArray:(NSArray *)array;

@end


@interface NSString(UPnP)

+(NSString *)stringWithDurationTime:(float)timeValue;
- (float)durationTime;

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPAVPositionInfo.m
================================================
//
//  CLUPnPAVPositionInfo.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/10.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import "CLUPnPAVPositionInfo.h"
#import "GDataXMLNode.h"

@implementation CLUPnPAVPositionInfo

- (void)setArray:(NSArray *)array {
    @autoreleasepool {
        for (int m = 0; m < array.count; m++) {
            GDataXMLElement *needEle = [array objectAtIndex:m];
            if ([needEle.name isEqualToString:@"TrackDuration"]) {
                self.trackDuration = [[needEle stringValue] durationTime];
            }
            if ([needEle.name isEqualToString:@"RelTime"]) {
                self.relTime = [[needEle stringValue] durationTime];
            }
            if ([needEle.name isEqualToString:@"AbsTime"]) {
                self.absTime = [[needEle stringValue] durationTime];
            }
        }
    }
}

@end

@implementation CLUPnPTransportInfo

- (void)setArray:(NSArray *)array {
    @autoreleasepool {        
        for (int m = 0; m < array.count; m++) {
            GDataXMLElement *needEle = [array objectAtIndex:m];
            if ([needEle.name isEqualToString:@"CurrentTransportState"]) {
                self.currentTransportState = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"CurrentTransportStatus"]) {
                self.currentTransportStatus = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"CurrentSpeed"]) {
                self.currentSpeed = [needEle stringValue];
            }
        }
    }
}

@end


@implementation  NSString(UPnP)
/*
 H+:MM:SS[.F+] or H+:MM:SS[.F0/F1]
 where :
 •	H+ means one or more digits to indicate elapsed hours
 •	MM means exactly 2 digits to indicate minutes (00 to 59)
 •	SS means exactly 2 digits to indicate seconds (00 to 59)
 •	[.F+] means optionally a dot followed by one or more digits to indicate fractions of seconds
 •	[.F0/F1] means optionally a dot followed by a fraction, with F0 and F1 at least one digit long, and F0 < F1
 */
+(NSString *)stringWithDurationTime:(float)timeValue
{
    return [NSString stringWithFormat:@"%02d:%02d:%02d",
            (int)(timeValue / 3600.0),
            (int)(fmod(timeValue, 3600.0) / 60.0),
            (int)fmod(timeValue, 60.0)];
}

- (float)durationTime
{
    NSArray *timeStrings = [self componentsSeparatedByString:@":"];
    int timeStringsCount = (int)[timeStrings count];
    if (timeStringsCount < 3)
        return -1.0f;
    float durationTime = 0.0;
    for (int n = 0; n<timeStringsCount; n++) {
        NSString *timeString = [timeStrings objectAtIndex:n];
        int timeIntValue = [timeString intValue];
        switch (n) {
            case 0: // HH
                durationTime += timeIntValue * (60 * 60);
                break;
            case 1: // MM
                durationTime += timeIntValue * 60;
                break;
            case 2: // SS
                durationTime += timeIntValue;
                break;
            case 3: // .F?
                durationTime += timeIntValue * 0.1;
                break;
            default:
                break;
        }
    }
    return durationTime;
}

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPAction.h
================================================
//
//  CLUPnPAction.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/10.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, CLUPnPServiceType) {
    CLUPnPServiceAVTransport,       // @"urn:schemas-upnp-org:service:AVTransport:1"
    CLUPnPServiceRenderingControl,  // @"urn:schemas-upnp-org:service:RenderingControl:1"
};

@class CLUPnPDevice;
@interface CLUPnPAction : NSObject

// serviceType 默认 CLUPnPServiceAVTransport
@property (nonatomic, assign) CLUPnPServiceType serviceType;

- (instancetype)initWithAction:(NSString *)action;

- (void)setArgumentValue:(NSString *)value forName:(NSString *)name;

- (NSString *)getServiceType;

- (NSString *)getSOAPAction;

- (NSString *)getPostUrlStrWith:(CLUPnPDevice *)model;

- (NSString *)getPostXMLFile;

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPAction.m
================================================
//
//  CLUPnPAction.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/10.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import "CLUPnPAction.h"
#import "GDataXMLNode.h"
#import "CLUPnP.h"

@interface CLUPnPAction () {
        NSString    *_action;
}
@property (nonatomic, strong) GDataXMLElement *XMLElement;

@end

@implementation CLUPnPAction

- (instancetype)initWithAction:(NSString *)action {
    self = [super init];
    if (self) {
        _action = action;
        _serviceType = CLUPnPServiceAVTransport;
        NSString *name = [NSString stringWithFormat:@"u:%@", _action];
        self.XMLElement = [GDataXMLElement elementWithName:name];
    }
    return self;
}

- (void)setServiceType:(CLUPnPServiceType)serviceType {
    _serviceType = serviceType;
}

- (void)setArgumentValue:(NSString *)value forName:(NSString *)name {
    [self.XMLElement addChild:[GDataXMLElement elementWithName:name stringValue:value]];
}

- (NSString *)getServiceType {
    if (_serviceType == CLUPnPServiceAVTransport) {
        return serviceType_AVTransport;
    }else{
        return serviceType_RenderingControl;
    }
}

- (NSString *)getSOAPAction {
    if (_serviceType == CLUPnPServiceAVTransport) {
        return [NSString stringWithFormat:@"\"%@#%@\"", serviceType_AVTransport, _action];
    } else {
        return [NSString stringWithFormat:@"\"%@#%@\"", serviceType_RenderingControl, _action];
    }
}

- (NSString *)getPostUrlStrWith:(CLUPnPDevice *)model {
    if (_serviceType == CLUPnPServiceAVTransport) {
        return [self getUPnPURLWithUrlModel:model.AVTransport urlHeader:model.URLHeader];
    } else {
        return [self getUPnPURLWithUrlModel:model.RenderingControl urlHeader:model.URLHeader];;
    }
}

- (NSString *)getUPnPURLWithUrlModel:(CLServiceModel *)model urlHeader:(NSString *)urlHeader {
    if ([[model.controlURL substringToIndex:1] isEqualToString:@"/"]) {
        return [NSString stringWithFormat:@"%@%@", urlHeader, model.controlURL];
    } else {
        return [NSString stringWithFormat:@"%@/%@", urlHeader, model.controlURL];
    }
}

- (NSString *)getPostXMLFile {
    GDataXMLElement *xmlEle = [GDataXMLElement elementWithName:@"s:Envelope"];
    [xmlEle addChild:[GDataXMLElement attributeWithName:@"s:encodingStyle" stringValue:@"http://schemas.xmlsoap.org/soap/encoding/"]];
    [xmlEle addChild:[GDataXMLElement attributeWithName:@"xmlns:s" stringValue:@"http://schemas.xmlsoap.org/soap/envelope/"]];
    [xmlEle addChild:[GDataXMLElement attributeWithName:@"xmlns:u" stringValue:[self getServiceType]]];
    GDataXMLElement *command = [GDataXMLElement elementWithName:@"s:Body"];
    [command addChild:self.XMLElement];
    [xmlEle addChild:command];
    return xmlEle.XMLString;
}

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPDevice.h
================================================
//
//  CLUPnPDevice.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 2017/7/31.
//  Copyright © 2017年 ClaudeLi. All rights reserved.
//

#import <Foundation/Foundation.h>

@class CLServiceModel;
@interface CLUPnPDevice : NSObject

@property (nonatomic, copy) NSString    *uuid;
@property (nonatomic, strong) NSURL     *loaction;
@property (nonatomic, copy) NSString    *URLHeader;

@property (nonatomic, copy) NSString *friendlyName;
@property (nonatomic, copy) NSString *modelName;

@property (nonatomic, strong) CLServiceModel *AVTransport;
@property (nonatomic, strong) CLServiceModel *RenderingControl;

- (void)setArray:(NSArray *)array;

@end

@interface CLServiceModel : NSObject

@property (nonatomic, copy) NSString *serviceType;
@property (nonatomic, copy) NSString *serviceId;
@property (nonatomic, copy) NSString *controlURL;
@property (nonatomic, copy) NSString *eventSubURL;
@property (nonatomic, copy) NSString *SCPDURL;

- (void)setArray:(NSArray *)array;

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPDevice.m
================================================
//
//  CLUPnPDevice.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 2017/7/31.
//  Copyright © 2017年 ClaudeLi. All rights reserved.
//

#import "CLUPnP.h"
#import "GDataXMLNode.h"

@implementation CLUPnPDevice

- (CLServiceModel *)AVTransport {
    if (!_AVTransport) {
        _AVTransport = [[CLServiceModel alloc] init];
    }
    return _AVTransport;
}

- (CLServiceModel *)RenderingControl {
    if (!_RenderingControl) {
        _RenderingControl = [[CLServiceModel alloc] init];
    }
    return _RenderingControl;
}

- (NSString *)URLHeader {
    if (!_URLHeader) {
        _URLHeader = [NSString stringWithFormat:@"%@://%@:%@", [self.loaction scheme], [self.loaction host], [self.loaction port]];
    }
    return _URLHeader;
}

- (void)setArray:(NSArray *)array {
    @autoreleasepool {
        for (int j = 0; j < [array count]; j++) {
            GDataXMLElement *ele = [array objectAtIndex:j];
            if ([ele.name isEqualToString:@"friendlyName"]) {
                self.friendlyName = [ele stringValue];
            }
            if ([ele.name isEqualToString:@"modelName"]) {
                self.modelName = [ele stringValue];
            }
            if ([ele.name isEqualToString:@"serviceList"]) {
                NSArray *serviceListArray = [ele children];
                for (int k = 0; k < [serviceListArray count]; k++) {
                    GDataXMLElement *listEle = [serviceListArray objectAtIndex:k];
                    if ([listEle.name isEqualToString:@"service"]) {
                        NSString *serviceString = [listEle stringValue];
                        if ([serviceString rangeOfString:serviceType_AVTransport].location != NSNotFound || [serviceString rangeOfString:serviceId_AVTransport].location != NSNotFound) {
                            
                            [self.AVTransport setArray:[listEle children]];
                            
                        }else if ([serviceString rangeOfString:serviceType_RenderingControl].location != NSNotFound || [serviceString rangeOfString:serviceId_RenderingControl].location != NSNotFound){
                            
                            [self.RenderingControl setArray:[listEle children]];
                        }
                    }
                }
                continue;
            }
        }
    }
}

@end

@implementation CLServiceModel

- (void)setArray:(NSArray *)array {
    @autoreleasepool {
        for (int m = 0; m < array.count; m++) {
            GDataXMLElement *needEle = [array objectAtIndex:m];
            if ([needEle.name isEqualToString:@"serviceType"]) {
                self.serviceType = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"serviceId"]) {
                self.serviceId = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"controlURL"]) {
                self.controlURL = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"eventSubURL"]) {
                self.eventSubURL = [needEle stringValue];
            }
            if ([needEle.name isEqualToString:@"SCPDURL"]) {
                self.SCPDURL = [needEle stringValue];
            }
        }
    }
}

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPRenderer.h
================================================
//
//  CLUPnPRenderer.h
//  Tiaooo
//
//  Created by ClaudeLi on 16/9/29.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "CLUPnPResponseDelegate.h"

@class CLUPnPDevice;
@interface CLUPnPRenderer : NSObject

@property (nonatomic, strong) CLUPnPDevice *model;

@property (nonatomic, weak) id<CLUPnPResponseDelegate>delegate;

/**
 初始化
 @param model 搜索得到的UPnPModel
 @return self
 */
- (instancetype)initWithModel:(CLUPnPDevice *)model;

/**
 设置投屏地址
 @param urlStr 视频url
 */
- (void)setAVTransportURL:(NSString *)urlStr;

/**
 设置下一个播放地址
 @param urlStr 下一个视频url
 */
- (void)setNextAVTransportURI:(NSString *)urlStr;

/**
 播放
 */
- (void)play;

/**
 暂停
 */
- (void)pause;

/**
 结束
 */
- (void)stop;

/**
 下一个
 */
- (void)next;

/**
 前一个
 */
- (void)previous;

/**
 跳转进度
 @param relTime 进度时间(单位秒)
 */
- (void)seek:(float)relTime;

/**
 跳转至特定进度或视频
 @param target 目标值,可以是 00:02:21 格式的进度或者整数的 TRACK_NR。
 @param unit   REL_TIME(跳转到某个进度)或 TRACK_NR(跳转到某个视频)。
 */
- (void)seekToTarget:(NSString *)target Unit:(NSString *)unit;

/**
 获取播放进度,可通过协议回调使用
 */
- (void)getPositionInfo;

/**
 获取播放状态,可通过协议回调使用
 */
- (void)getTransportInfo;

/**
 获取音频,可通过协议回调使用
 */
- (void)getVolume;

/**
 设置音频值
 @param value 值—>整数
 */
- (void)setVolumeWith:(NSString *)value;


@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPRenderer.m
================================================
//
//  CLUPnPRenderer.m
//  Tiaooo
//
//  Created by ClaudeLi on 16/9/29.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import "CLUPnP.h"
#import "GDataXMLNode.h"
#import "CLUPnPAction.h"

@implementation CLUPnPRenderer

- (instancetype)initWithModel:(CLUPnPDevice *)model {
    self = [super init];
    if (self) {
        _model = model;
    }
    return self;
}

- (void)setModel:(CLUPnPDevice *)model {
    _model = model;
}

#pragma mark -
#pragma mark -- AVTransport动作 --
- (void)setAVTransportURL:(NSString *)urlStr {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"SetAVTransportURI"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:urlStr forName:@"CurrentURI"];
    [action setArgumentValue:@"" forName:@"CurrentURIMetaData"];
    [self postRequestWith:action];
}

- (void)setNextAVTransportURI:(NSString *)urlStr {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"SetNextAVTransportURI"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:urlStr forName:@"NextURI"];
    [action setArgumentValue:@"" forName:@"NextURIMetaData"];
    [self postRequestWith:action];
}

- (void)play{
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Play"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:@"1" forName:@"Speed"];
    [self postRequestWith:action];
}

- (void)pause {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Pause"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)stop {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Stop"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)next {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Next"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)previous {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Previous"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)getPositionInfo {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"GetPositionInfo"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)getTransportInfo {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"GetTransportInfo"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [self postRequestWith:action];
}

- (void)seek:(float)relTime {
    [self seekToTarget:[NSString stringWithDurationTime:relTime] Unit:unitREL_TIME];
}

- (void)seekToTarget:(NSString *)target Unit:(NSString *)unit {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"Seek"];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:unit forName:@"Unit"];
    [action setArgumentValue:target forName:@"Target"];
    [self postRequestWith:action];
}

#pragma mark -
#pragma mark -- RenderingControl动作 --
- (void)getVolume {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"GetVolume"];
    [action setServiceType:CLUPnPServiceRenderingControl];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:@"Master" forName:@"Channel"];
    [self postRequestWith:action];
}

- (void)setVolumeWith:(NSString *)value {
    CLUPnPAction *action = [[CLUPnPAction alloc] initWithAction:@"SetVolume"];
    [action setServiceType:CLUPnPServiceRenderingControl];
    [action setArgumentValue:@"0" forName:@"InstanceID"];
    [action setArgumentValue:@"Master" forName:@"Channel"];
    [action setArgumentValue:value forName:@"DesiredVolume"];
    [self postRequestWith:action];
}

#pragma mark -
#pragma mark -- 发送动作请求 --
- (void)postRequestWith:(CLUPnPAction *)action {
    NSURLSession *session = [NSURLSession sharedSession];
    NSURL       *url = [NSURL URLWithString:[action getPostUrlStrWith:_model]];
    NSString    *postXML = [action getPostXMLFile];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    [request addValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
    [request addValue:[action getSOAPAction] forHTTPHeaderField:@"SOAPAction"];
    request.HTTPBody = [postXML dataUsingEncoding:NSUTF8StringEncoding];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error || data == nil) {
            [self _UndefinedResponse:nil postXML:postXML];
            return;
        } else {
            [self parseRequestResponseData:data postXML:postXML];
        }
    }];
    [dataTask resume];
}

#pragma mark -
#pragma mark -- 动作响应 --
- (void)parseRequestResponseData:(NSData *)data postXML:(NSString *)postXML {
    GDataXMLDocument *xmlDoc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
    GDataXMLElement *xmlEle = [xmlDoc rootElement];
    NSArray *bigArray = [xmlEle children];
    for (int i = 0; i < [bigArray count]; i++) {
        GDataXMLElement *element = [bigArray objectAtIndex:i];
        NSArray *needArr = [element children];
        if ([[element name] hasSuffix:@"Body"]) {
            [self resultsWith:needArr postXML:postXML];
        } else {
            [self _UndefinedResponse:[xmlEle XMLString] postXML:postXML];
        }
    }
}

- (void)resultsWith:(NSArray *)array postXML:(NSString *)postXML {
    for (int i = 0; i < array.count; i++) {
        GDataXMLElement *ele = [array objectAtIndex:i];
        if ([[ele name] hasSuffix:@"SetAVTransportURIResponse"]) {
            [self _SetAVTransportURIResponse];
            [self getTransportInfo];
        } else if ([[ele name] hasSuffix:@"SetNextAVTransportURIResponse"]) {
            [self _SetNextAVTransportURIResponse];
        } else if ([[ele name] hasSuffix:@"PauseResponse"]) {
            [self _PauseResponse];
        } else if ([[ele name] hasSuffix:@"PlayResponse"]) {
            [self _PlayResponse];
        } else if ([[ele name] hasSuffix:@"StopResponse"]) {
            [self _StopResponse];
        } else if ([[ele name] hasSuffix:@"SeekResponse"]) {
            [self _SeekResponse];
        } else if ([[ele name] hasSuffix:@"NextResponse"]) {
            [self _NextResponse];
        } else if ([[ele name] hasSuffix:@"PreviousResponse"]) {
            [self _PreviousResponse];
        } else if ([[ele name] hasSuffix:@"SetVolumeResponse"]) {
            [self _SetVolumeResponse];
        } else if ([[ele name] hasSuffix:@"GetVolumeResponse"]) {
            [self _GetVolumeSuccessWith:[ele children]];
        } else if ([[ele name] hasSuffix:@"GetPositionInfoResponse"]) {
            [self _GetPositionInfoResponseWith:[ele children]];
        } else if ([[ele name] hasSuffix:@"GetTransportInfoResponse"]) {
            [self _GetTransportInfoResponseWith:[ele children]];
        } else {
            [self _UndefinedResponse:[ele XMLString] postXML:postXML];
        }
    }
}

#pragma mark -
#pragma mark -- 回调协议 --
- (void)_SetAVTransportURIResponse {
    if ([self.delegate respondsToSelector:@selector(upnpSetAVTransportURIResponse)]) {
        [self.delegate upnpSetAVTransportURIResponse];
    }
}

- (void)_SetNextAVTransportURIResponse {
    if ([self.delegate respondsToSelector:@selector(upnpSetNextAVTransportURIResponse)]) {
        [self.delegate upnpSetNextAVTransportURIResponse];
    }
}

- (void)_PauseResponse {
    if ([self.delegate respondsToSelector:@selector(upnpPauseResponse)]) {
        [self.delegate upnpPauseResponse];
    }
}

- (void)_PlayResponse {
    if ([self.delegate respondsToSelector:@selector(upnpPlayResponse)]) {
        [self.delegate upnpPlayResponse];
    }
}

- (void)_StopResponse {
    if ([self.delegate respondsToSelector:@selector(upnpStopResponse)]) {
        [self.delegate upnpStopResponse];
    }
}

- (void)_SeekResponse {
    if ([self.delegate respondsToSelector:@selector(upnpSeekResponse)]) {
        [self.delegate upnpSeekResponse];
    }
}

- (void)_NextResponse{
    if ([self.delegate respondsToSelector:@selector(upnpNextResponse)]) {
        [self.delegate upnpNextResponse];
    }
}

- (void)_PreviousResponse {
    if ([self.delegate respondsToSelector:@selector(upnpPreviousResponse)]) {
        [self.delegate upnpPreviousResponse];
    }
}

- (void)_SetVolumeResponse {
    if ([self.delegate respondsToSelector:@selector(upnpSetVolumeResponse)]) {
        [self.delegate upnpSetVolumeResponse];
    }
}

- (void)_GetVolumeSuccessWith:(NSArray *)array {
    for (int j = 0; j < array.count; j++) {
        GDataXMLElement *eleXml = [array objectAtIndex:j];
        if ([[eleXml name] isEqualToString:@"CurrentVolume"]) {
            if ([self.delegate respondsToSelector:@selector(upnpGetVolumeResponse:)]) {
                [self.delegate upnpGetVolumeResponse:[eleXml stringValue]];
            }
        }
    }
}

- (void)_GetPositionInfoResponseWith:(NSArray *)array {
    CLUPnPAVPositionInfo *info = [[CLUPnPAVPositionInfo alloc] init];
    [info setArray:array];
    if ([self.delegate respondsToSelector:@selector(upnpGetPositionInfoResponse:)]) {
        [self.delegate upnpGetPositionInfoResponse:info];
    }
}

- (void)_GetTransportInfoResponseWith:(NSArray *)array {
    CLUPnPTransportInfo *info = [[CLUPnPTransportInfo alloc] init];
    [info setArray:array];
    if ([self.delegate respondsToSelector:@selector(upnpGetTransportInfoResponse:)]) {
        [self.delegate upnpGetTransportInfoResponse:info];
    }
}

- (void)_UndefinedResponse:(NSString *)xmlStr postXML:(NSString *)postXML {
    if ([self.delegate respondsToSelector:@selector(upnpUndefinedResponse:postXML:)]) {
        [self.delegate upnpUndefinedResponse:xmlStr postXML:postXML];
    }
}

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPResponseDelegate.h
================================================
//
//  CLUPnPResponseDelegate.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/10.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <Foundation/Foundation.h>

@class CLUPnPAVPositionInfo;
@class CLUPnPTransportInfo;

// 响应解析回调协议
@protocol CLUPnPResponseDelegate <NSObject>

@required

- (void)upnpSetAVTransportURIResponse;  // 设置url响应
- (void)upnpGetTransportInfoResponse:(CLUPnPTransportInfo *)info;   // 获取播放状态

@optional

/**
 未定义的响应/错误
 
 @param resXML  响应XML
 @param postXML 请求的动作
 */
- (void)upnpUndefinedResponse:(NSString *)resXML postXML:(NSString *)postXML;

- (void)upnpPlayResponse;                   // 播放响应
- (void)upnpPauseResponse;                  // 暂停响应
- (void)upnpStopResponse;                   // 停止投屏
- (void)upnpSeekResponse;                   // 跳转响应
- (void)upnpPreviousResponse;               // 以前的响应
- (void)upnpNextResponse;                   // 下一个响应
- (void)upnpSetVolumeResponse;              // 设置音量响应
- (void)upnpSetNextAVTransportURIResponse;  // 设置下一个url响应
- (void)upnpGetVolumeResponse:(NSString *)volume;                   // 获取音频信息
- (void)upnpGetPositionInfoResponse:(CLUPnPAVPositionInfo *)info;   // 获取播放进度


@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPServer.h
================================================
//
//  CLUPnPServer.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 2017/7/31.
//  Copyright © 2017年 ClaudeLi. All rights reserved.
//

#import <Foundation/Foundation.h>

@class CLUPnPDevice;
@protocol CLUPnPServerDelegate <NSObject>
@required
/**
 搜索结果

 @param devices 设备数组
 */
- (void)upnpSearchChangeWithResults:(NSArray <CLUPnPDevice *>*)devices;

@optional
/**
 搜索失败

 @param error error
 */
- (void)upnpSearchErrorWithError:(NSError *)error;

@end

@interface CLUPnPServer : NSObject

@property (nonatomic, weak) id<CLUPnPServerDelegate>delegate;

+ (instancetype)shareServer;

/**
 启动Server并搜索
 */
- (void)start;

/**
 停止
 */
- (void)stop;

/**
 搜索
 */
- (void)search;

/**
 获取已经发现的设备
 
 @return Device Array
 */
- (NSArray<CLUPnPDevice *> *)getDeviceList;

@end


================================================
FILE: DLNA_UPnP/Classes/UPnP/CLUPnPServer.m
================================================
//
//  CLUPnPServer.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 2017/7/31.
//  Copyright © 2017年 ClaudeLi. All rights reserved.
//

#import "CLUPnP.h"
#import "CLUPnPServer.h"
#import "GCDAsyncUdpSocket.h"
#import "GDataXMLNode.h"

@interface CLUPnPServer ()<GCDAsyncUdpSocketDelegate>

@property (nonatomic, strong) GCDAsyncUdpSocket *udpSocket;

// key: usn(uuid) string,  value: device
@property (nonatomic, strong) NSMutableDictionary<NSString *, CLUPnPDevice *> *deviceDictionary;

#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_queue_t                          queue;
#else
@property (nonatomic, assign) dispatch_queue_t                          queue;
#endif

@end

@implementation CLUPnPServer

@synthesize delegate = _delegate;

@synthesize deviceDictionary = _deviceDictionary;

- (void)dealloc{
#if !OS_OBJECT_USE_OBJC
    dispatch_release(_queue);
#endif
}

+ (instancetype)shareServer {
    static CLUPnPServer *server;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        server = [[self alloc] init];
    });
    return server;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _queue = dispatch_queue_create("com.tiaooo.upnp.dlna", DISPATCH_QUEUE_SERIAL);
        _deviceDictionary = [NSMutableDictionary dictionary];
        _udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
    }
    return self;
}

- (NSString *)getSearchString {
    return [NSString stringWithFormat:@"M-SEARCH * HTTP/1.1\r\nHOST: %@:%d\r\nMAN: \"ssdp:discover\"\r\nMX: 3\r\nST: %@\r\nUSER-AGENT: iOS UPnP/1.1 Tiaooo/1.0\r\n\r\n", ssdpAddres, ssdpPort, serviceType_AVTransport];
}

- (void)start {
    NSError *error = nil;
    if (![_udpSocket bindToPort:ssdpPort error:&error]) {
        [self onError:error];
    }
    
    if (![_udpSocket beginReceiving:&error])
    {
        [self onError:error];
    }
    
    if (![_udpSocket joinMulticastGroup:ssdpAddres error:&error])
    {
        [self onError:error];
    }
    [self search];
}

- (void)stop {
    [_udpSocket close];
}

- (void)search {
    // 搜索前先清空设备列表
    [self.deviceDictionary removeAllObjects];
    [self onChange];
    NSData * sendData = [[self getSearchString] dataUsingEncoding:NSUTF8StringEncoding];
    [_udpSocket sendData:sendData toHost:ssdpAddres port:ssdpPort withTimeout:-1 tag:1];
}

- (NSArray<CLUPnPDevice *> *)getDeviceList {
    return self.deviceDictionary.allValues;
}

#pragma mark -
#pragma mark -- GCDAsyncUdpSocketDelegate --
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
    CLLog(@"发送信息成功");
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError * _Nullable)error {
    [self onError:error];
}

- (void)udpSocketDidClose:(GCDAsyncUdpSocket *)sock withError:(NSError  * _Nullable)error {
    CLLog(@"udpSocket关闭");
}

- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
      fromAddress:(NSData *)address
withFilterContext:(nullable id)filterContext {
    [self JudgeDeviceWithData:data];
}

// 判断设备
- (void)JudgeDeviceWithData:(NSData *)data {
    @autoreleasepool {
        NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        if ([string hasPrefix:@"NOTIFY"]) {
            NSString *serviceType = [self headerValueForKey:@"NT:" inData:string];
            if ([serviceType isEqualToString:serviceType_AVTransport]) {
                NSString *location = [self headerValueForKey:@"Location:" inData:string];
                NSString *usn = [self headerValueForKey:@"USN:" inData:string];
                NSString *ssdp = [self headerValueForKey:@"NTS:" inData:string];
                if ([self isNilString:ssdp]) {
                    CLLog(@"ssdp = nil");
                    return;
                }
                if ([self isNilString:usn]) {
                    CLLog(@"usn = nil");
                    return;
                }
                if ([self isNilString:location]) {
                    CLLog(@"location = nil");
                    return;
                }
                if ([ssdp isEqualToString:@"ssdp:alive"])
                {
                    dispatch_async(_queue, ^{
                        if ([self.deviceDictionary objectForKey:usn] == nil)
                        {
                            [self addDevice:[self getDeviceWithLocation:location withUSN:usn] forUSN:usn];
                        }
                    });
                }
                else if ([ssdp isEqualToString:@"ssdp:byebye"])
                {
                    dispatch_async(_queue, ^{
                        [self removeDeviceWithUSN:usn];
                    });
                }
            }
        } else if ([string hasPrefix:@"HTTP/1.1"]) {
            NSString *location = [self headerValueForKey:@"Location:" inData:string];
            NSString *usn = [self headerValueForKey:@"USN:" inData:string];
            if ([self isNilString:usn]) {
                CLLog(@"usn = nil");
                return;
            }
            if ([self isNilString:location]) {
                CLLog(@"location = nil");
                return;
            }
            dispatch_async(_queue, ^{
                if ([self.deviceDictionary objectForKey:usn] == nil)
                {
                    [self addDevice:[self getDeviceWithLocation:location withUSN:usn] forUSN:usn];
                }
            });
        }
    }
}

- (void)addDevice:(CLUPnPDevice *)device forUSN:(NSString *)usn
{
    if (!device){
        return;
    }
    [self.deviceDictionary setObject:device forKey:usn];
    [self onChange];
}

- (void)removeDeviceWithUSN:(NSString *)usn
{
    [self.deviceDictionary removeObjectForKey:usn];
    [self onChange];
}

- (void)onChange {
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.delegate && [self.delegate respondsToSelector:@selector(upnpSearchChangeWithResults:)])
        {
            [self.delegate upnpSearchChangeWithResults:self.deviceDictionary.allValues];
        }
    });
}

- (void)onError:(NSError *)error {
    if (self.delegate && [self.delegate respondsToSelector:@selector(upnpSearchErrorWithError:)]) {
        [self.delegate upnpSearchErrorWithError:error];
    }
}

#pragma mark -
#pragma mark -- private method --
- (NSString *)headerValueForKey:(NSString *)key inData:(NSString *)data
{
    NSString *str = [NSString stringWithFormat:@"%@", data];
    
    NSRange keyRange = [str rangeOfString:key options:NSCaseInsensitiveSearch];
    
    if (keyRange.location == NSNotFound) {
        return @"";
    }
    
    str = [str substringFromIndex:keyRange.location + keyRange.length];
    
    NSRange enterRange = [str rangeOfString:@"\r\n"];
    
    NSString *value = [[str substringToIndex:enterRange.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    
    return value;
}

- (CLUPnPDevice *)getDeviceWithLocation:(NSString *)location withUSN:(NSString *)usn
{
    dispatch_semaphore_t seamphore = dispatch_semaphore_create(0);
    
    __block CLUPnPDevice *device = nil;
    NSURL *URL = [NSURL URLWithString:location];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
    request.HTTPMethod = @"GET";
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        if (error) {
            [self onError:error];
        } else {
            if (response != nil && data != nil) {
                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                if ([httpResponse statusCode] == 200) {
                    device = [[CLUPnPDevice alloc] init];
                    device.loaction = URL;
                    device.uuid = usn;
                    GDataXMLDocument *xmlDoc = [[GDataXMLDocument alloc] initWithData:data options:0 error:nil];
                    GDataXMLElement *xmlEle = [xmlDoc rootElement];
                    NSArray *xmlArray = [xmlEle children];
                    
                    for (int i = 0; i < [xmlArray count]; i++) {
                        GDataXMLElement *element = [xmlArray objectAtIndex:i];
                        if ([[element name] isEqualToString:@"device"]) {
                            [device setArray:[element children]];
                            continue;
                        }
                    }
                }
            }
        }
        dispatch_semaphore_signal(seamphore);
    }] resume];
    dispatch_semaphore_wait(seamphore, DISPATCH_TIME_FOREVER);
    return device;
}

- (BOOL)isNilString:(NSString *)_str {
    if (!_str) {
        return YES;
    }
    if (![_str isKindOfClass:[NSString class]]) {
        return YES;
    }
    _str = [NSString stringWithFormat:@"%@", _str];
    if(!_str.length){
        return YES;
    }
    return NO;
}

@end


================================================
FILE: DLNA_UPnP.podspec
================================================
#
# Be sure to run `pod lib lint DLNA_UPnP.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
  s.name             = 'DLNA_UPnP'
  s.version          = '1.0.0'
  s.summary          = 'A short description of DLNA_UPnP.'

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!

  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

  s.homepage         = 'https://github.com/ClaudeLi/DLNA_UPnP'
  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'ClaudeLi' => 'claudeli@yeah.net' }
  s.source           = { :git => 'https://github.com/ClaudeLi/DLNA_UPnP.git', :tag => s.version.to_s }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.ios.deployment_target = '8.0'

  s.default_subspecs = 'UPnP'

  s.subspec 'UPnP' do |ss|
    ss.dependency 'DLNA_UPnP/GData'
    ss.dependency 'CocoaAsyncSocket'
    ss.source_files         = 'DLNA_UPnP/Classes/UPnP/*.{h,m}'
  end

  s.subspec 'GData' do |ss|
    ss.requires_arc = false
    ss.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" }
    ss.source_files         = 'DLNA_UPnP/Classes/GData/*.{h,m}'
  end
end


================================================
FILE: Example/DLNA_UPnP/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="EHf-IW-A2E">
            <objects>
                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
                        <viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="53" y="375"/>
        </scene>
    </scenes>
</document>


================================================
FILE: Example/DLNA_UPnP/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="5jv-nF-6Lu">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Navigation Controller-->
        <scene sceneID="hmO-Bp-g0v">
            <objects>
                <navigationController id="5jv-nF-6Lu" sceneMemberID="viewController">
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="kcB-zS-dqF">
                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
                        <autoresizingMask key="autoresizingMask"/>
                    </navigationBar>
                    <connections>
                        <segue destination="wbI-16-wNE" kind="relationship" relationship="rootViewController" id="fcb-7I-aEf"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="jTv-wB-Wqv" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-17" y="60"/>
        </scene>
        <!--View Controller-->
        <scene sceneID="Upq-se-XVN">
            <objects>
                <viewController id="wbI-16-wNE" customClass="CLViewController" sceneMemberID="viewController">
                    <layoutGuides>
                        <viewControllerLayoutGuide type="top" id="oaQ-pI-daq"/>
                        <viewControllerLayoutGuide type="bottom" id="TdD-FA-QjL"/>
                    </layoutGuides>
                    <view key="view" contentMode="scaleToFill" id="b5G-tI-Xa4">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                    </view>
                    <navigationItem key="navigationItem" id="Tcd-pr-SBO"/>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Dj6-jb-Ijq" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="668" y="76"/>
        </scene>
    </scenes>
</document>


================================================
FILE: Example/DLNA_UPnP/CLAppDelegate.h
================================================
//
//  CLAppDelegate.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 04/12/2019.
//  Copyright (c) 2019 ClaudeLi. All rights reserved.
//

@import UIKit;

@interface CLAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@end


================================================
FILE: Example/DLNA_UPnP/CLAppDelegate.m
================================================
//
//  CLAppDelegate.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 04/12/2019.
//  Copyright (c) 2019 ClaudeLi. All rights reserved.
//

#import "CLAppDelegate.h"

@implementation CLAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application
{
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

- (void)applicationDidEnterBackground:(UIApplication *)application
{
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

- (void)applicationWillTerminate:(UIApplication *)application
{
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

@end


================================================
FILE: Example/DLNA_UPnP/CLControlViewController.h
================================================
//
//  CLControlViewController.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/8.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <CLUPnP.h>

@interface CLControlViewController : UIViewController

@property (nonatomic, strong) CLUPnPDevice *model;

@end


================================================
FILE: Example/DLNA_UPnP/CLControlViewController.m
================================================
//
//  CLControlViewController.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/10/8.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import "CLControlViewController.h"

static NSString *urlStr0 = @"http://v.tiaooo.com/ltQ3C0vts84B-UZ9BZNvTo9lUzWU";
static NSString *urlStr1 = @"http://v.tiaooo.com/llbizosAzGhJPXC0H4AHLTGHl42W";
static NSString *urlStr2 = @"http://live.hkstv.hk.lxdns.com/live/hks/playlist.m3u8";
//static NSString *urlStr3 = @"http://222.73.132.145/vkphls.tc.qq.com/mp4/8/yZ_j6ME6N3hgRF2xg_m13zCxeLHcQzm9bVK0v_J-08OdcAVc0rmGCA/q4WgUBCu27O21hhzjGXkPCaHr1EkTFuUGbXKrNbjMACA-wleQI3oi3woUdjgP-BtBxW34UkmIxlQ_TkPGeqTLwghaijDM7oFlQwmCbieZPLUh33Q7f8eag/i0021mzabfm.p209.mp4/i0021mzabfm.p209.mp4.av.m3u8";

@interface CLControlViewController ()<CLUPnPResponseDelegate>{
    BOOL _isPlaying;
    CLUPnPRenderer *render;
    NSInteger _valume;
}

@end

@implementation CLControlViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _valume = 0;
    
    render = [[CLUPnPRenderer alloc] initWithModel:self.model];
    render.delegate = self;
    [render setAVTransportURL:urlStr2];
    [render setNextAVTransportURI:urlStr1];
    _isPlaying = YES;
}

#pragma mark -
#pragma mark - 动作调用 -
- (IBAction)closeAction:(id)sender {
    [render stop];
}

- (IBAction)playOrPause:(id)sender {
    if (_isPlaying) {
        [render pause];
    }else{
        [render play];
    }
    _isPlaying = !_isPlaying;
}

- (IBAction)seekTo01:(id)sender {
    [render seekToTarget:@"00:00:01" Unit:unitREL_TIME];
}

- (IBAction)seekTo11:(id)sender {
    [render seekToTarget:@"00:00:11" Unit:unitREL_TIME];
}

- (IBAction)pro:(id)sender {
    [render previous];
}

- (IBAction)next:(id)sender {
    [render next];
}

- (IBAction)getPosition:(id)sender {
    [render getPositionInfo];
}

- (IBAction)getTransport:(id)sender {
    [render getTransportInfo];
}

- (IBAction)subV:(id)sender {
    [render setVolumeWith:@"0"];
}

- (IBAction)addV:(id)sender {
    [render setVolumeWith:@"25"];
}

#pragma mark -
#pragma mark - CLUPnPResponseDelegate -
- (void)upnpSetAVTransportURIResponse{
    [render play];
}

- (void)upnpGetTransportInfoResponse:(CLUPnPTransportInfo *)info{
    //    STOPPED
    //    PAUSED_PLAYBACK
    NSLog(@"%@ === %@", info.currentTransportState, info.currentTransportStatus);
    if (!([info.currentTransportState isEqualToString:@"PLAYING"] || [info.currentTransportState isEqualToString:@"TRANSITIONING"])) {
        [render play];
    }
}

- (void)upnpPlayResponse{
    NSLog(@"播放");
}

- (void)upnpPauseResponse{
    NSLog(@"暂停");
}

- (void)upnpStopResponse{
    NSLog(@"停止");
}

- (void)upnpSeekResponse{
    NSLog(@"跳转完成");
}

- (void)upnpPreviousResponse{
    NSLog(@"前一个");
}

- (void)upnpNextResponse{
    NSLog(@"下一个");
    [render setNextAVTransportURI:urlStr2];
}

- (void)upnpSetVolumeResponse{
    NSLog(@"设置音量成功");
}

- (void)upnpSetNextAVTransportURIResponse{
    NSLog(@"设置下一个url成功");
}

- (void)upnpGetVolumeResponse:(NSString *)volume{
    NSLog(@"音量=%@", volume);
}

- (void)upnpGetPositionInfoResponse:(CLUPnPAVPositionInfo *)info{
    NSLog(@"%f, === %f === %f", info.trackDuration, info.absTime, info.relTime);
}

- (void)upnpUndefinedResponse:(NSString *)resXML postXML:(NSString *)postXML{
    NSLog(@"postXML = %@", postXML);
    NSLog(@"resXML = %@", resXML);
}


@end


================================================
FILE: Example/DLNA_UPnP/CLControlViewController.xib
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11201" systemVersion="15G1004" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CLControlViewController">
            <connections>
                <outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
            </connections>
        </placeholder>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
            <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
            <subviews>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3L3-ZU-N1i">
                    <state key="normal" title="播放/暂停"/>
                    <connections>
                        <action selector="playOrPause:" destination="-1" eventType="touchUpInside" id="hFd-nc-2JZ"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vnW-bs-cFP">
                    <state key="normal" title="00:00:01"/>
                    <connections>
                        <action selector="seekTo01:" destination="-1" eventType="touchUpInside" id="Unk-D9-m0u"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1Ay-2z-Jlc">
                    <state key="normal" title="00:00:11"/>
                    <connections>
                        <action selector="seekTo11:" destination="-1" eventType="touchUpInside" id="Aeb-Sq-aUQ"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="REF-hg-8vW">
                    <state key="normal" title="关闭"/>
                    <connections>
                        <action selector="closeAction:" destination="-1" eventType="touchUpInside" id="upz-xr-LrK"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mDO-yv-K2A">
                    <state key="normal" title="Position"/>
                    <connections>
                        <action selector="getPosition:" destination="-1" eventType="touchUpInside" id="hpc-1z-5Dl"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xhG-TK-TXD">
                    <state key="normal" title="Transport"/>
                    <connections>
                        <action selector="getTransport:" destination="-1" eventType="touchUpInside" id="R6v-ow-R2V"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vby-3A-ENL">
                    <state key="normal" title="V--"/>
                    <connections>
                        <action selector="subV:" destination="-1" eventType="touchUpInside" id="PZW-mu-DGF"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="b2f-hI-LSW">
                    <state key="normal" title="V++"/>
                    <connections>
                        <action selector="addV:" destination="-1" eventType="touchUpInside" id="RkL-ZB-1OG"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hl1-sV-ULW">
                    <state key="normal" title="&lt;&lt;-"/>
                    <connections>
                        <action selector="pro:" destination="-1" eventType="touchUpInside" id="xg0-gC-5Gw"/>
                    </connections>
                </button>
                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="E04-ht-WHc">
                    <state key="normal" title="-&gt;&gt;"/>
                    <connections>
                        <action selector="next:" destination="-1" eventType="touchUpInside" id="Rd4-q0-g4b"/>
                    </connections>
                </button>
            </subviews>
            <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
            <constraints>
                <constraint firstAttribute="trailing" secondItem="b2f-hI-LSW" secondAttribute="trailing" constant="110" id="1Oz-rh-YkV"/>
                <constraint firstItem="Hl1-sV-ULW" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="10" id="BGB-vz-MME"/>
                <constraint firstItem="REF-hg-8vW" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="Bk3-mX-GYt"/>
                <constraint firstItem="mDO-yv-K2A" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="100" id="G10-Ig-NDk"/>
                <constraint firstItem="3L3-ZU-N1i" firstAttribute="centerX" secondItem="i5M-Pr-FkT" secondAttribute="centerX" id="HwP-Ir-g5u"/>
                <constraint firstItem="b2f-hI-LSW" firstAttribute="top" secondItem="xhG-TK-TXD" secondAttribute="bottom" constant="40" id="K2c-HK-1oz"/>
                <constraint firstItem="1Ay-2z-Jlc" firstAttribute="centerY" secondItem="3L3-ZU-N1i" secondAttribute="centerY" id="KhB-bg-yRA"/>
                <constraint firstItem="xhG-TK-TXD" firstAttribute="centerY" secondItem="mDO-yv-K2A" secondAttribute="centerY" id="MmW-Zs-thx"/>
                <constraint firstItem="vnW-bs-cFP" firstAttribute="centerY" secondItem="Hl1-sV-ULW" secondAttribute="centerY" id="PKr-RZ-w9L"/>
                <constraint firstItem="3L3-ZU-N1i" firstAttribute="centerY" secondItem="vnW-bs-cFP" secondAttribute="centerY" id="PbF-Vm-wpn"/>
                <constraint firstItem="3L3-ZU-N1i" firstAttribute="centerY" secondItem="i5M-Pr-FkT" secondAttribute="centerY" id="Xe7-sB-wYp"/>
                <constraint firstItem="vnW-bs-cFP" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="80" id="dpW-dW-jVS"/>
                <constraint firstItem="3L3-ZU-N1i" firstAttribute="top" secondItem="REF-hg-8vW" secondAttribute="bottom" constant="100" id="dvL-RD-MUv"/>
                <constraint firstAttribute="trailing" secondItem="E04-ht-WHc" secondAttribute="trailing" constant="10" id="fN3-cj-xIr"/>
                <constraint firstAttribute="trailing" secondItem="xhG-TK-TXD" secondAttribute="trailing" constant="100" id="fRD-ex-7GP"/>
                <constraint firstItem="E04-ht-WHc" firstAttribute="centerY" secondItem="Hl1-sV-ULW" secondAttribute="centerY" id="gVI-bl-ivO"/>
                <constraint firstItem="xhG-TK-TXD" firstAttribute="top" secondItem="i5M-Pr-FkT" secondAttribute="top" constant="380" id="hSl-km-EwV"/>
                <constraint firstAttribute="trailing" secondItem="1Ay-2z-Jlc" secondAttribute="trailing" constant="80" id="haW-zF-0qq"/>
                <constraint firstItem="b2f-hI-LSW" firstAttribute="centerY" secondItem="vby-3A-ENL" secondAttribute="centerY" id="rLp-3f-FIS"/>
                <constraint firstItem="vby-3A-ENL" firstAttribute="leading" secondItem="i5M-Pr-FkT" secondAttribute="leading" constant="110" id="tb9-XB-vu6"/>
            </constraints>
        </view>
    </objects>
</document>


================================================
FILE: Example/DLNA_UPnP/CLSearchDeviceController.h
================================================
//
//  CLSearchDeviceController.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/9/29.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface CLSearchDeviceController : UIViewController

@end


================================================
FILE: Example/DLNA_UPnP/CLSearchDeviceController.m
================================================
//
//  CLSearchDeviceController.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 16/9/29.
//  Copyright © 2016年 ClaudeLi. All rights reserved.
//

#import "CLSearchDeviceController.h"
#import "CLControlViewController.h"

static NSString *cellIdentifier = @"cellIdentifier";
@interface CLSearchDeviceController ()<UITableViewDataSource, UITableViewDelegate, CLUPnPServerDelegate>
{
    CLUPnPServer *upd;
}
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSMutableArray *dataArray;

@end

@implementation CLSearchDeviceController

- (NSMutableArray *)dataArray
{
    if (!_dataArray) {
        _dataArray = [NSMutableArray array];
    }
    return _dataArray;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    
    upd = [CLUPnPServer shareServer];
    upd.delegate = self;
    
    [self.view addSubview:self.tableView];
}

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [upd start];
}

- (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    [upd stop];
}

#pragma mark -
#pragma mark -- 搜索协议CLUPnPDeviceDelegate回调 --
- (void)upnpSearchChangeWithResults:(NSArray<CLUPnPDevice *> *)devices{
    self.dataArray = devices.mutableCopy;
    [self.tableView reloadData];
}

- (void)upnpSearchErrorWithError:(NSError *)error{
    NSLog(@"error==%@", error);
}


- (void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];
    self.tableView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
}

- (UITableView *)tableView{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.separatorStyle = NO;
    }
    return _tableView;
}

#pragma mark -
#pragma mark - UITableViewDelegate && UITableViewDataSource -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellIdentifier];
    }
    if (indexPath.row < self.dataArray.count) {
        CLUPnPDevice *model = self.dataArray[indexPath.row];
        cell.textLabel.text = model.friendlyName;
    }
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    if (indexPath.row < self.dataArray.count) {
        CLUPnPDevice *model = self.dataArray[indexPath.row];
        
        CLControlViewController *controlVC = [[CLControlViewController alloc] init];
        controlVC.model = model;
        [self.navigationController pushViewController:controlVC animated:YES];
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 50.0f;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end


================================================
FILE: Example/DLNA_UPnP/CLViewController.h
================================================
//
//  CLViewController.h
//  DLNA_UPnP
//
//  Created by ClaudeLi on 04/12/2019.
//  Copyright (c) 2019 ClaudeLi. All rights reserved.
//

@import UIKit;

@interface CLViewController : UIViewController

@end


================================================
FILE: Example/DLNA_UPnP/CLViewController.m
================================================
//
//  CLViewController.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 04/12/2019.
//  Copyright (c) 2019 ClaudeLi. All rights reserved.
//

#import "CLViewController.h"
#import "CLSearchDeviceController.h"

@interface CLViewController ()

@end

@implementation CLViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    UIButton* button = [UIButton new];
    button.backgroundColor = [UIColor redColor];
    [button setTitle:@" 搜索设备 " forState:UIControlStateNormal];
    [button addTarget:self action:@selector(clickButtonAction) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
    [button sizeToFit];
    button.center = self.view.center;
}

- (void)clickButtonAction{
    CLSearchDeviceController *search = [[CLSearchDeviceController alloc] init];
    [self.navigationController pushViewController:search animated:YES];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end


================================================
FILE: Example/DLNA_UPnP/DLNA_UPnP-Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleDisplayName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundleExecutable</key>
	<string>${EXECUTABLE_NAME}</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>${PRODUCT_NAME}</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1.0</string>
	<key>LSRequiresIPhoneOS</key>
	<true/>
	<key>UILaunchStoryboardName</key>
	<string>LaunchScreen</string>
	<key>UIMainStoryboardFile</key>
	<string>Main</string>
	<key>UIRequiredDeviceCapabilities</key>
	<array>
		<string>armv7</string>
	</array>
	<key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
	<key>UISupportedInterfaceOrientations~ipad</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationPortraitUpsideDown</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>
</dict>
</plist>


================================================
FILE: Example/DLNA_UPnP/DLNA_UPnP-Prefix.pch
================================================
//
//  Prefix header
//
//  The contents of this file are implicitly included at the beginning of every source file.
//

#import <Availability.h>

#ifndef __IPHONE_5_0
#warning "This project uses features only available in iOS SDK 5.0 and later."
#endif

#ifdef __OBJC__
    @import UIKit;
    @import Foundation;
#endif


================================================
FILE: Example/DLNA_UPnP/Device.xml
================================================
<root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:qq="http://www.tencent.com">
    <specVersion>
        <major>1</major>
        <minor>0</minor>
    </specVersion><device>
        <deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>
        <UDN>uuid:b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22</UDN>
        <friendlyName>书房的小米电视</friendlyName>
        <qq:X_QPlay_SoftwareCapability>QPlay:1</qq:X_QPlay_SoftwareCapability>
        <manufacturer>Xiaomi</manufacturer>
        <manufacturerURL>http://www.xiaomi.com/</manufacturerURL>
        <modelDescription>Xiaomi MediaRenderer</modelDescription>
        <modelName>Xiaomi MediaRenderer</modelName>
        <modelNumber>1</modelNumber>
        <modelURL>http://www.xiaomi.com/hezi</modelURL>
        <serialNumber>55MC5BF9J19620A</serialNumber>
        <presentationURL>device_presentation_page.html</presentationURL>
        <UPC>123456789012</UPC>
        <dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-1.50</dlna:X_DLNADOC>
        <dlna:X_DLNACAP xmlns:dlna="urn:schemas-dlna-org:device-1-0">,</dlna:X_DLNACAP>
        <iconList>
            <icon>
                <mimetype>image/jpeg</mimetype>
                <width>48</width>
                <height>48</height>
                <depth>8</depth>
                <url>icon/icon048x048.jpeg</url>
            </icon>
            <icon>
                <mimetype>image/png</mimetype>
                <width>48</width>
                <height>48</height>
                <depth>8</depth>
                <url>icon/icon048x048.png</url>
            </icon>
            <icon>
                <mimetype>image/jpeg</mimetype>
                <width>120</width>
                <height>120</height>
                <depth>8</depth>
                <url>icon/icon120x120.jpeg</url>
            </icon>
            <icon>
                <mimetype>image/png</mimetype>
                <width>120</width>
                <height>120</height>
                <depth>8</depth>
                <url>icon/icon120x120.png</url></icon>
        </iconList>
        <serviceList>
            <service>
                <serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>
                <serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
                <controlURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/AVTransport/action</controlURL>
                <eventSubURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/AVTransport/event</eventSubURL>
                <SCPDURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/AVTransport/desc.xml</SCPDURL>
            </service>
            <service>
                <serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType>
                <serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>
                <controlURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/action</controlURL>
                <eventSubURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/event</eventSubURL>
                <SCPDURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/desc.xml</SCPDURL>
            </service>
            <service>
                <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
                <serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
                <controlURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/action</controlURL>
                <eventSubURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/event</eventSubURL>
                <SCPDURL>/dev/b8c9ae9e-fb7f-2f09-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/desc.xml</SCPDURL>
            </service>
        </serviceList>
        <av:X_RController_DeviceInfo xmlns:av="urn:mi-com:av"><av:X_RController_Version>1.0</av:X_RController_Version><av:X_RController_ServiceList><av:X_RController_Service><av:X_RController_ServiceType>controller</av:X_RController_ServiceType><av:X_RController_ActionList_URL>http://192.168.1.4:6095/</av:X_RController_ActionList_URL></av:X_RController_Service><av:X_RController_Service><av:X_RController_ServiceType>data</av:X_RController_ServiceType><av:X_RController_ActionList_URL>http://api.tv.duokanbox.com/bolt/3party/</av:X_RController_ActionList_URL></av:X_RController_Service></av:X_RController_ServiceList>
        </av:X_RController_DeviceInfo>
    </device>
</root>


================================================
FILE: Example/DLNA_UPnP/Images.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "20x20",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "29x29",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "40x40",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "size" : "60x60",
      "scale" : "3x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "20x20",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "29x29",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "40x40",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "1x"
    },
    {
      "idiom" : "ipad",
      "size" : "76x76",
      "scale" : "2x"
    },
    {
      "idiom" : "ipad",
      "size" : "83.5x83.5",
      "scale" : "2x"
    },
    {
      "idiom" : "ios-marketing",
      "size" : "1024x1024",
      "scale" : "1x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}


================================================
FILE: Example/DLNA_UPnP/SetUrl.xml
================================================
<?xml version="1.0" encoding="UTF-8" ?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
    <s:Body>
        <u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
            <InstanceID>0</InstanceID>
            <CurrentURI>http://222.73.132.145/vkphls.tc.qq.com/mp4/8/yZ_j6ME6N3hgRF2xg_m13zCxeLHcQzm9bVK0v_J-08OdcAVc0rmGCA/q4WgUBCu27O21hhzjGXkPCaHr1EkTFuUGbXKrNbjMACA-wleQI3oi3woUdjgP-BtBxW34UkmIxlQ_TkPGeqTLwghaijDM7oFlQwmCbieZPLUh33Q7f8eag/i0021mzabfm.p209.mp4/i0021mzabfm.p209.mp4.av.m3u8?fn=p209&amp;bw=8000&amp;st=0&amp;et=0&amp;iv=&amp;ivfn=&amp;ivfc=&amp;ivt=&amp;ivs=&amp;ivd=&amp;ivl=&amp;ftype=mp4&amp;fbw=93&amp;type=m3u8&amp;drm=0&amp;sdtfrom=v3000&amp;platform=10403&amp;appver=5.1.1.14483&amp;projection=dlna</CurrentURI>
            <CurrentURIMetaData>&lt;DIDL-Lite xmlns=&quot;urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot; xmlns:dc=&quot;http://purl.org/dc/elements/1.1/&quot; xmlns:sec=&quot;http://www.sec.co.kr/&quot;&gt;&lt;item id=&quot;0&quot; parentID=&quot;0&quot; restricted=&quot;0&quot;&gt;&lt;res protocolInfo=&quot;http-get:*:video/mp4:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=01500000000000000000000000000000&quot;&gt;http://222.73.132.145/vkphls.tc.qq.com/mp4/8/yZ_j6ME6N3hgRF2xg_m13zCxeLHcQzm9bVK0v_J-08OdcAVc0rmGCA/q4WgUBCu27O21hhzjGXkPCaHr1EkTFuUGbXKrNbjMACA-wleQI3oi3woUdjgP-BtBxW34UkmIxlQ_TkPGeqTLwghaijDM7oFlQwmCbieZPLUh33Q7f8eag/i0021mzabfm.p209.mp4/i0021mzabfm.p209.mp4.av.m3u8?fn=p209&amp;amp;bw=8000&amp;amp;st=0&amp;amp;et=0&amp;amp;iv=&amp;amp;ivfn=&amp;amp;ivfc=&amp;amp;ivt=&amp;amp;ivs=&amp;amp;ivd=&amp;amp;ivl=&amp;amp;ftype=mp4&amp;amp;fbw=93&amp;amp;type=m3u8&amp;amp;drm=0&amp;amp;sdtfrom=v3000&amp;amp;platform=10403&amp;amp;appver=5.1.1.14483&amp;amp;projection=dlna&lt;/res&gt;&lt;upnp:albumArtURI&gt;&lt;/upnp:albumArtURI&gt;&lt;upnp:class&gt;object.item.videoItem&lt;/upnp:class&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;</CurrentURIMetaData>
        </u:SetAVTransportURI>
    </s:Body>
</s:Envelope>


================================================
FILE: Example/DLNA_UPnP/en.lproj/InfoPlist.strings
================================================
/* Localized versions of Info.plist keys */



================================================
FILE: Example/DLNA_UPnP/main.m
================================================
//
//  main.m
//  DLNA_UPnP
//
//  Created by ClaudeLi on 04/12/2019.
//  Copyright (c) 2019 ClaudeLi. All rights reserved.
//

@import UIKit;
#import "CLAppDelegate.h"

int main(int argc, char * argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([CLAppDelegate class]));
    }
}


================================================
FILE: Example/DLNA_UPnP.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 46;
	objects = {

/* Begin PBXBuildFile section */
		52800E5949DE6A30C8FF09BD /* libPods-DLNA_UPnP_Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B60E5231AE749B97F1D1C462 /* libPods-DLNA_UPnP_Example.a */; };
		6003F58E195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
		6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58F195388D20070C39A /* CoreGraphics.framework */; };
		6003F592195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
		6003F598195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F596195388D20070C39A /* InfoPlist.strings */; };
		6003F59A195388D20070C39A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F599195388D20070C39A /* main.m */; };
		6003F59E195388D20070C39A /* CLAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F59D195388D20070C39A /* CLAppDelegate.m */; };
		6003F5A7195388D20070C39A /* CLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5A6195388D20070C39A /* CLViewController.m */; };
		6003F5A9195388D20070C39A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5A8195388D20070C39A /* Images.xcassets */; };
		6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F5AF195388D20070C39A /* XCTest.framework */; };
		6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; };
		6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; };
		6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; };
		6003F5BC195388D20070C39A /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6003F5BB195388D20070C39A /* Tests.m */; };
		71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; };
		873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
		D3D9AF2022606B340076323F /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D3D9AF1F22606B340076323F /* libxml2.tbd */; };
		D3D9AF2822606BB20076323F /* CLSearchDeviceController.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D9AF2222606BB10076323F /* CLSearchDeviceController.m */; };
		D3D9AF2922606BB20076323F /* CLControlViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D3D9AF2422606BB10076323F /* CLControlViewController.xib */; };
		D3D9AF2A22606BB20076323F /* SetUrl.xml in Resources */ = {isa = PBXBuildFile; fileRef = D3D9AF2522606BB10076323F /* SetUrl.xml */; };
		D3D9AF2B22606BB20076323F /* Device.xml in Resources */ = {isa = PBXBuildFile; fileRef = D3D9AF2622606BB10076323F /* Device.xml */; };
		D3D9AF2C22606BB20076323F /* CLControlViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D3D9AF2722606BB20076323F /* CLControlViewController.m */; };
		F0F7FC3678F11303AEC0E8A2 /* libPods-DLNA_UPnP_Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BA06F854290C9B1A65680FDC /* libPods-DLNA_UPnP_Tests.a */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		6003F5B3195388D20070C39A /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 6003F582195388D10070C39A /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 6003F589195388D20070C39A;
			remoteInfo = DLNA_UPnP;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		33877A4B74C76500B3F15B08 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
		3AA9BB091492B50428FADD70 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
		4EAFDA8B30FC0ACC8E63421A /* Pods-DLNA_UPnP_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DLNA_UPnP_Example.debug.xcconfig"; path = "Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example.debug.xcconfig"; sourceTree = "<group>"; };
		526C746C7AA9B05CCD545EA6 /* DLNA_UPnP.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = DLNA_UPnP.podspec; path = ../DLNA_UPnP.podspec; sourceTree = "<group>"; };
		6003F58A195388D20070C39A /* DLNA_UPnP_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DLNA_UPnP_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
		6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
		6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
		6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
		6003F595195388D20070C39A /* DLNA_UPnP-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DLNA_UPnP-Info.plist"; sourceTree = "<group>"; };
		6003F597195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
		6003F599195388D20070C39A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		6003F59B195388D20070C39A /* DLNA_UPnP-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DLNA_UPnP-Prefix.pch"; sourceTree = "<group>"; };
		6003F59C195388D20070C39A /* CLAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CLAppDelegate.h; sourceTree = "<group>"; };
		6003F59D195388D20070C39A /* CLAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CLAppDelegate.m; sourceTree = "<group>"; };
		6003F5A5195388D20070C39A /* CLViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CLViewController.h; sourceTree = "<group>"; };
		6003F5A6195388D20070C39A /* CLViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CLViewController.m; sourceTree = "<group>"; };
		6003F5A8195388D20070C39A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
		6003F5AE195388D20070C39A /* DLNA_UPnP_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DLNA_UPnP_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
		6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; };
		6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
		6003F5BB195388D20070C39A /* Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Tests.m; sourceTree = "<group>"; };
		606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tests-Prefix.pch"; sourceTree = "<group>"; };
		71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		873B8AEA1B1F5CCA007FD442 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
		B60E5231AE749B97F1D1C462 /* libPods-DLNA_UPnP_Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DLNA_UPnP_Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
		B638DE6E697A5E707378525C /* Pods-DLNA_UPnP_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DLNA_UPnP_Tests.debug.xcconfig"; path = "Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests.debug.xcconfig"; sourceTree = "<group>"; };
		BA06F854290C9B1A65680FDC /* libPods-DLNA_UPnP_Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-DLNA_UPnP_Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
		C413FBDAEFF4E761BF1DB8C6 /* Pods-DLNA_UPnP_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DLNA_UPnP_Example.release.xcconfig"; path = "Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example.release.xcconfig"; sourceTree = "<group>"; };
		D3D9AF1F22606B340076323F /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
		D3D9AF2122606BB00076323F /* CLSearchDeviceController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLSearchDeviceController.h; sourceTree = "<group>"; };
		D3D9AF2222606BB10076323F /* CLSearchDeviceController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLSearchDeviceController.m; sourceTree = "<group>"; };
		D3D9AF2322606BB10076323F /* CLControlViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CLControlViewController.h; sourceTree = "<group>"; };
		D3D9AF2422606BB10076323F /* CLControlViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CLControlViewController.xib; sourceTree = "<group>"; };
		D3D9AF2522606BB10076323F /* SetUrl.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = SetUrl.xml; sourceTree = "<group>"; };
		D3D9AF2622606BB10076323F /* Device.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Device.xml; sourceTree = "<group>"; };
		D3D9AF2722606BB20076323F /* CLControlViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CLControlViewController.m; sourceTree = "<group>"; };
		EAC0222A22A35805116A2C89 /* Pods-DLNA_UPnP_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DLNA_UPnP_Tests.release.xcconfig"; path = "Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		6003F587195388D20070C39A /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D3D9AF2022606B340076323F /* libxml2.tbd in Frameworks */,
				6003F590195388D20070C39A /* CoreGraphics.framework in Frameworks */,
				6003F592195388D20070C39A /* UIKit.framework in Frameworks */,
				6003F58E195388D20070C39A /* Foundation.framework in Frameworks */,
				52800E5949DE6A30C8FF09BD /* libPods-DLNA_UPnP_Example.a in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		6003F5AB195388D20070C39A /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				6003F5B0195388D20070C39A /* XCTest.framework in Frameworks */,
				6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */,
				6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */,
				F0F7FC3678F11303AEC0E8A2 /* libPods-DLNA_UPnP_Tests.a in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		6003F581195388D10070C39A = {
			isa = PBXGroup;
			children = (
				60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
				6003F593195388D20070C39A /* Example for DLNA_UPnP */,
				6003F5B5195388D20070C39A /* Tests */,
				6003F58C195388D20070C39A /* Frameworks */,
				6003F58B195388D20070C39A /* Products */,
				6B6D773769F276D3400215BD /* Pods */,
			);
			sourceTree = "<group>";
		};
		6003F58B195388D20070C39A /* Products */ = {
			isa = PBXGroup;
			children = (
				6003F58A195388D20070C39A /* DLNA_UPnP_Example.app */,
				6003F5AE195388D20070C39A /* DLNA_UPnP_Tests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		6003F58C195388D20070C39A /* Frameworks */ = {
			isa = PBXGroup;
			children = (
				D3D9AF1F22606B340076323F /* libxml2.tbd */,
				6003F58D195388D20070C39A /* Foundation.framework */,
				6003F58F195388D20070C39A /* CoreGraphics.framework */,
				6003F591195388D20070C39A /* UIKit.framework */,
				6003F5AF195388D20070C39A /* XCTest.framework */,
				B60E5231AE749B97F1D1C462 /* libPods-DLNA_UPnP_Example.a */,
				BA06F854290C9B1A65680FDC /* libPods-DLNA_UPnP_Tests.a */,
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
		6003F593195388D20070C39A /* Example for DLNA_UPnP */ = {
			isa = PBXGroup;
			children = (
				6003F59C195388D20070C39A /* CLAppDelegate.h */,
				6003F59D195388D20070C39A /* CLAppDelegate.m */,
				873B8AEA1B1F5CCA007FD442 /* Main.storyboard */,
				6003F5A5195388D20070C39A /* CLViewController.h */,
				6003F5A6195388D20070C39A /* CLViewController.m */,
				D3D9AF2122606BB00076323F /* CLSearchDeviceController.h */,
				D3D9AF2222606BB10076323F /* CLSearchDeviceController.m */,
				D3D9AF2322606BB10076323F /* CLControlViewController.h */,
				D3D9AF2722606BB20076323F /* CLControlViewController.m */,
				D3D9AF2422606BB10076323F /* CLControlViewController.xib */,
				D3D9AF2622606BB10076323F /* Device.xml */,
				D3D9AF2522606BB10076323F /* SetUrl.xml */,
				71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */,
				6003F5A8195388D20070C39A /* Images.xcassets */,
				6003F594195388D20070C39A /* Supporting Files */,
			);
			name = "Example for DLNA_UPnP";
			path = DLNA_UPnP;
			sourceTree = "<group>";
		};
		6003F594195388D20070C39A /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				6003F595195388D20070C39A /* DLNA_UPnP-Info.plist */,
				6003F596195388D20070C39A /* InfoPlist.strings */,
				6003F599195388D20070C39A /* main.m */,
				6003F59B195388D20070C39A /* DLNA_UPnP-Prefix.pch */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
		6003F5B5195388D20070C39A /* Tests */ = {
			isa = PBXGroup;
			children = (
				6003F5BB195388D20070C39A /* Tests.m */,
				6003F5B6195388D20070C39A /* Supporting Files */,
			);
			path = Tests;
			sourceTree = "<group>";
		};
		6003F5B6195388D20070C39A /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				6003F5B7195388D20070C39A /* Tests-Info.plist */,
				6003F5B8195388D20070C39A /* InfoPlist.strings */,
				606FC2411953D9B200FFA9A0 /* Tests-Prefix.pch */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
		60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
			isa = PBXGroup;
			children = (
				526C746C7AA9B05CCD545EA6 /* DLNA_UPnP.podspec */,
				3AA9BB091492B50428FADD70 /* README.md */,
				33877A4B74C76500B3F15B08 /* LICENSE */,
			);
			name = "Podspec Metadata";
			sourceTree = "<group>";
		};
		6B6D773769F276D3400215BD /* Pods */ = {
			isa = PBXGroup;
			children = (
				4EAFDA8B30FC0ACC8E63421A /* Pods-DLNA_UPnP_Example.debug.xcconfig */,
				C413FBDAEFF4E761BF1DB8C6 /* Pods-DLNA_UPnP_Example.release.xcconfig */,
				B638DE6E697A5E707378525C /* Pods-DLNA_UPnP_Tests.debug.xcconfig */,
				EAC0222A22A35805116A2C89 /* Pods-DLNA_UPnP_Tests.release.xcconfig */,
			);
			path = Pods;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		6003F589195388D20070C39A /* DLNA_UPnP_Example */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "DLNA_UPnP_Example" */;
			buildPhases = (
				8DC65AAB26777F8507A5BECA /* [CP] Check Pods Manifest.lock */,
				6003F586195388D20070C39A /* Sources */,
				6003F587195388D20070C39A /* Frameworks */,
				6003F588195388D20070C39A /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = DLNA_UPnP_Example;
			productName = DLNA_UPnP;
			productReference = 6003F58A195388D20070C39A /* DLNA_UPnP_Example.app */;
			productType = "com.apple.product-type.application";
		};
		6003F5AD195388D20070C39A /* DLNA_UPnP_Tests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "DLNA_UPnP_Tests" */;
			buildPhases = (
				4B2C4B2F56961C91910CB474 /* [CP] Check Pods Manifest.lock */,
				6003F5AA195388D20070C39A /* Sources */,
				6003F5AB195388D20070C39A /* Frameworks */,
				6003F5AC195388D20070C39A /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				6003F5B4195388D20070C39A /* PBXTargetDependency */,
			);
			name = DLNA_UPnP_Tests;
			productName = DLNA_UPnPTests;
			productReference = 6003F5AE195388D20070C39A /* DLNA_UPnP_Tests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		6003F582195388D10070C39A /* Project object */ = {
			isa = PBXProject;
			attributes = {
				CLASSPREFIX = CL;
				LastUpgradeCheck = 0720;
				ORGANIZATIONNAME = ClaudeLi;
				TargetAttributes = {
					6003F5AD195388D20070C39A = {
						TestTargetID = 6003F589195388D20070C39A;
					};
				};
			};
			buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "DLNA_UPnP" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = English;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 6003F581195388D10070C39A;
			productRefGroup = 6003F58B195388D20070C39A /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				6003F589195388D20070C39A /* DLNA_UPnP_Example */,
				6003F5AD195388D20070C39A /* DLNA_UPnP_Tests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		6003F588195388D20070C39A /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				D3D9AF2A22606BB20076323F /* SetUrl.xml in Resources */,
				873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */,
				71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */,
				6003F5A9195388D20070C39A /* Images.xcassets in Resources */,
				D3D9AF2B22606BB20076323F /* Device.xml in Resources */,
				6003F598195388D20070C39A /* InfoPlist.strings in Resources */,
				D3D9AF2922606BB20076323F /* CLControlViewController.xib in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		6003F5AC195388D20070C39A /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
		4B2C4B2F56961C91910CB474 /* [CP] Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
				"${PODS_ROOT}/Manifest.lock",
			);
			name = "[CP] Check Pods Manifest.lock";
			outputFileListPaths = (
			);
			outputPaths = (
				"$(DERIVED_FILE_DIR)/Pods-DLNA_UPnP_Tests-checkManifestLockResult.txt",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
			showEnvVarsInLog = 0;
		};
		8DC65AAB26777F8507A5BECA /* [CP] Check Pods Manifest.lock */ = {
			isa = PBXShellScriptBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			inputFileListPaths = (
			);
			inputPaths = (
				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
				"${PODS_ROOT}/Manifest.lock",
			);
			name = "[CP] Check Pods Manifest.lock";
			outputFileListPaths = (
			);
			outputPaths = (
				"$(DERIVED_FILE_DIR)/Pods-DLNA_UPnP_Example-checkManifestLockResult.txt",
			);
			runOnlyForDeploymentPostprocessing = 0;
			shellPath = /bin/sh;
			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
			showEnvVarsInLog = 0;
		};
/* End PBXShellScriptBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		6003F586195388D20070C39A /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				6003F59E195388D20070C39A /* CLAppDelegate.m in Sources */,
				6003F5A7195388D20070C39A /* CLViewController.m in Sources */,
				D3D9AF2822606BB20076323F /* CLSearchDeviceController.m in Sources */,
				6003F59A195388D20070C39A /* main.m in Sources */,
				D3D9AF2C22606BB20076323F /* CLControlViewController.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		6003F5AA195388D20070C39A /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				6003F5BC195388D20070C39A /* Tests.m in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		6003F5B4195388D20070C39A /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 6003F589195388D20070C39A /* DLNA_UPnP_Example */;
			targetProxy = 6003F5B3195388D20070C39A /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
		6003F596195388D20070C39A /* InfoPlist.strings */ = {
			isa = PBXVariantGroup;
			children = (
				6003F597195388D20070C39A /* en */,
			);
			name = InfoPlist.strings;
			sourceTree = "<group>";
		};
		6003F5B8195388D20070C39A /* InfoPlist.strings */ = {
			isa = PBXVariantGroup;
			children = (
				6003F5B9195388D20070C39A /* en */,
			);
			name = InfoPlist.strings;
			sourceTree = "<group>";
		};
		71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */ = {
			isa = PBXVariantGroup;
			children = (
				71719F9E1E33DC2100824A3D /* Base */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";
		};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
		6003F5BD195388D20070C39A /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = NO;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		6003F5BE195388D20070C39A /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
				COPY_PHASE_STRIP = YES;
				ENABLE_NS_ASSERTIONS = NO;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
				SDKROOT = iphoneos;
				TARGETED_DEVICE_FAMILY = "1,2";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		6003F5C0195388D20070C39A /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = 4EAFDA8B30FC0ACC8E63421A /* Pods-DLNA_UPnP_Example.debug.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				GCC_PRECOMPILE_PREFIX_HEADER = YES;
				GCC_PREFIX_HEADER = "DLNA_UPnP/DLNA_UPnP-Prefix.pch";
				HEADER_SEARCH_PATHS = (
					"$(inherited)",
					"\"${PODS_CONFIGURATION_BUILD_DIR}/CocoaAsyncSocket/CocoaAsyncSocket.framework/Headers\"",
					"\"${PODS_CONFIGURATION_BUILD_DIR}/DLNA_UPnP/DLNA_UPnP.framework/Headers\"",
				);
				INFOPLIST_FILE = "DLNA_UPnP/DLNA_UPnP-Info.plist";
				MODULE_NAME = ExampleApp;
				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
				PRODUCT_NAME = "$(TARGET_NAME)";
				WRAPPER_EXTENSION = app;
			};
			name = Debug;
		};
		6003F5C1195388D20070C39A /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = C413FBDAEFF4E761BF1DB8C6 /* Pods-DLNA_UPnP_Example.release.xcconfig */;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				GCC_PRECOMPILE_PREFIX_HEADER = YES;
				GCC_PREFIX_HEADER = "DLNA_UPnP/DLNA_UPnP-Prefix.pch";
				HEADER_SEARCH_PATHS = (
					"$(inherited)",
					"\"${PODS_CONFIGURATION_BUILD_DIR}/CocoaAsyncSocket/CocoaAsyncSocket.framework/Headers\"",
					"\"${PODS_CONFIGURATION_BUILD_DIR}/DLNA_UPnP/DLNA_UPnP.framework/Headers\"",
				);
				INFOPLIST_FILE = "DLNA_UPnP/DLNA_UPnP-Info.plist";
				MODULE_NAME = ExampleApp;
				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
				PRODUCT_NAME = "$(TARGET_NAME)";
				WRAPPER_EXTENSION = app;
			};
			name = Release;
		};
		6003F5C3195388D20070C39A /* Debug */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = B638DE6E697A5E707378525C /* Pods-DLNA_UPnP_Tests.debug.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				FRAMEWORK_SEARCH_PATHS = (
					"$(SDKROOT)/Developer/Library/Frameworks",
					"$(inherited)",
					"$(DEVELOPER_FRAMEWORKS_DIR)",
				);
				GCC_PRECOMPILE_PREFIX_HEADER = YES;
				GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				INFOPLIST_FILE = "Tests/Tests-Info.plist";
				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DLNA_UPnP_Example.app/DLNA_UPnP_Example";
				WRAPPER_EXTENSION = xctest;
			};
			name = Debug;
		};
		6003F5C4195388D20070C39A /* Release */ = {
			isa = XCBuildConfiguration;
			baseConfigurationReference = EAC0222A22A35805116A2C89 /* Pods-DLNA_UPnP_Tests.release.xcconfig */;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				FRAMEWORK_SEARCH_PATHS = (
					"$(SDKROOT)/Developer/Library/Frameworks",
					"$(inherited)",
					"$(DEVELOPER_FRAMEWORKS_DIR)",
				);
				GCC_PRECOMPILE_PREFIX_HEADER = YES;
				GCC_PREFIX_HEADER = "Tests/Tests-Prefix.pch";
				INFOPLIST_FILE = "Tests/Tests-Info.plist";
				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.${PRODUCT_NAME:rfc1034identifier}";
				PRODUCT_NAME = "$(TARGET_NAME)";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DLNA_UPnP_Example.app/DLNA_UPnP_Example";
				WRAPPER_EXTENSION = xctest;
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		6003F585195388D10070C39A /* Build configuration list for PBXProject "DLNA_UPnP" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				6003F5BD195388D20070C39A /* Debug */,
				6003F5BE195388D20070C39A /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		6003F5BF195388D20070C39A /* Build configuration list for PBXNativeTarget "DLNA_UPnP_Example" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				6003F5C0195388D20070C39A /* Debug */,
				6003F5C1195388D20070C39A /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		6003F5C2195388D20070C39A /* Build configuration list for PBXNativeTarget "DLNA_UPnP_Tests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				6003F5C3195388D20070C39A /* Debug */,
				6003F5C4195388D20070C39A /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 6003F582195388D10070C39A /* Project object */;
}


================================================
FILE: Example/DLNA_UPnP.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:DLNA_UPnP.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: Example/DLNA_UPnP.xcodeproj/xcshareddata/xcschemes/DLNA_UPnP-Example.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "0720"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "6003F589195388D20070C39A"
               BuildableName = "DLNA_UPnP_Example.app"
               BlueprintName = "DLNA_UPnP_Example"
               ReferencedContainer = "container:DLNA_UPnP.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <Testables>
         <TestableReference
            skipped = "NO">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "6003F5AD195388D20070C39A"
               BuildableName = "DLNA_UPnP_Tests.xctest"
               BlueprintName = "DLNA_UPnP_Tests"
               ReferencedContainer = "container:DLNA_UPnP.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "6003F589195388D20070C39A"
            BuildableName = "DLNA_UPnP_Example.app"
            BlueprintName = "DLNA_UPnP_Example"
            ReferencedContainer = "container:DLNA_UPnP.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <AdditionalOptions>
      </AdditionalOptions>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "6003F589195388D20070C39A"
            BuildableName = "DLNA_UPnP_Example.app"
            BlueprintName = "DLNA_UPnP_Example"
            ReferencedContainer = "container:DLNA_UPnP.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
      <AdditionalOptions>
      </AdditionalOptions>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "6003F589195388D20070C39A"
            BuildableName = "DLNA_UPnP_Example.app"
            BlueprintName = "DLNA_UPnP_Example"
            ReferencedContainer = "container:DLNA_UPnP.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: Example/DLNA_UPnP.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:DLNA_UPnP.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: Example/DLNA_UPnP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: Example/Podfile
================================================
platform :ios, '8.0'

target 'DLNA_UPnP_Example' do
  pod 'DLNA_UPnP', :path => '../'

  target 'DLNA_UPnP_Tests' do
    inherit! :search_paths

    
  end
end


================================================
FILE: Example/Pods/CocoaAsyncSocket/LICENSE.txt
================================================
This library is in the public domain.
However, not all organizations are allowed to use such a license.
For example, Germany doesn't recognize the Public Domain and one is not allowed to use libraries under such license (or similar).

Thus, the library is now dual licensed,
and one is allowed to choose which license they would like to use.

##################################################
License Option #1 :
##################################################

Public Domain

##################################################
License Option #2 :
##################################################

Software License Agreement (BSD License)

Copyright (c) 2017, Deusty, LLC
All rights reserved.

Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above
  copyright notice, this list of conditions and the
  following disclaimer.

* Neither the name of Deusty LLC nor the names of its
  contributors may be used to endorse or promote products
  derived from this software without specific prior
  written permission of Deusty LLC.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

================================================
FILE: Example/Pods/CocoaAsyncSocket/README.markdown
================================================
# CocoaAsyncSocket
[![Build Status](https://travis-ci.org/robbiehanson/CocoaAsyncSocket.svg?branch=master)](https://travis-ci.org/robbiehanson/CocoaAsyncSocket) [![Version Status](https://img.shields.io/cocoapods/v/CocoaAsyncSocket.svg?style=flat)](http://cocoadocs.org/docsets/CocoaAsyncSocket) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Platform](http://img.shields.io/cocoapods/p/CocoaAsyncSocket.svg?style=flat)](http://cocoapods.org/?q=CocoaAsyncSocket) [![license Public Domain](https://img.shields.io/badge/license-Public%20Domain-orange.svg?style=flat)](https://en.wikipedia.org/wiki/Public_domain)


CocoaAsyncSocket provides easy-to-use and powerful asynchronous socket libraries for Mac and iOS. The classes are described below.

## Installation

#### CocoaPods

Install using [CocoaPods](http://cocoapods.org) by adding this line to your Podfile:

````ruby
use_frameworks! # Add this if you are targeting iOS 8+ or using Swift
pod 'CocoaAsyncSocket'  
````

#### Carthage

CocoaAsyncSocket is [Carthage](https://github.com/Carthage/Carthage) compatible. To include it add the following line to your `Cartfile`

```bash
github "robbiehanson/CocoaAsyncSocket" "master"
```

The project is currently configured to build for **iOS**, **tvOS** and **Mac**.  After building with carthage the resultant frameworks will be stored in:

* `Carthage/Build/iOS/CocoaAsyncSocket.framework`
* `Carthage/Build/tvOS/CocoaAsyncSocket.framework`
* `Carthage/Build/Mac/CocoaAsyncSocket.framework`

Select the correct framework(s) and drag it into your project.

#### Manual

You can also include it into your project by adding the source files directly, but you should probably be using a dependency manager to keep up to date.

### Importing

Using Objective-C:

```obj-c
// When using iOS 8+ frameworks
@import CocoaAsyncSocket; 

// OR when not using frameworks, targeting iOS 7 or below
#import "GCDAsyncSocket.h" // for TCP
#import "GCDAsyncUdpSocket.h" // for UDP
```

Using Swift:

```swift
import CocoaAsyncSocket
```

## TCP

**GCDAsyncSocket** is a TCP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available:

- Native objective-c, fully self-contained in one class.<br/>
  _No need to muck around with sockets or streams. This class handles everything for you._

- Full delegate support<br/>
  _Errors, connections, read completions, write completions, progress, and disconnections all result in a call to your delegate method._

- Queued non-blocking reads and writes, with optional timeouts.<br/>
  _You tell it what to read or write, and it handles everything for you. Queueing, buffering, and searching for termination sequences within the stream - all handled for you automatically._

- Automatic socket acceptance.<br/>
  _Spin up a server socket, tell it to accept connections, and it will call you with new instances of itself for each connection._

- Support for TCP streams over IPv4 and IPv6.<br/>
  _Automatically connect to IPv4 or IPv6 hosts. Automatically accept incoming connections over both IPv4 and IPv6 with a single instance of this class. No more worrying about multiple sockets._

- Support for TLS / SSL<br/>
  _Secure your socket with ease using just a single method call. Available for both client and server sockets._

- Fully GCD based and Thread-Safe<br/>
  _It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._

- The Latest Technology & Performance Optimizations<br/>
  _Internally the library takes advantage of technologies such as [kqueue's](http://en.wikipedia.org/wiki/Kqueue) to limit [system calls](http://en.wikipedia.org/wiki/System_call) and optimize buffer allocations. In other words, peak performance._

## UDP

**GCDAsyncUdpSocket** is a UDP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available:

- Native objective-c, fully self-contained in one class.<br/>
  _No need to muck around with low-level sockets. This class handles everything for you._

- Full delegate support.<br/>
  _Errors, send completions, receive completions, and disconnections all result in a call to your delegate method._

- Queued non-blocking send and receive operations, with optional timeouts.<br/>
  _You tell it what to send or receive, and it handles everything for you. Queueing, buffering, waiting and checking errno - all handled for you automatically._

- Support for IPv4 and IPv6.<br/>
  _Automatically send/recv using IPv4 and/or IPv6. No more worrying about multiple sockets._

- Fully GCD based and Thread-Safe<br/>
  _It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._

***

For those new(ish) to networking, it's recommended you **[read the wiki](https://github.com/robbiehanson/CocoaAsyncSocket/wiki)**.<br/>_Sockets might not work exactly like you think they do..._

**Still got questions?** Try the **[CocoaAsyncSocket Mailing List](http://groups.google.com/group/cocoaasyncsocket)**.
***

Love the project? Wanna buy me a ☕️&nbsp;&nbsp;? (or a 🍺&nbsp;&nbsp;😀&nbsp;):

[![donation-bitcoin](https://bitpay.com/img/donate-sm.png)](https://onename.com/robbiehanson)
[![donation-paypal](https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2M8C699FQ8AW2)



================================================
FILE: Example/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h
================================================
//  
//  GCDAsyncSocket.h
//  
//  This class is in the public domain.
//  Originally created by Robbie Hanson in Q3 2010.
//  Updated and maintained by Deusty LLC and the Apple development community.
//  
//  https://github.com/robbiehanson/CocoaAsyncSocket
//

#import <Foundation/Foundation.h>
#import <Security/Security.h>
#import <Security/SecureTransport.h>
#import <dispatch/dispatch.h>
#import <Availability.h>

#include <sys/socket.h> // AF_INET, AF_INET6

@class GCDAsyncReadPacket;
@class GCDAsyncWritePacket;
@class GCDAsyncSocketPreBuffer;
@protocol GCDAsyncSocketDelegate;

NS_ASSUME_NONNULL_BEGIN

extern NSString *const GCDAsyncSocketException;
extern NSString *const GCDAsyncSocketErrorDomain;

extern NSString *const GCDAsyncSocketQueueName;
extern NSString *const GCDAsyncSocketThreadName;

extern NSString *const GCDAsyncSocketManuallyEvaluateTrust;
#if TARGET_OS_IPHONE
extern NSString *const GCDAsyncSocketUseCFStreamForTLS;
#endif
#define GCDAsyncSocketSSLPeerName     (NSString *)kCFStreamSSLPeerName
#define GCDAsyncSocketSSLCertificates (NSString *)kCFStreamSSLCertificates
#define GCDAsyncSocketSSLIsServer     (NSString *)kCFStreamSSLIsServer
extern NSString *const GCDAsyncSocketSSLPeerID;
extern NSString *const GCDAsyncSocketSSLProtocolVersionMin;
extern NSString *const GCDAsyncSocketSSLProtocolVersionMax;
extern NSString *const GCDAsyncSocketSSLSessionOptionFalseStart;
extern NSString *const GCDAsyncSocketSSLSessionOptionSendOneByteRecord;
extern NSString *const GCDAsyncSocketSSLCipherSuites;
#if !TARGET_OS_IPHONE
extern NSString *const GCDAsyncSocketSSLDiffieHellmanParameters;
#endif

#define GCDAsyncSocketLoggingContext 65535


typedef NS_ENUM(NSInteger, GCDAsyncSocketError) {
	GCDAsyncSocketNoError = 0,           // Never used
	GCDAsyncSocketBadConfigError,        // Invalid configuration
	GCDAsyncSocketBadParamError,         // Invalid parameter was passed
	GCDAsyncSocketConnectTimeoutError,   // A connect operation timed out
	GCDAsyncSocketReadTimeoutError,      // A read operation timed out
	GCDAsyncSocketWriteTimeoutError,     // A write operation timed out
	GCDAsyncSocketReadMaxedOutError,     // Reached set maxLength without completing
	GCDAsyncSocketClosedError,           // The remote peer closed the connection
	GCDAsyncSocketOtherError,            // Description provided in userInfo
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


@interface GCDAsyncSocket : NSObject

/**
 * GCDAsyncSocket uses the standard delegate paradigm,
 * but executes all delegate callbacks on a given delegate dispatch queue.
 * This allows for maximum concurrency, while at the same time providing easy thread safety.
 * 
 * You MUST set a delegate AND delegate dispatch queue before attempting to
 * use the socket, or you will get an error.
 * 
 * The socket queue is optional.
 * If you pass NULL, GCDAsyncSocket will automatically create it's own socket queue.
 * If you choose to provide a socket queue, the socket queue must not be a concurrent queue.
 * If you choose to provide a socket queue, and the socket queue has a configured target queue,
 * then please see the discussion for the method markSocketQueueTargetQueue.
 * 
 * The delegate queue and socket queue can optionally be the same.
**/
- (instancetype)init;
- (instancetype)initWithSocketQueue:(nullable dispatch_queue_t)sq;
- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq;
- (instancetype)initWithDelegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq;

/**
 * Create GCDAsyncSocket from already connect BSD socket file descriptor
**/
+ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD socketQueue:(nullable dispatch_queue_t)sq error:(NSError**)error;

+ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq error:(NSError**)error;

+ (nullable instancetype)socketFromConnectedSocketFD:(int)socketFD delegate:(nullable id<GCDAsyncSocketDelegate>)aDelegate delegateQueue:(nullable dispatch_queue_t)dq socketQueue:(nullable dispatch_queue_t)sq error:(NSError **)error;

#pragma mark Configuration

@property (atomic, weak, readwrite, nullable) id<GCDAsyncSocketDelegate> delegate;
#if OS_OBJECT_USE_OBJC
@property (atomic, strong, readwrite, nullable) dispatch_queue_t delegateQueue;
#else
@property (atomic, assign, readwrite, nullable) dispatch_queue_t delegateQueue;
#endif

- (void)getDelegate:(id<GCDAsyncSocketDelegate> __nullable * __nullable)delegatePtr delegateQueue:(dispatch_queue_t __nullable * __nullable)delegateQueuePtr;
- (void)setDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;

/**
 * If you are setting the delegate to nil within the delegate's dealloc method,
 * you may need to use the synchronous versions below.
**/
- (void)synchronouslySetDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate;
- (void)synchronouslySetDelegateQueue:(nullable dispatch_queue_t)delegateQueue;
- (void)synchronouslySetDelegate:(nullable id<GCDAsyncSocketDelegate>)delegate delegateQueue:(nullable dispatch_queue_t)delegateQueue;

/**
 * By default, both IPv4 and IPv6 are enabled.
 * 
 * For accepting incoming connections, this means GCDAsyncSocket automatically supports both protocols,
 * and can simulataneously accept incoming connections on either protocol.
 * 
 * For outgoing connections, this means GCDAsyncSocket can connect to remote hosts running either protocol.
 * If a DNS lookup returns only IPv4 results, GCDAsyncSocket will automatically use IPv4.
 * If a DNS lookup returns only IPv6 results, GCDAsyncSocket will automatically use IPv6.
 * If a DNS lookup returns both IPv4 and IPv6 results, the preferred protocol will be chosen.
 * By default, the preferred protocol is IPv4, but may be configured as desired.
**/

@property (atomic, assign, readwrite, getter=isIPv4Enabled) BOOL IPv4Enabled;
@property (atomic, assign, readwrite, getter=isIPv6Enabled) BOOL IPv6Enabled;

@property (atomic, assign, readwrite, getter=isIPv4PreferredOverIPv6) BOOL IPv4PreferredOverIPv6;

/** 
 * When connecting to both IPv4 and IPv6 using Happy Eyeballs (RFC 6555) https://tools.ietf.org/html/rfc6555
 * this is the delay between connecting to the preferred protocol and the fallback protocol.
 *
 * Defaults to 300ms.
**/
@property (atomic, assign, readwrite) NSTimeInterval alternateAddressDelay;

/**
 * User data allows you to associate arbitrary information with the socket.
 * This data is not used internally by socket in any way.
**/
@property (atomic, strong, readwrite, nullable) id userData;

#pragma mark Accepting

/**
 * Tells the socket to begin listening and accepting connections on the given port.
 * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it,
 * and the socket:didAcceptNewSocket: delegate method will be invoked.
 * 
 * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
**/
- (BOOL)acceptOnPort:(uint16_t)port error:(NSError **)errPtr;

/**
 * This method is the same as acceptOnPort:error: with the
 * additional option of specifying which interface to listen on.
 * 
 * For example, you could specify that the socket should only accept connections over ethernet,
 * and not other interfaces such as wifi.
 * 
 * The interface may be specified by name (e.g. "en1" or "lo0") or by IP address (e.g. "192.168.4.34").
 * You may also use the special strings "localhost" or "loopback" to specify that
 * the socket only accept connections from the local machine.
 * 
 * You can see the list of interfaces via the command line utility "ifconfig",
 * or programmatically via the getifaddrs() function.
 * 
 * To accept connections on any interface pass nil, or simply use the acceptOnPort:error: method.
**/
- (BOOL)acceptOnInterface:(nullable NSString *)interface port:(uint16_t)port error:(NSError **)errPtr;

/**
 * Tells the socket to begin listening and accepting connections on the unix domain at the given url.
 * When a connection is accepted, a new instance of GCDAsyncSocket will be spawned to handle it,
 * and the socket:didAcceptNewSocket: delegate method will be invoked.
 *
 * The socket will listen on all available interfaces (e.g. wifi, ethernet, etc)
 **/
- (BOOL)acceptOnUrl:(NSURL *)url error:(NSError **)errPtr;

#pragma mark Connecting

/**
 * Connects to the given host and port.
 * 
 * This method invokes connectToHost:onPort:viaInterface:withTimeout:error:
 * and uses the default interface, and no timeout.
**/
- (BOOL)connectToHost:(NSString *)host onPort:(uint16_t)port error:(NSError **)errPtr;

/**
 * Connects to the given host and port with an optional timeout.
 * 
 * This method invokes connectToHost:onPort:viaInterface:withTimeout:error: and uses the default interface.
**/
- (BOOL)connectToHost:(NSString *)host
               onPort:(uint16_t)port
          withTimeout:(NSTimeInterval)timeout
                error:(NSError **)errPtr;

/**
 * Connects to the given host & port, via the optional interface, with an optional timeout.
 * 
 * The host may be a domain name (e.g. "deusty.com") or an IP address string (e.g. "192.168.0.2").
 * The host may also be the special strings "localhost" or "loopback" to specify connecting
 * to a service on the local machine.
 * 
 * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
 * The interface may also be used to specify the local port (see below).
 * 
 * To not time out use a negative time interval.
 * 
 * This method will return NO if an error is detected, and set the error pointer (if one was given).
 * Possible errors would be a nil host, invalid interface, or socket is already connected.
 * 
 * If no errors are detected, this method will start a background connect operation and immediately return YES.
 * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable.
 * 
 * Since this class supports queued reads and writes, you can immediately start reading and/or writing.
 * All read/write operations will be queued, and upon socket connection,
 * the operations will be dequeued and processed in order.
 * 
 * The interface may optionally contain a port number at the end of the string, separated by a colon.
 * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end)
 * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424".
 * To specify only local port: ":8082".
 * Please note this is an advanced feature, and is somewhat hidden on purpose.
 * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection.
 * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere.
 * Local ports do NOT need to match remote ports. In fact, they almost never do.
 * This feature is here for networking professionals using very advanced techniques.
**/
- (BOOL)connectToHost:(NSString *)host
               onPort:(uint16_t)port
         viaInterface:(nullable NSString *)interface
          withTimeout:(NSTimeInterval)timeout
                error:(NSError **)errPtr;

/**
 * Connects to the given address, specified as a sockaddr structure wrapped in a NSData object.
 * For example, a NSData object returned from NSNetService's addresses method.
 * 
 * If you have an existing struct sockaddr you can convert it to a NSData object like so:
 * struct sockaddr sa  -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
 * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
 * 
 * This method invokes connectToAdd
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;

/**
 * This method is the same as connectToAddress:error: with an additional timeout option.
 * To not time out use a negative time interval, or simply use the connectToAddress:error: method.
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;

/**
 * Connects to the given address, using the specified interface and timeout.
 * 
 * The address is specified as a sockaddr structure wrapped in a NSData object.
 * For example, a NSData object returned from NSNetService's addresses method.
 * 
 * If you have an existing struct sockaddr you can convert it to a NSData object like so:
 * struct sockaddr sa  -> NSData *dsa = [NSData dataWithBytes:&remoteAddr length:remoteAddr.sa_len];
 * struct sockaddr *sa -> NSData *dsa = [NSData dataWithBytes:remoteAddr length:remoteAddr->sa_len];
 * 
 * The interface may be a name (e.g. "en1" or "lo0") or the corresponding IP address (e.g. "192.168.4.35").
 * The interface may also be used to specify the local port (see below).
 * 
 * The timeout is optional. To not time out use a negative time interval.
 * 
 * This method will return NO if an error is detected, and set the error pointer (if one was given).
 * Possible errors would be a nil host, invalid interface, or socket is already connected.
 * 
 * If no errors are detected, this method will start a background connect operation and immediately return YES.
 * The delegate callbacks are used to notify you when the socket connects, or if the host was unreachable.
 * 
 * Since this class supports queued reads and writes, you can immediately start reading and/or writing.
 * All read/write operations will be queued, and upon socket connection,
 * the operations will be dequeued and processed in order.
 * 
 * The interface may optionally contain a port number at the end of the string, separated by a colon.
 * This allows you to specify the local port that should be used for the outgoing connection. (read paragraph to end)
 * To specify both interface and local port: "en1:8082" or "192.168.4.35:2424".
 * To specify only local port: ":8082".
 * Please note this is an advanced feature, and is somewhat hidden on purpose.
 * You should understand that 99.999% of the time you should NOT specify the local port for an outgoing connection.
 * If you think you need to, there is a very good chance you have a fundamental misunderstanding somewhere.
 * Local ports do NOT need to match remote ports. In fact, they almost never do.
 * This feature is here for networking professionals using very advanced techniques.
**/
- (BOOL)connectToAddress:(NSData *)remoteAddr
            viaInterface:(nullable NSString *)interface
             withTimeout:(NSTimeInterval)timeout
                   error:(NSError **)errPtr;
/**
 * Connects to the unix domain socket at the given url, using the specified timeout.
 */
- (BOOL)connectToUrl:(NSURL *)url withTimeout:(NSTimeInterval)timeout error:(NSError **)errPtr;

#pragma mark Disconnecting

/**
 * Disconnects immediately (synchronously). Any pending reads or writes are dropped.
 * 
 * If the socket is not already disconnected, an invocation to the socketDidDisconnect:withError: delegate method
 * will be queued onto the delegateQueue asynchronously (behind any previously queued delegate methods).
 * In other words, the disconnected delegate method will be invoked sometime shortly after this method returns.
 * 
 * Please note the recommended way of releasing a GCDAsyncSocket instance (e.g. in a dealloc method)
 * [asyncSocket setDelegate:nil];
 * [asyncSocket disconnect];
 * [asyncSocket release];
 * 
 * If you plan on disconnecting the socket, and then immediately asking it to connect again,
 * you'll likely want to do so like this:
 * [asyncSocket setDelegate:nil];
 * [asyncSocket disconnect];
 * [asyncSocket setDelegate:self];
 * [asyncSocket connect...];
**/
- (void)disconnect;

/**
 * Disconnects after all pending reads have completed.
 * After calling this, the read and write methods will do nothing.
 * The socket will disconnect even if there are still pending writes.
**/
- (void)disconnectAfterReading;

/**
 * Disconnects after all pending writes have completed.
 * After calling this, the read and write methods will do nothing.
 * The socket will disconnect even if there are still pending reads.
**/
- (void)disconnectAfterWriting;

/**
 * Disconnects after all pending reads and writes have completed.
 * After calling this, the read and write methods will do nothing.
**/
- (void)disconnectAfterReadingAndWriting;

#pragma mark Diagnostics

/**
 * Returns whether the socket is disconnected or connected.
 * 
 * A disconnected socket may be recycled.
 * That is, it can be used again for connecting or listening.
 * 
 * If a socket is in the process of connecting, it may be neither disconnected nor connected.
**/
@property (atomic, readonly) BOOL isDisconnected;
@property (atomic, readonly) BOOL isConnected;

/**
 * Returns the local or remote host and port to which this socke
Download .txt
gitextract_m2svozsz/

├── .gitignore
├── .travis.yml
├── DLNA_UPnP/
│   ├── Assets/
│   │   └── .gitkeep
│   └── Classes/
│       ├── GData/
│       │   ├── GDataXMLNode.h
│       │   └── GDataXMLNode.m
│       └── UPnP/
│           ├── CLUPnP.h
│           ├── CLUPnPAVPositionInfo.h
│           ├── CLUPnPAVPositionInfo.m
│           ├── CLUPnPAction.h
│           ├── CLUPnPAction.m
│           ├── CLUPnPDevice.h
│           ├── CLUPnPDevice.m
│           ├── CLUPnPRenderer.h
│           ├── CLUPnPRenderer.m
│           ├── CLUPnPResponseDelegate.h
│           ├── CLUPnPServer.h
│           └── CLUPnPServer.m
├── DLNA_UPnP.podspec
├── Example/
│   ├── DLNA_UPnP/
│   │   ├── Base.lproj/
│   │   │   ├── LaunchScreen.storyboard
│   │   │   └── Main.storyboard
│   │   ├── CLAppDelegate.h
│   │   ├── CLAppDelegate.m
│   │   ├── CLControlViewController.h
│   │   ├── CLControlViewController.m
│   │   ├── CLControlViewController.xib
│   │   ├── CLSearchDeviceController.h
│   │   ├── CLSearchDeviceController.m
│   │   ├── CLViewController.h
│   │   ├── CLViewController.m
│   │   ├── DLNA_UPnP-Info.plist
│   │   ├── DLNA_UPnP-Prefix.pch
│   │   ├── Device.xml
│   │   ├── Images.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── SetUrl.xml
│   │   ├── en.lproj/
│   │   │   └── InfoPlist.strings
│   │   └── main.m
│   ├── DLNA_UPnP.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   └── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── DLNA_UPnP-Example.xcscheme
│   ├── DLNA_UPnP.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   ├── Podfile
│   ├── Pods/
│   │   ├── CocoaAsyncSocket/
│   │   │   ├── LICENSE.txt
│   │   │   ├── README.markdown
│   │   │   └── Source/
│   │   │       └── GCD/
│   │   │           ├── GCDAsyncSocket.h
│   │   │           ├── GCDAsyncSocket.m
│   │   │           ├── GCDAsyncUdpSocket.h
│   │   │           └── GCDAsyncUdpSocket.m
│   │   ├── Local Podspecs/
│   │   │   └── DLNA_UPnP.podspec.json
│   │   ├── Pods.xcodeproj/
│   │   │   └── project.pbxproj
│   │   └── Target Support Files/
│   │       ├── CocoaAsyncSocket/
│   │       │   ├── CocoaAsyncSocket-Info.plist
│   │       │   ├── CocoaAsyncSocket-dummy.m
│   │       │   ├── CocoaAsyncSocket-prefix.pch
│   │       │   ├── CocoaAsyncSocket-umbrella.h
│   │       │   ├── CocoaAsyncSocket.modulemap
│   │       │   └── CocoaAsyncSocket.xcconfig
│   │       ├── DLNA_UPnP/
│   │       │   ├── DLNA_UPnP-Info.plist
│   │       │   ├── DLNA_UPnP-dummy.m
│   │       │   ├── DLNA_UPnP-prefix.pch
│   │       │   ├── DLNA_UPnP-umbrella.h
│   │       │   ├── DLNA_UPnP.modulemap
│   │       │   └── DLNA_UPnP.xcconfig
│   │       ├── Pods-DLNA_UPnP_Example/
│   │       │   ├── Pods-DLNA_UPnP_Example-Info.plist
│   │       │   ├── Pods-DLNA_UPnP_Example-acknowledgements.markdown
│   │       │   ├── Pods-DLNA_UPnP_Example-acknowledgements.plist
│   │       │   ├── Pods-DLNA_UPnP_Example-dummy.m
│   │       │   ├── Pods-DLNA_UPnP_Example-frameworks.sh
│   │       │   ├── Pods-DLNA_UPnP_Example-umbrella.h
│   │       │   ├── Pods-DLNA_UPnP_Example.debug.xcconfig
│   │       │   ├── Pods-DLNA_UPnP_Example.modulemap
│   │       │   └── Pods-DLNA_UPnP_Example.release.xcconfig
│   │       └── Pods-DLNA_UPnP_Tests/
│   │           ├── Pods-DLNA_UPnP_Tests-Info.plist
│   │           ├── Pods-DLNA_UPnP_Tests-acknowledgements.markdown
│   │           ├── Pods-DLNA_UPnP_Tests-acknowledgements.plist
│   │           ├── Pods-DLNA_UPnP_Tests-dummy.m
│   │           ├── Pods-DLNA_UPnP_Tests-umbrella.h
│   │           ├── Pods-DLNA_UPnP_Tests.debug.xcconfig
│   │           ├── Pods-DLNA_UPnP_Tests.modulemap
│   │           └── Pods-DLNA_UPnP_Tests.release.xcconfig
│   └── Tests/
│       ├── Tests-Info.plist
│       ├── Tests-Prefix.pch
│       ├── Tests.m
│       └── en.lproj/
│           └── InfoPlist.strings
├── LICENSE
└── README.md
Download .txt
SYMBOL INDEX (3 symbols across 2 files)

FILE: DLNA_UPnP/Classes/GData/GDataXMLNode.h
  type NSUInteger (line 96) | typedef NSUInteger GDataXMLNodeKind;
  function interface (line 98) | interface GDataXMLNode : NSObject {

FILE: DLNA_UPnP/Classes/UPnP/CLUPnPAction.h
  type CLUPnPServiceAVTransport (line 11) | typedef NS_ENUM(NSInteger, CLUPnPServiceType) {
Condensed preview — 85 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (790K chars).
[
  {
    "path": ".gitignore",
    "chars": 738,
    "preview": "# OS X\n.DS_Store\n\n# Xcode\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.pers"
  },
  {
    "path": ".travis.yml",
    "chars": 549,
    "preview": "# references:\n# * https://www.objc.io/issues/6-build-tools/travis-ci/\n# * https://github.com/supermarin/xcpretty#usage\n\n"
  },
  {
    "path": "DLNA_UPnP/Assets/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "DLNA_UPnP/Classes/GData/GDataXMLNode.h",
    "chars": 7336,
    "preview": "/* Copyright (c) 2008 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not u"
  },
  {
    "path": "DLNA_UPnP/Classes/GData/GDataXMLNode.m",
    "chars": 51488,
    "preview": "/* Copyright (c) 2008 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not u"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnP.h",
    "chars": 1081,
    "preview": "//\n//  CLUPnP.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/9/29.\n//  Copyright © 2016年 ClaudeLi. All rights reserved"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPAVPositionInfo.h",
    "chars": 777,
    "preview": "//\n//  CLUPnPAVPositionInfo.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/10.\n//  Copyright © 2016年 ClaudeLi. All "
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPAVPositionInfo.m",
    "chars": 3192,
    "preview": "//\n//  CLUPnPAVPositionInfo.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/10.\n//  Copyright © 2016年 ClaudeLi. All "
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPAction.h",
    "chars": 832,
    "preview": "//\n//  CLUPnPAction.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/10.\n//  Copyright © 2016年 ClaudeLi. All rights r"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPAction.m",
    "chars": 2751,
    "preview": "//\n//  CLUPnPAction.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/10.\n//  Copyright © 2016年 ClaudeLi. All rights r"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPDevice.h",
    "chars": 975,
    "preview": "//\n//  CLUPnPDevice.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 2017/7/31.\n//  Copyright © 2017年 ClaudeLi. All rights "
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPDevice.m",
    "chars": 3236,
    "preview": "//\n//  CLUPnPDevice.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 2017/7/31.\n//  Copyright © 2017年 ClaudeLi. All rights "
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPRenderer.h",
    "chars": 1323,
    "preview": "//\n//  CLUPnPRenderer.h\n//  Tiaooo\n//\n//  Created by ClaudeLi on 16/9/29.\n//  Copyright © 2016年 ClaudeLi. All rights res"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPRenderer.m",
    "chars": 9964,
    "preview": "//\n//  CLUPnPRenderer.m\n//  Tiaooo\n//\n//  Created by ClaudeLi on 16/9/29.\n//  Copyright © 2016年 ClaudeLi. All rights res"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPResponseDelegate.h",
    "chars": 1178,
    "preview": "//\n//  CLUPnPResponseDelegate.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/10.\n//  Copyright © 2016年 ClaudeLi. Al"
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPServer.h",
    "chars": 769,
    "preview": "//\n//  CLUPnPServer.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 2017/7/31.\n//  Copyright © 2017年 ClaudeLi. All rights "
  },
  {
    "path": "DLNA_UPnP/Classes/UPnP/CLUPnPServer.m",
    "chars": 9101,
    "preview": "//\n//  CLUPnPServer.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 2017/7/31.\n//  Copyright © 2017年 ClaudeLi. All rights "
  },
  {
    "path": "DLNA_UPnP.podspec",
    "chars": 1734,
    "preview": "#\n# Be sure to run `pod lib lint DLNA_UPnP.podspec' to ensure this is a\n# valid spec before submitting.\n#\n# Any lines st"
  },
  {
    "path": "Example/DLNA_UPnP/Base.lproj/LaunchScreen.storyboard",
    "chars": 1844,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Example/DLNA_UPnP/Base.lproj/Main.storyboard",
    "chars": 2929,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Example/DLNA_UPnP/CLAppDelegate.h",
    "chars": 271,
    "preview": "//\n//  CLAppDelegate.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. All righ"
  },
  {
    "path": "Example/DLNA_UPnP/CLAppDelegate.m",
    "chars": 2004,
    "preview": "//\n//  CLAppDelegate.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. All righ"
  },
  {
    "path": "Example/DLNA_UPnP/CLControlViewController.h",
    "chars": 299,
    "preview": "//\n//  CLControlViewController.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/8.\n//  Copyright © 2016年 ClaudeLi. Al"
  },
  {
    "path": "Example/DLNA_UPnP/CLControlViewController.m",
    "chars": 3334,
    "preview": "//\n//  CLControlViewController.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/10/8.\n//  Copyright © 2016年 ClaudeLi. Al"
  },
  {
    "path": "Example/DLNA_UPnP/CLControlViewController.xib",
    "chars": 9622,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" versi"
  },
  {
    "path": "Example/DLNA_UPnP/CLSearchDeviceController.h",
    "chars": 230,
    "preview": "//\n//  CLSearchDeviceController.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/9/29.\n//  Copyright © 2016年 ClaudeLi. A"
  },
  {
    "path": "Example/DLNA_UPnP/CLSearchDeviceController.m",
    "chars": 3414,
    "preview": "//\n//  CLSearchDeviceController.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 16/9/29.\n//  Copyright © 2016年 ClaudeLi. A"
  },
  {
    "path": "Example/DLNA_UPnP/CLViewController.h",
    "chars": 209,
    "preview": "//\n//  CLViewController.h\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. All r"
  },
  {
    "path": "Example/DLNA_UPnP/CLViewController.m",
    "chars": 1003,
    "preview": "//\n//  CLViewController.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. All r"
  },
  {
    "path": "Example/DLNA_UPnP/DLNA_UPnP-Info.plist",
    "chars": 1563,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/DLNA_UPnP/DLNA_UPnP-Prefix.pch",
    "chars": 321,
    "preview": "//\n//  Prefix header\n//\n//  The contents of this file are implicitly included at the beginning of every source file.\n//\n"
  },
  {
    "path": "Example/DLNA_UPnP/Device.xml",
    "chars": 4504,
    "preview": "<root xmlns=\"urn:schemas-upnp-org:device-1-0\" xmlns:qq=\"http://www.tencent.com\">\n    <specVersion>\n        <major>1</maj"
  },
  {
    "path": "Example/DLNA_UPnP/Images.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1591,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "Example/DLNA_UPnP/SetUrl.xml",
    "chars": 2177,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\""
  },
  {
    "path": "Example/DLNA_UPnP/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "Example/DLNA_UPnP/main.m",
    "chars": 330,
    "preview": "//\n//  main.m\n//  DLNA_UPnP\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. All rights reserv"
  },
  {
    "path": "Example/DLNA_UPnP.xcodeproj/project.pbxproj",
    "chars": 31091,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Example/DLNA_UPnP.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 154,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:DLNA_UPnP.xcode"
  },
  {
    "path": "Example/DLNA_UPnP.xcodeproj/xcshareddata/xcschemes/DLNA_UPnP-Example.xcscheme",
    "chars": 3829,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0720\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "Example/DLNA_UPnP.xcworkspace/contents.xcworkspacedata",
    "chars": 227,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:DLNA_UPnP.xcod"
  },
  {
    "path": "Example/DLNA_UPnP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Podfile",
    "chars": 160,
    "preview": "platform :ios, '8.0'\n\ntarget 'DLNA_UPnP_Example' do\n  pod 'DLNA_UPnP', :path => '../'\n\n  target 'DLNA_UPnP_Tests' do\n   "
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/LICENSE.txt",
    "chars": 1948,
    "preview": "This library is in the public domain.\nHowever, not all organizations are allowed to use such a license.\nFor example, Ger"
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/README.markdown",
    "chars": 5792,
    "preview": "# CocoaAsyncSocket\n[![Build Status](https://travis-ci.org/robbiehanson/CocoaAsyncSocket.svg?branch=master)](https://trav"
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.h",
    "chars": 60012,
    "preview": "//  \n//  GCDAsyncSocket.h\n//  \n//  This class is in the public domain.\n//  Originally created by Robbie Hanson in Q3 201"
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncSocket.m",
    "chars": 231313,
    "preview": "//\n//  GCDAsyncSocket.m\n//  \n//  This class is in the public domain.\n//  Originally created by Robbie Hanson in Q4 2010."
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.h",
    "chars": 47549,
    "preview": "//  \n//  GCDAsyncUdpSocket\n//  \n//  This class is in the public domain.\n//  Originally created by Robbie Hanson of Deust"
  },
  {
    "path": "Example/Pods/CocoaAsyncSocket/Source/GCD/GCDAsyncUdpSocket.m",
    "chars": 138018,
    "preview": "//  \n//  GCDAsyncUdpSocket\n//  \n//  This class is in the public domain.\n//  Originally created by Robbie Hanson of Deust"
  },
  {
    "path": "Example/Pods/Local Podspecs/DLNA_UPnP.podspec.json",
    "chars": 936,
    "preview": "{\n  \"name\": \"DLNA_UPnP\",\n  \"version\": \"1.0.0\",\n  \"summary\": \"A short description of DLNA_UPnP.\",\n  \"description\": \"TODO:"
  },
  {
    "path": "Example/Pods/Pods.xcodeproj/project.pbxproj",
    "chars": 44289,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket-Info.plist",
    "chars": 828,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket-dummy.m",
    "chars": 136,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_CocoaAsyncSocket : NSObject\n@end\n@implementation PodsDummy_CocoaA"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket-prefix.pch",
    "chars": 195,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket-umbrella.h",
    "chars": 381,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket.modulemap",
    "chars": 122,
    "preview": "framework module CocoaAsyncSocket {\n  umbrella header \"CocoaAsyncSocket-umbrella.h\"\n\n  export *\n  module * { export * }\n"
  },
  {
    "path": "Example/Pods/Target Support Files/CocoaAsyncSocket/CocoaAsyncSocket.xcconfig",
    "chars": 614,
    "preview": "CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/CocoaAsyncSocket\nGCC_PREPROCESSOR_DEFINITIONS = $(inherited) C"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP-Info.plist",
    "chars": 828,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP-dummy.m",
    "chars": 122,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_DLNA_UPnP : NSObject\n@end\n@implementation PodsDummy_DLNA_UPnP\n@en"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP-prefix.pch",
    "chars": 195,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP-umbrella.h",
    "chars": 505,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP.modulemap",
    "chars": 108,
    "preview": "framework module DLNA_UPnP {\n  umbrella header \"DLNA_UPnP-umbrella.h\"\n\n  export *\n  module * { export * }\n}\n"
  },
  {
    "path": "Example/Pods/Target Support Files/DLNA_UPnP/DLNA_UPnP.xcconfig",
    "chars": 660,
    "preview": "CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/DLNA_UPnP\nGCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPOD"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-Info.plist",
    "chars": 828,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-acknowledgements.markdown",
    "chars": 1479,
    "preview": "# Acknowledgements\nThis application makes use of the following third party libraries:\n\n## CocoaAsyncSocket\n\nPublic Domai"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-acknowledgements.plist",
    "chars": 2569,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-dummy.m",
    "chars": 148,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_Pods_DLNA_UPnP_Example : NSObject\n@end\n@implementation PodsDummy_"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-frameworks.sh",
    "chars": 7407,
    "preview": "#!/bin/sh\nset -e\nset -u\nset -o pipefail\n\nfunction on_error {\n  echo \"$(realpath -mq \"${0}\"):$1: error: Unexpected failur"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example-umbrella.h",
    "chars": 336,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example.debug.xcconfig",
    "chars": 672,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example.modulemap",
    "chars": 134,
    "preview": "framework module Pods_DLNA_UPnP_Example {\n  umbrella header \"Pods-DLNA_UPnP_Example-umbrella.h\"\n\n  export *\n  module * {"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Example/Pods-DLNA_UPnP_Example.release.xcconfig",
    "chars": 672,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests-Info.plist",
    "chars": 828,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests-acknowledgements.markdown",
    "chars": 133,
    "preview": "# Acknowledgements\nThis application makes use of the following third party libraries:\nGenerated by CocoaPods - https://c"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests-acknowledgements.plist",
    "chars": 805,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests-dummy.m",
    "chars": 144,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_Pods_DLNA_UPnP_Tests : NSObject\n@end\n@implementation PodsDummy_Po"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests-umbrella.h",
    "chars": 332,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests.debug.xcconfig",
    "chars": 506,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests.modulemap",
    "chars": 130,
    "preview": "framework module Pods_DLNA_UPnP_Tests {\n  umbrella header \"Pods-DLNA_UPnP_Tests-umbrella.h\"\n\n  export *\n  module * { exp"
  },
  {
    "path": "Example/Pods/Target Support Files/Pods-DLNA_UPnP_Tests/Pods-DLNA_UPnP_Tests.release.xcconfig",
    "chars": 506,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "Example/Tests/Tests-Info.plist",
    "chars": 674,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Example/Tests/Tests-Prefix.pch",
    "chars": 132,
    "preview": "//  The contents of this file are implicitly included at the beginning of every test case source file.\n\n#ifdef __OBJC__\n"
  },
  {
    "path": "Example/Tests/Tests.m",
    "chars": 614,
    "preview": "//\n//  DLNA_UPnPTests.m\n//  DLNA_UPnPTests\n//\n//  Created by ClaudeLi on 04/12/2019.\n//  Copyright (c) 2019 ClaudeLi. Al"
  },
  {
    "path": "Example/Tests/en.lproj/InfoPlist.strings",
    "chars": 45,
    "preview": "/* Localized versions of Info.plist keys */\n\n"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "Copyright (c) 2019 ClaudeLi <claudeli@yeah.net>\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 2818,
    "preview": "# DLNA_UPnP\n基于DLNA实现iOS投屏,SSDP发现设备及SOAP控制设备\n\n## Installation\n```\n一. local pod 导入\n在Build Phases->link binary with librari"
  }
]

About this extraction

This page contains the full source code of the ClaudeLi/DLNA_UPnP GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 85 files (708.9 KB), approximately 196.7k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!