[
  {
    "path": ".gitignore",
    "content": "\n\n\n\n# OSX\n#\n.DS_Store\n\n.vscode\n\n# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n*.xccheckout\n*.moved-aside\nDerivedData\n*.hmap\n*.ipa\n*.xcuserstate\nproject.xcworkspace\n\n# Android/IJ\n#\n.idea\n*.iml\n.gradle\nlocal.properties\n\n# node.js\n#\nnode_modules/\nnpm-debug.log\n\n"
  },
  {
    "path": ".npmignore",
    "content": ".*.swp\n._*\n.DS_Store\n.git\n.hg\n.npmrc\n.lock-wscript\n.svn\n.wafpickle-*\nconfig.gypi\nCVS\nnpm-debug.log\n"
  },
  {
    "path": "AudioStreaming.js",
    "content": "/**\n * Created by buhe on 16/4/29.\n */\nimport React, {\n    Component,\n    PropTypes\n} from 'react';\nimport {\n    requireNativeComponent,\n    View,\n} from 'react-native';\n\nclass AudioStreaming extends Component {\n\n  constructor(props, context) {\n    super(props, context);\n    this._onReady = this._onReady.bind(this);\n    this._onConnecting = this._onConnecting.bind(this);\n    this._onStreaming = this._onStreaming.bind(this);\n    this._onShutdown = this._onShutdown.bind(this);\n    this._onIOError = this._onIOError.bind(this);\n    this._onDisconnected = this._onDisconnected.bind(this);\n  }\n\n  _onReady(event) {\n    this.props.onReady && this.props.onReady(event.nativeEvent);\n  }\n\n  _onConnecting(event) {\n    this.props.onConnecting && this.props.onConnecting(event.nativeEvent);\n  }\n\n  _onStreaming(event) {\n    this.props.onStreaming && this.props.onStreaming(event.nativeEvent);\n  }\n\n  _onShutdown(event) {\n    this.props.onShutdown && this.props.onShutdown(event.nativeEvent);\n  }\n\n  _onIOError(event) {\n    this.props.onIOError && this.props.onIOError(event.nativeEvent);\n  }\n\n  _onDisconnected(event) {\n    this.props.onDisconnected && this.props.onDisconnected(event.nativeEvent);\n  }\n\n  render() {\n    const nativeProps = Object.assign({}, this.props);\n    Object.assign(nativeProps, {\n      onReady: this._onReady,\n      onConnecting: this._onConnecting,\n      onStreaming: this._onStreaming,\n      onShutdown: this._onShutdown,\n      onIOError: this._onIOError,\n      onDisconnected: this._onDisconnected,\n    });\n    return (\n        <RCTAudioStreaming\n            {...nativeProps}\n            />\n    )\n  }\n}\n\nAudioStreaming.propTypes = {\n  rtmpURL: PropTypes.string,\n  muted: PropTypes.bool,\n  profile: PropTypes.shape({                          // 是否符合指定格式的物件\n    audio: PropTypes.shape({\n      rate: PropTypes.number.isRequired,\n      bitrate: PropTypes.number.isRequired,\n    }).isRequired,\n  }).isRequired,\n  started: PropTypes.bool,\n\n  onReady: PropTypes.func,\n  onConnecting: PropTypes.func,\n  onStreaming: PropTypes.func,\n  onShutdown: PropTypes.func,\n  onIOError: PropTypes.func,\n  onDisconnected: PropTypes.func,\n  ...View.propTypes,\n}\n\nconst RCTAudioStreaming = requireNativeComponent('RCTAudioStreaming', AudioStreaming);\n\nmodule.exports = AudioStreaming;"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 buhe\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Player.js",
    "content": "/**\n * Created by buhe on 16/5/4.\n */\nimport React, {\n    Component,\n    PropTypes\n} from 'react';\nimport {\n    requireNativeComponent,\n    View,\n} from 'react-native';\n\nclass Player extends Component {\n\n  constructor(props, context) {\n    super(props, context);\n    this._onLoading = this._onLoading.bind(this);\n    this._onPaused = this._onPaused.bind(this);\n    this._onShutdown = this._onShutdown.bind(this);\n    this._onError = this._onError.bind(this);\n    this._onPlaying = this._onPlaying.bind(this);\n  }\n\n  _onLoading(event) {\n    this.props.onLoading && this.props.onLoading(event.nativeEvent);\n  }\n\n  _onPaused(event) {\n    this.props.onPaused && this.props.onPaused(event.nativeEvent);\n  }\n\n  _onShutdown(event) {\n    this.props.onShutdown && this.props.onShutdown(event.nativeEvent);\n  }\n\n\n  _onError(event) {\n    this.props.onPlayerError && this.props.onPlayerError(event.nativeEvent);\n  }\n\n  _onPlaying(event) {\n    this.props.onPlaying && this.props.onPlaying(event.nativeEvent);\n  }\n\n  render() {\n    const nativeProps = Object.assign({}, this.props);\n    Object.assign(nativeProps, {\n      onLoading: this._onLoading,\n      onPaused: this._onPaused,\n      onShutdown: this._onShutdown,\n      onPlayerError: this._onPlayerError,\n      onPlaying: this._onPlaying,\n    });\n    return (\n        <RCTPlayer\n            {...nativeProps}\n            />\n    )\n  }\n}\n\nPlayer.propTypes = {\n  source: PropTypes.shape({                          // 是否符合指定格式的物件\n    uri: PropTypes.string.isRequired,\n    controller: PropTypes.bool, //Android only\n    timeout: PropTypes.number, //Android only\n    hardCodec: PropTypes.bool, //Android only\n    live: PropTypes.bool, //Android only\n  }).isRequired,\n  started:PropTypes.bool,\n  muted:PropTypes.bool, //iOS only\n  aspectRatio: PropTypes.oneOf([0, 1, 2, 3, 4]),\n  onLoading: PropTypes.func,\n  onPaused: PropTypes.func,\n  onShutdown: PropTypes.func,\n  onPlayerError: PropTypes.func,\n  onPlaying: PropTypes.func,\n  ...View.propTypes,\n}\n\nconst RCTPlayer = requireNativeComponent('RCTPlayer', Player);\n\nmodule.exports = Player;\n"
  },
  {
    "path": "README.md",
    "content": "# Deprecated\n# Pili Streaming Cloud React Native SDK\n\n## Introduction\n\n### Warning\n\nThis lib dependency  PLMediaStreamingKit (2.1.1) and PLPlayerKit (2.2.4) , the latest native sdk break origin API, i will match the latest version be free..\n\n这*可能*是**第一个**在 React Native 上实现全功能的直播 SDK 了，底层基于 [Pili-SDK](https://github.com/pili-engineering)，把 iOS 和 Android 的 API 尽量统一。\n\n2.0 版本为了更容易集成第三方 SDK ，对原有的 React Native 项目进行了改造，iOS 的依赖采用了 Cocoapod 进行管理，当然你也可以采用原来的方式，毕竟是可以共存的。具体可以参考 [AirApps](https://github.com/airapps/airapps) 可以查看如何进行集成。项目的简单的例子是 [react-native-pili](https://github.com/airapps/react-native-living) \n\nThis may be the **first** React Native to achieve full-featured live SDK, the bottom based on [Pili-SDK] (https://github.com/pili-engineering), the iOS and Android API as unified as possible.\n\n2.0 version In order to more easily integrate third-party SDK, the original React Native project has been modified, iOS rely on the use of Cocoapod management, of course, you can also use the original way, after all, can coexist. Specifically, you can see how to integrate with AirApps (https://github.com/airapps/airapps). A simple example of a project is [react-native-pili] (https://github.com/airapps/react-native-living) \n\n\n## Installation\n\n```bash\ngit clone https://github.com/buhe/pili-startkit YourProjectName\n\ncd YourProjectName/js && npm install\n\ncd ../ios && pod install\n```\n\n### Javascript\n\n```bash\ncd YourProjectName/js\nnpm start\n```\n\n### iOS\n1. Open ios/YourProjectName.xcworkspace (这里请注意是打开 .xcworkspace!请确认) (Please note that it opens .xcworkspace! Confirm it does)\n2. Just run your project (Cmd+R)\n3. 如果是 iOS 10 需要在 info 中额外添加如下权限: (If it is iOS 10 you need to add the following additional auth in info:)\n```\n    <key>NSCameraUsageDescription</key>    \n    <string>cameraDesciption</string>\n\n    <key>NSContactsUsageDescription</key>    \n    <string>contactsDesciption</string>\n\n    <key>NSMicrophoneUsageDescription</key>    \n    <string>microphoneDesciption</string>\n```    \nref: [iOS 10](http://www.jianshu.com/p/c212cde86877)\n\n\n### Android\n1. Open android use Android Studio\n2. Just run your project\n\n## TODO\n- [x] Android Player\n- [x] Android Streaming\n- [x] iOS Player\n- [x] iOS Streaming\n- [ ] 美颜和水印支持 (Design and watermark support)\n\n## Usage\n### 1. 推流 (Streaming)\n```javascript\n<Streaming\n    rtmpURL={\"rtmp://pili-publish.pilitest.qiniucdn.com/pilitest/demo_test?key=6eeee8a82246636e\"}\n    style={{\n        height:400,\n        width:400,\n    }}\n    zoom={1} //zoom \n    muted={true} //muted\n    focus={false} //focus\n    profile={{  //video and audio profile\n       video:{\n         fps:30,\n         bps:1000 * 1024,\n         maxFrameInterval:48\n       },\n       audio:{\n         rate:44100,\n         bitrate:96 * 1024\n       },\n    started={false} //streaming status\n    onReady={()=>{}} //onReady event\n    onConnecting={()=>{}} //onConnecting event\n    onStreaming={()=>{}} //onStreaming event\n    onShutdown={()=>{}} //onShutdown event\n    onIOError={()=>{}} //onIOError event\n    onDisconnected={()=>{}} //onDisconnected event\n    />\n```\n### 2. 直播播放 (Live player)\n```javascript\n<Player\n  source={{\n    uri:\"rtmp://pili-live-rtmp.pilitest.qiniucdn.com/pilitest/xxx\",\n    timeout: 10 * 1000, //live streaming timeout (ms) Android only\n    live:true, //live streaming ? Android only\n    hardCodec:false, //hard codec [recommended false]  Android only\n    }}\n    started={true} //iOS only\n    muted={false} //iOS only\n    style={{\n      height:200,\n      width:200,\n    }}\n    onLoading={()=>{}} //loading from remote or local\n    onPaused={()=>{}} //pause event\n    onShutdown={()=>{}} //stopped event\n    onError={()=>{}} //error event\n    onPlaying={()=>{}} //play event\n    />\n```\n## Release Note\n## 2.1.1\n- [x] Android Player\n- [x] Android Streaming\n- [x] iOS Player\n- [x] iOS Streaming \n"
  },
  {
    "path": "Streaming.js",
    "content": "/**\n * Created by buhe on 16/4/29.\n */\nimport React, {\n    Component,\n    PropTypes\n} from 'react';\nimport {\n    requireNativeComponent,\n    View,\n} from 'react-native';\n\nimport StreamingConst  from './StreamingConst';\n\nclass Streaming extends Component {\n\n  constructor(props, context) {\n    super(props, context);\n    this._onReady = this._onReady.bind(this);\n    this._onConnecting = this._onConnecting.bind(this);\n    this._onStreaming = this._onStreaming.bind(this);\n    this._onShutdown = this._onShutdown.bind(this);\n    this._onIOError = this._onIOError.bind(this);\n    this._onDisconnected = this._onDisconnected.bind(this);\n  }\n\n  _onReady(event) {\n    this.props.onReady && this.props.onReady(event.nativeEvent);\n  }\n\n  _onConnecting(event) {\n    this.props.onConnecting && this.props.onConnecting(event.nativeEvent);\n  }\n\n  _onStreaming(event) {\n    this.props.onStreaming && this.props.onStreaming(event.nativeEvent);\n  }\n\n  _onShutdown(event) {\n    this.props.onShutdown && this.props.onShutdown(event.nativeEvent);\n  }\n\n  _onIOError(event) {\n    this.props.onIOError && this.props.onIOError(event.nativeEvent);\n  }\n\n  _onDisconnected(event) {\n    this.props.onDisconnected && this.props.onDisconnected(event.nativeEvent);\n  }\n\n  render() {\n    const nativeProps = Object.assign({}, this.props);\n    Object.assign(nativeProps, {\n      onReady: this._onReady,\n      onConnecting: this._onConnecting,\n      onStreaming: this._onStreaming,\n      onShutdown: this._onShutdown,\n      onIOError: this._onIOError,\n      onDisconnected: this._onDisconnected,\n    });\n    return (\n        <RCTStreaming\n            {...nativeProps}\n            />\n    )\n  }\n}\n\nStreaming.propTypes = {\n  rtmpURL: PropTypes.string,\n  camera: PropTypes.oneOf(['front','back']),\n  muted: PropTypes.bool,\n  zoom: PropTypes.number,\n  focus: PropTypes.bool,\n  profile: PropTypes.shape({                          // 是否符合指定格式的物件\n    video: PropTypes.shape({\n      fps: PropTypes.number.isRequired,\n      bps: PropTypes.number.isRequired,\n      maxFrameInterval: PropTypes.number.isRequired\n    }).isRequired,\n    audio: PropTypes.shape({\n      rate: PropTypes.number.isRequired,\n      bitrate: PropTypes.number.isRequired,\n    }).isRequired,\n    encodingSize: PropTypes.oneOf([StreamingConst.encodingSize._240, StreamingConst.encodingSize._480, StreamingConst.encodingSize._544, StreamingConst.encodingSize._720, StreamingConst.encodingSize._1088]).isRequired\n  }).isRequired,\n  started: PropTypes.bool,\n  settings: PropTypes.object,\n\n  onReady: PropTypes.func,\n  onConnecting: PropTypes.func,\n  onStreaming: PropTypes.func,\n  onShutdown: PropTypes.func,\n  onIOError: PropTypes.func,\n  onDisconnected: PropTypes.func,\n  ...View.propTypes,\n}\n\nconst RCTStreaming = requireNativeComponent('RCTStreaming', Streaming);\n\nmodule.exports = Streaming;"
  },
  {
    "path": "StreamingConst.js",
    "content": "/**\n * Created by buhe on 16/7/14.\n */\nconst video_encoding = {\n  get _240(){\n    return 0;\n  },\n  get _480(){\n    return 1;\n  },\n  get _544(){\n    return 2;\n  },\n  get _720(){\n    return 3;\n  },\n  get _1088(){\n    return 4;\n  },\n}\n\nmodule.exports = {\n  encodingSize:video_encoding\n};\n"
  },
  {
    "path": "android/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        minSdkVersion 16\n        targetSdkVersion 22\n        versionCode 1\n        versionName \"1.0\"\n    }\n//    buildTypes {\n//        release {\n//            minifyEnabled false\n//            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n//        }\n//    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        maven { url \"$projectDir/../../react-native/android\" }\n    }\n}\n\ndependencies {\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.facebook.react:react-native:+'\n    compile 'com.qiniu:happy-dns:0.2.+'\n    compile 'com.qiniu.pili:pili-android-qos:0.8.13'\n    compile files('libs/pldroid-media-streaming-2.0.4.jar')\n    compile files('libs/pldroid-player-1.3.2.jar')\n}\n"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Dec 28 10:00:20 PST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.10-all.zip\n"
  },
  {
    "path": "android/gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "android/gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "android/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/guguyanhua/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "android/src/androidTest/java/com/pili/rnpili/ApplicationTest.java",
    "content": "package com.pili.rnpili;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "android/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.pili.rnpili\">\n\n</manifest>\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/CameraPreviewFrameView.java",
    "content": "package com.pili.rnpili;\n\nimport android.content.Context;\nimport android.opengl.GLSurfaceView;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\nimport android.view.View;\n\nimport com.qiniu.android.dns.DnsManager;\nimport com.qiniu.android.dns.IResolver;\nimport com.qiniu.android.dns.NetworkInfo;\nimport com.qiniu.android.dns.http.DnspodFree;\nimport com.qiniu.android.dns.local.AndroidDnsServer;\nimport com.qiniu.android.dns.local.Resolver;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\n\n/**\n * Created by buhe on 16/4/29.\n * A Pili Streaming View\n */\npublic class CameraPreviewFrameView extends GLSurfaceView {\n\n    private static final String TAG = \"CameraPreviewFrameView\";\n\n    public interface Listener {\n        boolean onSingleTapUp(MotionEvent e);\n        boolean onZoomValueChanged(float factor);\n    }\n\n    private Listener mListener;\n    private ScaleGestureDetector mScaleDetector;\n    private GestureDetector mGestureDetector;\n\n    public CameraPreviewFrameView(Context context) {\n        super(context);\n        initialize(context);\n    }\n\n    public CameraPreviewFrameView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        initialize(context);\n    }\n\n    public void setListener(Listener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        if (!mGestureDetector.onTouchEvent(event)) {\n            return mScaleDetector.onTouchEvent(event);\n        }\n        return false;\n    }\n\n    private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() {\n        @Override\n        public boolean onSingleTapUp(MotionEvent e) {\n            if (mListener != null) {\n                mListener.onSingleTapUp(e);\n            }\n            return false;\n        }\n    };\n\n    private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {\n\n        private float mScaleFactor = 1.0f;\n\n        @Override\n        public boolean onScaleBegin(ScaleGestureDetector detector) {\n            return true;\n        }\n\n        @Override\n        public boolean onScale(ScaleGestureDetector detector) {\n            // factor > 1, zoom\n            // factor < 1, pinch\n            mScaleFactor *= detector.getScaleFactor();\n\n            // Don't let the object get too small or too large.\n            mScaleFactor = Math.max(0.01f, Math.min(mScaleFactor, 1.0f));\n\n            return mListener != null && mListener.onZoomValueChanged(mScaleFactor);\n        }\n    };\n\n    private void initialize(Context context) {\n        Log.i(TAG, \"initialize\");\n        mScaleDetector = new ScaleGestureDetector(context, mScaleListener);\n        mGestureDetector = new GestureDetector(context, mGestureListener);\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/PiliAudioStreamingViewManager.java",
    "content": "package com.pili.rnpili;\n\nimport android.hardware.Camera;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.facebook.react.bridge.Arguments;\nimport com.facebook.react.bridge.LifecycleEventListener;\nimport com.facebook.react.bridge.ReadableMap;\nimport com.facebook.react.common.MapBuilder;\nimport com.facebook.react.uimanager.SimpleViewManager;\nimport com.facebook.react.uimanager.ThemedReactContext;\nimport com.facebook.react.uimanager.annotations.ReactProp;\nimport com.facebook.react.uimanager.events.RCTEventEmitter;\nimport com.pili.rnpili.support.Config;\nimport com.pili.rnpili.support.RotateLayout;\nimport com.qiniu.android.dns.DnsManager;\nimport com.qiniu.android.dns.IResolver;\nimport com.qiniu.android.dns.NetworkInfo;\nimport com.qiniu.android.dns.http.DnspodFree;\nimport com.qiniu.android.dns.local.AndroidDnsServer;\nimport com.qiniu.android.dns.local.Resolver;\nimport com.qiniu.pili.droid.streaming.AVCodecType;\nimport com.qiniu.pili.droid.streaming.CameraStreamingSetting;\nimport com.qiniu.pili.droid.streaming.CameraStreamingSetting.CAMERA_FACING_ID;\nimport com.qiniu.pili.droid.streaming.MediaStreamingManager;\nimport com.qiniu.pili.droid.streaming.MicrophoneStreamingSetting;\nimport com.qiniu.pili.droid.streaming.StreamingEnv;\nimport com.qiniu.pili.droid.streaming.StreamingProfile;\nimport com.qiniu.pili.droid.streaming.StreamingSessionListener;\nimport com.qiniu.pili.droid.streaming.StreamingState;\nimport com.qiniu.pili.droid.streaming.StreamingStateChangedListener;\nimport com.qiniu.pili.droid.streaming.widget.AspectFrameLayout;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.URISyntaxException;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\n/**\n * Created by buhe on 16/4/29.\n */\npublic class PiliAudioStreamingViewManager extends SimpleViewManager<View>\n        implements\n//        CameraPreviewFrameView.Listener,\n        StreamingSessionListener,\n        StreamingStateChangedListener,\n        LifecycleEventListener\n\n\n{\n    public enum Events {\n        READY(\"onReady\"),\n        CONNECTING(\"onConnecting\"),\n        STREAMING(\"onStreaming\"),\n        SHUTDOWN(\"onShutdown\"),\n        IOERROR(\"onIOError\"),\n        DISCONNECTED(\"onDisconnected\");\n\n        private final String mName;\n\n        Events(final String name) {\n            mName = name;\n        }\n\n        @Override\n        public String toString() {\n            return mName;\n        }\n    }\n\n    private static final String TAG = \"PiliStreamingView\";\n    protected static final int MSG_START_STREAMING = 0;\n    protected static final int MSG_STOP_STREAMING = 1;\n    private static final int MSG_SET_ZOOM = 2;\n    private static final int MSG_MUTE = 3;\n    private static final int ZOOM_MINIMUM_WAIT_MILLIS = 33; //ms\n\n    protected MediaStreamingManager mMediaStreamingManager;\n    protected boolean mIsReady = false;\n\n    private int mCurrentZoom = 0;\n    private int mMaxZoom = 0;\n    private StreamingProfile mProfile;\n//    private CameraStreamingSetting setting;\n//    private MicrophoneStreamingSetting microphoneSetting;\n    private ThemedReactContext context;\n    private RotateLayout mRotateLayout;\n    private CameraPreviewFrameView previewFrameView;\n    private View piliStreamPreview;\n    private boolean focus = false;\n    private boolean started = false;//default start attach on parent view\n    private RCTEventEmitter mEventEmitter;\n\n\n    private void initializeStreamingSessionIfNeeded(View view) {\n        if (mMediaStreamingManager == null) {\n            mMediaStreamingManager = new MediaStreamingManager(\n                    context,\n                    AVCodecType.SW_AUDIO_CODEC);  // soft codec\n            mProfile = new StreamingProfile();\n            mProfile\n                    .setAudioQuality(StreamingProfile.AUDIO_QUALITY_LOW1)\n                    .setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY)\n                    .setDnsManager(getMyDnsManager())\n                    .setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3))\n                    .setSendingBufferProfile(new StreamingProfile.SendingBufferProfile(0.2f, 0.8f, 3.0f, 20 * 1000))\n            ;\n\n            mMediaStreamingManager.setStreamingStateListener(this);\n            mMediaStreamingManager.setStreamingSessionListener(this);\n            mMediaStreamingManager.prepare(mProfile);\n\n            context.addLifecycleEventListener(this);\n\n        }\n    }\n\n    @Override\n    @Nullable\n    public Map getExportedCustomDirectEventTypeConstants() {\n        MapBuilder.Builder builder = MapBuilder.builder();\n        for (Events event : Events.values()) {\n            builder.put(event.toString(), MapBuilder.of(\"registrationName\", event.toString()));\n        }\n        return builder.build();\n    }\n\n    @Override\n    public View createViewInstance(ThemedReactContext context) {\n        this.context = context;\n        StreamingEnv.init(context.getApplicationContext());\n        mEventEmitter = context.getJSModule(RCTEventEmitter.class);\n\n        piliStreamPreview = new View(context);\n\n        initializeStreamingSessionIfNeeded(piliStreamPreview);\n\n        piliStreamPreview.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n            @Override\n            public void onViewAttachedToWindow(View v) {\n                mMediaStreamingManager.resume();\n            }\n\n            @Override\n            public void onViewDetachedFromWindow(View v) {\n                mHandler.removeCallbacksAndMessages(null);\n                mMediaStreamingManager.pause();\n                mMediaStreamingManager.stopStreaming();\n                mMediaStreamingManager.destroy();\n                mMediaStreamingManager = null;\n            }\n        });\n\n        return piliStreamPreview;\n    }\n\n    @Override\n    /**\n     * <Streaming />\n     */\n    public String getName() {\n        return \"RCTAudioStreaming\";\n    }\n\n    @ReactProp(name = \"rtmpURL\")\n    public void setRtmpURL(View view, @Nullable String rtmpURL) {\n        try {\n            mProfile.setPublishUrl(rtmpURL);\n        } catch (URISyntaxException e) {\n            Log.e(TAG, \"RTMP URL is syntax error.\");\n        }\n        mMediaStreamingManager.setStreamingProfile(mProfile);\n    }\n\n    @ReactProp(name = \"profile\")\n    public void setProfile(View view, @Nullable ReadableMap profile) {\n        ReadableMap audio = profile.getMap(\"audio\");\n\n        StreamingProfile.VideoProfile vProfile =\n                new StreamingProfile.VideoProfile(0,0,0);//hack for bad API\n\n        StreamingProfile.AudioProfile aProfile =\n                new StreamingProfile.AudioProfile(audio.getInt(\"rate\"), audio.getInt(\"bitrate\")); //audio sample rate, audio bitrate\n        StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);\n        mProfile.setAVProfile(avProfile);\n        mMediaStreamingManager.setStreamingProfile(mProfile);\n\n    }\n\n    @ReactProp(name = \"muted\")\n    public void setMuted(View view, boolean muted) {\n        mMediaStreamingManager.mute(muted);\n    }\n\n\n    @ReactProp(name = \"started\")\n    public void setStarted(View view, boolean started) {\n        if(this.started == started){\n            //ignore\n            return;\n        }\n        this.started = started;\n        if (mIsReady) {  //没有准备好则只赋值,等待onStateChanged 唤起\n            if (started) {\n                startStreaming();\n            } else {\n                stopStreaming();\n            }\n        }\n    }\n\n\n    public int getTargetId() {\n        return piliStreamPreview.getId();\n    }\n\n    @Override\n    public void onStateChanged(StreamingState state, Object extra) {\n        switch (state) {\n            case PREPARING:\n                break;\n            case READY:\n                mIsReady = true;\n                mMaxZoom = mMediaStreamingManager.getMaxZoom();\n                if (started) {\n                    startStreaming();\n                }\n                mEventEmitter.receiveEvent(getTargetId(), Events.READY.toString(), Arguments.createMap());\n                break;\n            case CONNECTING:\n                mEventEmitter.receiveEvent(getTargetId(), Events.CONNECTING.toString(), Arguments.createMap());\n                break;\n            case STREAMING:\n                mEventEmitter.receiveEvent(getTargetId(), Events.STREAMING.toString(), Arguments.createMap());\n                break;\n            case SHUTDOWN:\n                mEventEmitter.receiveEvent(getTargetId(), Events.SHUTDOWN.toString(), Arguments.createMap());\n                break;\n            case IOERROR:\n                mEventEmitter.receiveEvent(getTargetId(), Events.IOERROR.toString(), Arguments.createMap());\n                break;\n            case UNKNOWN:\n                break;\n            case SENDING_BUFFER_EMPTY:\n                break;\n            case SENDING_BUFFER_FULL:\n                break;\n            case AUDIO_RECORDING_FAIL:\n                break;\n            case OPEN_CAMERA_FAIL:\n                break;\n            case DISCONNECTED:\n                mEventEmitter.receiveEvent(getTargetId(), Events.DISCONNECTED.toString(), Arguments.createMap());\n                break;\n            case CAMERA_SWITCHED:\n                if (extra != null) {\n                    Log.i(TAG, \"current camera id:\" + (Integer) extra);\n                }\n                Log.i(TAG, \"camera switched\");\n                break;\n            case TORCH_INFO:\n                if (extra != null) {\n                    final boolean isSupportedTorch = (Boolean) extra;\n                    Log.i(TAG, \"isSupportedTorch=\" + isSupportedTorch);\n//                    this.runOnUiThread(new Runnable() {\n//                        @Override\n//                        public void run() {\n//                            if (isSupportedTorch) {\n//                                mTorchBtn.setVisibility(View.VISIBLE);\n//                            } else {\n//                                mTorchBtn.setVisibility(View.GONE);\n//                            }\n//                        }\n//                    });\n                }\n                break;\n        }\n    }\n\n\n    @Override\n    public boolean onRecordAudioFailedHandled(int err) {\n        mMediaStreamingManager.updateEncodingType(AVCodecType.SW_VIDEO_CODEC);\n        mMediaStreamingManager.startStreaming();\n        return true;\n    }\n\n    @Override\n    public boolean onRestartStreamingHandled(int err) {\n        Log.i(TAG, \"onRestartStreamingHandled\");\n        return mMediaStreamingManager.startStreaming();\n    }\n\n    @Override\n    public Camera.Size onPreviewSizeSelected(List<Camera.Size> list) {\n        Camera.Size size = null;\n//        if (list != null) {\n//            for (Camera.Size s : list) {\n//                Log.i(TAG, \"w:\" + s.width + \", h:\" + s.height);\n//            }\n//        }\n//        Log.e(TAG, \"selected size :\" + size.width + \"x\" + size.height);\n        return size;\n    }\n\n    @Override\n    public void onHostResume() {\n        mMediaStreamingManager.resume();\n    }\n\n    @Override\n    public void onHostPause() {\n        mHandler.removeCallbacksAndMessages(null);\n        mMediaStreamingManager.pause();\n\n    }\n\n    @Override\n    public void onHostDestroy() {\n        mMediaStreamingManager.destroy();\n    }\n\n\n    protected Handler mHandler = new Handler(Looper.getMainLooper()) {\n        @Override\n        public void handleMessage(Message msg) {\n            switch (msg.what) {\n                case MSG_START_STREAMING:\n                    new Thread(new Runnable() {\n                        @Override\n                        public void run() {\n                            boolean res = mMediaStreamingManager.startStreaming();\n                            Log.i(TAG, \"res:\" + res);\n                        }\n                    }).start();\n                    break;\n                case MSG_STOP_STREAMING:\n                    boolean res = mMediaStreamingManager.stopStreaming();\n                    break;\n                case MSG_SET_ZOOM:\n                    mMediaStreamingManager.setZoomValue(mCurrentZoom);\n                    break;\n                default:\n                    Log.e(TAG, \"Invalid message\");\n            }\n        }\n    };\n\n    private void startStreaming() {\n        mHandler.removeCallbacksAndMessages(null);\n        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_STREAMING), 50);\n    }\n\n    private void stopStreaming() {\n        mHandler.removeCallbacksAndMessages(null);\n        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_STOP_STREAMING), 50);\n    }\n\n    private DnsManager getMyDnsManager() {\n        IResolver r0 = new DnspodFree();\n        IResolver r1 = AndroidDnsServer.defaultResolver();\n        IResolver r2 = null;\n        try {\n            r2 = new Resolver(InetAddress.getByName(\"119.29.29.29\"));\n        } catch (IOException ex) {\n            ex.printStackTrace();\n        }\n        return new DnsManager(NetworkInfo.normal, new IResolver[]{r0, r1, r2});\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/PiliPackage.java",
    "content": "package com.pili.rnpili;\n\nimport android.app.Activity;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.JavaScriptModule;\nimport com.facebook.react.bridge.NativeModule;\nimport com.facebook.react.bridge.ReactApplicationContext;\nimport com.facebook.react.uimanager.ViewManager;\nimport com.qiniu.pili.droid.streaming.StreamingEnv;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Created by buhe on 16/4/29.\n */\npublic class PiliPackage implements ReactPackage {\n\n\n    public PiliPackage() {\n    }\n\n    @Override\n    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<Class<? extends JavaScriptModule>> createJSModules() {\n        return Collections.emptyList();\n    }\n\n    @Override\n    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {\n        return Arrays.<ViewManager>asList(\n                new PiliStreamingViewManager(),\n                new PiliPlayerViewManager(),\n                new PiliAudioStreamingViewManager()\n        );\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/PiliPlayerViewManager.java",
    "content": "package com.pili.rnpili;\n\nimport android.util.Log;\n\nimport com.facebook.react.bridge.Arguments;\nimport com.facebook.react.bridge.LifecycleEventListener;\nimport com.facebook.react.bridge.ReadableMap;\nimport com.facebook.react.bridge.WritableMap;\nimport com.facebook.react.common.MapBuilder;\nimport com.facebook.react.uimanager.SimpleViewManager;\nimport com.facebook.react.uimanager.ThemedReactContext;\nimport com.facebook.react.uimanager.annotations.ReactProp;\nimport com.facebook.react.uimanager.events.RCTEventEmitter;\nimport com.pili.pldroid.player.AVOptions;\nimport com.pili.pldroid.player.PLMediaPlayer;\nimport com.pili.pldroid.player.widget.PLVideoView;\n//import com.pili.rnpili.support.MediaController;\n\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\n/**\n * Created by buhe on 16/4/29.\n */\npublic class PiliPlayerViewManager extends SimpleViewManager<PLVideoView> implements LifecycleEventListener {\n    private ThemedReactContext reactContext;\n    private static final String TAG = PiliPlayerViewManager.class.getSimpleName();\n    private PLVideoView mVideoView;\n    private RCTEventEmitter mEventEmitter;\n\n    private static final int MEDIA_INFO_UNKNOWN = 1;\n    private static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;\n    private static final int MEDIA_INFO_BUFFERING_START = 701;\n    private static final int MEDIA_INFO_BUFFERING_END = 702;\n    private static final int MEDIA_INFO_AUDIO_RENDERING_START = 10002;\n    private boolean started;\n    private int aspectRatio;\n\n    public enum Events {\n        LOADING(\"onLoading\"),\n        PAUSE(\"onPaused\"),\n        SHUTDOWN(\"onShutdown\"),\n        ERROR(\"onPlayerError\"),\n        PLAYING(\"onPlaying\");\n\n        private final String mName;\n\n        Events(final String name) {\n            mName = name;\n        }\n\n        @Override\n        public String toString() {\n            return mName;\n        }\n    }\n\n    @Override\n    public String getName() {\n        return \"RCTPlayer\";\n    }\n\n    @Override\n    @Nullable\n    public Map getExportedCustomDirectEventTypeConstants() {\n        MapBuilder.Builder builder = MapBuilder.builder();\n        for (Events event : Events.values()) {\n            builder.put(event.toString(), MapBuilder.of(\"registrationName\", event.toString()));\n        }\n        return builder.build();\n    }\n\n    @Override\n    protected PLVideoView createViewInstance(ThemedReactContext reactContext) {\n        this.reactContext = reactContext;\n        mEventEmitter = reactContext.getJSModule(RCTEventEmitter.class);\n        mVideoView = new PLVideoView(reactContext);\n        // Set some listeners\n        mVideoView.setOnPreparedListener(mOnPreparedListener);\n        mVideoView.setOnInfoListener(mOnInfoListener);\n        mVideoView.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener);\n        mVideoView.setOnBufferingUpdateListener(mOnBufferingUpdateListener);\n        mVideoView.setOnCompletionListener(mOnCompletionListener);\n        mVideoView.setOnSeekCompleteListener(mOnSeekCompleteListener);\n        mVideoView.setOnErrorListener(mOnErrorListener);\n\n        reactContext.addLifecycleEventListener(this);\n        return mVideoView;\n    }\n\n    private boolean isLiveStreaming(String url) {\n        if (url.startsWith(\"rtmp://\")\n                || (url.startsWith(\"http://\") && url.endsWith(\".m3u8\"))\n                || (url.startsWith(\"http://\") && url.endsWith(\".flv\"))) {\n            return true;\n        }\n        return false;\n    }\n\n    @ReactProp(name = \"source\")\n    public void setSource(PLVideoView mVideoView, ReadableMap source) {\n        AVOptions options = new AVOptions();\n        String uri = source.getString(\"uri\");\n        boolean mediaController = source.hasKey(\"controller\") && source.getBoolean(\"controller\");\n        int avFrameTimeout = source.hasKey(\"timeout\") ? source.getInt(\"timeout\") : -1;        //10 * 1000 ms\n        boolean liveStreaming = source.hasKey(\"live\") && source.getBoolean(\"live\");  //1 or 0 // 1 -> live\n        boolean codec = source.hasKey(\"hardCodec\") && source.getBoolean(\"hardCodec\");  //1 or 0  // 1 -> hw codec enable, 0 -> disable [recommended]\n        // the unit of timeout is ms\n        if (avFrameTimeout >= 0) {\n            options.setInteger(AVOptions.KEY_GET_AV_FRAME_TIMEOUT, avFrameTimeout);\n        }\n        // Some optimization with buffering mechanism when be set to 1\n        if (liveStreaming) {\n            options.setInteger(AVOptions.KEY_LIVE_STREAMING, 1);\n        } else {\n            options.setInteger(AVOptions.KEY_LIVE_STREAMING, 0);\n        }\n//        }\n\n        // 1 -> hw codec enable, 0 -> disable [recommended]\n        if (codec) {\n            options.setInteger(AVOptions.KEY_MEDIACODEC, 1);\n        } else {\n            options.setInteger(AVOptions.KEY_MEDIACODEC, 0);\n        }\n\n        mVideoView.setAVOptions(options);\n\n        // After setVideoPath, the play will start automatically\n        // mVideoView.start() is not required\n\n        mVideoView.setVideoPath(uri);\n\n//        if (mediaController) {\n//            // You can also use a custom `MediaController` widget\n//            MediaController mMediaController = new MediaController(reactContext, false, isLiveStreaming(uri));\n//            mVideoView.setMediaController(mMediaController);\n//        }\n\n    }\n\n    @ReactProp(name = \"aspectRatio\")\n    public void setAspectRatio(PLVideoView mVideoView, int aspectRatio) {\n        /**\n         *  ASPECT_RATIO_ORIGIN = 0;\n         *  ASPECT_RATIO_FIT_PARENT = 1\n         *  ASPECT_RATIO_PAVED_PARENT = 2\n         *  ASPECT_RATIO_16_9 = 3\n         *  ASPECT_RATIO_4_3 = 4\n         */\n        this.aspectRatio = aspectRatio;\n        mVideoView.setDisplayAspectRatio(aspectRatio);\n    }\n\n    @ReactProp(name = \"started\")\n    public void setStarted(PLVideoView mVideoView,  boolean started) {\n        this.started = started;\n        if (started) {\n            mVideoView.start();\n        } else {\n            mVideoView.pause();\n            mEventEmitter.receiveEvent(getTargetId(), Events.PAUSE.toString(), Arguments.createMap());\n        }\n    }\n\n    @ReactProp(name = \"muted\")\n    public void setMuted(PLVideoView mVideoView, boolean muted){\n//        mVideoView.mute\n        //Android not implements\n    }\n\n    private PLMediaPlayer.OnPreparedListener mOnPreparedListener = new PLMediaPlayer.OnPreparedListener() {\n        @Override\n        public void onPrepared(PLMediaPlayer plMediaPlayer) {\n            Log.d(TAG, \"onPrepared ! \");\n            mEventEmitter.receiveEvent(getTargetId(), Events.LOADING.toString(), Arguments.createMap());\n        }\n    };\n\n    private PLMediaPlayer.OnInfoListener mOnInfoListener = new PLMediaPlayer.OnInfoListener() {\n        @Override\n        public boolean onInfo(PLMediaPlayer plMediaPlayer, int what, int extra) {\n            Log.d(TAG, \"onInfo: \" + what + \", \" + extra);\n\n            switch (what) {\n                case MEDIA_INFO_VIDEO_RENDERING_START:\n                    mEventEmitter.receiveEvent(getTargetId(), Events.PLAYING.toString(), Arguments.createMap());\n                    break;\n                case MEDIA_INFO_BUFFERING_START:\n                    mEventEmitter.receiveEvent(getTargetId(), Events.LOADING.toString(), Arguments.createMap());\n                    break;\n                case MEDIA_INFO_BUFFERING_END:\n                    mEventEmitter.receiveEvent(getTargetId(), Events.PLAYING.toString(), Arguments.createMap());\n                    break;\n            }\n            return true;\n        }\n    };\n\n    private PLMediaPlayer.OnErrorListener mOnErrorListener = new PLMediaPlayer.OnErrorListener() {\n        @Override\n        public boolean onError(PLMediaPlayer plMediaPlayer, int errorCode) {\n            Log.e(TAG, \"Error happened, errorCode = \" + errorCode);\n            WritableMap event = Arguments.createMap();\n            event.putInt(\"errorCode\",errorCode);\n            mEventEmitter.receiveEvent(getTargetId(), Events.ERROR.toString(), Arguments.createMap());\n            return true;\n        }\n    };\n\n    private PLMediaPlayer.OnCompletionListener mOnCompletionListener = new PLMediaPlayer.OnCompletionListener() {\n        @Override\n        public void onCompletion(PLMediaPlayer plMediaPlayer) {\n            Log.d(TAG, \"Play Completed !\");\n            mEventEmitter.receiveEvent(getTargetId(), Events.SHUTDOWN.toString(), Arguments.createMap());\n        }\n    };\n\n    private PLMediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener = new PLMediaPlayer.OnBufferingUpdateListener() {\n        @Override\n        public void onBufferingUpdate(PLMediaPlayer plMediaPlayer, int precent) {\n            Log.d(TAG, \"onBufferingUpdate: \" + precent);\n        }\n    };\n\n    private PLMediaPlayer.OnSeekCompleteListener mOnSeekCompleteListener = new PLMediaPlayer.OnSeekCompleteListener() {\n        @Override\n        public void onSeekComplete(PLMediaPlayer plMediaPlayer) {\n            Log.d(TAG, \"onSeekComplete !\");\n        }\n\n        ;\n    };\n\n    private PLMediaPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener = new PLMediaPlayer.OnVideoSizeChangedListener() {\n        @Override\n        public void onVideoSizeChanged(PLMediaPlayer plMediaPlayer, int width, int height) {\n            Log.d(TAG, \"onVideoSizeChanged: \" + width + \",\" + height);\n        }\n    };\n\n    @Override\n    public void onHostResume() {\n        mVideoView.start();\n    }\n\n    @Override\n    public void onHostPause() {\n        mVideoView.pause();\n    }\n\n    @Override\n    public void onHostDestroy() {\n        mVideoView.stopPlayback();\n    }\n\n    public int getTargetId() {\n        return mVideoView.getId();\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/PiliStreamingViewManager.java",
    "content": "package com.pili.rnpili;\n\nimport android.app.Activity;\nimport android.hardware.Camera;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\n\nimport com.facebook.react.bridge.Arguments;\nimport com.facebook.react.bridge.LifecycleEventListener;\nimport com.facebook.react.bridge.ReadableMap;\nimport com.facebook.react.common.MapBuilder;\nimport com.facebook.react.uimanager.SimpleViewManager;\nimport com.facebook.react.uimanager.ThemedReactContext;\nimport com.facebook.react.uimanager.annotations.ReactProp;\nimport com.facebook.react.uimanager.events.RCTEventEmitter;\nimport com.pili.rnpili.support.Config;\nimport com.pili.rnpili.support.Jsons;\nimport com.pili.rnpili.support.RotateLayout;\nimport com.qiniu.android.dns.DnsManager;\nimport com.qiniu.android.dns.IResolver;\nimport com.qiniu.android.dns.NetworkInfo;\nimport com.qiniu.android.dns.http.DnspodFree;\nimport com.qiniu.android.dns.local.AndroidDnsServer;\nimport com.qiniu.android.dns.local.Resolver;\nimport com.qiniu.pili.droid.streaming.AVCodecType;\nimport com.qiniu.pili.droid.streaming.CameraStreamingSetting;\nimport com.qiniu.pili.droid.streaming.MediaStreamingManager;\nimport com.qiniu.pili.droid.streaming.MicrophoneStreamingSetting;\nimport com.qiniu.pili.droid.streaming.StreamingEnv;\nimport com.qiniu.pili.droid.streaming.StreamingProfile;\nimport com.qiniu.pili.droid.streaming.widget.AspectFrameLayout;\nimport com.qiniu.pili.droid.streaming.CameraStreamingSetting;\nimport com.qiniu.pili.droid.streaming.CameraStreamingSetting.CAMERA_FACING_ID;\nimport com.qiniu.pili.droid.streaming.FrameCapturedCallback;\nimport com.qiniu.pili.droid.streaming.MediaStreamingManager;\nimport com.qiniu.pili.droid.streaming.MicrophoneStreamingSetting;\nimport com.qiniu.pili.droid.streaming.StreamStatusCallback;\nimport com.qiniu.pili.droid.streaming.StreamingPreviewCallback;\nimport com.qiniu.pili.droid.streaming.StreamingProfile;\nimport com.qiniu.pili.droid.streaming.StreamingSessionListener;\nimport com.qiniu.pili.droid.streaming.StreamingState;\nimport com.qiniu.pili.droid.streaming.StreamingStateChangedListener;\nimport com.qiniu.pili.droid.streaming.SurfaceTextureCallback;\n\nimport org.json.JSONObject;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.URISyntaxException;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Nullable;\n\n/**\n * Created by buhe on 16/4/29.\n */\npublic class PiliStreamingViewManager extends SimpleViewManager<AspectFrameLayout>\n        implements\n        CameraPreviewFrameView.Listener,\n        StreamingSessionListener,\n        StreamingStateChangedListener,\n        LifecycleEventListener\n\n\n{\n    public enum Events {\n        READY(\"onReady\"),\n        CONNECTING(\"onConnecting\"),\n        STREAMING(\"onStreaming\"),\n        SHUTDOWN(\"onShutdown\"),\n        IOERROR(\"onIOError\"),\n        DISCONNECTED(\"onDisconnected\");\n\n        private final String mName;\n\n        Events(final String name) {\n            mName = name;\n        }\n\n        @Override\n        public String toString() {\n            return mName;\n        }\n    }\n\n    private static final String TAG = \"PiliStreamingView\";\n    protected static final int MSG_START_STREAMING = 0;\n    protected static final int MSG_STOP_STREAMING = 1;\n    private static final int MSG_SET_ZOOM = 2;\n    private static final int MSG_MUTE = 3;\n    private static final int ZOOM_MINIMUM_WAIT_MILLIS = 33; //ms\n\n    protected MediaStreamingManager mMediaStreamingManager;\n    protected boolean mIsReady = false;\n\n    private int mCurrentZoom = 0;\n    private int mMaxZoom = 0;\n    private StreamingProfile mProfile;\n    private CameraStreamingSetting setting;\n    private MicrophoneStreamingSetting microphoneSetting;\n    private ThemedReactContext context;\n    private RotateLayout mRotateLayout;\n    private CameraPreviewFrameView previewFrameView;\n    private AspectFrameLayout piliStreamPreview;\n    private boolean focus = false;\n    private boolean started = false;//default start attach on parent view\n    private RCTEventEmitter mEventEmitter;\n\n\n    private void initializeStreamingSessionIfNeeded(AspectFrameLayout afl, CameraPreviewFrameView previewFrameView) throws URISyntaxException {\n        if (mMediaStreamingManager == null) {\n            mMediaStreamingManager = new MediaStreamingManager(\n                    context,\n                    afl,\n                    previewFrameView,\n                    AVCodecType.SW_VIDEO_WITH_SW_AUDIO_CODEC);  // soft codec\n            mProfile = new StreamingProfile();\n            StreamingProfile.AudioProfile aProfile = new StreamingProfile.AudioProfile(44100, 96 * 1024); //audio sample rate, audio bitrate\n            StreamingProfile.VideoProfile vProfile = new StreamingProfile.VideoProfile(30, 1000 * 1024, 48);//fps bps maxFrameInterval\n            StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);\n            mProfile.setVideoQuality(StreamingProfile.VIDEO_QUALITY_HIGH3)\n//                    .setPublishUrl(\"rtmp://pili-publish.wantplus.cn/wantplus-1/bhjkhjgcnkknn?key=f4f380426f4bdb1a\")\n                    .setAudioQuality(StreamingProfile.AUDIO_QUALITY_MEDIUM2)\n//                .setPreferredVideoEncodingSize(960, 544)\n                    .setEncodingSizeLevel(Config.ENCODING_LEVEL)\n                    .setEncoderRCMode(StreamingProfile.EncoderRCModes.QUALITY_PRIORITY)\n//                    .setStream(stream)   //set Stream\n                    .setAVProfile(avProfile)\n                    .setDnsManager(getMyDnsManager())\n                    .setStreamStatusConfig(new StreamingProfile.StreamStatusConfig(3))\n//                .setEncodingOrientation(StreamingProfile.ENCODING_ORIENTATION.PORT)\n                    .setSendingBufferProfile(new StreamingProfile.SendingBufferProfile(0.2f, 0.8f, 3.0f, 20 * 1000))\n            ;\n\n            setting = new CameraStreamingSetting();\n            setting.setCameraId(Camera.CameraInfo.CAMERA_FACING_BACK)\n                    .setContinuousFocusModeEnabled(true)\n                    .setRecordingHint(false)\n                    .setResetTouchFocusDelayInMs(3000)\n                    .setFocusMode(CameraStreamingSetting.FOCUS_MODE_CONTINUOUS_PICTURE)\n                    .setCameraPrvSizeLevel(CameraStreamingSetting.PREVIEW_SIZE_LEVEL.MEDIUM)\n                    .setCameraPrvSizeRatio(CameraStreamingSetting.PREVIEW_SIZE_RATIO.RATIO_16_9);\n\n            microphoneSetting = new MicrophoneStreamingSetting();\n            microphoneSetting.setBluetoothSCOEnabled(false);\n            mMediaStreamingManager.setStreamingStateListener(this);\n            mMediaStreamingManager.setStreamingSessionListener(this);\n            mMediaStreamingManager.prepare(setting, microphoneSetting, mProfile);\n\n            context.addLifecycleEventListener(this);\n\n        }\n    }\n\n    @Override\n    @Nullable\n    public Map getExportedCustomDirectEventTypeConstants() {\n        MapBuilder.Builder builder = MapBuilder.builder();\n        for (Events event : Events.values()) {\n            builder.put(event.toString(), MapBuilder.of(\"registrationName\", event.toString()));\n        }\n        return builder.build();\n    }\n\n    @Override\n    public AspectFrameLayout createViewInstance(ThemedReactContext context) {\n        this.context = context;\n        StreamingEnv.init(context.getApplicationContext());\n        mEventEmitter = context.getJSModule(RCTEventEmitter.class);\n\n        piliStreamPreview = new AspectFrameLayout(context);\n\n        piliStreamPreview.setShowMode(AspectFrameLayout.SHOW_MODE.REAL);\n\n        previewFrameView = new CameraPreviewFrameView(context);\n        previewFrameView.setListener(this);\n        previewFrameView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));\n        piliStreamPreview.addView(previewFrameView);\n        try {\n            initializeStreamingSessionIfNeeded(piliStreamPreview, previewFrameView);\n        } catch (URISyntaxException e) {\n            e.printStackTrace();\n        }\n\n        piliStreamPreview.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {\n            @Override\n            public void onViewAttachedToWindow(View v) {\n                mMediaStreamingManager.resume();\n            }\n\n            @Override\n            public void onViewDetachedFromWindow(View v) {\n                mHandler.removeCallbacksAndMessages(null);\n                mMediaStreamingManager.pause();\n                mMediaStreamingManager.stopStreaming();\n                mMediaStreamingManager.destroy();\n                mMediaStreamingManager = null;\n            }\n        });\n\n        return piliStreamPreview;\n    }\n\n    @Override\n    /**\n     * <Streaming />\n     */\n    public String getName() {\n        return \"RCTStreaming\";\n    }\n\n    @ReactProp(name = \"camera\")\n    public void setCamera(AspectFrameLayout view, @Nullable String camera){\n        if(camera.equals(\"front\")){\n            mMediaStreamingManager.switchCamera(CAMERA_FACING_ID.CAMERA_FACING_FRONT);\n        }else if(camera.equals(\"back\")){\n            mMediaStreamingManager.switchCamera(CAMERA_FACING_ID.CAMERA_FACING_BACK);\n        }else{\n\n        }\n\n    }\n\n    @ReactProp(name = \"rtmpURL\")\n    public void setRtmpURL(AspectFrameLayout view, @Nullable String rtmpURL) {\n        try {\n            mProfile.setPublishUrl(rtmpURL);\n        } catch (URISyntaxException e) {\n            Log.e(TAG, \"RTMP URL is syntax error.\");\n        }\n        mMediaStreamingManager.setStreamingProfile(mProfile);\n    }\n\n    @ReactProp(name = \"profile\")\n    public void setProfile(AspectFrameLayout view, @Nullable ReadableMap profile) {\n        ReadableMap video = profile.getMap(\"video\");\n        ReadableMap audio = profile.getMap(\"audio\");\n        int encodingSize = profile.getInt(\"encodingSize\");\n\n        StreamingProfile.AudioProfile aProfile =\n                new StreamingProfile.AudioProfile(audio.getInt(\"rate\"), audio.getInt(\"bitrate\")); //audio sample rate, audio bitrate\n        StreamingProfile.VideoProfile vProfile =\n                new StreamingProfile.VideoProfile(video.getInt(\"fps\"), video.getInt(\"bps\"), video.getInt(\"maxFrameInterval\"));//fps bps maxFrameInterval\n        StreamingProfile.AVProfile avProfile = new StreamingProfile.AVProfile(vProfile, aProfile);\n        mProfile.setAVProfile(avProfile);\n        mProfile.setEncodingSizeLevel(encodingSize);\n        mMediaStreamingManager.setStreamingProfile(mProfile);\n\n    }\n\n    @ReactProp(name = \"muted\")\n    public void setMuted(AspectFrameLayout view, boolean muted) {\n        mMediaStreamingManager.mute(muted);\n    }\n\n    @ReactProp(name = \"zoom\")\n    public void setZoom(AspectFrameLayout view, int zoom) {\n        mCurrentZoom = zoom;\n        mCurrentZoom = Math.min(mCurrentZoom, mMaxZoom);\n        mCurrentZoom = Math.max(0, mCurrentZoom);\n        mMediaStreamingManager.setZoomValue(zoom);\n    }\n\n    @ReactProp(name = \"focus\")\n    public void setFocus(AspectFrameLayout view, boolean focus) {\n        this.focus = focus;\n    }\n\n    @ReactProp(name = \"started\")\n    public void setStarted(AspectFrameLayout view, boolean started) {\n        if(this.started == started){\n            //ignore\n            return;\n        }\n        this.started = started;\n        if (mIsReady) {  //没有准备好则只赋值,等待onStateChanged 唤起\n            if (started) {\n                startStreaming();\n            } else {\n                stopStreaming();\n            }\n        }\n    }\n\n    protected void setFocusAreaIndicator() {\n//        if (mRotateLayout == null) {\n//            mRotateLayout = new FocusIndicatorRotateLayout(context, null);\n//            mRotateLayout\n//                    .setLayoutParams(new FrameLayout.LayoutParams(\n//                            FrameLayout.LayoutParams.WRAP_CONTENT,\n//                            FrameLayout.LayoutParams.WRAP_CONTENT,\n//                            Gravity.CENTER\n//                    ));\n//            View indicator = new View(context);\n//            indicator.setLayoutParams(new ViewGroup.LayoutParams(120, 120));\n//            mRotateLayout.addView(indicator);\n//            mRotateLayout.setChild(indicator);\n//            piliStreamPreview.addView(mRotateLayout);\n//            mMediaStreamingManager.setFocusAreaIndicator(mRotateLayout,\n//                    indicator);\n//        }\n    }\n\n    public int getTargetId() {\n        return piliStreamPreview.getId();\n    }\n\n    @Override\n    public void onStateChanged(StreamingState state, Object extra) {\n        switch (state) {\n            case PREPARING:\n                break;\n            case READY:\n                mIsReady = true;\n                mMaxZoom = mMediaStreamingManager.getMaxZoom();\n                if (started) {\n                    startStreaming();\n                }\n                mEventEmitter.receiveEvent(getTargetId(), Events.READY.toString(), Arguments.createMap());\n                break;\n            case CONNECTING:\n                mEventEmitter.receiveEvent(getTargetId(), Events.CONNECTING.toString(), Arguments.createMap());\n                break;\n            case STREAMING:\n                mEventEmitter.receiveEvent(getTargetId(), Events.STREAMING.toString(), Arguments.createMap());\n                break;\n            case SHUTDOWN:\n                mEventEmitter.receiveEvent(getTargetId(), Events.SHUTDOWN.toString(), Arguments.createMap());\n                break;\n            case IOERROR:\n                mEventEmitter.receiveEvent(getTargetId(), Events.IOERROR.toString(), Arguments.createMap());\n                break;\n            case UNKNOWN:\n                break;\n            case SENDING_BUFFER_EMPTY:\n                break;\n            case SENDING_BUFFER_FULL:\n                break;\n            case AUDIO_RECORDING_FAIL:\n                break;\n            case OPEN_CAMERA_FAIL:\n                break;\n            case DISCONNECTED:\n                mEventEmitter.receiveEvent(getTargetId(), Events.DISCONNECTED.toString(), Arguments.createMap());\n                break;\n            case CAMERA_SWITCHED:\n                if (extra != null) {\n                    Log.i(TAG, \"current camera id:\" + (Integer) extra);\n                }\n                Log.i(TAG, \"camera switched\");\n                break;\n            case TORCH_INFO:\n                if (extra != null) {\n                    final boolean isSupportedTorch = (Boolean) extra;\n                    Log.i(TAG, \"isSupportedTorch=\" + isSupportedTorch);\n//                    this.runOnUiThread(new Runnable() {\n//                        @Override\n//                        public void run() {\n//                            if (isSupportedTorch) {\n//                                mTorchBtn.setVisibility(View.VISIBLE);\n//                            } else {\n//                                mTorchBtn.setVisibility(View.GONE);\n//                            }\n//                        }\n//                    });\n                }\n                break;\n        }\n    }\n\n\n    @Override\n    public boolean onRecordAudioFailedHandled(int err) {\n        mMediaStreamingManager.updateEncodingType(AVCodecType.SW_VIDEO_CODEC);\n        mMediaStreamingManager.startStreaming();\n        return true;\n    }\n\n    @Override\n    public boolean onRestartStreamingHandled(int err) {\n        Log.i(TAG, \"onRestartStreamingHandled\");\n        return mMediaStreamingManager.startStreaming();\n    }\n\n    @Override\n    public Camera.Size onPreviewSizeSelected(List<Camera.Size> list) {\n        Camera.Size size = null;\n//        if (list != null) {\n//            for (Camera.Size s : list) {\n//                Log.i(TAG, \"w:\" + s.width + \", h:\" + s.height);\n//            }\n//        }\n//        Log.e(TAG, \"selected size :\" + size.width + \"x\" + size.height);\n        return size;\n    }\n\n    @Override\n    public boolean onSingleTapUp(MotionEvent e) {\n        Log.i(TAG, \"onSingleTapUp X:\" + e.getX() + \",Y:\" + e.getY());\n\n        if (mIsReady && focus) {\n            setFocusAreaIndicator();\n            try {\n                mMediaStreamingManager.doSingleTapUp((int) e.getX(), (int) e.getY());\n            } catch (Exception ex) {\n                Log.e(TAG, ex.getMessage());\n            }\n\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean onZoomValueChanged(float factor) {\n        if (mIsReady && mMediaStreamingManager.isZoomSupported()) {\n            mCurrentZoom = (int) (mMaxZoom * factor);\n            mCurrentZoom = Math.min(mCurrentZoom, mMaxZoom);\n            mCurrentZoom = Math.max(0, mCurrentZoom);\n\n            Log.d(TAG, \"zoom ongoing, scale: \" + mCurrentZoom + \",factor:\" + factor + \",maxZoom:\" + mMaxZoom);\n            if (!mHandler.hasMessages(MSG_SET_ZOOM)) {\n                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SET_ZOOM), ZOOM_MINIMUM_WAIT_MILLIS);\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n//    @Override\n//    public boolean onStateHandled(final int state, Object extra) {\n//        switch (state) {\n//            case SENDING_BUFFER_HAS_FEW_ITEMS:\n//                return false;\n//            case SENDING_BUFFER_HAS_MANY_ITEMS:\n//                return false;\n//        }\n//        return false;\n//    }\n\n\n    @Override\n    public void onHostResume() {\n        mMediaStreamingManager.resume();\n    }\n\n    @Override\n    public void onHostPause() {\n        mHandler.removeCallbacksAndMessages(null);\n        mMediaStreamingManager.pause();\n\n    }\n\n    @Override\n    public void onHostDestroy() {\n        mMediaStreamingManager.destroy();\n    }\n\n\n    protected Handler mHandler = new Handler(Looper.getMainLooper()) {\n        @Override\n        public void handleMessage(Message msg) {\n            switch (msg.what) {\n                case MSG_START_STREAMING:\n                    new Thread(new Runnable() {\n                        @Override\n                        public void run() {\n                            boolean res = mMediaStreamingManager.startStreaming();\n                            Log.i(TAG, \"res:\" + res);\n                        }\n                    }).start();\n                    break;\n                case MSG_STOP_STREAMING:\n                    boolean res = mMediaStreamingManager.stopStreaming();\n                    break;\n                case MSG_SET_ZOOM:\n                    mMediaStreamingManager.setZoomValue(mCurrentZoom);\n                    break;\n                default:\n                    Log.e(TAG, \"Invalid message\");\n            }\n        }\n    };\n\n    private void startStreaming() {\n        mHandler.removeCallbacksAndMessages(null);\n        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_STREAMING), 50);\n    }\n\n    private void stopStreaming() {\n        mHandler.removeCallbacksAndMessages(null);\n        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_STOP_STREAMING), 50);\n    }\n\n    private DnsManager getMyDnsManager() {\n        IResolver r0 = new DnspodFree();\n        IResolver r1 = AndroidDnsServer.defaultResolver();\n        IResolver r2 = null;\n        try {\n            r2 = new Resolver(InetAddress.getByName(\"119.29.29.29\"));\n        } catch (IOException ex) {\n            ex.printStackTrace();\n        }\n        return new DnsManager(NetworkInfo.normal, new IResolver[]{r0, r1, r2});\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/Config.java",
    "content": "package com.pili.rnpili.support;\n\nimport android.content.pm.ActivityInfo;\n\nimport com.qiniu.pili.droid.streaming.StreamingProfile;\n\n\n/**\n * Created by jerikc on 15/12/8.\n */\npublic class Config {\n    public static final boolean DEBUG_MODE = false;\n    public static final boolean FILTER_ENABLED = false;\n    public static final int ENCODING_LEVEL = StreamingProfile.VIDEO_ENCODING_HEIGHT_480;\n    public static final int SCREEN_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;\n\n\n    public static final String EXTRA_KEY_STREAM_JSON = \"stream_json_str\";\n\n    public static final String HINT_ENCODING_ORIENTATION_CHANGED =\n            \"Encoding orientation had been changed. Stop streaming first and restart streaming will take effect\";\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/FocusIndicatorRotateLayout.java",
    "content": "//package com.pili.rnpili.support;\n//\n//import android.annotation.TargetApi;\n//import android.content.Context;\n//import android.os.Build;\n//import android.util.AttributeSet;\n//import android.util.Log;\n//\n//import com.pili.rnpili.R;\n//import com.qiniu.pili.droid.streaming.ui.FocusIndicator;\n//\n//// A view that indicates the focus area or the metering area.\n//public class FocusIndicatorRotateLayout extends RotateLayout implements FocusIndicator {\n//    private static final String TAG = \"FocusIndicatorLayout\";\n//\n//    // Sometimes continuous autofucus starts and stops several times quickly.\n//    // These states are used to make sure the animation is run for at least some\n//    // time.\n//    private int mState;\n//    private static final int STATE_IDLE = 0;\n//    private static final int STATE_FOCUSING = 1;\n//    private static final int STATE_FINISHING = 2;\n//\n//    private Runnable mDisappear = new Disappear();\n//    private Runnable mEndAction = new EndAction();\n//\n//    private static final int SCALING_UP_TIME = 1000;\n//    private static final int SCALING_DOWN_TIME = 200;\n//    private static final int DISAPPEAR_TIMEOUT = 200;\n//\n//    public FocusIndicatorRotateLayout(Context context, AttributeSet attrs) {\n//        super(context, attrs);\n//    }\n//\n//    private void setDrawable(int resid) {\n//        mChild.setBackgroundDrawable(getResources().getDrawable(resid));\n//    }\n//\n//    @Override\n//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n//    public void showStart() {\n//        Log.i(TAG, \"showStart\");\n//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n//            return;\n//        }\n//        if (mState == STATE_IDLE) {\n//            setDrawable(R.drawable.ic_focus_focusing);\n//            animate().withLayer().setDuration(SCALING_UP_TIME)\n//                    .scaleX(1.5f).scaleY(1.5f);\n//            mState = STATE_FOCUSING;\n//        }\n//    }\n//\n//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n//    @Override\n//    public void showSuccess(boolean timeout) {\n//        Log.i(TAG, \"showSuccess\");\n//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n//            return;\n//        }\n//        if (mState == STATE_FOCUSING) {\n//            setDrawable(R.drawable.ic_focus_focused);\n//            animate().withLayer().setDuration(SCALING_DOWN_TIME).scaleX(1f)\n//                    .scaleY(1f).withEndAction(timeout ? mEndAction : null);\n//            mState = STATE_FINISHING;\n//        }\n//    }\n//\n//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n//    @Override\n//    public void showFail(boolean timeout) {\n//        Log.i(TAG, \"showFail\");\n//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n//            return;\n//        }\n//        if (mState == STATE_FOCUSING) {\n//            setDrawable(R.drawable.ic_focus_failed);\n//            animate().withLayer().setDuration(SCALING_DOWN_TIME).scaleX(1f)\n//                    .scaleY(1f).withEndAction(timeout ? mEndAction : null);\n//            mState = STATE_FINISHING;\n//        }\n//    }\n//\n//    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)\n//    @Override\n//    public void clear() {\n//        Log.i(TAG, \"clear\");\n//        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n//            return;\n//        }\n//        animate().cancel();\n//        removeCallbacks(mDisappear);\n//        mDisappear.run();\n//        setScaleX(1f);\n//        setScaleY(1f);\n//    }\n//\n//    private class EndAction implements Runnable {\n//        @Override\n//        public void run() {\n//            // Keep the focus indicator for some time.\n//            postDelayed(mDisappear, DISAPPEAR_TIMEOUT);\n//        }\n//    }\n//\n//    private class Disappear implements Runnable {\n//        @Override\n//        public void run() {\n//            mChild.setBackgroundDrawable(null);\n//            mState = STATE_IDLE;\n//        }\n//    }\n//}"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/Jsons.java",
    "content": "package com.pili.rnpili.support;\n\nimport com.facebook.react.bridge.ReadableMap;\nimport com.facebook.react.bridge.ReadableMapKeySetIterator;\nimport com.facebook.react.bridge.ReadableType;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * Created by buhe on 16/5/5.\n */\npublic class Jsons {\n    public static JSONObject readableMapToJson(ReadableMap readableMap) {\n        JSONObject jsonObject = new JSONObject();\n\n        if (readableMap == null) {\n            return null;\n        }\n\n        ReadableMapKeySetIterator iterator = readableMap.keySetIterator();\n        if (!iterator.hasNextKey()) {\n            return null;\n        }\n\n        while (iterator.hasNextKey()) {\n            String key = iterator.nextKey();\n            ReadableType readableType = readableMap.getType(key);\n\n            try {\n                switch (readableType) {\n                    case Null:\n                        jsonObject.put(key, null);\n                        break;\n                    case Boolean:\n                        jsonObject.put(key, readableMap.getBoolean(key));\n                        break;\n                    case Number:\n                        // Can be int or double.\n                        jsonObject.put(key, readableMap.getInt(key));\n                        break;\n                    case String:\n                        jsonObject.put(key, readableMap.getString(key));\n                        break;\n                    case Map:\n                        jsonObject.put(key, readableMapToJson(readableMap.getMap(key)));\n                        break;\n                    case Array:\n                        jsonObject.put(key, readableMap.getArray(key));\n                    default:\n                        // Do nothing and fail silently\n                }\n            } catch (JSONException ex) {\n                // Do nothing and fail silently\n            }\n        }\n\n        return jsonObject;\n    }\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/MediaController.java",
    "content": "//package com.pili.rnpili.support;\n//\n//import android.annotation.SuppressLint;\n//import android.content.Context;\n//import android.content.res.Resources;\n//import android.graphics.Rect;\n//import android.media.AudioManager;\n//import android.os.Build;\n//import android.os.Handler;\n//import android.os.Message;\n//import android.util.AttributeSet;\n//import android.util.Log;\n//import android.view.Gravity;\n//import android.view.KeyEvent;\n//import android.view.LayoutInflater;\n//import android.view.MotionEvent;\n//import android.view.View;\n//import android.widget.FrameLayout;\n//import android.widget.ImageButton;\n//import android.widget.PopupWindow;\n//import android.widget.ProgressBar;\n//import android.widget.SeekBar;\n//import android.widget.TextView;\n//\n//import com.pili.pldroid.player.IMediaController;\n//\n//import java.util.Locale;\n//\n///**\n// * You can write a custom MediaController instead of this class\n// * A MediaController widget must implement all the interface defined by com.pili.pldroid.player.IMediaController\n// */\n//public class MediaController extends FrameLayout implements IMediaController {\n//\n//    private static final String TAG = \"PLMediaController\";\n//    private IMediaController.MediaPlayerControl mPlayer;\n//    private Context mContext;\n//    private PopupWindow mWindow;\n//    private int mAnimStyle;\n//    private View mAnchor;\n//    private View mRoot;\n//    private ProgressBar mProgress;\n//    private TextView mEndTime, mCurrentTime;\n//    private long mDuration;\n//    private boolean mShowing;\n//    private boolean mDragging;\n//    private boolean mInstantSeeking = true;\n//    private static int sDefaultTimeout = 3000;\n//    private static final int SEEK_TO_POST_DELAY_MILLIS = 200;\n//\n//    private static final int FADE_OUT = 1;\n//    private static final int SHOW_PROGRESS = 2;\n//    private boolean mFromXml = false;\n//    private ImageButton mPauseButton;\n//    private ImageButton mFfwdButton;\n//    private ImageButton mRewButton;\n//    private ImageButton mNextButton;\n//    private ImageButton mPrevButton;\n//\n//    private boolean mUseFastForward;\n//\n//    private static final int IC_MEDIA_PAUSE_ID = Resources.getSystem().getIdentifier(\"ic_media_pause\",\"drawable\", \"android\");\n//    private static final int IC_MEDIA_PLAY_ID = Resources.getSystem().getIdentifier(\"ic_media_play\",\"drawable\", \"android\");\n//    private static final int MEDIA_CONTROLLER_ID = Resources.getSystem().getIdentifier(\"media_controller\", \"layout\", \"android\");\n//    private static final int PRV_BUTTON_ID = Resources.getSystem().getIdentifier(\"prev\",\"id\", \"android\");\n//    private static final int FFWD_BUTTON_ID = Resources.getSystem().getIdentifier(\"ffwd\",\"id\", \"android\");\n//    private static final int NEXT_BUTTON_ID = Resources.getSystem().getIdentifier(\"next\",\"id\", \"android\");\n//    private static final int REW_BUTTON_ID = Resources.getSystem().getIdentifier(\"rew\",\"id\", \"android\");\n//    private static final int PAUSE_BUTTON_ID = Resources.getSystem().getIdentifier(\"pause\",\"id\", \"android\");\n//    private static final int MEDIACONTROLLER_PROGRESS_ID = Resources.getSystem().getIdentifier(\"mediacontroller_progress\",\"id\", \"android\");\n//    private static final int END_TIME_ID = Resources.getSystem().getIdentifier(\"time\",\"id\", \"android\");\n//    private static final int CURRENT_TIME_ID = Resources.getSystem().getIdentifier(\"time_current\",\"id\", \"android\");\n//\n//    private AudioManager mAM;\n//    private Runnable mLastSeekBarRunnable;\n//    private boolean mDisableProgress = false;\n//\n//    public MediaController(Context context, AttributeSet attrs) {\n//        super(context, attrs);\n//        mRoot = this;\n//        mFromXml = true;\n//        initController(context);\n//    }\n//\n//    public MediaController(Context context) {\n//        super(context);\n//        if (!mFromXml && initController(context))\n//            initFloatingWindow();\n//    }\n//\n//    public MediaController(Context context, boolean useFastForward, boolean disableProgressBar) {\n//        this(context);\n//        mUseFastForward = useFastForward;\n//        mDisableProgress = disableProgressBar;\n//    }\n//\n//    public MediaController(Context context, boolean useFastForward) {\n//        this(context);\n//        mUseFastForward = useFastForward;\n//    }\n//\n//    private boolean initController(Context context) {\n//        mUseFastForward = true;\n//        mContext = context;\n//        mAM = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);\n//        return true;\n//    }\n//\n//    @Override\n//    public void onFinishInflate() {\n//        if (mRoot != null)\n//            initControllerView(mRoot);\n//        super.onFinishInflate();\n//    }\n//\n//    private void initFloatingWindow() {\n//        mWindow = new PopupWindow(mContext);\n//        mWindow.setFocusable(false);\n//        mWindow.setBackgroundDrawable(null);\n//        mWindow.setOutsideTouchable(true);\n//        mAnimStyle = android.R.style.Animation;\n//    }\n//\n//    /**\n//     * Create the view that holds the widgets that control playback. Derived\n//     * classes can override this to create their own.\n//     *\n//     * @return The controller view.\n//     */\n//    protected View makeControllerView() {\n//        return ((LayoutInflater) mContext\n//                .getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(MEDIA_CONTROLLER_ID, this);\n//    }\n//\n//    private void initControllerView(View v) {\n//        // By default these are hidden.\n//        mPrevButton = (ImageButton) v.findViewById(PRV_BUTTON_ID);\n//        if (mPrevButton != null) {\n//            mPrevButton.setVisibility(View.GONE);\n//        }\n//        mNextButton = (ImageButton) v.findViewById(NEXT_BUTTON_ID);\n//        if (mNextButton != null) {\n//            mNextButton.setVisibility(View.GONE);\n//        }\n//\n//        mFfwdButton = (ImageButton) v.findViewById(FFWD_BUTTON_ID);\n//        if (mFfwdButton != null) {\n//            mFfwdButton.setOnClickListener(mFfwdListener);\n//            if (!mFromXml) {\n//                mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);\n//            }\n//        }\n//\n//        mRewButton = (ImageButton) v.findViewById(REW_BUTTON_ID);\n//        if (mRewButton != null) {\n//            mRewButton.setOnClickListener(mRewListener);\n//            if (!mFromXml) {\n//                mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);\n//            }\n//        }\n//        mPauseButton = (ImageButton) v.findViewById(PAUSE_BUTTON_ID);\n//        if (mPauseButton != null) {\n//            mPauseButton.requestFocus();\n//            mPauseButton.setOnClickListener(mPauseListener);\n//        }\n//\n//        mProgress = (ProgressBar) v.findViewById(MEDIACONTROLLER_PROGRESS_ID);\n//        if (mProgress != null) {\n//            if (mProgress instanceof SeekBar) {\n//                SeekBar seeker = (SeekBar) mProgress;\n//                seeker.setOnSeekBarChangeListener(mSeekListener);\n//                seeker.setThumbOffset(1);\n//            }\n//            mProgress.setMax(1000);\n//            mProgress.setEnabled(!mDisableProgress);\n//        }\n//\n//        mEndTime = (TextView) v.findViewById(END_TIME_ID);\n//        mCurrentTime = (TextView) v.findViewById(CURRENT_TIME_ID);\n//    }\n//\n//    /**\n//     * Control the action when the seekbar dragged by user\n//     *\n//     * @param seekWhenDragging\n//     * True the media will seek periodically\n//     */\n//    public void setInstantSeeking(boolean seekWhenDragging) {\n//        mInstantSeeking = seekWhenDragging;\n//    }\n//\n//    private void disableUnsupportedButtons() {\n//        try {\n//            if (mPauseButton != null && !mPlayer.canPause())\n//                mPauseButton.setEnabled(false);\n//        } catch (IncompatibleClassChangeError ex) {\n//        }\n//    }\n//\n//    /**\n//     * <p>\n//     * Change the animation style resource for this controller.\n//     * </p>\n//     *\n//     * <p>\n//     * If the controller is showing, calling this method will take effect only\n//     * the next time the controller is shown.\n//     * </p>\n//     *\n//     * @param animationStyle\n//     * animation style to use when the controller appears and disappears.\n//     * Set to -1 for the default animation, 0 for no animation,\n//     * or a resource identifier for an explicit animation.\n//     *\n//     */\n//    public void setAnimationStyle(int animationStyle) {\n//        mAnimStyle = animationStyle;\n//    }\n//\n//    public interface OnShownListener {\n//        public void onShown();\n//    }\n//\n//    private OnShownListener mShownListener;\n//\n//    public void setOnShownListener(OnShownListener l) {\n//        mShownListener = l;\n//    }\n//\n//    public interface OnHiddenListener {\n//        public void onHidden();\n//    }\n//\n//    private OnHiddenListener mHiddenListener;\n//\n//    public void setOnHiddenListener(OnHiddenListener l) {\n//        mHiddenListener = l;\n//    }\n//\n//    @SuppressLint(\"HandlerLeak\")\n//    private Handler mHandler = new Handler() {\n//        @Override\n//        public void handleMessage(Message msg) {\n//            long pos;\n//            switch (msg.what) {\n//                case FADE_OUT:\n//                    hide();\n//                    break;\n//                case SHOW_PROGRESS:\n//                    pos = setProgress();\n//                    if (!mDragging && mShowing) {\n//                        msg = obtainMessage(SHOW_PROGRESS);\n//                        sendMessageDelayed(msg, 1000 - (pos % 1000));\n//                        updatePausePlay();\n//                    }\n//                    break;\n//            }\n//        }\n//    };\n//\n//    private long setProgress() {\n//        if (mPlayer == null || mDragging)\n//            return 0;\n//\n//        long position = mPlayer.getCurrentPosition();\n//        long duration = mPlayer.getDuration();\n//        if (mProgress != null) {\n//            if (duration > 0) {\n//                long pos = 1000L * position / duration;\n//                mProgress.setProgress((int) pos);\n//            }\n//            int percent = mPlayer.getBufferPercentage();\n//            mProgress.setSecondaryProgress(percent * 10);\n//        }\n//\n//        mDuration = duration;\n//\n//        if (mEndTime != null)\n//            mEndTime.setText(generateTime(mDuration));\n//        if (mCurrentTime != null)\n//            mCurrentTime.setText(generateTime(position));\n//\n//        return position;\n//    }\n//\n//    private static String generateTime(long position) {\n//        int totalSeconds = (int) (position / 1000);\n//\n//        int seconds = totalSeconds % 60;\n//        int minutes = (totalSeconds / 60) % 60;\n//        int hours = totalSeconds / 3600;\n//\n//        if (hours > 0) {\n//            return String.format(Locale.US, \"%02d:%02d:%02d\", hours, minutes,\n//                    seconds).toString();\n//        } else {\n//            return String.format(Locale.US, \"%02d:%02d\", minutes, seconds)\n//                    .toString();\n//        }\n//    }\n//\n//    @Override\n//    public boolean onTouchEvent(MotionEvent event) {\n//        show(sDefaultTimeout);\n//        return true;\n//    }\n//\n//    @Override\n//    public boolean onTrackballEvent(MotionEvent ev) {\n//        show(sDefaultTimeout);\n//        return false;\n//    }\n//\n//    @Override\n//    public boolean dispatchKeyEvent(KeyEvent event) {\n//        int keyCode = event.getKeyCode();\n//        if (event.getRepeatCount() == 0\n//                && (keyCode == KeyEvent.KEYCODE_HEADSETHOOK\n//                || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyCode == KeyEvent.KEYCODE_SPACE)) {\n//            doPauseResume();\n//            show(sDefaultTimeout);\n//            if (mPauseButton != null)\n//                mPauseButton.requestFocus();\n//            return true;\n//        } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {\n//            if (mPlayer.isPlaying()) {\n//                mPlayer.pause();\n//                updatePausePlay();\n//            }\n//            return true;\n//        } else if (keyCode == KeyEvent.KEYCODE_BACK\n//                || keyCode == KeyEvent.KEYCODE_MENU) {\n//            hide();\n//            return true;\n//        } else {\n//            show(sDefaultTimeout);\n//        }\n//        return super.dispatchKeyEvent(event);\n//    }\n//\n//    private OnClickListener mPauseListener = new OnClickListener() {\n//        public void onClick(View v) {\n//            doPauseResume();\n//            show(sDefaultTimeout);\n//        }\n//    };\n//\n//    private void updatePausePlay() {\n//        if (mRoot == null || mPauseButton == null)\n//            return;\n//\n//        if (mPlayer.isPlaying())\n//            mPauseButton.setImageResource(IC_MEDIA_PAUSE_ID);\n//        else\n//            mPauseButton.setImageResource(IC_MEDIA_PLAY_ID);\n//    }\n//\n//    private void doPauseResume() {\n//        if (mPlayer.isPlaying())\n//            mPlayer.pause();\n//        else\n//            mPlayer.start();\n//        updatePausePlay();\n//    }\n//\n//    private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() {\n//\n//        public void onStartTrackingTouch(SeekBar bar) {\n//            mDragging = true;\n//            show(3600000);\n//            mHandler.removeMessages(SHOW_PROGRESS);\n//            if (mInstantSeeking)\n//                mAM.setStreamMute(AudioManager.STREAM_MUSIC, true);\n//        }\n//\n//        public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {\n//            if (!fromuser)\n//                return;\n//\n//            final int newposition = (int) (mDuration * progress) / 1000;\n//            String time = generateTime(newposition);\n//            if (mInstantSeeking) {\n//                mHandler.removeCallbacks(mLastSeekBarRunnable);\n//                mLastSeekBarRunnable = new Runnable() {\n//                    @Override\n//                    public void run() {\n//                        mPlayer.seekTo(newposition);\n//                    }\n//                };\n//                mHandler.postDelayed(mLastSeekBarRunnable, SEEK_TO_POST_DELAY_MILLIS);\n//            }\n//            if (mCurrentTime != null)\n//                mCurrentTime.setText(time);\n//        }\n//\n//        public void onStopTrackingTouch(SeekBar bar) {\n//            if (!mInstantSeeking)\n//                mPlayer.seekTo((int)(mDuration * bar.getProgress()) / 1000);\n//\n//            show(sDefaultTimeout);\n//            mHandler.removeMessages(SHOW_PROGRESS);\n//            mAM.setStreamMute(AudioManager.STREAM_MUSIC, false);\n//            mDragging = false;\n//            mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000);\n//        }\n//    };\n//\n//    private OnClickListener mRewListener = new OnClickListener() {\n//        public void onClick(View v) {\n//            int pos = (int)mPlayer.getCurrentPosition();\n//            pos -= 5000; // milliseconds\n//            mPlayer.seekTo(pos);\n//            setProgress();\n//\n//            show(sDefaultTimeout);\n//        }\n//    };\n//\n//    private OnClickListener mFfwdListener = new OnClickListener() {\n//        public void onClick(View v) {\n//            int pos = (int)mPlayer.getCurrentPosition();\n//            pos += 15000; // milliseconds\n//            mPlayer.seekTo(pos);\n//            setProgress();\n//\n//            show(sDefaultTimeout);\n//        }\n//    };\n//\n//    /**\n//     * Set the view that acts as the anchor for the control view.\n//     *\n//     * - This can for example be a VideoView, or your Activity's main view.\n//     * - AudioPlayer has no anchor view, so the view parameter will be null.\n//     *\n//     * @param view\n//     * The view to which to anchor the controller when it is visible.\n//     */\n//    @Override\n//    public void setAnchorView(View view) {\n//        mAnchor = view;\n//        if (mAnchor == null) {\n//            sDefaultTimeout = 0; // show forever\n//        }\n//        if (!mFromXml) {\n//            removeAllViews();\n//            mRoot = makeControllerView();\n//            mWindow.setContentView(mRoot);\n//            mWindow.setWidth(LayoutParams.MATCH_PARENT);\n//            mWindow.setHeight(LayoutParams.WRAP_CONTENT);\n//        }\n//        initControllerView(mRoot);\n//    }\n//\n//    @Override\n//    public void setMediaPlayer(MediaPlayerControl player) {\n//        mPlayer = player;\n//        updatePausePlay();\n//    }\n//\n//    @Override\n//    public void show() {\n//        show(sDefaultTimeout);\n//    }\n//\n//    /**\n//     * Show the controller on screen. It will go away automatically after\n//     * 'timeout' milliseconds of inactivity.\n//     *\n//     * @param timeout\n//     * The timeout in milliseconds. Use 0 to show the controller until hide() is called.\n//     */\n//    @Override\n//    public void show(int timeout) {\n//        if (!mShowing) {\n//            if (mAnchor != null && mAnchor.getWindowToken() != null) {\n//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n//                    mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);\n//                }\n//            }\n//            if (mPauseButton != null)\n//                mPauseButton.requestFocus();\n//            disableUnsupportedButtons();\n//\n//            if (mFromXml) {\n//                setVisibility(View.VISIBLE);\n//            } else {\n//                int[] location = new int[2];\n//\n//                if (mAnchor != null) {\n//                    mAnchor.getLocationOnScreen(location);\n//                    Rect anchorRect = new Rect(location[0], location[1],\n//                            location[0] + mAnchor.getWidth(), location[1]\n//                            + mAnchor.getHeight());\n//\n//                    mWindow.setAnimationStyle(mAnimStyle);\n//                    mWindow.showAtLocation(mAnchor, Gravity.BOTTOM,\n//                            anchorRect.left, 0);\n//                } else {\n//                    Rect anchorRect = new Rect(location[0], location[1],\n//                            location[0] + mRoot.getWidth(), location[1]\n//                            + mRoot.getHeight());\n//\n//                    mWindow.setAnimationStyle(mAnimStyle);\n//                    mWindow.showAtLocation(mRoot, Gravity.BOTTOM,\n//                            anchorRect.left, 0);\n//                }\n//            }\n//            mShowing = true;\n//            if (mShownListener != null)\n//                mShownListener.onShown();\n//        }\n//        updatePausePlay();\n//        mHandler.sendEmptyMessage(SHOW_PROGRESS);\n//\n//        if (timeout != 0) {\n//            mHandler.removeMessages(FADE_OUT);\n//            mHandler.sendMessageDelayed(mHandler.obtainMessage(FADE_OUT),\n//                    timeout);\n//        }\n//    }\n//\n//    @Override\n//    public boolean isShowing() {\n//        return mShowing;\n//    }\n//\n//    @Override\n//    public void hide() {\n//        if (mShowing) {\n//            if (mAnchor != null) {\n//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {\n//                    //mAnchor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);\n//                }\n//            }\n//            try {\n//                mHandler.removeMessages(SHOW_PROGRESS);\n//                if (mFromXml)\n//                    setVisibility(View.GONE);\n//                else\n//                    mWindow.dismiss();\n//            } catch (IllegalArgumentException ex) {\n//                Log.d(TAG, \"MediaController already removed\");\n//            }\n//            mShowing = false;\n//            if (mHiddenListener != null)\n//                mHiddenListener.onHidden();\n//        }\n//    }\n//\n//    @Override\n//    public void setEnabled(boolean enabled) {\n//        if (mPauseButton != null) {\n//            mPauseButton.setEnabled(enabled);\n//        }\n//        if (mFfwdButton != null) {\n//            mFfwdButton.setEnabled(enabled);\n//        }\n//        if (mRewButton != null) {\n//            mRewButton.setEnabled(enabled);\n//        }\n//        if (mProgress != null && !mDisableProgress)\n//            mProgress.setEnabled(enabled);\n//        disableUnsupportedButtons();\n//        super.setEnabled(enabled);\n//    }\n//}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/Rotatable.java",
    "content": "package com.pili.rnpili.support;\n\n/**\n * Created by jerikc on 16/2/5.\n */\npublic interface Rotatable {\n    // Set parameter 'animation' to true to have animation when rotation.\n    void setOrientation(int orientation, boolean animation);\n}\n"
  },
  {
    "path": "android/src/main/java/com/pili/rnpili/support/RotateLayout.java",
    "content": "package com.pili.rnpili.support;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n// A RotateLayout is designed to display a single item and provides the\n// capabilities to rotate the item.\npublic class RotateLayout extends ViewGroup implements Rotatable {\n    @SuppressWarnings(\"unused\")\n    private static final String TAG = \"RotateLayout\";\n    private int mOrientation;\n    protected View mChild;\n    public RotateLayout(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        // The transparent background here is a workaround of the render issue\n        // happened when the view is rotated as the device's orientation\n        // changed. The view looks fine in landscape. After rotation, the view\n        // is invisible.\n        setBackgroundResource(android.R.color.transparent);\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    @Override\n    protected void onFinishInflate() {\n        super.onFinishInflate();\n        mChild = getChildAt(0);\n        mChild.setPivotX(0);\n        mChild.setPivotY(0);\n    }\n\n    public void setChild(View mChild) {\n        this.mChild = mChild;\n    }\n\n    @Override\n    protected void onLayout(\n            boolean change, int left, int top, int right, int bottom) {\n        int width = right - left;\n        int height = bottom - top;\n        switch (mOrientation) {\n            case 0:\n            case 180:\n                mChild.layout(0, 0, width, height);\n                break;\n            case 90:\n            case 270:\n                mChild.layout(0, 0, height, width);\n                break;\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    @Override\n    protected void onMeasure(int widthSpec, int heightSpec) {\n        int w = 0, h = 0;\n        switch(mOrientation) {\n            case 0:\n            case 180:\n                measureChild(mChild, widthSpec, heightSpec);\n                w = mChild.getMeasuredWidth();\n                h = mChild.getMeasuredHeight();\n                break;\n            case 90:\n            case 270:\n                measureChild(mChild, heightSpec, widthSpec);\n                w = mChild.getMeasuredHeight();\n                h = mChild.getMeasuredWidth();\n                break;\n        }\n        setMeasuredDimension(w, h);\n        switch (mOrientation) {\n            case 0:\n                mChild.setTranslationX(0);\n                mChild.setTranslationY(0);\n                break;\n            case 90:\n                mChild.setTranslationX(0);\n                mChild.setTranslationY(h);\n                break;\n            case 180:\n                mChild.setTranslationX(w);\n                mChild.setTranslationY(h);\n                break;\n            case 270:\n                mChild.setTranslationX(w);\n                mChild.setTranslationY(0);\n                break;\n        }\n        mChild.setRotation(-mOrientation);\n    }\n\n    @Override\n    public boolean shouldDelayChildPressedState() {\n        return false;\n    }\n\n    // Rotate the view counter-clockwise\n    @Override\n    public void setOrientation(int orientation, boolean animation) {\n        orientation = orientation % 360;\n        if (mOrientation == orientation) return;\n        mOrientation = orientation;\n        requestLayout();\n    }\n}"
  },
  {
    "path": "android/src/test/java/com/pili/rnpili/ExampleUnitTest.java",
    "content": "package com.pili.rnpili;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "index.js",
    "content": "/**\n * Created by buhe on 16/4/28.\n */\nmodule.exports = {\n  Streaming: require('./Streaming'),\n  AudioStreaming: require('./AudioStreaming'),\n  Player: require('./Player'),\n  StreamingConst: require('./StreamingConst')\n};\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>en</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>NSPrincipalClass</key>\n\t<string></string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTAudioStreaming.h",
    "content": "//\n//  RCTStreaming.h\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"RCTView.h\"\n#import \"PLCameraStreamingKit.h\"\n#import \"Reachability.h\"\n#import <asl.h>\n\n@class RCTEventDispatcher;\n\n@interface RCTAudioStreaming : UIView<PLCameraStreamingSessionDelegate,PLStreamingSendingBufferDelegate>\n\n@property (nonatomic, strong) PLCameraStreamingSession  *session;\n@property (nonatomic, strong) dispatch_queue_t sessionQueue;\n@property (nonatomic, strong) Reachability *internetReachability;\n@property (nonatomic, strong) NSDictionary  *profile;\n@property (nonatomic, strong) NSString *rtmpURL;\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTAudioStreaming.m",
    "content": "//\n//  RCTStreaming.m\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTAudioStreaming.h\"\n#import \"RCTBridgeModule.h\"\n#import \"UIView+React.h\"\n#import \"RCTEventDispatcher.h\"\n\n\n@implementation RCTAudioStreaming{\n    RCTEventDispatcher *_eventDispatcher;\n    BOOL _started;\n    BOOL _muted;\n}\n\nconst char *audioStateNames[] = {\n    \"Unknow\",\n    \"Connecting\",\n    \"Connected\",\n    \"Disconnecting\",\n    \"Disconnected\",\n    \"Error\"\n};\n\nconst char *audioNetworkStatus[] = {\n    \"Not Reachable\",\n    \"Reachable via WiFi\",\n    \"Reachable via CELL\"\n};\n\n\n\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher\n{\n    if ((self = [super init])) {\n        [PLStreamingEnv initEnv];\n        _eventDispatcher = eventDispatcher;\n        _started = YES;\n        _muted = NO;\n\n        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];\n        self.internetReachability = [Reachability reachabilityForInternetConnection];\n        [self.internetReachability startNotifier];\n        \n        [[NSNotificationCenter defaultCenter] addObserver:self\n                                                 selector:@selector(handleInterruption:)\n                                                     name:AVAudioSessionInterruptionNotification\n                                                   object:[AVAudioSession sharedInstance]];\n        CGSize videoSize = CGSizeMake(480 , 640);\n        UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];\n        if (orientation <= AVCaptureVideoOrientationLandscapeLeft) {\n            if (orientation > AVCaptureVideoOrientationPortraitUpsideDown) {\n                videoSize = CGSizeMake(640 , 480);\n            }\n        }\n        self.sessionQueue = dispatch_queue_create(\"pili.queue.streaming\", DISPATCH_QUEUE_SERIAL);\n    }\n    \n    return self;\n};\n\n- (void) setRtmpURL:(NSString *)rtmpURL\n{\n    _rtmpURL = rtmpURL;\n    [self setSourceAndProfile];\n}\n\n- (void)setProfile:(NSDictionary *)profile{\n    _profile = profile;\n    [self setSourceAndProfile];\n}\n\n- (void) setSourceAndProfile{\n    if(self.profile && self.rtmpURL){\n        \n        void (^permissionBlock)(void) = ^{\n            dispatch_async(self.sessionQueue, ^{\n                NSDictionary *audio = self.profile[@\"audio\"];\n                \n                PLAudioCaptureConfiguration *audioCaptureConfiguration = [PLAudioCaptureConfiguration defaultConfiguration];\n                // 音频编码配置\n                PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration];\n                AVCaptureVideoOrientation orientation = (AVCaptureVideoOrientation)(([[UIDevice currentDevice] orientation] <= UIDeviceOrientationLandscapeRight && [[UIDevice currentDevice] orientation] != UIDeviceOrientationUnknown) ? [[UIDevice currentDevice] orientation]: UIDeviceOrientationPortrait);\n                // 推流 session\n                self.session = [[PLCameraStreamingSession alloc] initWithVideoCaptureConfiguration:nil audioCaptureConfiguration:audioCaptureConfiguration videoStreamingConfiguration:nil audioStreamingConfiguration:audioStreamingConfiguration stream:nil videoOrientation:orientation];\n                self.session.delegate = self;\n \n                dispatch_async(dispatch_get_main_queue(), ^{\n                    \n                    if(_muted){\n                        [self setMuted:_muted];\n                    }\n                    \n                    [self startSession];\n                });\n            });\n        };\n        void (^noAccessBlock)(void) = ^{\n            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@\"No Access\", nil)\n                                                                message:NSLocalizedString(@\"!\", nil)\n                                                               delegate:nil\n                                                      cancelButtonTitle:NSLocalizedString(@\"Cancel\", nil)\n                                                      otherButtonTitles:nil];\n            [alertView show];\n        };\n        \n        switch ([PLCameraStreamingSession cameraAuthorizationStatus]) {\n            case PLAuthorizationStatusAuthorized:\n                permissionBlock();\n                break;\n            case PLAuthorizationStatusNotDetermined: {\n                [PLCameraStreamingSession requestCameraAccessWithCompletionHandler:^(BOOL granted) {\n                    granted ? permissionBlock() : noAccessBlock();\n                }];\n            }\n                break;\n            default:\n                noAccessBlock();\n                break;\n        }\n\n    }\n}\n\n- (void)setStarted:(BOOL) started {\n    if(started != _started){\n        if(started){\n            [self startSession];\n            _started = started;\n        }else{\n            [self stopSession];\n            _started = started;\n        }\n    }\n}\n\n-(void)setMuted:(BOOL) muted {\n    _muted = muted;\n    [self.session setMuted:muted];\n}\n\n\n- (void)streamingSessionSendingBufferDidFull:(id)session {\n    NSString *log = @\"Buffer is full\";\n    NSLog(@\"%@\", log);\n}\n\n- (void)streamingSession:(id)session sendingBufferDidDropItems:(NSArray *)items {\n    NSString *log = @\"Frame dropped\";\n    NSLog(@\"%@\", log);\n}\n\n\n\n- (void)stopSession {\n    dispatch_async(self.sessionQueue, ^{\n        [self.session stop];\n    });\n}\n\n- (void)startSession {\n    dispatch_async(self.sessionQueue, ^{\n        NSURL *streamURL = [NSURL URLWithString:self.rtmpURL];\n        [self.session startWithPushURL:streamURL feedback:^(PLStreamStartStateFeedback feedback) {\n            dispatch_async(dispatch_get_main_queue(), ^{\n                NSLog(@\"success \");\n            });\n        }];\n    });\n}\n\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session streamStatusDidUpdate:(PLStreamStatus *)status {\n    NSString *log = [NSString stringWithFormat:@\"Stream Status: %@\", status];\n    NSLog(@\"%@\", log);\n}\n\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session streamStateDidChange:(PLStreamState)state {\n    NSString *log = [NSString stringWithFormat:@\"Stream State: %s\", audioStateNames[state]];\n    NSLog(@\"%@\", log);\n    \n    switch (state) {\n        case PLStreamStateUnknow:\n            [_eventDispatcher sendInputEventWithName:@\"onLoading\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateConnecting:\n            [_eventDispatcher sendInputEventWithName:@\"onConnecting\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateConnected:\n            [_eventDispatcher sendInputEventWithName:@\"onStreaming\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateDisconnecting:\n            \n            break;\n        case PLStreamStateDisconnected:\n            [_eventDispatcher sendInputEventWithName:@\"onDisconnected\" body:@{@\"target\": self.reactTag}];\n            [_eventDispatcher sendInputEventWithName:@\"onShutdown\" body:@{@\"target\": self.reactTag}]; //FIXME\n            break;\n        case PLStreamStateError:\n            [_eventDispatcher sendInputEventWithName:@\"onIOError\" body:@{@\"target\": self.reactTag}];\n            break;\n        default:\n            break;\n    }\n\n}\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session didDisconnectWithError:(NSError *)error {\n    NSString *log = [NSString stringWithFormat:@\"Stream State: Error. %@\", error];\n    NSLog(@\"%@\", log);\n    [self startSession];\n}\n\n- (void)reachabilityChanged:(NSNotification *)notif{\n    Reachability *curReach = [notif object];\n    NSParameterAssert([curReach isKindOfClass:[Reachability class]]);\n    NetworkStatus status = [curReach currentReachabilityStatus];\n    \n    if (NotReachable == status) {\n        // 对断网情况做处理\n        [self stopSession];\n    }\n    \n    NSString *log = [NSString stringWithFormat:@\"Networkt Status: %s\", audioNetworkStatus[status]];\n    NSLog(@\"%@\", log);\n}\n\n- (void)handleInterruption:(NSNotification *)notification {\n    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {\n        NSLog(@\"Interruption notification\");\n        \n        if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {\n            NSLog(@\"InterruptionTypeBegan\");\n        } else {\n            // the facetime iOS 9 has a bug: 1 does not send interrupt end 2 you can use application become active, and repeat set audio session acitve until success.  ref http://blog.corywiles.com/broken-facetime-audio-interruptions-in-ios-9\n            NSLog(@\"InterruptionTypeEnded\");\n            AVAudioSession *session = [AVAudioSession sharedInstance];\n            [session setActive:YES error:nil];\n        }\n    }\n}\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTAudioStreamingManager.h",
    "content": "//\n//  RCTStreamingManager.h\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTViewManager.h\"\n\n@interface RCTAudioStreamingManager : RCTViewManager\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTAudioStreamingManager.m",
    "content": "//\n//  RCTStreamingManager.m\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTAudioStreamingManager.h\"\n#import \"RCTAudioStreaming.h\"\n\n@implementation RCTAudioStreamingManager\nRCT_EXPORT_MODULE();\n\n@synthesize bridge = _bridge;\n\n- (UIView *)view\n{\n    return [[RCTAudioStreaming alloc] initWithEventDispatcher:self.bridge.eventDispatcher];\n}\n\n- (NSArray *)customDirectEventTypes\n{\n    return @[\n             @\"onReady\",\n             @\"onConnecting\",\n             @\"onStreaming\",\n             @\"onShutdown\",\n             @\"onIOError\",\n             @\"onDisconnected\"\n             ];\n}\n\n- (dispatch_queue_t)methodQueue\n{\n    return dispatch_get_main_queue();\n}\n\nRCT_EXPORT_VIEW_PROPERTY(rtmpURL, NSString);\nRCT_EXPORT_VIEW_PROPERTY(profile, NSDictionary);\nRCT_EXPORT_VIEW_PROPERTY(started, BOOL);\nRCT_EXPORT_VIEW_PROPERTY(muted, BOOL);\n\n\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPili.h",
    "content": "//\n//  RCTPili.h\n//  RCTPili\n//\n//  Created by buhe on 16/5/11.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import <Foundation/Foundation.h>\n\n@interface RCTPili : NSObject\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPili.m",
    "content": "//\n//  RCTPili.m\n//  RCTPili\n//\n//  Created by buhe on 16/5/11.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTPili.h\"\n\n@implementation RCTPili\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPlayer.h",
    "content": "//\n//  RCTPlayer.h\n//  RCTPili\n//\n//  Created by buhe on 16/5/12.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"RCTView.h\"\n#import \"PLPlayer.h\"\n\n@class RCTEventDispatcher;\n\n@interface RCTPlayer : UIView<PLPlayerDelegate>\n\n@property (nonatomic, assign) int reconnectCount;\n\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;\n\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPlayer.m",
    "content": "//\n//  RCTPlayer.m\n//  RCTPili\n//\n//  Created by buhe on 16/5/12.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTPlayer.h\"\n#import \"RCTBridgeModule.h\"\n#import \"RCTEventDispatcher.h\"\n#import \"UIView+React.h\"\n\n@implementation RCTPlayer{\n    RCTEventDispatcher *_eventDispatcher;\n    PLPlayer *_plplayer;\n    bool _started;\n    bool _muted;\n}\n\nstatic NSString *status[] = {\n    @\"PLPlayerStatusUnknow\",\n    @\"PLPlayerStatusPreparing\",\n    @\"PLPlayerStatusReady\",\n    @\"PLPlayerStatusCaching\",\n    @\"PLPlayerStatusPlaying\",\n    @\"PLPlayerStatusPaused\",\n    @\"PLPlayerStatusStopped\",\n    @\"PLPlayerStatusError\"\n};\n\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher\n{\n    if ((self = [super init])) {\n        _eventDispatcher = eventDispatcher;\n        _started = YES;\n        _muted = NO;\n        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];\n         self.reconnectCount = 0;\n    }\n    \n    return self;\n};\n\n- (void) setSource:(NSDictionary *)source\n{\n    NSString *uri = source[@\"uri\"];\n    bool backgroundPlay = source[@\"backgroundPlay\"] == nil ? false : source[@\"backgroundPlay\"];\n    \n    PLPlayerOption *option = [PLPlayerOption defaultOption];\n    \n    // 更改需要修改的 option 属性键所对应的值\n    [option setOptionValue:@15 forKey:PLPlayerOptionKeyTimeoutIntervalForMediaPackets];\n    \n    if(_plplayer){\n        [_plplayer stop]; //TODO View 被卸载时 也要调用\n    }\n\n    _plplayer = [PLPlayer playerWithURL:[[NSURL alloc] initWithString:uri] option:option];\n\n    _plplayer.delegate = self;\n    _plplayer.delegateQueue = dispatch_get_main_queue();\n    _plplayer.backgroundPlayEnable = backgroundPlay;\n    if(backgroundPlay){\n        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startPlayer) name:UIApplicationWillEnterForegroundNotification object:nil];\n    }\n    [self setupUI];\n    \n    [self startPlayer];\n    \n}\n\n- (void)setupUI {\n    if (_plplayer.status != PLPlayerStatusError) {\n        // add player view\n        UIView *playerView = _plplayer.playerView;\n        [self addSubview:playerView];\n         [playerView setTranslatesAutoresizingMaskIntoConstraints:NO];\n        \n        NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:playerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];\n        NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:playerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];\n        NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:playerView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];\n        NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:playerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0];\n        \n        NSArray *constraints = [NSArray arrayWithObjects:centerX, centerY,width,height, nil];\n        [self addConstraints: constraints];\n    }\n    \n}\n\n- (void) setStarted:(BOOL) started{\n    if(started != _started){\n    if(started){\n        [_plplayer resume];\n        _started = started;\n    }else{\n        [_plplayer pause];\n        _started = started;\n    }\n    }\n}\n\n- (void) setMuted:(BOOL) muted {\n    _muted = muted;\n    [_plplayer setMute:muted];\n    \n}\n\n- (void)startPlayer {\n    [UIApplication sharedApplication].idleTimerDisabled = YES;\n    [_plplayer play];\n    _started = true;\n}\n\n#pragma mark - <PLPlayerDelegate>\n\n- (void)player:(nonnull PLPlayer *)player statusDidChange:(PLPlayerStatus)state {\n    switch (state) {\n        case PLPlayerStatusCaching:\n            [_eventDispatcher sendInputEventWithName:@\"onLoading\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLPlayerStatusPlaying:\n            [_eventDispatcher sendInputEventWithName:@\"onPlaying\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLPlayerStatusPaused:\n            [_eventDispatcher sendInputEventWithName:@\"onPaused\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLPlayerStatusStopped:\n            [_eventDispatcher sendInputEventWithName:@\"onShutdown\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLPlayerStatusError:\n            [_eventDispatcher sendInputEventWithName:@\"onError\" body:@{@\"target\": self.reactTag , @\"errorCode\": [NSNumber numberWithUnsignedInt:0]}];\n            break;\n        default:\n            break;\n    }\n    NSLog(@\"%@\", status[state]);\n}\n\n- (void)player:(nonnull PLPlayer *)player stoppedWithError:(nullable NSError *)error {\n    [self tryReconnect:error];\n}\n\n- (void)tryReconnect:(nullable NSError *)error {\n    if (self.reconnectCount < 3) {\n        _reconnectCount ++;\n        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@\"错误\" message:[NSString stringWithFormat:@\"错误 %@，播放器将在%.1f秒后进行第 %d 次重连\", error.localizedDescription,0.5 * pow(2, self.reconnectCount - 1), _reconnectCount] delegate:nil cancelButtonTitle:@\"OK\" otherButtonTitles:nil];\n        [alert show];\n        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * pow(2, self.reconnectCount) * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n            [_plplayer play];\n        });\n    }else {\n        [UIApplication sharedApplication].idleTimerDisabled = NO;\n        NSLog(@\"%@\", error);\n    }\n}\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPlayerManager.h",
    "content": "//\n//  RCTPlayerManger.h\n//  RCTPili\n//\n//  Created by buhe on 16/5/12.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"RCTViewManager.h\"\n\n@interface RCTPlayerManager : RCTViewManager\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTPlayerManager.m",
    "content": "//\n//  RCTPlayerManger.m\n//  RCTPili\n//\n//  Created by buhe on 16/5/12.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTPlayerManager.h\"\n#import \"RCTPlayer.h\"\n\n@implementation RCTPlayerManager\nRCT_EXPORT_MODULE();\n\n@synthesize bridge = _bridge;\n\n- (UIView *)view\n{\n    return [[RCTPlayer alloc] initWithEventDispatcher:self.bridge.eventDispatcher];\n}\n\n- (NSArray *)customDirectEventTypes\n{\n    return @[\n             @\"onLoading\",\n             @\"onPaused\",\n             @\"onShutdown\",\n             @\"onError\",\n             @\"onPlaying\"\n             ];\n}\n\n- (dispatch_queue_t)methodQueue\n{\n    return dispatch_get_main_queue();\n}\n\nRCT_EXPORT_VIEW_PROPERTY(source, NSDictionary);\nRCT_EXPORT_VIEW_PROPERTY(started, BOOL);\nRCT_EXPORT_VIEW_PROPERTY(muted, BOOL);\n\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTStreaming.h",
    "content": "//\n//  RCTStreaming.h\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n#import \"RCTView.h\"\n#import \"PLCameraStreamingKit.h\"\n#import \"Reachability.h\"\n#import <asl.h>\n\n@class RCTEventDispatcher;\n\n@interface RCTStreaming : UIView<PLCameraStreamingSessionDelegate,PLStreamingSendingBufferDelegate>\n\n@property (nonatomic, strong) PLCameraStreamingSession  *session;\n@property (nonatomic, strong) dispatch_queue_t sessionQueue;\n@property (nonatomic, strong) Reachability *internetReachability;\n@property (nonatomic, strong) NSDictionary  *profile;\n@property (nonatomic, strong) NSString *rtmpURL;\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTStreaming.m",
    "content": "//\n//  RCTStreaming.m\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTStreaming.h\"\n#import \"RCTBridgeModule.h\"\n#import \"UIView+React.h\"\n#import \"RCTEventDispatcher.h\"\n\n\n@implementation RCTStreaming{\n    RCTEventDispatcher *_eventDispatcher;\n    BOOL _started;\n    BOOL _muted;\n    BOOL _focus;\n    NSString *_camera;\n}\n\nconst char *stateNames[] = {\n    \"Unknow\",\n    \"Connecting\",\n    \"Connected\",\n    \"Disconnecting\",\n    \"Disconnected\",\n    \"Error\"\n};\n\nconst char *networkStatus[] = {\n    \"Not Reachable\",\n    \"Reachable via WiFi\",\n    \"Reachable via CELL\"\n};\n\n\n\n\n- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher\n{\n    if ((self = [super init])) {\n        [PLStreamingEnv initEnv];\n        _eventDispatcher = eventDispatcher;\n        _started = YES;\n        _muted = NO;\n        _focus = NO;\n        _camera = @\"front\";\n\n        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reachabilityChanged:) name:kReachabilityChangedNotification object:nil];\n        self.internetReachability = [Reachability reachabilityForInternetConnection];\n        [self.internetReachability startNotifier];\n        \n        [[NSNotificationCenter defaultCenter] addObserver:self\n                                                 selector:@selector(handleInterruption:)\n                                                     name:AVAudioSessionInterruptionNotification\n                                                   object:[AVAudioSession sharedInstance]];\n        CGSize videoSize = CGSizeMake(480 , 640);\n        UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];\n        if (orientation <= AVCaptureVideoOrientationLandscapeLeft) {\n            if (orientation > AVCaptureVideoOrientationPortraitUpsideDown) {\n                videoSize = CGSizeMake(640 , 480);\n            }\n        }\n        self.sessionQueue = dispatch_queue_create(\"pili.queue.streaming\", DISPATCH_QUEUE_SERIAL);\n    }\n    \n    return self;\n};\n\n- (void) setRtmpURL:(NSString *)rtmpURL\n{\n    _rtmpURL = rtmpURL;\n    [self setSourceAndProfile];\n}\n\n- (void)setProfile:(NSDictionary *)profile{\n    _profile = profile;\n    [self setSourceAndProfile];\n}\n\n- (void) setSourceAndProfile{\n    if(self.profile && self.rtmpURL){\n        \n        void (^permissionBlock)(void) = ^{\n            dispatch_async(self.sessionQueue, ^{\n                NSDictionary *video = self.profile[@\"video\"];\n                NSDictionary *audio = self.profile[@\"audio\"];\n                \n                int *fps = [video[@\"fps\"] integerValue];\n                int *bps = [video[@\"bps\"] integerValue];\n                int *maxFrameInterval = [video[@\"maxFrameInterval\"] integerValue];\n                //TODO\n                double height = 800;\n                double width = 640;\n                \n                //TODO videoProfileLevel 需要通过 分辨率 选择\n                \n                PLVideoStreamingConfiguration *videoStreamingConfiguration = [[PLVideoStreamingConfiguration alloc] initWithVideoSize:CGSizeMake(width, height) expectedSourceVideoFrameRate:fps videoMaxKeyframeInterval:maxFrameInterval averageVideoBitRate:bps videoProfileLevel:AVVideoProfileLevelH264Baseline31];\n                \n                PLVideoCaptureConfiguration *videoCaptureConfiguration = [PLVideoCaptureConfiguration defaultConfiguration];\n\n                PLAudioCaptureConfiguration *audioCaptureConfiguration = [PLAudioCaptureConfiguration defaultConfiguration];\n                // 音频编码配置\n                PLAudioStreamingConfiguration *audioStreamingConfiguration = [PLAudioStreamingConfiguration defaultConfiguration];\n                AVCaptureVideoOrientation orientation = (AVCaptureVideoOrientation)(([[UIDevice currentDevice] orientation] <= UIDeviceOrientationLandscapeRight && [[UIDevice currentDevice] orientation] != UIDeviceOrientationUnknown) ? [[UIDevice currentDevice] orientation]: UIDeviceOrientationPortrait);\n                // 推流 session\n                self.session = [[PLCameraStreamingSession alloc] initWithVideoCaptureConfiguration:videoCaptureConfiguration audioCaptureConfiguration:audioCaptureConfiguration videoStreamingConfiguration:videoStreamingConfiguration audioStreamingConfiguration:audioStreamingConfiguration stream:nil videoOrientation:orientation];\n                self.session.delegate = self;\n                \n                //            UIImage *waterMark = [UIImage imageNamed:@\"qiniu.png\"];\n                //            PLFilterHandler handler = [self.session addWaterMark:waterMark origin:CGPointMake(100, 300)];\n                //            self.filterHandlers = [@[handler] mutableCopy];//TODO -  水印暂时注释\n                dispatch_async(dispatch_get_main_queue(), ^{\n                    UIView *previewView = self.session.previewView;\n                    [self addSubview:previewView];\n                    [previewView setTranslatesAutoresizingMaskIntoConstraints:NO];\n                    \n                    NSLayoutConstraint *centerX = [NSLayoutConstraint constraintWithItem:previewView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];\n                    NSLayoutConstraint *centerY = [NSLayoutConstraint constraintWithItem:previewView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0];\n                    NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:previewView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0];\n                    NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:previewView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0];\n                    \n                    NSArray *constraints = [NSArray arrayWithObjects:centerX, centerY,width,height, nil];\n                    [self addConstraints: constraints];\n                    \n                    NSString *log = [NSString stringWithFormat:@\"Zoom Range: [1..%.0f]\", self.session.videoActiveFormat.videoMaxZoomFactor];\n                    NSLog(@\"%@\", log);\n                    \n                    if(_focus){\n                        [self.session setSmoothAutoFocusEnabled:_focus];\n                        [self.session setTouchToFocusEnable:_focus];\n                    }\n                    \n                    if(_muted){\n                        [self setMuted:_muted];\n                    }\n                    \n                    [self startSession];\n                });\n            });\n        };\n        void (^noAccessBlock)(void) = ^{\n            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@\"No Access\", nil)\n                                                                message:NSLocalizedString(@\"!\", nil)\n                                                               delegate:nil\n                                                      cancelButtonTitle:NSLocalizedString(@\"Cancel\", nil)\n                                                      otherButtonTitles:nil];\n            [alertView show];\n        };\n        \n        switch ([PLCameraStreamingSession cameraAuthorizationStatus]) {\n            case PLAuthorizationStatusAuthorized:\n                permissionBlock();\n                break;\n            case PLAuthorizationStatusNotDetermined: {\n                [PLCameraStreamingSession requestCameraAccessWithCompletionHandler:^(BOOL granted) {\n                    granted ? permissionBlock() : noAccessBlock();\n                }];\n            }\n                break;\n            default:\n                noAccessBlock();\n                break;\n        }\n\n    }\n}\n\n- (void)setStarted:(BOOL) started {\n    if(started != _started){\n        if(started){\n            [self startSession];\n            _started = started;\n        }else{\n            [self stopSession];\n            _started = started;\n        }\n    }\n}\n\n-(void)setMuted:(BOOL) muted {\n    _muted = muted;\n    [self.session setMuted:muted];\n}\n\n-(void)setFocus:(BOOL) focus {\n    _focus = focus;\n    [self.session setSmoothAutoFocusEnabled:focus];\n    [self.session setTouchToFocusEnable:focus];\n}\n\n-(void)setZoom:(NSNumber*) zoom {\n    self.session.videoZoomFactor = [zoom integerValue];\n}\n\n-(void)setCamera:(NSString*)camera{\n    if([camera isEqualToString:@\"front\"] || [camera isEqualToString:@\"back\"]){\n        if(![camera isEqualToString:_camera]){\n            _camera = camera;\n            [self.session toggleCamera];\n        }\n    }\n    \n}\n\n\n- (void)streamingSessionSendingBufferDidFull:(id)session {\n    NSString *log = @\"Buffer is full\";\n    NSLog(@\"%@\", log);\n}\n\n- (void)streamingSession:(id)session sendingBufferDidDropItems:(NSArray *)items {\n    NSString *log = @\"Frame dropped\";\n    NSLog(@\"%@\", log);\n}\n\n\n\n- (void)stopSession {\n    dispatch_async(self.sessionQueue, ^{\n        [self.session stop];\n    });\n}\n\n- (void)startSession {\n    dispatch_async(self.sessionQueue, ^{\n        NSURL *streamURL = [NSURL URLWithString:self.rtmpURL];\n        [self.session startWithPushURL:streamURL feedback:^(PLStreamStartStateFeedback feedback) {\n            dispatch_async(dispatch_get_main_queue(), ^{\n                NSLog(@\"success \");\n            });\n        }];\n    });\n}\n\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session streamStatusDidUpdate:(PLStreamStatus *)status {\n    NSString *log = [NSString stringWithFormat:@\"Stream Status: %@\", status];\n    NSLog(@\"%@\", log);\n}\n\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session streamStateDidChange:(PLStreamState)state {\n    NSString *log = [NSString stringWithFormat:@\"Stream State: %s\", stateNames[state]];\n    NSLog(@\"%@\", log);\n    \n    switch (state) {\n        case PLStreamStateUnknow:\n            [_eventDispatcher sendInputEventWithName:@\"onLoading\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateConnecting:\n            [_eventDispatcher sendInputEventWithName:@\"onConnecting\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateConnected:\n            [_eventDispatcher sendInputEventWithName:@\"onStreaming\" body:@{@\"target\": self.reactTag}];\n            break;\n        case PLStreamStateDisconnecting:\n            \n            break;\n        case PLStreamStateDisconnected:\n            [_eventDispatcher sendInputEventWithName:@\"onDisconnected\" body:@{@\"target\": self.reactTag}];\n            [_eventDispatcher sendInputEventWithName:@\"onShutdown\" body:@{@\"target\": self.reactTag}]; //FIXME\n            break;\n        case PLStreamStateError:\n            [_eventDispatcher sendInputEventWithName:@\"onIOError\" body:@{@\"target\": self.reactTag}];\n            break;\n        default:\n            break;\n    }\n\n}\n- (void)cameraStreamingSession:(PLCameraStreamingSession *)session didDisconnectWithError:(NSError *)error {\n    NSString *log = [NSString stringWithFormat:@\"Stream State: Error. %@\", error];\n    NSLog(@\"%@\", log);\n    [self startSession];\n}\n\n- (void)reachabilityChanged:(NSNotification *)notif{\n    Reachability *curReach = [notif object];\n    NSParameterAssert([curReach isKindOfClass:[Reachability class]]);\n    NetworkStatus status = [curReach currentReachabilityStatus];\n    \n    if (NotReachable == status) {\n        // 对断网情况做处理\n        [self stopSession];\n    }\n    \n    NSString *log = [NSString stringWithFormat:@\"Networkt Status: %s\", networkStatus[status]];\n    NSLog(@\"%@\", log);\n}\n\n- (void)handleInterruption:(NSNotification *)notification {\n    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {\n        NSLog(@\"Interruption notification\");\n        \n        if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {\n            NSLog(@\"InterruptionTypeBegan\");\n        } else {\n            // the facetime iOS 9 has a bug: 1 does not send interrupt end 2 you can use application become active, and repeat set audio session acitve until success.  ref http://blog.corywiles.com/broken-facetime-audio-interruptions-in-ios-9\n            NSLog(@\"InterruptionTypeEnded\");\n            AVAudioSession *session = [AVAudioSession sharedInstance];\n            [session setActive:YES error:nil];\n        }\n    }\n}\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTStreamingManager.h",
    "content": "//\n//  RCTStreamingManager.h\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTViewManager.h\"\n\n@interface RCTStreamingManager : RCTViewManager\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/RCTStreamingManager.m",
    "content": "//\n//  RCTStreamingManager.m\n//  RCTPili\n//\n//  Created by guguyanhua on 16/5/26.\n//  Copyright © 2016年 pili. All rights reserved.\n//\n\n#import \"RCTStreamingManager.h\"\n#import \"RCTStreaming.h\"\n\n@implementation RCTStreamingManager\nRCT_EXPORT_MODULE();\n\n@synthesize bridge = _bridge;\n\n- (UIView *)view\n{\n    return [[RCTStreaming alloc] initWithEventDispatcher:self.bridge.eventDispatcher];\n}\n\n- (NSArray *)customDirectEventTypes\n{\n    return @[\n             @\"onReady\",\n             @\"onConnecting\",\n             @\"onStreaming\",\n             @\"onShutdown\",\n             @\"onIOError\",\n             @\"onDisconnected\"\n             ];\n}\n\n- (dispatch_queue_t)methodQueue\n{\n    return dispatch_get_main_queue();\n}\n\nRCT_EXPORT_VIEW_PROPERTY(rtmpURL, NSString);\nRCT_EXPORT_VIEW_PROPERTY(profile, NSDictionary);\nRCT_EXPORT_VIEW_PROPERTY(started, BOOL);\nRCT_EXPORT_VIEW_PROPERTY(muted, BOOL);\nRCT_EXPORT_VIEW_PROPERTY(zoom, NSNumber);\nRCT_EXPORT_VIEW_PROPERTY(focus, BOOL);\nRCT_EXPORT_VIEW_PROPERTY(camera, NSString);\n\n\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/Reachability.h",
    "content": "/*\n     File: Reachability.h\n Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.\n  Version: 3.5\n \n Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple\n Inc. (\"Apple\") in consideration of your agreement to the following\n terms, and your use, installation, modification or redistribution of\n this Apple software constitutes acceptance of these terms.  If you do\n not agree with these terms, please do not use, install, modify or\n redistribute this Apple software.\n \n In consideration of your agreement to abide by the following terms, and\n subject to these terms, Apple grants you a personal, non-exclusive\n license, under Apple's copyrights in this original Apple software (the\n \"Apple Software\"), to use, reproduce, modify and redistribute the Apple\n Software, with or without modifications, in source and/or binary forms;\n provided that if you redistribute the Apple Software in its entirety and\n without modifications, you must retain this notice and the following\n text and disclaimers in all such redistributions of the Apple Software.\n Neither the name, trademarks, service marks or logos of Apple Inc. may\n be used to endorse or promote products derived from the Apple Software\n without specific prior written permission from Apple.  Except as\n expressly stated in this notice, no other rights or licenses, express or\n implied, are granted by Apple herein, including but not limited to any\n patent rights that may be infringed by your derivative works or by other\n works in which the Apple Software may be incorporated.\n \n The Apple Software is provided by Apple on an \"AS IS\" basis.  APPLE\n MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION\n THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS\n FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND\n OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.\n \n IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,\n MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED\n AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),\n STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\n \n Copyright (C) 2014 Apple Inc. All Rights Reserved.\n \n */\n\n#import <Foundation/Foundation.h>\n#import <SystemConfiguration/SystemConfiguration.h>\n#import <netinet/in.h>\n\n\ntypedef enum : NSInteger {\n\tNotReachable = 0,\n\tReachableViaWiFi,\n\tReachableViaWWAN\n} NetworkStatus;\n\n\nextern NSString *kReachabilityChangedNotification;\n\n\n@interface Reachability : NSObject\n\n/*!\n * Use to check the reachability of a given host name.\n */\n+ (instancetype)reachabilityWithHostName:(NSString *)hostName;\n\n/*!\n * Use to check the reachability of a given IP address.\n */\n+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress;\n\n/*!\n * Checks whether the default route is available. Should be used by applications that do not connect to a particular host.\n */\n+ (instancetype)reachabilityForInternetConnection;\n\n/*!\n * Checks whether a local WiFi connection is available.\n */\n+ (instancetype)reachabilityForLocalWiFi;\n\n/*!\n * Start listening for reachability notifications on the current run loop.\n */\n- (BOOL)startNotifier;\n- (void)stopNotifier;\n\n- (NetworkStatus)currentReachabilityStatus;\n\n/*!\n * WWAN may be available, but not active until a connection has been established. WiFi may require a connection for VPN on Demand.\n */\n- (BOOL)connectionRequired;\n\n@end\n\n\n"
  },
  {
    "path": "ios/RCTPili/RCTPili/Reachability.m",
    "content": "/*\n     File: Reachability.m\n Abstract: Basic demonstration of how to use the SystemConfiguration Reachablity APIs.\n  Version: 3.5\n \n Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple\n Inc. (\"Apple\") in consideration of your agreement to the following\n terms, and your use, installation, modification or redistribution of\n this Apple software constitutes acceptance of these terms.  If you do\n not agree with these terms, please do not use, install, modify or\n redistribute this Apple software.\n \n In consideration of your agreement to abide by the following terms, and\n subject to these terms, Apple grants you a personal, non-exclusive\n license, under Apple's copyrights in this original Apple software (the\n \"Apple Software\"), to use, reproduce, modify and redistribute the Apple\n Software, with or without modifications, in source and/or binary forms;\n provided that if you redistribute the Apple Software in its entirety and\n without modifications, you must retain this notice and the following\n text and disclaimers in all such redistributions of the Apple Software.\n Neither the name, trademarks, service marks or logos of Apple Inc. may\n be used to endorse or promote products derived from the Apple Software\n without specific prior written permission from Apple.  Except as\n expressly stated in this notice, no other rights or licenses, express or\n implied, are granted by Apple herein, including but not limited to any\n patent rights that may be infringed by your derivative works or by other\n works in which the Apple Software may be incorporated.\n \n The Apple Software is provided by Apple on an \"AS IS\" basis.  APPLE\n MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION\n THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS\n FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND\n OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.\n \n IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL\n OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,\n MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED\n AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),\n STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE\n POSSIBILITY OF SUCH DAMAGE.\n \n Copyright (C) 2014 Apple Inc. All Rights Reserved.\n \n */\n\n#import <arpa/inet.h>\n#import <ifaddrs.h>\n#import <netdb.h>\n#import <sys/socket.h>\n\n#import <CoreFoundation/CoreFoundation.h>\n\n#import \"Reachability.h\"\n\n\nNSString *kReachabilityChangedNotification = @\"kNetworkReachabilityChangedNotification\";\n\n\n#pragma mark - Supporting functions\n\n#define kShouldPrintReachabilityFlags 0\n\nstatic void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)\n{\n#if kShouldPrintReachabilityFlags\n\n    NSLog(@\"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\\n\",\n          (flags & kSCNetworkReachabilityFlagsIsWWAN)\t\t\t\t? 'W' : '-',\n          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',\n\n          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',\n          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',\n          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',\n          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',\n          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',\n          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',\n          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',\n          comment\n          );\n#endif\n}\n\n\nstatic void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)\n{\n#pragma unused (target, flags)\n\tNSCAssert(info != NULL, @\"info was NULL in ReachabilityCallback\");\n\tNSCAssert([(__bridge NSObject*) info isKindOfClass: [Reachability class]], @\"info was wrong class in ReachabilityCallback\");\n\n    Reachability* noteObject = (__bridge Reachability *)info;\n    // Post a notification to notify the client that the network reachability changed.\n    [[NSNotificationCenter defaultCenter] postNotificationName: kReachabilityChangedNotification object: noteObject];\n}\n\n\n#pragma mark - Reachability implementation\n\n@implementation Reachability\n{\n\tBOOL _alwaysReturnLocalWiFiStatus; //default is NO\n\tSCNetworkReachabilityRef _reachabilityRef;\n}\n\n+ (instancetype)reachabilityWithHostName:(NSString *)hostName\n{\n\tReachability* returnValue = NULL;\n\tSCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);\n\tif (reachability != NULL)\n\t{\n\t\treturnValue= [[self alloc] init];\n\t\tif (returnValue != NULL)\n\t\t{\n\t\t\treturnValue->_reachabilityRef = reachability;\n\t\t\treturnValue->_alwaysReturnLocalWiFiStatus = NO;\n\t\t}\n\t}\n\treturn returnValue;\n}\n\n\n+ (instancetype)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress\n{\n\tSCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)hostAddress);\n\n\tReachability* returnValue = NULL;\n\n\tif (reachability != NULL)\n\t{\n\t\treturnValue = [[self alloc] init];\n\t\tif (returnValue != NULL)\n\t\t{\n\t\t\treturnValue->_reachabilityRef = reachability;\n\t\t\treturnValue->_alwaysReturnLocalWiFiStatus = NO;\n\t\t}\n\t}\n\treturn returnValue;\n}\n\n\n\n+ (instancetype)reachabilityForInternetConnection\n{\n\tstruct sockaddr_in zeroAddress;\n\tbzero(&zeroAddress, sizeof(zeroAddress));\n\tzeroAddress.sin_len = sizeof(zeroAddress);\n\tzeroAddress.sin_family = AF_INET;\n    \n\treturn [self reachabilityWithAddress:&zeroAddress];\n}\n\n\n+ (instancetype)reachabilityForLocalWiFi\n{\n\tstruct sockaddr_in localWifiAddress;\n\tbzero(&localWifiAddress, sizeof(localWifiAddress));\n\tlocalWifiAddress.sin_len = sizeof(localWifiAddress);\n\tlocalWifiAddress.sin_family = AF_INET;\n\n\t// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0.\n\tlocalWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);\n\n\tReachability* returnValue = [self reachabilityWithAddress: &localWifiAddress];\n\tif (returnValue != NULL)\n\t{\n\t\treturnValue->_alwaysReturnLocalWiFiStatus = YES;\n\t}\n    \n\treturn returnValue;\n}\n\n\n#pragma mark - Start and stop notifier\n\n- (BOOL)startNotifier\n{\n\tBOOL returnValue = NO;\n\tSCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};\n\n\tif (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context))\n\t{\n\t\tif (SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode))\n\t\t{\n\t\t\treturnValue = YES;\n\t\t}\n\t}\n    \n\treturn returnValue;\n}\n\n\n- (void)stopNotifier\n{\n\tif (_reachabilityRef != NULL)\n\t{\n\t\tSCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);\n\t}\n}\n\n\n- (void)dealloc\n{\n\t[self stopNotifier];\n\tif (_reachabilityRef != NULL)\n\t{\n\t\tCFRelease(_reachabilityRef);\n\t}\n}\n\n\n#pragma mark - Network Flag Handling\n\n- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags\n{\n\tPrintReachabilityFlags(flags, \"localWiFiStatusForFlags\");\n\tNetworkStatus returnValue = NotReachable;\n\n\tif ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect))\n\t{\n\t\treturnValue = ReachableViaWiFi;\n\t}\n    \n\treturn returnValue;\n}\n\n\n- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags\n{\n\tPrintReachabilityFlags(flags, \"networkStatusForFlags\");\n\tif ((flags & kSCNetworkReachabilityFlagsReachable) == 0)\n\t{\n\t\t// The target host is not reachable.\n\t\treturn NotReachable;\n\t}\n\n    NetworkStatus returnValue = NotReachable;\n\n\tif ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0)\n\t{\n\t\t/*\n         If the target host is reachable and no connection is required then we'll assume (for now) that you're on Wi-Fi...\n         */\n\t\treturnValue = ReachableViaWiFi;\n\t}\n\n\tif ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||\n        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))\n\t{\n        /*\n         ... and the connection is on-demand (or on-traffic) if the calling application is using the CFSocketStream or higher APIs...\n         */\n\n        if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0)\n        {\n            /*\n             ... and no [user] intervention is needed...\n             */\n            returnValue = ReachableViaWiFi;\n        }\n    }\n\n\tif ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN)\n\t{\n\t\t/*\n         ... but WWAN connections are OK if the calling application is using the CFNetwork APIs.\n         */\n\t\treturnValue = ReachableViaWWAN;\n\t}\n    \n\treturn returnValue;\n}\n\n\n- (BOOL)connectionRequired\n{\n\tNSAssert(_reachabilityRef != NULL, @\"connectionRequired called with NULL reachabilityRef\");\n\tSCNetworkReachabilityFlags flags;\n\n\tif (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))\n\t{\n\t\treturn (flags & kSCNetworkReachabilityFlagsConnectionRequired);\n\t}\n\n    return NO;\n}\n\n\n- (NetworkStatus)currentReachabilityStatus\n{\n\tNSAssert(_reachabilityRef != NULL, @\"currentNetworkStatus called with NULL SCNetworkReachabilityRef\");\n\tNetworkStatus returnValue = NotReachable;\n\tSCNetworkReachabilityFlags flags;\n    \n\tif (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags))\n\t{\n\t\tif (_alwaysReturnLocalWiFiStatus)\n\t\t{\n\t\t\treturnValue = [self localWiFiStatusForFlags:flags];\n\t\t}\n\t\telse\n\t\t{\n\t\t\treturnValue = [self networkStatusForFlags:flags];\n\t\t}\n\t}\n    \n\treturn returnValue;\n}\n\n\n@end\n"
  },
  {
    "path": "ios/RCTPili/RCTPili.podspec",
    "content": "#\n#  Be sure to run `pod spec lint RCTPili.podspec' to ensure this is a\n#  valid spec and to remove all comments including this before submitting the spec.\n#\n#  To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html\n#  To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/\n#\n\nPod::Spec.new do |s|\n  s.name         = \"RCTPili\"\n  s.version      = \"2.0.0\"\n  s.summary      = \"React Native Pili SDK Bridge\"\n\n  # This description is used to generate tags and improve search results.\n  #   * Think: What does it do? Why did you write it? What is the focus?\n  #   * Try to keep it short, snappy and to the point.\n  #   * Write the description between the DESC delimiters below.\n  #   * Finally, don't worry about the indent, CocoaPods strips it!\n  s.description  = <<-DESC\n                  React Native Pili SDK Bridge\n                   DESC\n\n  s.homepage     = \"http://www.airapps.cn/package/react-native-pili\"\n  s.license      = \"MIT\"\n  # s.license      = { :type => \"MIT\", :file => \"FILE_LICENSE\" }\n  s.author             = { \"buhe\" => \"bugu1986@126.com\" }\n  s.platform     = :ios, \"7.0\"\n  s.source       = { :git => \"https://github.com/buhe/react-native-piliv2.git\", :tag => \"master\" }\n  s.source_files  = \"RCTPili/**/*.{h,m}\"\n  s.requires_arc = true\n\n  # s.xcconfig = { \"HEADER_SEARCH_PATHS\" => \"$(SDKROOT)/usr/include/libxml2\" }\n  s.dependency \"React\"\n  s.dependency \"PLMediaStreamingKit\"\n  s.dependency \"PLPlayerKit\"\n\nend\n"
  },
  {
    "path": "ios/RCTPili/RCTPili.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t78CFC6511D8942EC00386E6B /* RCTPili.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC6451D8942EC00386E6B /* RCTPili.h */; };\n\t\t78CFC6521D8942EC00386E6B /* RCTPili.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC6461D8942EC00386E6B /* RCTPili.m */; };\n\t\t78CFC6531D8942EC00386E6B /* RCTPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC6471D8942EC00386E6B /* RCTPlayer.h */; };\n\t\t78CFC6541D8942EC00386E6B /* RCTPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC6481D8942EC00386E6B /* RCTPlayer.m */; };\n\t\t78CFC6551D8942EC00386E6B /* RCTPlayerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC6491D8942EC00386E6B /* RCTPlayerManager.h */; };\n\t\t78CFC6561D8942EC00386E6B /* RCTPlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC64A1D8942EC00386E6B /* RCTPlayerManager.m */; };\n\t\t78CFC6571D8942EC00386E6B /* RCTStreaming.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC64B1D8942EC00386E6B /* RCTStreaming.h */; };\n\t\t78CFC6581D8942EC00386E6B /* RCTStreaming.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC64C1D8942EC00386E6B /* RCTStreaming.m */; };\n\t\t78CFC6591D8942EC00386E6B /* RCTStreamingManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC64D1D8942EC00386E6B /* RCTStreamingManager.h */; };\n\t\t78CFC65A1D8942EC00386E6B /* RCTStreamingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC64E1D8942EC00386E6B /* RCTStreamingManager.m */; };\n\t\t78CFC65B1D8942EC00386E6B /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 78CFC64F1D8942EC00386E6B /* Reachability.h */; };\n\t\t78CFC65C1D8942EC00386E6B /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 78CFC6501D8942EC00386E6B /* Reachability.m */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXFileReference section */\n\t\t78CFC63A1D89426D00386E6B /* RCTPili.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = RCTPili.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t78CFC63F1D89426D00386E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\t78CFC6451D8942EC00386E6B /* RCTPili.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPili.h; sourceTree = \"<group>\"; };\n\t\t78CFC6461D8942EC00386E6B /* RCTPili.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPili.m; sourceTree = \"<group>\"; };\n\t\t78CFC6471D8942EC00386E6B /* RCTPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPlayer.h; sourceTree = \"<group>\"; };\n\t\t78CFC6481D8942EC00386E6B /* RCTPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPlayer.m; sourceTree = \"<group>\"; };\n\t\t78CFC6491D8942EC00386E6B /* RCTPlayerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPlayerManager.h; sourceTree = \"<group>\"; };\n\t\t78CFC64A1D8942EC00386E6B /* RCTPlayerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPlayerManager.m; sourceTree = \"<group>\"; };\n\t\t78CFC64B1D8942EC00386E6B /* RCTStreaming.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStreaming.h; sourceTree = \"<group>\"; };\n\t\t78CFC64C1D8942EC00386E6B /* RCTStreaming.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStreaming.m; sourceTree = \"<group>\"; };\n\t\t78CFC64D1D8942EC00386E6B /* RCTStreamingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStreamingManager.h; sourceTree = \"<group>\"; };\n\t\t78CFC64E1D8942EC00386E6B /* RCTStreamingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStreamingManager.m; sourceTree = \"<group>\"; };\n\t\t78CFC64F1D8942EC00386E6B /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = \"<group>\"; };\n\t\t78CFC6501D8942EC00386E6B /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t78CFC6361D89426D00386E6B /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t78CFC6301D89426D00386E6B = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t78CFC63C1D89426D00386E6B /* RCTPili */,\n\t\t\t\t78CFC63B1D89426D00386E6B /* Products */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t78CFC63B1D89426D00386E6B /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t78CFC63A1D89426D00386E6B /* RCTPili.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t78CFC63C1D89426D00386E6B /* RCTPili */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t78CFC6451D8942EC00386E6B /* RCTPili.h */,\n\t\t\t\t78CFC6461D8942EC00386E6B /* RCTPili.m */,\n\t\t\t\t78CFC6471D8942EC00386E6B /* RCTPlayer.h */,\n\t\t\t\t78CFC6481D8942EC00386E6B /* RCTPlayer.m */,\n\t\t\t\t78CFC6491D8942EC00386E6B /* RCTPlayerManager.h */,\n\t\t\t\t78CFC64A1D8942EC00386E6B /* RCTPlayerManager.m */,\n\t\t\t\t78CFC64B1D8942EC00386E6B /* RCTStreaming.h */,\n\t\t\t\t78CFC64C1D8942EC00386E6B /* RCTStreaming.m */,\n\t\t\t\t78CFC64D1D8942EC00386E6B /* RCTStreamingManager.h */,\n\t\t\t\t78CFC64E1D8942EC00386E6B /* RCTStreamingManager.m */,\n\t\t\t\t78CFC64F1D8942EC00386E6B /* Reachability.h */,\n\t\t\t\t78CFC6501D8942EC00386E6B /* Reachability.m */,\n\t\t\t\t78CFC63F1D89426D00386E6B /* Info.plist */,\n\t\t\t);\n\t\t\tpath = RCTPili;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\t78CFC6371D89426D00386E6B /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t78CFC6511D8942EC00386E6B /* RCTPili.h in Headers */,\n\t\t\t\t78CFC6591D8942EC00386E6B /* RCTStreamingManager.h in Headers */,\n\t\t\t\t78CFC6551D8942EC00386E6B /* RCTPlayerManager.h in Headers */,\n\t\t\t\t78CFC65B1D8942EC00386E6B /* Reachability.h in Headers */,\n\t\t\t\t78CFC6531D8942EC00386E6B /* RCTPlayer.h in Headers */,\n\t\t\t\t78CFC6571D8942EC00386E6B /* RCTStreaming.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\t78CFC6391D89426D00386E6B /* RCTPili */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 78CFC6421D89426D00386E6B /* Build configuration list for PBXNativeTarget \"RCTPili\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t78CFC6351D89426D00386E6B /* Sources */,\n\t\t\t\t78CFC6361D89426D00386E6B /* Frameworks */,\n\t\t\t\t78CFC6371D89426D00386E6B /* Headers */,\n\t\t\t\t78CFC6381D89426D00386E6B /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = RCTPili;\n\t\t\tproductName = RCTPili;\n\t\t\tproductReference = 78CFC63A1D89426D00386E6B /* RCTPili.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t78CFC6311D89426D00386E6B /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 0730;\n\t\t\t\tORGANIZATIONNAME = airapps;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t78CFC6391D89426D00386E6B = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 78CFC6341D89426D00386E6B /* Build configuration list for PBXProject \"RCTPili\" */;\n\t\t\tcompatibilityVersion = \"Xcode 3.2\";\n\t\t\tdevelopmentRegion = English;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t);\n\t\t\tmainGroup = 78CFC6301D89426D00386E6B;\n\t\t\tproductRefGroup = 78CFC63B1D89426D00386E6B /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t78CFC6391D89426D00386E6B /* RCTPili */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t78CFC6381D89426D00386E6B /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t78CFC6351D89426D00386E6B /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t78CFC6581D8942EC00386E6B /* RCTStreaming.m in Sources */,\n\t\t\t\t78CFC65A1D8942EC00386E6B /* RCTStreamingManager.m in Sources */,\n\t\t\t\t78CFC6541D8942EC00386E6B /* RCTPlayer.m in Sources */,\n\t\t\t\t78CFC6521D8942EC00386E6B /* RCTPili.m in Sources */,\n\t\t\t\t78CFC65C1D8942EC00386E6B /* Reachability.m in Sources */,\n\t\t\t\t78CFC6561D8942EC00386E6B /* RCTPlayerManager.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin XCBuildConfiguration section */\n\t\t78CFC6401D89426D00386E6B /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.3;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t78CFC6411D89426D00386E6B /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 9.3;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t78CFC6431D89426D00386E6B /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public/React\\\"\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = RCTPili/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = cn.airapps.RCTPili;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t78CFC6441D89426D00386E6B /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tHEADER_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"\\\"${PODS_ROOT}/Headers/Public/React\\\"\",\n\t\t\t\t);\n\t\t\t\tINFOPLIST_FILE = RCTPili/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(inherited) @executable_path/Frameworks @loader_path/Frameworks\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = cn.airapps.RCTPili;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t78CFC6341D89426D00386E6B /* Build configuration list for PBXProject \"RCTPili\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t78CFC6401D89426D00386E6B /* Debug */,\n\t\t\t\t78CFC6411D89426D00386E6B /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t78CFC6421D89426D00386E6B /* Build configuration list for PBXNativeTarget \"RCTPili\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t78CFC6431D89426D00386E6B /* Debug */,\n\t\t\t\t78CFC6441D89426D00386E6B /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 78CFC6311D89426D00386E6B /* Project object */;\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-native-pili\",\n  \"version\": \"2.3.0\",\n  \"description\": \"Pili Streaming Cloud React Native SDK\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"npm test\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/buhe/react-native-pili.git\"\n  },\n  \"keywords\": [\n    \"React\",\n    \"Native\",\n    \"SDK\",\n    \"Qiniu\",\n    \"Pili\",\n    \"Stream\",\n    \"Cloud\"\n  ],\n  \"author\": \"buhe\",\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/buhe/react-native-pili/issues\"\n  },\n  \"homepage\": \"https://github.com/buhe/react-native-pili#readme\",\n  \"devDependencies\": {\n    \"react\": \"^0.14.8\",\n    \"react-native\": \"^0.24.1\"\n  }\n}\n"
  }
]