{"action":"created","comment":{"url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/comments/88772113","pull_request_review_id":9338748,"id":88772113,"diff_hunk":"@@ -0,0 +1,461 @@\n+/*----------------------------------------------------------------------------*/\n+/* Copyright (c) FIRST 2016. All Rights Reserved.                             */\n+/* Open Source Software - may be modified and shared by FRC teams. The code   */\n+/* must be accompanied by the FIRST BSD license file in the root directory of */\n+/* the project.                                                               */\n+/*----------------------------------------------------------------------------*/\n+\n+package edu.wpi.first.wpilibj;\n+\n+import edu.wpi.cscore.CameraServerJNI;\n+import edu.wpi.cscore.CvSink;\n+import edu.wpi.cscore.CvSource;\n+import edu.wpi.cscore.MJPEGServer;\n+import edu.wpi.cscore.USBCamera;\n+import edu.wpi.cscore.VideoEvent;\n+import edu.wpi.cscore.VideoException;\n+import edu.wpi.cscore.VideoListener;\n+import edu.wpi.cscore.VideoMode;\n+import edu.wpi.cscore.VideoSink;\n+import edu.wpi.cscore.VideoSource;\n+import edu.wpi.first.wpilibj.networktables.NetworkTable;\n+import edu.wpi.first.wpilibj.networktables.NetworkTablesJNI;\n+import edu.wpi.first.wpilibj.tables.ITable;\n+import java.util.ArrayList;\n+import java.util.HashMap;\n+\n+/**\n+ * Singleton class for creating and keeping camera servers.\n+ * Also publishes camera information to NetworkTables.\n+ */\n+public class CameraServer {\n+  public static final int kBasePort = 1181;\n+  public static final int kSize640x480 = 0;\n+  public static final int kSize320x240 = 1;\n+  public static final int kSize160x120 = 2;\n+  private static final String kPublishName = \"/CameraPublisher\";\n+  private static CameraServer server;\n+\n+  /**\n+   * Get the CameraServer instance.\n+   */\n+  public static CameraServer getInstance() {\n+    if (server == null) {\n+      server = new CameraServer();\n+    }\n+    return server;\n+  }\n+\n+  private String m_primarySourceName;\n+  private HashMap<String, VideoSource> m_sources;\n+  private HashMap<String, VideoSink> m_sinks;\n+  private HashMap<Integer, ITable> m_tables;  // indexed by source handle\n+  private ITable m_publishTable;\n+  private VideoListener m_videoListener;\n+  private int m_tableListener;\n+  private int m_nextPort;\n+  private String[] m_addresses;\n+\n+  @SuppressWarnings(\"JavadocMethod\")\n+  private static String makeSourceValue(int source) {\n+    switch (VideoSource.getKindFromInt(CameraServerJNI.getSourceKind(source))) {\n+      case kUSB:\n+        return \"usb:\" + CameraServerJNI.getUSBCameraPath(source);\n+      case kHTTP:\n+        // TODO\n+        return \"ip:\";\n+      case kCv:\n+        return \"cv:\";\n+      default:\n+        return \"unknown:\";\n+    }\n+  }\n+\n+  @SuppressWarnings(\"JavadocMethod\")\n+  private static String makeStreamValue(String address, int port) {\n+    return \"mjpg:http://\" + address + \":\" + port + \"/?action=stream\";\n+  }\n+\n+  @SuppressWarnings(\"JavadocMethod\")\n+  private synchronized ITable getSourceTable(int source) {\n+    return m_tables.get(source);\n+  }\n+\n+  @SuppressWarnings(\"JavadocMethod\")\n+  private synchronized void updateStreamValues() {\n+    // Over all the sinks...\n+    for (VideoSink i : m_sinks.values()) {\n+      // Ignore all but MJPEGServer\n+      if (i.getKind() != VideoSink.Kind.kMJPEG) {\n+        continue;\n+      }\n+      int sink = i.getHandle();\n+\n+      // Get the source's subtable (if none exists, we're done)\n+      int source = CameraServerJNI.getSinkSource(sink);\n+      ITable table = m_tables.get(source);\n+      if (table == null) {\n+        continue;\n+      }\n+\n+      // Get port\n+      int port = CameraServerJNI.getMJPEGServerPort(sink);\n+\n+      // Generate values\n+      ArrayList<String> values = new ArrayList(m_addresses.length + 1);\n+      String listenAddress = CameraServerJNI.getMJPEGServerListenAddress(sink);\n+      if (!listenAddress.isEmpty()) {\n+        // If a listen address is specified, only use that\n+        values.add(makeStreamValue(listenAddress, port));\n+      } else {\n+        // Otherwise generate for hostname and all interface addresses\n+        values.add(makeStreamValue(CameraServerJNI.getHostname() + \".local\", port));\n+        for (String addr : m_addresses) {\n+          if (addr.equals(\"127.0.0.1\")) {\n+            continue;  // ignore localhost\n+          }\n+          values.add(makeStreamValue(addr, port));\n+        }\n+      }\n+\n+      // Set table value\n+      table.putStringArray(\"streams\", (String[]) values.toArray());\n+    }\n+  }\n+\n+  @SuppressWarnings(\"JavadocMethod\")\n+  private CameraServer() {\n+    m_sources = new HashMap<String, VideoSource>();\n+    m_sinks = new HashMap<String, VideoSink>();\n+    m_tables = new HashMap<Integer, ITable>();\n+    m_publishTable = NetworkTable.getTable(kPublishName);\n+    m_nextPort = kBasePort;\n+    m_addresses = new String[0];\n+\n+    // We publish sources to NetworkTables using the following structure:\n+    // \"/CameraPublisher/{Source.Name}/\" - root\n+    // - \"source\" (string): Descriptive, prefixed with type (e.g. \"usb:0\")\n+    // - \"streams\" (string array): URLs that can be used to stream data\n+    // - properties (scaled units)\n+\n+    // Listener for video events\n+    m_videoListener = new VideoListener(event -> {\n+      //System.out.println(\"got event kind=\" + event.kind);","path":"wpilibj/src/athena/java/edu/wpi/first/wpilibj/CameraServer.java","position":143,"original_position":143,"commit_id":"f78639de479aab1c8fea18c29911efdda2c88938","original_commit_id":"f78639de479aab1c8fea18c29911efdda2c88938","user":{"login":"AustinShalit","id":3799260,"avatar_url":"https://avatars.githubusercontent.com/u/3799260?v=3","gravatar_id":"","url":"https://api.github.com/users/AustinShalit","html_url":"https://github.com/AustinShalit","followers_url":"https://api.github.com/users/AustinShalit/followers","following_url":"https://api.github.com/users/AustinShalit/following{/other_user}","gists_url":"https://api.github.com/users/AustinShalit/gists{/gist_id}","starred_url":"https://api.github.com/users/AustinShalit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/AustinShalit/subscriptions","organizations_url":"https://api.github.com/users/AustinShalit/orgs","repos_url":"https://api.github.com/users/AustinShalit/repos","events_url":"https://api.github.com/users/AustinShalit/events{/privacy}","received_events_url":"https://api.github.com/users/AustinShalit/received_events","type":"User","site_admin":false},"body":"Remove","created_at":"2016-11-19T04:20:34Z","updated_at":"2016-11-19T04:24:02Z","html_url":"https://github.com/wpilibsuite/allwpilib/pull/352#discussion_r88772113","pull_request_url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352","_links":{"self":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/comments/88772113"},"html":{"href":"https://github.com/wpilibsuite/allwpilib/pull/352#discussion_r88772113"},"pull_request":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352"}}},"pull_request":{"url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352","id":94468902,"html_url":"https://github.com/wpilibsuite/allwpilib/pull/352","diff_url":"https://github.com/wpilibsuite/allwpilib/pull/352.diff","patch_url":"https://github.com/wpilibsuite/allwpilib/pull/352.patch","issue_url":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/352","number":352,"state":"open","locked":false,"title":"Add cscore-based CameraServer","user":{"login":"PeterJohnson","id":297126,"avatar_url":"https://avatars.githubusercontent.com/u/297126?v=3","gravatar_id":"","url":"https://api.github.com/users/PeterJohnson","html_url":"https://github.com/PeterJohnson","followers_url":"https://api.github.com/users/PeterJohnson/followers","following_url":"https://api.github.com/users/PeterJohnson/following{/other_user}","gists_url":"https://api.github.com/users/PeterJohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/PeterJohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PeterJohnson/subscriptions","organizations_url":"https://api.github.com/users/PeterJohnson/orgs","repos_url":"https://api.github.com/users/PeterJohnson/repos","events_url":"https://api.github.com/users/PeterJohnson/events{/privacy}","received_events_url":"https://api.github.com/users/PeterJohnson/received_events","type":"User","site_admin":false},"body":"Not fully functional yet, but allows interoperation with LabView dashboard.","created_at":"2016-11-19T03:25:33Z","updated_at":"2016-11-19T04:24:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e0681c77871b23ecaf416bab5eea192dd14448bb","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352/commits","review_comments_url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352/comments","review_comment_url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/comments{/number}","comments_url":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/352/comments","statuses_url":"https://api.github.com/repos/wpilibsuite/allwpilib/statuses/f78639de479aab1c8fea18c29911efdda2c88938","head":{"label":"PeterJohnson:cameraserver-update","ref":"cameraserver-update","sha":"f78639de479aab1c8fea18c29911efdda2c88938","user":{"login":"PeterJohnson","id":297126,"avatar_url":"https://avatars.githubusercontent.com/u/297126?v=3","gravatar_id":"","url":"https://api.github.com/users/PeterJohnson","html_url":"https://github.com/PeterJohnson","followers_url":"https://api.github.com/users/PeterJohnson/followers","following_url":"https://api.github.com/users/PeterJohnson/following{/other_user}","gists_url":"https://api.github.com/users/PeterJohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/PeterJohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PeterJohnson/subscriptions","organizations_url":"https://api.github.com/users/PeterJohnson/orgs","repos_url":"https://api.github.com/users/PeterJohnson/repos","events_url":"https://api.github.com/users/PeterJohnson/events{/privacy}","received_events_url":"https://api.github.com/users/PeterJohnson/received_events","type":"User","site_admin":false},"repo":{"id":59071800,"name":"allwpilib","full_name":"PeterJohnson/allwpilib","owner":{"login":"PeterJohnson","id":297126,"avatar_url":"https://avatars.githubusercontent.com/u/297126?v=3","gravatar_id":"","url":"https://api.github.com/users/PeterJohnson","html_url":"https://github.com/PeterJohnson","followers_url":"https://api.github.com/users/PeterJohnson/followers","following_url":"https://api.github.com/users/PeterJohnson/following{/other_user}","gists_url":"https://api.github.com/users/PeterJohnson/gists{/gist_id}","starred_url":"https://api.github.com/users/PeterJohnson/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PeterJohnson/subscriptions","organizations_url":"https://api.github.com/users/PeterJohnson/orgs","repos_url":"https://api.github.com/users/PeterJohnson/repos","events_url":"https://api.github.com/users/PeterJohnson/events{/privacy}","received_events_url":"https://api.github.com/users/PeterJohnson/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/PeterJohnson/allwpilib","description":"Official Repository of WPILibJ and WPILibC","fork":true,"url":"https://api.github.com/repos/PeterJohnson/allwpilib","forks_url":"https://api.github.com/repos/PeterJohnson/allwpilib/forks","keys_url":"https://api.github.com/repos/PeterJohnson/allwpilib/keys{/key_id}","collaborators_url":"https://api.github.com/repos/PeterJohnson/allwpilib/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/PeterJohnson/allwpilib/teams","hooks_url":"https://api.github.com/repos/PeterJohnson/allwpilib/hooks","issue_events_url":"https://api.github.com/repos/PeterJohnson/allwpilib/issues/events{/number}","events_url":"https://api.github.com/repos/PeterJohnson/allwpilib/events","assignees_url":"https://api.github.com/repos/PeterJohnson/allwpilib/assignees{/user}","branches_url":"https://api.github.com/repos/PeterJohnson/allwpilib/branches{/branch}","tags_url":"https://api.github.com/repos/PeterJohnson/allwpilib/tags","blobs_url":"https://api.github.com/repos/PeterJohnson/allwpilib/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/PeterJohnson/allwpilib/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/PeterJohnson/allwpilib/git/refs{/sha}","trees_url":"https://api.github.com/repos/PeterJohnson/allwpilib/git/trees{/sha}","statuses_url":"https://api.github.com/repos/PeterJohnson/allwpilib/statuses/{sha}","languages_url":"https://api.github.com/repos/PeterJohnson/allwpilib/languages","stargazers_url":"https://api.github.com/repos/PeterJohnson/allwpilib/stargazers","contributors_url":"https://api.github.com/repos/PeterJohnson/allwpilib/contributors","subscribers_url":"https://api.github.com/repos/PeterJohnson/allwpilib/subscribers","subscription_url":"https://api.github.com/repos/PeterJohnson/allwpilib/subscription","commits_url":"https://api.github.com/repos/PeterJohnson/allwpilib/commits{/sha}","git_commits_url":"https://api.github.com/repos/PeterJohnson/allwpilib/git/commits{/sha}","comments_url":"https://api.github.com/repos/PeterJohnson/allwpilib/comments{/number}","issue_comment_url":"https://api.github.com/repos/PeterJohnson/allwpilib/issues/comments{/number}","contents_url":"https://api.github.com/repos/PeterJohnson/allwpilib/contents/{+path}","compare_url":"https://api.github.com/repos/PeterJohnson/allwpilib/compare/{base}...{head}","merges_url":"https://api.github.com/repos/PeterJohnson/allwpilib/merges","archive_url":"https://api.github.com/repos/PeterJohnson/allwpilib/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/PeterJohnson/allwpilib/downloads","issues_url":"https://api.github.com/repos/PeterJohnson/allwpilib/issues{/number}","pulls_url":"https://api.github.com/repos/PeterJohnson/allwpilib/pulls{/number}","milestones_url":"https://api.github.com/repos/PeterJohnson/allwpilib/milestones{/number}","notifications_url":"https://api.github.com/repos/PeterJohnson/allwpilib/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/PeterJohnson/allwpilib/labels{/name}","releases_url":"https://api.github.com/repos/PeterJohnson/allwpilib/releases{/id}","deployments_url":"https://api.github.com/repos/PeterJohnson/allwpilib/deployments","created_at":"2016-05-18T01:30:24Z","updated_at":"2016-05-18T01:30:31Z","pushed_at":"2016-11-19T04:05:21Z","git_url":"git://github.com/PeterJohnson/allwpilib.git","ssh_url":"git@github.com:PeterJohnson/allwpilib.git","clone_url":"https://github.com/PeterJohnson/allwpilib.git","svn_url":"https://github.com/PeterJohnson/allwpilib","homepage":"","size":80379,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"wpilibsuite:master","ref":"master","sha":"ac50d7cf8a6d3d1010924cb6a78da7d4cf264ee1","user":{"login":"wpilibsuite","id":19267233,"avatar_url":"https://avatars.githubusercontent.com/u/19267233?v=3","gravatar_id":"","url":"https://api.github.com/users/wpilibsuite","html_url":"https://github.com/wpilibsuite","followers_url":"https://api.github.com/users/wpilibsuite/followers","following_url":"https://api.github.com/users/wpilibsuite/following{/other_user}","gists_url":"https://api.github.com/users/wpilibsuite/gists{/gist_id}","starred_url":"https://api.github.com/users/wpilibsuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wpilibsuite/subscriptions","organizations_url":"https://api.github.com/users/wpilibsuite/orgs","repos_url":"https://api.github.com/users/wpilibsuite/repos","events_url":"https://api.github.com/users/wpilibsuite/events{/privacy}","received_events_url":"https://api.github.com/users/wpilibsuite/received_events","type":"Organization","site_admin":false},"repo":{"id":24655143,"name":"allwpilib","full_name":"wpilibsuite/allwpilib","owner":{"login":"wpilibsuite","id":19267233,"avatar_url":"https://avatars.githubusercontent.com/u/19267233?v=3","gravatar_id":"","url":"https://api.github.com/users/wpilibsuite","html_url":"https://github.com/wpilibsuite","followers_url":"https://api.github.com/users/wpilibsuite/followers","following_url":"https://api.github.com/users/wpilibsuite/following{/other_user}","gists_url":"https://api.github.com/users/wpilibsuite/gists{/gist_id}","starred_url":"https://api.github.com/users/wpilibsuite/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wpilibsuite/subscriptions","organizations_url":"https://api.github.com/users/wpilibsuite/orgs","repos_url":"https://api.github.com/users/wpilibsuite/repos","events_url":"https://api.github.com/users/wpilibsuite/events{/privacy}","received_events_url":"https://api.github.com/users/wpilibsuite/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/wpilibsuite/allwpilib","description":"Official Repository of WPILibJ and WPILibC","fork":false,"url":"https://api.github.com/repos/wpilibsuite/allwpilib","forks_url":"https://api.github.com/repos/wpilibsuite/allwpilib/forks","keys_url":"https://api.github.com/repos/wpilibsuite/allwpilib/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wpilibsuite/allwpilib/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wpilibsuite/allwpilib/teams","hooks_url":"https://api.github.com/repos/wpilibsuite/allwpilib/hooks","issue_events_url":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/events{/number}","events_url":"https://api.github.com/repos/wpilibsuite/allwpilib/events","assignees_url":"https://api.github.com/repos/wpilibsuite/allwpilib/assignees{/user}","branches_url":"https://api.github.com/repos/wpilibsuite/allwpilib/branches{/branch}","tags_url":"https://api.github.com/repos/wpilibsuite/allwpilib/tags","blobs_url":"https://api.github.com/repos/wpilibsuite/allwpilib/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wpilibsuite/allwpilib/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wpilibsuite/allwpilib/git/refs{/sha}","trees_url":"https://api.github.com/repos/wpilibsuite/allwpilib/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wpilibsuite/allwpilib/statuses/{sha}","languages_url":"https://api.github.com/repos/wpilibsuite/allwpilib/languages","stargazers_url":"https://api.github.com/repos/wpilibsuite/allwpilib/stargazers","contributors_url":"https://api.github.com/repos/wpilibsuite/allwpilib/contributors","subscribers_url":"https://api.github.com/repos/wpilibsuite/allwpilib/subscribers","subscription_url":"https://api.github.com/repos/wpilibsuite/allwpilib/subscription","commits_url":"https://api.github.com/repos/wpilibsuite/allwpilib/commits{/sha}","git_commits_url":"https://api.github.com/repos/wpilibsuite/allwpilib/git/commits{/sha}","comments_url":"https://api.github.com/repos/wpilibsuite/allwpilib/comments{/number}","issue_comment_url":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/comments{/number}","contents_url":"https://api.github.com/repos/wpilibsuite/allwpilib/contents/{+path}","compare_url":"https://api.github.com/repos/wpilibsuite/allwpilib/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wpilibsuite/allwpilib/merges","archive_url":"https://api.github.com/repos/wpilibsuite/allwpilib/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wpilibsuite/allwpilib/downloads","issues_url":"https://api.github.com/repos/wpilibsuite/allwpilib/issues{/number}","pulls_url":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls{/number}","milestones_url":"https://api.github.com/repos/wpilibsuite/allwpilib/milestones{/number}","notifications_url":"https://api.github.com/repos/wpilibsuite/allwpilib/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wpilibsuite/allwpilib/labels{/name}","releases_url":"https://api.github.com/repos/wpilibsuite/allwpilib/releases{/id}","deployments_url":"https://api.github.com/repos/wpilibsuite/allwpilib/deployments","created_at":"2014-09-30T20:51:33Z","updated_at":"2016-11-17T23:28:45Z","pushed_at":"2016-11-19T04:05:22Z","git_url":"git://github.com/wpilibsuite/allwpilib.git","ssh_url":"git@github.com:wpilibsuite/allwpilib.git","clone_url":"https://github.com/wpilibsuite/allwpilib.git","svn_url":"https://github.com/wpilibsuite/allwpilib","homepage":"","size":87284,"stargazers_count":51,"watchers_count":51,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":26,"mirror_url":null,"open_issues_count":31,"forks":26,"open_issues":31,"watchers":51,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352"},"html":{"href":"https://github.com/wpilibsuite/allwpilib/pull/352"},"issue":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/352"},"comments":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/issues/352/comments"},"review_comments":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352/comments"},"review_comment":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/pulls/352/commits"},"statuses":{"href":"https://api.github.com/repos/wpilibsuite/allwpilib/statuses/f78639de479aab1c8fea18c29911efdda2c88938"}}}}
{"actor":{"display_login":"AustinShalit"}}