summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Green <andy.green@linaro.org>2015-10-24 14:13:38 +0800
committerAndy Green <andy.green@linaro.org>2015-10-24 14:13:38 +0800
commitc3fd3cb4df292aa53f80ddeb837fb72f299d8dbe (patch)
tree51b80ef756625ed5989189ccc6a5c314287f3c68
-rw-r--r--Makefile7
-rw-r--r--hdmicap-server.c1062
-rw-r--r--hdmicap.html327
-rw-r--r--hdmicap.pngbin0 -> 6696 bytes
4 files changed, 1396 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ac10506
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+IDIR=/usr/local/share
+
+hdmicap-server: hdmicap-server.c hdmicap.html hdmicap.png
+ gcc -o hdmicap-server hdmicap-server.c -lwebsockets
+ mkdir -p $(IDIR)/hdmicap
+ cp hdmicap.png hdmicap.html $(IDIR)/hdmicap
+
diff --git a/hdmicap-server.c b/hdmicap-server.c
new file mode 100644
index 0000000..c9d7596
--- /dev/null
+++ b/hdmicap-server.c
@@ -0,0 +1,1062 @@
+/*
+ * libwebsockets-test-server - libwebsockets test implementation
+ *
+ * Copyright (C) 2010-2011 Andy Green <andy@warmcat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation:
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#include "lws_config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#include <libwebsockets.h>
+
+#define SYSFS "/sys/devices/soc0/amba/43c10000.hdmicap"
+#define DEBUGFS "/sys/kernel/debug/framestore"
+
+struct cea {
+ int cea_index;
+ int px_clock;
+ int interlaced;
+ char hpol;
+ int hact;
+ int hblank;
+ int hfp;
+ int hsa;
+ char vpol;
+ int vact;
+ int vblank;
+ int vfp;
+ int vsa;
+};
+
+struct cea cea[] = {
+ { 2, 27000000, 0, '-', 720, 138, 16, 62, '-', 480, 45, 9, 6 },
+ { 17, 27000000, 0, '-', 720, 144, 12, 64, '-', 576, 49, 5, 5 },
+ { 19, 74250000, 0, '+', 1280, 700, 440, 40, '+', 720, 30, 5, 5 },
+};
+
+struct shared {
+ char buf[2][4096];
+ int len[2];
+ int using;
+ int set;
+ int lock_framebuffer;
+ int png_frame;
+ int htot, vtot, onset;
+ int force_exit;
+ int bcounts[256];
+ int force_cea;
+ int pll_locked;
+ int syncs_detected;
+ int sanity;
+ char spd_vendor[10];
+ char spd_product[16];
+};
+struct shared *shared;
+
+static unsigned char bd[4 * 1024 * 1024];
+
+static int close_testing;
+int max_poll_elements;
+
+static struct libwebsocket_context *context;
+enum demo_protocols {
+ /* always first */
+ PROTOCOL_HTTP = 0,
+
+ PROTOCOL_HDMICAP,
+
+ /* always last */
+ DEMO_PROTOCOL_COUNT
+};
+
+struct cea * get_cea(int idx)
+{
+ int n;
+
+ for (n = 0; n < ARRAY_SIZE(cea); n++)
+ if (cea[n].cea_index == idx)
+ return &cea[n];
+
+ return NULL;
+}
+
+#define LOCAL_RESOURCE_PATH "/usr/local/share/hdmicap"
+char *resource_path = LOCAL_RESOURCE_PATH;
+
+
+int get_data_stats(unsigned char *b, int len)
+{
+ int n, m, sam;
+ char s[32], cea = 0;
+
+ memset(shared->bcounts, 0, sizeof(shared->bcounts));
+
+ while (len >= 36) {
+ shared->bcounts[b[0]]++;
+
+ if (b[0] == 0x82) /* AVI IF */
+ cea = b[4 + 4] & 0x7f;
+
+ switch (b[0]) {
+ case 0:
+ break;
+ case 1:
+ //printf("ACR\n");
+ break;
+ case 2:
+#if 0
+ n = 0;
+ if (b[1] & 1)
+ n++;
+ if (b[1] & 2)
+ n++;
+ if (b[1] & 4)
+ n++;
+ if (b[1] & 8)
+ n++;
+ printf("Audio sample: %d x 24-bit stereo samples, layout %d\n",
+ n, (b[1] >> 4) & 1);
+ for (m = 4; m < (n * 8) + 4; m += 8) {
+ printf(" L: 0x%06x, R: 0x%06X\n",
+ (b[m + 2] << 16) | (b[m + 1] << 8) | b[m],
+ (b[m + 5] << 16) | (b[m + 4] << 8) | b[m + 3]);
+ #if 0
+ sam = (d[m + 2] << 8) | d[m + 1];
+ write(fd, &sam, 2);
+ sam = (d[m + 5] << 8) | d[m + 4];
+ write(fd, &sam, 2);
+ #endif
+ }
+#endif
+ break;
+ case 3:
+ //printf("General Control\n");
+ break;
+ case 4:
+ //printf("ACP\n");
+ break;
+ case 5:
+ //printf("ISRC1\n");
+ break;
+ case 6:
+ //printf("ISRC2\n");
+ break;
+ case 7:
+ //printf("1-bit audio\n");
+ break;
+ case 8:
+ //printf("DST audio\n");
+ break;
+ case 9:
+ //printf("HBR audio\n");
+ break;
+ case 10:
+ //printf("Gamut metadata\n");
+ break;
+ case 0x81:
+ //printf("Vendor-specific Infoframe (v%d, len %d)\n", h[1], h[2]);
+ break;
+ case 0x82:
+ #if 0
+ printf("AVI Infoframe (v%d, len %d)\n", h[1], h[2]);
+ {
+ static const char *fmt[] = {
+ "RGB", "YCbCr422", "YCbCr444", "future" };
+ static const char *bar[] = { "none", "V", "H", "H+V" };
+ static const char *scan[] = { "unk", "over/TV",
+ "under/Computer", "future" };
+ static const char *col[] = { "unk", "SMPTE170M/ITU601",
+ "ITU709", "future" };
+ static const char *asp[] = { "unk", "4:3", "16:9",
+ "future" };
+ static const char *afmt[] = {
+ "same as src", "4:3", "16:9", "14:9",
+ "reserved", "4:3/14:9",
+ "16:9/14:9", "reserved",
+ "same as asp", "4:3", "16:9", "14:9",
+ "c", "d", "e", "f", };
+ static const char *scaled[] = {
+ "no", "H", "V", "H+V" };
+
+ printf(" fmt: %s, act: %d, bars: %s, \n"
+ " scan: %s, col: %s, asp: %s, \n"
+ " active-fmt: %s, scaled: %s, \n"
+ " VIC: %d, rep: %d\n",
+ fmt[(d[1] >> 5) & 3],
+ !!(d[1] & 4),
+ bar[(d[1] >> 2) & 3],
+ scan[(d[1] & 3)],
+ col[(d[2] >> 6) & 3],
+ asp[(d[2] >> 4) & 3],
+ afmt[d[2] & 0xf],
+ scaled[d[3] & 3],
+ d[4] & 0x7f,
+ d[5] & 0xf
+ );
+ }
+ #endif
+ break;
+
+ case 0x83: /* Source Product Descriptor */
+ m = 0;
+ for (n = 5; n < 14; n++) {
+ if (n == 11)
+ n++;
+ shared->spd_vendor[m++] = b[n];
+ }
+ shared->spd_vendor[8] = '\0';
+ m = 0;
+ for (n = 14; n < 32; n++) {
+ if (n == 19 || n == 27)
+ n++;
+ shared->spd_product[m++] = b[n];
+ }
+ shared->spd_product[16] = '\0';
+ break;
+
+ case 0x84: /* audio infoframe */
+// printf("Audio Infoframe (v%d, len %d)\n", h[1], h[2]);
+ break;
+ case 0x85:
+// printf("MPEG Source Infoframe (v%d, len %d)\n", h[1], h[2]);
+ break;
+
+ default:
+ fprintf(stderr, "bad hdr 0x%x\n", b[0]);
+ break;
+ }
+#if 0
+ printf("hdr 0x%02x 0x%02x 0x%02x (pol 0x%02x)\n",
+ h[0], h[1], h[2], h[3]);
+
+ if ((b[0] & 0x80) && b[2] > 0x1b) {
+ fprintf(stderr, "length is wrong on infoframe\n");
+ return 1;
+ }
+
+ for (m = 0; m < 32; m += 8) {
+ printf(" ");
+ for (n = 0; n < 8; n++)
+ printf("%02x ", d[m + n]);
+ printf("\n");
+ }
+#endif
+ b += 36;
+ len -= 36;
+ }
+
+ shared->force_cea = cea;
+
+ return 0;
+}
+
+
+/*
+ * We take a strict whitelist approach to stop ../ attacks
+ */
+
+struct serveable {
+ const char *urlpath;
+ const char *mimetype;
+};
+
+struct per_session_data__http {
+ int fd;
+};
+
+const char * get_mimetype(const char *file)
+{
+ int n = strlen(file);
+
+ if (n < 5)
+ return NULL;
+
+ if (!strcmp(&file[n - 4], ".ico"))
+ return "image/x-icon";
+
+ if (!strcmp(&file[n - 4], ".png"))
+ return "image/png";
+
+ if (!strcmp(&file[n - 5], ".html"))
+ return "text/html";
+
+ return NULL;
+}
+
+/* this protocol server (always the first one) just knows how to do HTTP */
+
+static int callback_http(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason, void *user,
+ void *in, size_t len)
+{
+ char buf[256];
+ char leaf_path[1024];
+ char b64[64];
+ struct timeval tv;
+ int n, m;
+ unsigned char *p;
+ char *other_headers;
+ static unsigned char buffer[4096];
+ struct stat stat_buf;
+ struct per_session_data__http *pss =
+ (struct per_session_data__http *)user;
+ const char *mimetype;
+ unsigned char *end;
+ int status;
+
+ switch (reason) {
+ case LWS_CALLBACK_HTTP:
+
+ if (len < 1) {
+ libwebsockets_return_http_status(context, wsi,
+ HTTP_STATUS_BAD_REQUEST, NULL);
+ goto try_to_reuse;
+ }
+
+ /* this example server has no concept of directories */
+ if (strchr((const char *)in + 1, '/')) {
+ libwebsockets_return_http_status(context, wsi,
+ HTTP_STATUS_FORBIDDEN, NULL);
+ goto try_to_reuse;
+ }
+
+ /* if a legal POST URL, let it continue and accept data */
+ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
+ return 0;
+
+ if (!strcmp(in, "/dump.png")) {
+ strcpy(buf, "/tmp/dump.png");
+ goto found;
+ }
+
+ strcpy(buf, resource_path);
+ if (strcmp(in, "/")) {
+ if (*((const char *)in) != '/')
+ strcat(buf, "/");
+ strncat(buf, in, sizeof(buf) - strlen(resource_path));
+ } else /* default file to serve */
+ strcat(buf, "/hdmicap.html");
+
+found:
+ buf[sizeof(buf) - 1] = '\0';
+
+ /* refuse to serve files we don't understand */
+ mimetype = get_mimetype(buf);
+ if (!mimetype) {
+ lwsl_err("Unknown mimetype for %s\n", buf);
+ libwebsockets_return_http_status(context, wsi,
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL);
+ return -1;
+ }
+
+ /* demostrates how to set a cookie on / */
+
+ other_headers = NULL;
+ n = 0;
+ if (!strcmp((const char *)in, "/") &&
+ !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
+ /* this isn't very unguessable but it'll do for us */
+ gettimeofday(&tv, NULL);
+ n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
+ (unsigned int)tv.tv_sec,
+ (unsigned int)tv.tv_usec);
+
+ p = (unsigned char *)leaf_path;
+
+ if (lws_add_http_header_by_name(context, wsi,
+ (unsigned char *)"set-cookie:",
+ (unsigned char *)b64, n, &p,
+ (unsigned char *)leaf_path + sizeof(leaf_path)))
+ return 1;
+ n = (char *)p - leaf_path;
+ other_headers = leaf_path;
+ }
+
+ n = libwebsockets_serve_http_file(context, wsi, buf,
+ mimetype, other_headers, n);
+ if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi)))
+ return -1; /* error or can't reuse connection: close the socket */
+
+ /*
+ * notice that the sending of the file completes asynchronously,
+ * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when
+ * it's done
+ */
+
+ break;
+
+ case LWS_CALLBACK_HTTP_BODY:
+ strncpy(buf, in, 20);
+ buf[20] = '\0';
+ if (len < 20)
+ buf[len] = '\0';
+
+ lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n",
+ (const char *)buf, (int)len);
+
+ break;
+
+ case LWS_CALLBACK_HTTP_BODY_COMPLETION:
+ lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n");
+ /* the whole of the sent body arrived, close or reuse the connection */
+ libwebsockets_return_http_status(context, wsi,
+ HTTP_STATUS_OK, NULL);
+ goto try_to_reuse;
+
+ case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+// lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n");
+ /* kill the connection after we sent one file */
+ goto try_to_reuse;
+
+ case LWS_CALLBACK_HTTP_WRITEABLE:
+ /*
+ * we can send more of whatever it is we were sending
+ */
+ do {
+ /* we'd like the send this much */
+ n = sizeof(buffer) - LWS_SEND_BUFFER_PRE_PADDING;
+
+ /* but if the peer told us he wants less, we can adapt */
+ m = lws_get_peer_write_allowance(wsi);
+
+ /* -1 means not using a protocol that has this info */
+ if (m == 0)
+ /* right now, peer can't handle anything */
+ goto later;
+
+ if (m != -1 && m < n)
+ /* he couldn't handle that much */
+ n = m;
+
+ n = read(pss->fd, buffer + LWS_SEND_BUFFER_PRE_PADDING,
+ n);
+ /* problem reading, close conn */
+ if (n < 0)
+ goto bail;
+ /* sent it all, close conn */
+ if (n == 0)
+ goto flush_bail;
+ /*
+ * To support HTTP2, must take care about preamble space
+ *
+ * identification of when we send the last payload frame
+ * is handled by the library itself if you sent a
+ * content-length header
+ */
+ m = libwebsocket_write(wsi,
+ buffer + LWS_SEND_BUFFER_PRE_PADDING,
+ n, LWS_WRITE_HTTP);
+ if (m < 0)
+ /* write failed, close conn */
+ goto bail;
+
+ /*
+ * http2 won't do this
+ */
+ if (m != n)
+ /* partial write, adjust */
+ if (lseek(pss->fd, m - n, SEEK_CUR) < 0)
+ goto bail;
+
+ if (m) /* while still active, extend timeout */
+ libwebsocket_set_timeout(wsi,
+ PENDING_TIMEOUT_HTTP_CONTENT, 5);
+
+ /* if we have indigestion, let him clear it before eating more */
+ if (lws_partial_buffered(wsi))
+ break;
+
+ waitpid(-1, &status, WNOHANG);
+
+ } while (!lws_send_pipe_choked(wsi));
+
+later:
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+flush_bail:
+ /* true if still partial pending */
+ if (lws_partial_buffered(wsi)) {
+ libwebsocket_callback_on_writable(context, wsi);
+ break;
+ }
+ close(pss->fd);
+ goto try_to_reuse;
+
+bail:
+ close(pss->fd);
+ return -1;
+
+ /*
+ * callback for confirming to continue with client IP appear in
+ * protocol 0 callback since no websocket protocol has been agreed
+ * yet. You can just ignore this if you won't filter on client IP
+ * since the default uhandled callback return is 0 meaning let the
+ * connection continue.
+ */
+
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+
+ /* if we returned non-zero from here, we kill the connection */
+ break;
+
+ case LWS_CALLBACK_GET_THREAD_ID:
+ /*
+ * if you will call "libwebsocket_callback_on_writable"
+ * from a different thread, return the caller thread ID
+ * here so lws can use this information to work out if it
+ * should signal the poll() loop to exit and restart early
+ */
+
+ /* return pthread_getthreadid_np(); */
+
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+try_to_reuse:
+ if (lws_http_transaction_completed(wsi))
+ return -1;
+
+ return 0;
+}
+
+struct per_session_data__hdmicap {
+ int number;
+};
+
+static int
+callback_hdmicap(struct libwebsocket_context *context,
+ struct libwebsocket *wsi,
+ enum libwebsocket_callback_reasons reason,
+ void *user, void *in, size_t len)
+{
+ int n, m;
+ unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 8192 +
+ LWS_SEND_BUFFER_POST_PADDING];
+ unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING], *pp = p;
+ struct per_session_data__hdmicap *pss = user;
+ struct cea *cea_told;
+ int fd;
+
+ switch (reason) {
+
+ case LWS_CALLBACK_ESTABLISHED:
+ lwsl_info("%s: LWS_CALLBACK_ESTABLISHED\n", __func__);
+ pss->number = 0;
+ break;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+ pp += sprintf(pp, "{ \"data_islands\":{"
+ " \"stats\": {"
+ " \"Null\":\"%d\",\n"
+ " \"ACR\":\"%d\",\n"
+ " \"Audio\":\"%d\",\n"
+ " \"Gencon\":\"%d\",\n"
+ " \"ACP\":\"%d\",\n"
+ " \"ISRC1\":\"%d\",\n"
+ " \"ISRC2\":\"%d\",\n"
+ " \"onebit\":\"%d\",\n"
+ " \"DST\":\"%d\",\n"
+ " \"HBR\":\"%d\",\n"
+ " \"Gamut\":\"%d\",\n"
+ " \"VendorIF\":\"%d\",\n"
+ " \"AVIIF\":\"%d\",\n"
+ " \"SPDIF\":\"%d\",\n"
+ " \"AudioIF\":\"%d\",\n"
+ " \"MPEGIF\":\"%d\"\n"
+ "},"
+ " \"spd\": {"
+ " \"spd_name\":\"%s.%s\""
+ "},\n"
+ " \"avi\": {"
+ " \"cea\":\"%d\"",
+ shared->bcounts[0],
+ shared->bcounts[1],
+ shared->bcounts[2],
+ shared->bcounts[3],
+ shared->bcounts[4],
+ shared->bcounts[5],
+ shared->bcounts[6],
+ shared->bcounts[7],
+ shared->bcounts[8],
+ shared->bcounts[9],
+ shared->bcounts[10],
+ shared->bcounts[0x81],
+ shared->bcounts[0x82],
+ shared->bcounts[0x83],
+ shared->bcounts[0x84],
+ shared->bcounts[0x85],
+ shared->spd_vendor, shared->spd_product,
+ shared->force_cea);
+ if (shared->force_cea) {
+ cea_told = get_cea(shared->force_cea);
+ if (cea_told) {
+ int vrate = (((long long)cea_told->px_clock) * 1000ll) /
+ ((cea_told->hact + cea_told->hblank) * (cea_told->vact + cea_told->vblank));
+ pp += sprintf(pp, ","
+ " \"hact\":\"%d\",\n"
+ " \"vact\":\"%d\",\n"
+ " \"hpol\":\"%c\",\n"
+ " \"hrate_kHz\":\"%d.%03d\",\n"
+ " \"vpol\":\"%c\",\n"
+ " \"vrate_Hz\":\"%d.%03d\",\n"
+ " \"htot\":\"%d\",\n"
+ " \"hbl\":\"%d\",\n"
+ " \"hsa\":\"%d\",\n"
+ " \"hfp\":\"%d\",\n"
+ " \"hbp\":\"%d\",\n"
+ " \"vtot\":\"%d\",\n"
+ " \"vbl\":\"%d\",\n"
+ " \"vsa\":\"%d\",\n"
+ " \"vfp\":\"%d\",\n"
+ " \"vbp\":\"%d\",\n"
+ " \"pxclk_MHz\":\"%d.%03d\",\n"
+ " \"px_in_frame\":\"%d\"\n",
+ cea_told->hact,
+ cea_told->vact,
+ cea_told->hpol,
+ (cea_told->px_clock / (cea_told->hact + cea_told->hblank)) / 1000,
+ (cea_told->px_clock / (cea_told->hact + cea_told->hblank)) % 1000,
+ cea_told->vpol,
+ vrate / 1000, vrate % 1000,
+ cea_told->hact + cea_told->hblank,
+ cea_told->hblank,
+ cea_told->hsa,
+ cea_told->hfp,
+ cea_told->hblank - cea_told->hsa - cea_told->hfp,
+ cea_told->vact + cea_told->vblank,
+ cea_told->vblank,
+ cea_told->vsa,
+ cea_told->vfp,
+ cea_told->vblank - cea_told->vsa - cea_told->vfp,
+ cea_told->px_clock / 1000000, (cea_told->px_clock % 1000000) / 1000,
+ (cea_told->hact + cea_told->hblank) * (cea_told->vact + cea_told->vblank)
+ );
+ }
+ }
+ pp += sprintf(pp,"}},\"metadata\":");
+ if (shared->set == shared->using)
+ break;
+
+ shared->using = shared->set;
+ if (!shared->len[shared->using]) {
+ printf("no len\n");
+ break;
+ }
+ memcpy(pp, shared->buf[shared->using], shared->len[shared->using]);
+ pp += shared->len[shared->using];
+
+ pp += sprintf(pp, ",\"png_frame\":\"%d\"}", shared->png_frame);
+ m = libwebsocket_write(wsi, p, pp - p, LWS_WRITE_TEXT);
+ if (m < n) {
+ lwsl_err("ERROR %d / %d writing to di socket\n", m, pp - p);
+ return -1;
+ }
+ break;
+
+ case LWS_CALLBACK_RECEIVE:
+// fprintf(stderr, "rx %d\n", (int)len);
+ if (len < 6)
+ break;
+ if (strcmp((const char *)in, "reset\n") == 0)
+ pss->number = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/* list of supported protocols and callbacks */
+
+static struct libwebsocket_protocols protocols[] = {
+ /* first protocol must always be HTTP handler */
+
+ {
+ "http-only", /* name */
+ callback_http, /* callback */
+ sizeof (struct per_session_data__http), /* per_session_data_size */
+ 0, /* max frame size / rx buffer */
+ },
+ {
+ "linaro.hdmicap",
+ callback_hdmicap,
+ sizeof(struct per_session_data__hdmicap),
+ 10,
+ },
+ { NULL, NULL, 0, 0 } /* terminator */
+};
+
+
+int collect_info(void)
+{
+ int action = 0;
+ int fd;
+ char cmdline[256];
+ const char *p;
+ int v, n;
+
+ while (!shared->force_exit) {
+ switch (action) {
+ case 0:
+ v = shared->vtot;
+ if (!shared->htot || !shared->vtot || !shared->pll_locked || !shared->syncs_detected || !shared->sanity)
+ break;
+ /* convert can't handle incomplete line */
+ if (shared->onset)
+ v--;
+ if (shared->lock_framebuffer)
+ break;
+ shared->lock_framebuffer = 1;
+ fd = open(SYSFS"/frames", O_RDWR);
+ if (fd < 0)
+ goto unlock;
+ n = write(fd, "0\n", 2);
+ close(fd);
+ if (n < 0)
+ goto unlock;
+
+ fd = open(SYSFS"/grab", O_RDWR);
+ if (fd < 0)
+ goto unlock;
+
+ n = write(fd, "1\n", 2);
+ close(fd);
+ if (n < 0)
+ goto unlock;
+
+ sprintf(cmdline, "/usr/bin/convert -size %dx%d -quality 0 -depth 8 rgba:%s /tmp/dump1.png",
+ shared->htot, v, DEBUGFS);
+ if (!fork()) {
+ system(cmdline);
+ unlink("/tmp/dump.png");
+ rename("/tmp/dump1.png", "/tmp/dump.png");
+ shared->png_frame++;
+ shared->lock_framebuffer = -1;
+ exit(0);
+ }
+ break;
+
+ case 1:
+ if (shared->lock_framebuffer > 0 || !shared->pll_locked || !shared->syncs_detected || !shared->sanity)
+ break;
+ if (shared->lock_framebuffer < 0)
+ shared->lock_framebuffer++;
+ else
+ shared->lock_framebuffer = 1;
+ fd = open(SYSFS"/frames", O_RDWR);
+ if (fd < 0)
+ goto unlock;
+ n = write(fd, "0\n", 2);
+ close(fd);
+ if (n < 0)
+ goto unlock;
+
+ fd = open(SYSFS"/grab_bindata", O_RDWR);
+ if (fd < 0)
+ goto unlock;
+ n = write(fd, "1\n", 2);
+ close(fd);
+ if (n < 0)
+ goto unlock;
+
+ fd = open(DEBUGFS, O_RDONLY);
+ if (fd < 0)
+ goto unlock;
+
+ /* align to the first 0x00000000 and skip */
+ bd[0] = 1;
+ while (*((int *)&bd[0]) && n >= 0)
+ n = read(fd, bd, 4);
+ if (n < 0)
+ goto unlock;
+
+ n = read(fd, bd, sizeof bd);
+ close(fd);
+ if (n < 0)
+ goto unlock;
+
+ get_data_stats(bd, n);
+unlock:
+ shared->lock_framebuffer = 0;
+ break;
+ case 2:
+ fd = open(SYSFS"/state", O_RDONLY);
+ shared->len[shared->using ^ 1] = read(fd, shared->buf[shared->using ^ 1], sizeof(shared->buf[0]) - 1);
+ close(fd);
+ if (shared->len[shared->using ^ 1] < 0) {
+ shared->len[shared->using ^ 1] = 0;
+ break;
+ }
+
+ shared->buf[shared->using ^ 1][shared->len[shared->using ^ 1]] = '\0';
+ p = strstr(shared->buf[shared->using ^ 1], "\"htot\":\"");
+ if (p)
+ shared->htot = atoi(&p[8]);
+ else
+ shared->htot = 0;
+ p = strstr(shared->buf[shared->using ^ 1], "\"vtot\":\"");
+ if (p)
+ shared->vtot = atoi(&p[8]);
+ else
+ shared->vtot = 0;
+ p = strstr(shared->buf[shared->using ^ 1], "\"vsync_onset_hpx\":\"");
+ if (p)
+ shared->onset = atoi(&p[19]);
+ else
+ shared->onset = 0;
+ p = strstr(shared->buf[shared->using ^ 1], "\"pll_locked\":\"");
+ if (p)
+ shared->pll_locked = atoi(&p[14]);
+ else
+ shared->pll_locked = 0;
+ p = strstr(shared->buf[shared->using ^ 1], "\"syncs_detected\":\"");
+ if (p)
+ shared->syncs_detected = atoi(&p[18]);
+ else
+ shared->syncs_detected = 0;
+ p = strstr(shared->buf[shared->using ^ 1], "\"sanity\":\"");
+ if (p)
+ shared->sanity = atoi(&p[10]);
+ else
+ shared->sanity = 0;
+ shared->set = shared->using ^ 1;
+ break;
+ }
+ action = (action + 1) % 3;
+ }
+ printf("exiting\n");
+ return 0;
+}
+
+void sighandler(int sig)
+{
+ shared->force_exit = 1;
+ libwebsocket_cancel_service(context);
+}
+
+static struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "debug", required_argument, NULL, 'd' },
+ { "port", required_argument, NULL, 'p' },
+ { "ssl", no_argument, NULL, 's' },
+ { "allow-non-ssl", no_argument, NULL, 'a' },
+ { "interface", required_argument, NULL, 'i' },
+ { "closetest", no_argument, NULL, 'c' },
+ { "libev", no_argument, NULL, 'e' },
+ #ifndef LWS_NO_DAEMONIZE
+ { "daemonize", no_argument, NULL, 'D' },
+#endif
+ { "resource_path", required_argument, NULL, 'r' },
+ { NULL, 0, 0, 0 }
+};
+
+int main(int argc, char **argv)
+{
+ char cert_path[1024];
+ char key_path[1024];
+ int n = 0;
+ int use_ssl = 0;
+ int opts = 0;
+ char interface_name[128] = "";
+ const char *iface = NULL;
+#ifndef WIN32
+ int syslog_options = LOG_PID | LOG_PERROR;
+#endif
+ unsigned int ms, oldms = 0;
+ struct lws_context_creation_info info;
+
+ int debug_level = 1;
+#ifndef LWS_NO_DAEMONIZE
+ int daemonize = 0;
+#endif
+
+ memset(&info, 0, sizeof info);
+ info.port = 80;
+
+ while (n >= 0) {
+ n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
+ if (n < 0)
+ continue;
+ switch (n) {
+ case 'e':
+ opts |= LWS_SERVER_OPTION_LIBEV;
+ break;
+#ifndef LWS_NO_DAEMONIZE
+ case 'D':
+ daemonize = 1;
+ #ifndef WIN32
+ syslog_options &= ~LOG_PERROR;
+ #endif
+ break;
+#endif
+ case 'd':
+ debug_level = atoi(optarg);
+ break;
+ case 's':
+ use_ssl = 1;
+ break;
+ case 'a':
+ opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
+ break;
+ case 'p':
+ info.port = atoi(optarg);
+ break;
+ case 'i':
+ strncpy(interface_name, optarg, sizeof interface_name);
+ interface_name[(sizeof interface_name) - 1] = '\0';
+ iface = interface_name;
+ break;
+ case 'c':
+ close_testing = 1;
+ fprintf(stderr, " Close testing mode -- closes on "
+ "client after 50 dumb increments"
+ "and suppresses lws_mirror spam\n");
+ break;
+ case 'r':
+ resource_path = optarg;
+ printf("Setting resource path to \"%s\"\n", resource_path);
+ break;
+ case 'h':
+ fprintf(stderr, "Usage: test-server "
+ "[--port=<p>] [--ssl] "
+ "[-d <log bitfield>] "
+ "[--resource_path <path>]\n");
+ exit(1);
+ }
+ }
+
+#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
+ /*
+ * normally lock path would be /var/lock/lwsts or similar, to
+ * simplify getting started without having to take care about
+ * permissions or running as root, set to /tmp/.lwsts-lock
+ */
+ if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
+ fprintf(stderr, "Failed to daemonize\n");
+ return 1;
+ }
+#endif
+
+ signal(SIGINT, sighandler);
+
+#ifndef WIN32
+ /* we will only try to log things according to our debug_level */
+ setlogmask(LOG_UPTO (LOG_DEBUG));
+ openlog("lwsts", syslog_options, LOG_DAEMON);
+#endif
+
+ /* tell the library what debug level to emit and to send it to syslog */
+ lws_set_log_level(debug_level, lwsl_emit_syslog);
+
+ lwsl_notice("hdmicap - "
+ "(C) Copyright 2010-2015 Andy Green <andy@warmcat.com> - "
+ "licensed under LGPL2.1\n");
+
+ printf("Using resource path \"%s\"\n", resource_path);
+
+ info.iface = iface;
+ info.protocols = protocols;
+#ifndef LWS_NO_EXTENSIONS
+ info.extensions = libwebsocket_get_internal_extensions();
+#endif
+ if (!use_ssl) {
+ info.ssl_cert_filepath = NULL;
+ info.ssl_private_key_filepath = NULL;
+ } else {
+ if (strlen(resource_path) > sizeof(cert_path) - 32) {
+ lwsl_err("resource path too long\n");
+ return -1;
+ }
+ sprintf(cert_path, "%s/libwebsockets-test-server.pem",
+ resource_path);
+ if (strlen(resource_path) > sizeof(key_path) - 32) {
+ lwsl_err("resource path too long\n");
+ return -1;
+ }
+ sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
+ resource_path);
+
+ info.ssl_cert_filepath = cert_path;
+ info.ssl_private_key_filepath = key_path;
+ }
+
+ /* nobody */
+ info.gid = 99;
+ info.uid = 99;
+ info.options = opts;
+
+ context = libwebsocket_create_context(&info);
+ if (context == NULL) {
+ lwsl_err("libwebsocket init failed\n");
+ return -1;
+ }
+
+ n = open("/dev/zero", O_RDWR);
+ shared = mmap(NULL, sizeof (struct shared),
+ PROT_READ | PROT_WRITE, MAP_SHARED, n, 0);
+ close(n);
+
+ if (!fork()) {
+ collect_info();
+ }
+
+ n = 0;
+ while (n >= 0 && !shared->force_exit) {
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if ((ms - oldms) > 50) {
+ libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_HDMICAP]);
+ oldms = ms;
+ }
+
+ n = libwebsocket_service(context, 50);
+ }
+
+ libwebsocket_context_destroy(context);
+
+ lwsl_notice("libwebsockets-test-server exited cleanly\n");
+
+ closelog();
+
+ return 0;
+}
diff --git a/hdmicap.html b/hdmicap.html
new file mode 100644
index 0000000..11c60b8
--- /dev/null
+++ b/hdmicap.html
@@ -0,0 +1,327 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset=utf-8 http-equiv="Content-Language" content="en"/>
+ <title>hdmicap</title>
+<style type="text/css">
+ td { font-size:8pt; font: Arial; font-weight:normal; text-align:center; color:#404040; text-align:center; background:#efefff; padding:1px; -webkit-border-radius:1px; -moz-border-radius:1px; border-radius:1px;white-space:nowrap;}
+ td.dump { font-size:8pt; font: Arial; font-weight:normal; text-align:center; color:#404040; vertical-align:top; background:#efefff; padding:1px; -webkit-border-radius:1px; -moz-border-radius:1px; border-radius:1px;}
+
+ div.title { font-size:12pt; font: Arial; font-weight:normal; text-align:center; color:#000000; background:#d8d8e8; }
+
+ div.capdata { font-size:10pt; font: Arial; font-weight:normal; text-align:center; color:#000000; vertical-align:middle; text-align:center; background:#e0e0f0; padding:1px; -webkit-border-radius:1px; -moz-border-radius:1px; border-radius:1px;}
+ div.capdata_cea { font-size:10pt; font: Arial; font-weight:normal; text-align:center; color:#000000; vertical-align:middle; text-align:center; background:#e0ffe0; padding:1px; -webkit-border-radius:1px; -moz-border-radius:1px; border-radius:1px;}
+ .browser { font-size:18pt; font: Arial; font-weight:normal; text-align:center; color:#ffff00; vertical-align:middle; text-align:center; background:#d0b070; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+ .tr-enabled { font-size:10pt; font: Arial; font-weight:normal; text-align:center; color:#000000; vertical-align:middle; text-align:center; background:#d0d0ff; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+ .tr-disabled { font-size:10pt; font: Arial; font-weight:normal; text-align:center; color:#808080; vertical-align:middle; text-align:center; background:#c0c0c0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px;}
+
+ .group2 { width:600px; vertical-align:middle; text-align:center; background:#f0f0e0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+ .explain { vertical-align:middle; text-align:center; background:#f0f0c0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; color:#404000; }
+ .content { vertical-align:top; text-align:center; background:#fffff0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+ .canvas { vertical-align:top; text-align:center; background:#efefd0; padding:12px; -webkit-border-radius:10px; -moz-border-radius:10px; border-radius:10px; }
+</style>
+</head>
+<body>
+<header></header>
+<article>
+<table><tr style="vertical-align:top"><td style="vertical-align:top">
+ <table width="100%" style="vertical-align:top"><tr style="vertical-align:top"><td style="vertical-align:top">
+ <table style="vertical-align:top"><tr style="vertical-align:top"><td style="vertical-align:top"><a href="http://libwebsockets.org">
+ <img src="/hdmicap.png"></a><td></td>
+ <tr><td class="td.dump">
+ <table>
+ <tr><td colspan="4" align="center"><div class="title">HDMI clock<div></td></tr>
+ <tr><td width="99%">PLL lock</td>
+ <td colspan="2" tip="Whether PLL could lock to perform pixel clock recovery"><div id="pll_locked" class="capdata"></div></td><td></td></tr>
+ <tr id="cea-info" class="tr-enabled" tip="VIC code reported in AVI infoframe"><td>AVI VIC</td>
+ <td></td><td><div id="vic_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="clock-info" class="tr-enabled" tip="HDMI pixel clock rate"><td>Clock rate</td>
+ <td><div id="clk_rate" class="capdata"></div></td><td><div id="clk_rate_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="dlq"><td colspan="4" align="center"><div class="title">Data lane quality<div></td></tr>
+ <tr id="lane-info" class="tr-enabled" tip="ctrl symbol detection probability, numbers depend on mode blanking content, ch0 may have slightly smaller results."><td>Ctrl symbols</td><td colspan="2" align="center">
+ <table>
+ <tr><td>ch0</td><td>ch1</td><td>ch2</td></tr>
+ <tr><td><div id="ctrl_ch0_pc" class="capdata"></div></td>
+ <td><div id="ctrl_ch1_pc" class="capdata"></div></td>
+ <td><div id="ctrl_ch2_pc" class="capdata"></div></td></tr>
+ </table>
+ </td><td></td></tr>
+
+ <tr id="vt"><td colspan="4" align="center"><div class="title">Video Timing<div></td></tr>
+ <tr id="vsl"tip="HSYNC and VSYNC are detected and polarity confirmed"><td>Video Sync</td>
+ <td id="sync-info"><div id="video_syncs" class="capdata"></div></td><td id="sync-info_cea"><div id="video_syncs_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="ar" tip="HACT x VACT, active video region"><td>Active region</td><td><div id="active_details" class="capdata"></div></td><td><div id="active_details_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="hsr"><td>HSYNC rate</td><td><div id="hsync_rate" class="capdata"></div></td><td><div id="hsync_rate_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="ot" tip="Frame refresh rate"><td>VSYNC rate</td><td><div id="vsync_rate" class="capdata"></div></td><td><div id="vsync_rate_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="tfs" tip="HTOT x VTOT, Active and blanking area combined"><td>Total frame</td><td><div id="total_details" class="capdata"></div></td><td><div id="total_details_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="apv" tip="Measured number of pixel-times in a frame"><td style="padding-left:10px;">Px between VSYNCs</td><td style="padding-left:10px;"><div id="total_px_details" class="capdata"></div></td><td style="padding-left:10px;"><div id="total_px_details_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="hif" tip="The number of HTOT periods should exactly match VTOT"><td style="padding-left:20px;">Frame HTOT</td><td style="padding-left:10px;"><div id="total_px1_details" class="capdata"></div></td><td style="padding-left:10px;"><div id="total_px1_details_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="vhp" tip="VSYNC should come on the last line at 0 offset from HSYNC (ie, at HTOT + 1 on the last line)"><td style="padding-left:20px;">VSYNC HPOS</td><td style="padding-left:10px;"><div id="vsync_hpos" class="capdata"></div></td><td style="padding-left:10px;"><div id="vsync_hpos_cea" class="capdata_cea"></div></td><td></td></tr>
+
+ <tr id="thb" tip="HBLANK = HFP + HSA + HBP, pixels on a line without active data"><td>HBLANK</td><td><div id="hblank" class="capdata"></div></td><td><div id="hblank_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="thfp" tip="HFP, Horizontal Front Porch (aka Right Margin) timing"><td style="padding-left:10px;">HFP</td><td style="padding-left:10px;"><div id="hfp" class="capdata"></div></td><td style="padding-left:10px;"><div id="hfp_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="thsa" tip="HSA, HSYNC Active timing"><td style="padding-left:10px;">HSA</td><td style="padding-left:10px;"><div id="hsa" class="capdata"></div></td><td style="padding-left:10px;"><div id="hsa_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="thbp" tip="HBP, Horizontal Back Porch (aka Left Margin) timing"><td style="padding-left:10px;">HBP</td><td style="padding-left:10px;"><div id="hbp" class="capdata"></div></td><td style="padding-left:10px;"><div id="hbp_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="tvb" tip="VBLANK = VFP + VSA + VBP, lines in a frame without active data"><td>Total V blanking</td><td><div id="vblank" class="capdata"></div></td><td><div id="vblank_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="tvfp" tip="VFP, Vertical Front Porch (aka Bottom Margin) lines"><td style="padding-left:10px;">VFP</td><td style="padding-left:10px;"><div id="vfp" class="capdata"></div></td><td style="padding-left:10px;"><div id="vfp_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="tvsa" tip="VSA, VSYNC Active lines"><td style="padding-left:10px;">VSA</td><td style="padding-left:10px;"><div id="vsa" class="capdata"></div></td><td style="padding-left:10px;"><div id="vsa_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="tvbp" tip="VBP, Vertical Back Porch (aka Top Margin) lines"><td style="padding-left:10px;">VBP</td><td style="padding-left:10px;"><div id="vbp" class="capdata"></div></td><td style="padding-left:10px;"><div id="vbp_cea" class="capdata_cea"></div></td><td></td></tr>
+ <tr id="md"><td colspan="4" align="center"><div class="title">Frame Metadata<div></td></tr>
+ <tr id="dif" tip="Blanking px used for data in active region"><td>Data in active</td>
+ <td id="dia-info"><div id="data_active" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="div" tip="VBLANK px used for data"><td>Data in VBLANK</td>
+ <td id="div-info"><div id="data_vblank" class="capdata"></div></td><td></td><td></td></tr>
+
+ <tr id="di0" tip="Data island types per frame"><td>NULL data pkts</td>
+ <td id="tdd0"><div id="ddi0" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di1" tip="Data island types per frame"><td>ACR pkts</td>
+ <td id="tdd1"><div id="ddi1" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di2" tip="Data island types per frame"><td>Audio pkts</td>
+ <td id="tdd2"><div id="ddi2" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di3" tip="Data island types per frame"><td>General Control pkts</td>
+ <td id="tdd3"><div id="ddi3" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di4" tip="Data island types per frame"><td>ACP</td>
+ <td id="tdd4"><div id="ddi4" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di5" tip="Data island types per frame"><td>ISRC1</td>
+ <td id="tdd5"><div id="ddi5" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di6" tip="Data island types per frame"><td>ISRC2</td>
+ <td id="tdd6"><div id="ddi6" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di7" tip="Data island types per frame"><td>1-bit Audio</td>
+ <td id="tdd7"><div id="ddi7" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di8" tip="Data island types per frame"><td>DST Audio</td>
+ <td id="tdd8"><div id="ddi8" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di9" tip="Data island types per frame"><td>HBR Audio</td>
+ <td id="tdd9"><div id="ddi9" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di10" tip="Data island types per frame"><td>Gamut</td>
+ <td id="tdd10"><div id="ddi10" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di11" tip="Data island types per frame"><td>Vendor IF</td>
+ <td id="tdd11"><div id="ddi11" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di12" tip="Data island types per frame"><td>AVI IF</td>
+ <td id="tdd12"><div id="ddi12" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di13" tip="Data island types per frame"><td>SPD IF</td>
+ <td id="tdd13"><div id="ddi13" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di14" tip="Data island types per frame"><td>Audio IF</td>
+ <td id="tdd14"><div id="ddi14" class="capdata"></div></td><td></td><td></td></tr>
+ <tr id="di15" tip="Data island types per frame"><td>MPEG SRC IF</td>
+ <td id="tdd15"><div id="ddi15" class="capdata"></div></td><td></td><td></td></tr>
+ </table>
+ </td></tr>
+ </table>
+ </td></tr>
+ </table>
+ </td><td id="show" class="td.dump" width="99%" style="display:none"><div id="dump"></div><div id="capture"><img id="imgtag" style="object-fit:none;max-device-width=864;" src="/dump.png"></div></td></tr>
+</table>
+</article>
+
+<script>
+
+
+ var tds_pll = [ "cea-info", "clock-info", "dlq", "lane-info" ];
+ var tds_syncs = [ "vt", "vsl" ];
+ var tds_sane = [ "ar", "hsr", "ot", "tfs", "apv", "hif", "vhp", "thb", "thfp", "thsa", "thbp",
+ "tvb", "tvfp", "tvsa", "tvbp", "md", "dif", "div", "show",
+ "di0", "di1", "di2", "di3", "di4", "di5", "di6", "di7", "di8", "di9", "di10", "di11", "di12",
+ "di13", "di14", "di15"];
+
+ var tds_cea = [ "vic_cea", "clk_rate_cea", "video_syncs_cea",
+ "active_details_cea", "hsync_rate_cea",
+ "vsync_rate_cea", "total_details_cea",
+ "total_px_details_cea", "total_px1_details_cea",
+ "vsync_hpos_cea", "hblank_cea", "hbp_cea", "hsa_cea",
+ "hfp_cea", "vblank_cea", "vbp_cea", "vsa_cea",
+ "vfp_cea" ];
+
+ var tds_compare = [ "clk_rate", "video_syncs", "active_details",
+ "hsync_rate", "vsync_rate", "total_details",
+ "total_px_details", "total_px1_details",
+ "vsync_hpos", "hblank", "hbp", "hsa", "hfp",
+ "vblank", "vbp", "vsa", "vfp" ];
+
+var pos = 0;
+
+function get_appropriate_ws_url()
+{
+ var pcol;
+ var u = document.URL;
+
+ if (u.substring(0, 5) == "https") {
+ pcol = "wss://";
+ u = u.substr(8);
+ } else {
+ pcol = "ws://";
+ if (u.substring(0, 4) == "http")
+ u = u.substr(7);
+ }
+
+ u = u.split('/');
+
+ /* + "/xxx" bit is for IE10 workaround */
+ return pcol + u[0] + "/xxx";
+}
+
+var connection = 0;
+var obj;
+var png_frame;
+
+function conn_retry()
+{
+ var r1, r2, r3, x, y, chan, z, block, channel_data = 0, tpt, tot;
+
+ if (connection)
+ return;
+
+ socket = new WebSocket(get_appropriate_ws_url(), "linaro.hdmicap");
+
+ try {
+ socket.onopen = function() {
+ connection = 1;
+ }
+
+ socket.onmessage = function got_packet(msg) {
+ //document.getElementById("dump").textContent = msg.data;
+ obj = JSON.parse(msg.data);
+ if (obj.metadata.pll_locked == 0){
+ document.getElementById("pll_locked").textContent = "No";
+ for (n = 0; n < tds_pll.length; n++)
+ document.getElementById(tds_pll[n]).style.display = "none";
+ for (n = 0; n < tds_syncs.length; n++)
+ document.getElementById(tds_syncs[n]).style.display = "none";
+ for (n = 0; n < tds_sane.length; n++)
+ document.getElementById(tds_sane[n]).style.display = "none";
+ return;
+ }
+ document.getElementById("pll_locked").textContent = "Yes";
+ document.getElementById("clk_rate").textContent = obj.metadata.pxclk_MHz + "MHz";
+ tot = Number(obj.metadata.ctrl_ch0_pc) + Number(obj.metadata.ctrl_ch1_pc) + Number(obj.metadata.ctrl_ch2_pc);
+ document.getElementById("ctrl_ch0_pc").textContent = ((Number(obj.metadata.ctrl_ch0_pc) * 100) / tot).toPrecision(2) + "%";
+ document.getElementById("ctrl_ch1_pc").textContent = ((Number(obj.metadata.ctrl_ch1_pc) * 100) / tot).toPrecision(2) + "%";
+ document.getElementById("ctrl_ch2_pc").textContent = ((Number(obj.metadata.ctrl_ch2_pc) * 100) / tot).toPrecision(2) + "%";
+
+ for (n = 0; n < tds_pll.length; n++)
+ document.getElementById(tds_pll[n]).style.display = "table-row";
+ if (obj.metadata.sync_detected == 0){
+ document.getElementById("video_syncs").textContent = "No";
+ for (n = 0; n < tds_syncs.length; n++)
+ document.getElementById(tds_syncs[n]).style.display = "none";
+ for (n = 0; n < tds_sane.length; n++)
+ document.getElementById(tds_sane[n]).style.display = "none";
+ document.getElementById("imgtag").style.display = "none";
+ return;
+ }
+
+ document.getElementById("video_syncs").textContent =
+ "H"+obj.metadata.hpol+", V"+obj.metadata.vpol;
+ for (n = 0; n < tds_syncs.length; n++)
+ document.getElementById(tds_syncs[n]).style.display = "table-row";
+
+ if (obj.metadata.sanity == 0) {
+ for (n = 0; n < tds_sane.length; n++)
+ document.getElementById(tds_sane[n]).style.display = "none";
+ document.getElementById("imgtag").style.display = "none";
+ return;
+ }
+ for (n = 0; n < tds_sane.length; n++)
+ document.getElementById(tds_sane[n]).style.display = "table-row";
+
+ document.getElementById("active_details").innerHTML = "<b>"+obj.metadata.hact + "x" +
+ obj.metadata.vact + "</b>";
+ document.getElementById("hsync_rate").textContent = obj.metadata.hrate_kHz + "kHz";
+ document.getElementById("vsync_rate").textContent = obj.metadata.vrate_Hz + "Hz";
+ document.getElementById("total_details").textContent = obj.metadata.htot + "x" +
+ obj.metadata.vtot;
+ document.getElementById("total_px_details").textContent = obj.metadata.px_in_frame;
+ document.getElementById("total_px1_details").textContent = obj.metadata.htot_in_frame;
+ document.getElementById("vsync_hpos").textContent = "+" + obj.metadata.vsync_onset_hpx + " px";
+ document.getElementById("hblank").textContent = obj.metadata.hbl;
+ document.getElementById("hbp").textContent = obj.metadata.hbp;
+ document.getElementById("hsa").textContent = obj.metadata.hsa;
+ document.getElementById("hfp").textContent = obj.metadata.hfp;
+
+ document.getElementById("vblank").textContent = obj.metadata.vbl;
+ document.getElementById("vbp").textContent = obj.metadata.vbp;
+ document.getElementById("vsa").textContent = obj.metadata.vsa;
+ document.getElementById("vfp").textContent = obj.metadata.vfp;
+
+ document.getElementById("data_active").textContent = obj.metadata.data_px_active;
+ document.getElementById("data_vblank").textContent = obj.metadata.data_vb;
+
+ //document.getElementById("blanking_px").textContent =;
+ //document.getElementById("").textContent = obj.metadata.data_px_active;
+ document.getElementById("dump").textContent = obj.png_frame;
+ if (obj.png_frame != png_frame) {
+ png_frame = obj.png_frame;
+ document.getElementById("imgtag").src = "/dump.png?x=" + png_frame;
+ document.getElementById("imgtag").style.display = "table-row";
+ }
+
+ document.getElementById("ddi0").textContent = obj.data_islands.stats.Null;
+ document.getElementById("ddi1").textContent = obj.data_islands.stats.ACR;
+ document.getElementById("ddi2").textContent = obj.data_islands.stats.Audio;
+ document.getElementById("ddi3").textContent = obj.data_islands.stats.Gencon;
+ document.getElementById("ddi4").textContent = obj.data_islands.stats.ACP;
+ document.getElementById("ddi5").textContent = obj.data_islands.stats.ISRC1;
+ document.getElementById("ddi6").textContent = obj.data_islands.stats.ISRC2;
+ document.getElementById("ddi7").textContent = obj.data_islands.stats.onebit;
+ document.getElementById("ddi8").textContent = obj.data_islands.stats.DST;
+ document.getElementById("ddi9").textContent = obj.data_islands.stats.HBR;
+ document.getElementById("ddi10").textContent = obj.data_islands.stats.Gamut;
+ document.getElementById("ddi11").textContent = obj.data_islands.stats.VendorIF;
+ document.getElementById("ddi12").textContent = obj.data_islands.stats.AVIIF;
+ document.getElementById("ddi13").textContent = obj.data_islands.stats.SPDIF + " (" + obj.data_islands.spd.spd_name + ")";
+ document.getElementById("ddi14").textContent = obj.data_islands.stats.AudioIF;
+ document.getElementById("ddi15").textContent = obj.data_islands.stats.MPEGIF;
+
+ if (obj.data_islands.avi.cea != 0) {
+ document.getElementById("vic_cea").textContent = obj.data_islands.avi.cea;
+ document.getElementById("clk_rate_cea").textContent = obj.data_islands.avi.pxclk_MHz + "MHz";
+ document.getElementById("video_syncs_cea").textContent = "H"+obj.data_islands.avi.hpol + ", V"+obj.data_islands.avi.vpol;
+
+ document.getElementById("active_details_cea").innerHTML = "<b>"+obj.data_islands.avi.hact + "x" +
+ obj.data_islands.avi.vact + "</b>";
+ document.getElementById("hsync_rate_cea").textContent = obj.data_islands.avi.hrate_kHz + "kHz";
+ document.getElementById("vsync_rate_cea").textContent = obj.data_islands.avi.vrate_Hz + "Hz";
+ document.getElementById("total_details_cea").textContent = obj.data_islands.avi.htot + "x" +
+ obj.data_islands.avi.vtot;
+ document.getElementById("total_px_details_cea").textContent = obj.data_islands.avi.px_in_frame;
+ document.getElementById("total_px1_details_cea").textContent = obj.data_islands.avi.vtot+".000";
+ document.getElementById("vsync_hpos_cea").textContent = "+0 px";
+ document.getElementById("hblank_cea").textContent = obj.data_islands.avi.hbl;
+ document.getElementById("hbp_cea").textContent = obj.data_islands.avi.hbp;
+ document.getElementById("hsa_cea").textContent = obj.data_islands.avi.hsa;
+ document.getElementById("hfp_cea").textContent = obj.data_islands.avi.hfp;
+
+ document.getElementById("vblank_cea").textContent = obj.data_islands.avi.vbl;
+ document.getElementById("vbp_cea").textContent = obj.data_islands.avi.vbp;
+ document.getElementById("vsa_cea").textContent = obj.data_islands.avi.vsa;
+ document.getElementById("vfp_cea").textContent = obj.data_islands.avi.vfp;
+ } else {
+ for (n = 0; n < tds_cea.length; n++)
+ document.getElementById(tds_cea[n]).textContent = "";
+
+ document.getElementById("vic_cea").textContent = "none";
+ }
+
+ for (n = 0; n < tds_compare.length; n++) {
+ if (document.getElementById(tds_compare[n]).textContent ==
+ document.getElementById(tds_cea[n + 1]).textContent)
+ document.getElementById(tds_compare[n]).style.backgroundColor = "#e0ffe0";
+ else
+ document.getElementById(tds_compare[n]).style.backgroundColor = "#e0e0f0";
+ }
+ }
+
+ socket.onclose = function(){
+ connection = 0;
+ // grayOut(true,{'zindex':'499'});
+ setTimeout("conn_retry();", 1000);
+ }
+ } catch(exception) {
+ setTimeout("conn_retry", 1000);
+ }
+}
+
+conn_retry();
+
+</script>
+</body>
+</html>
diff --git a/hdmicap.png b/hdmicap.png
new file mode 100644
index 0000000..f4b7aa8
--- /dev/null
+++ b/hdmicap.png
Binary files differ