diff options
author | Andy Green <andy.green@linaro.org> | 2015-10-24 14:13:38 +0800 |
---|---|---|
committer | Andy Green <andy.green@linaro.org> | 2015-10-24 14:13:38 +0800 |
commit | c3fd3cb4df292aa53f80ddeb837fb72f299d8dbe (patch) | |
tree | 51b80ef756625ed5989189ccc6a5c314287f3c68 |
initial commitHEADmaster-test-2015-10-25-1master
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | hdmicap-server.c | 1062 | ||||
-rw-r--r-- | hdmicap.html | 327 | ||||
-rw-r--r-- | hdmicap.png | bin | 0 -> 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 Binary files differnew file mode 100644 index 0000000..f4b7aa8 --- /dev/null +++ b/hdmicap.png |