Created
November 9, 2017 23:22
-
-
Save mbebenita/16b6f8e60a3614f1e0b503899ff08acf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From a907364f7c8e1d615ed470498be4c581fc64febd Mon Sep 17 00:00:00 2001 | |
From: Michael Bebenita <[email protected]> | |
Date: Fri, 3 Mar 2017 00:20:28 -0800 | |
Subject: [PATCH] Port inspection code. | |
--- | |
build/make/configure.sh | 2 +- | |
configure | 3 + | |
examples.mk | 17 ++ | |
inspect.c | 613 ++++++++++++++++++++++++++++++++++++++++++ | |
vp9/decoder/inspection.c | 66 +++++ | |
vp9/decoder/inspection.h | 44 +++ | |
vp9/decoder/vp9_decodeframe.c | 10 + | |
vp9/decoder/vp9_decoder.h | 11 + | |
vp9/vp9_dx_iface.c | 22 ++ | |
vp9/vp9_dx_iface.h | 6 + | |
vp9/vp9dx.mk | 6 + | |
vpx/vp8dx.h | 26 ++ | |
12 files changed, 825 insertions(+), 1 deletion(-) | |
create mode 100644 inspect.c | |
create mode 100644 vp9/decoder/inspection.c | |
create mode 100644 vp9/decoder/inspection.h | |
diff --git a/build/make/configure.sh b/build/make/configure.sh | |
index ac60f50..96ef42d 100644 | |
--- a/build/make/configure.sh | |
+++ b/build/make/configure.sh | |
@@ -456,7 +456,7 @@ NM=${NM} | |
CFLAGS = ${CFLAGS} | |
CXXFLAGS = ${CXXFLAGS} | |
-ARFLAGS = -crs\$(if \$(quiet),,v) | |
+# ARFLAGS = -crs\$(if \$(quiet),,v) | |
LDFLAGS = ${LDFLAGS} | |
ASFLAGS = ${ASFLAGS} | |
extralibs = ${extralibs} | |
diff --git a/configure b/configure | |
index 379c2f4..bee74e7 100755 | |
--- a/configure | |
+++ b/configure | |
@@ -63,6 +63,7 @@ Advanced options: | |
enable vp9 temporal denoising | |
${toggle_webm_io} enable input from and output to WebM container | |
${toggle_libyuv} enable libyuv | |
+ ${toggle_inspection} enable bitstream inspection | |
Codecs: | |
Codecs can be selectively enabled or disabled individually, or by family: | |
@@ -302,6 +303,7 @@ CONFIG_LIST=" | |
unit_tests | |
webm_io | |
libyuv | |
+ inspection | |
decode_perf_tests | |
encode_perf_tests | |
multi_res_encoding | |
@@ -362,6 +364,7 @@ CMDLINE_SELECT=" | |
unit_tests | |
webm_io | |
libyuv | |
+ inspection | |
decode_perf_tests | |
encode_perf_tests | |
multi_res_encoding | |
diff --git a/examples.mk b/examples.mk | |
index 38c4d75..4030099 100644 | |
--- a/examples.mk | |
+++ b/examples.mk | |
@@ -134,6 +134,7 @@ vpx_temporal_svc_encoder.SRCS += video_writer.h video_writer.c | |
vpx_temporal_svc_encoder.SRCS += vpx_ports/msvc.h | |
vpx_temporal_svc_encoder.GUID = B18C08F2-A439-4502-A78E-849BE3D60947 | |
vpx_temporal_svc_encoder.DESCRIPTION = Temporal SVC Encoder | |
+ | |
EXAMPLES-$(CONFIG_DECODERS) += simple_decoder.c | |
simple_decoder.GUID = D3BBF1E9-2427-450D-BBFF-B2843C1D44CC | |
simple_decoder.SRCS += ivfdec.h ivfdec.c | |
@@ -144,6 +145,22 @@ simple_decoder.SRCS += vpx_ports/mem_ops.h | |
simple_decoder.SRCS += vpx_ports/mem_ops_aligned.h | |
simple_decoder.SRCS += vpx_ports/msvc.h | |
simple_decoder.DESCRIPTION = Simplified decoder loop | |
+ | |
+ifeq ($(CONFIG_INSPECTION),yes) | |
+UTILS-$(CONFIG_DECODERS) += inspect.c | |
+inspect.GUID = FA46A420-3356-441F-B0FD-60AA1345C181 | |
+inspect.SRCS += ivfdec.h ivfdec.c | |
+inspect.SRCS += args.c args.h | |
+inspect.SRCS += tools_common.h tools_common.c | |
+inspect.SRCS += video_common.h | |
+inspect.SRCS += video_reader.h video_reader.c | |
+inspect.SRCS += vpx_ports/mem_ops.h | |
+inspect.SRCS += vpx_ports/mem_ops_aligned.h | |
+inspect.SRCS += vpx_ports/msvc.h | |
+inspect.DESCRIPTION = Dump inspection data | |
+endif | |
+ | |
+ | |
EXAMPLES-$(CONFIG_DECODERS) += postproc.c | |
postproc.SRCS += ivfdec.h ivfdec.c | |
postproc.SRCS += tools_common.h tools_common.c | |
diff --git a/inspect.c b/inspect.c | |
new file mode 100644 | |
index 0000000..e27f453 | |
--- /dev/null | |
+++ b/inspect.c | |
@@ -0,0 +1,613 @@ | |
+/* | |
+ * Copyright (c) 2016, Alliance for Open Media. All rights reserved | |
+ * | |
+ * This source code is subject to the terms of the BSD 2 Clause License and | |
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | |
+ * was not distributed with this source code in the LICENSE file, you can | |
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open | |
+ * Media Patent License 1.0 was not distributed with this source code in the | |
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent. | |
+ */ | |
+ | |
+// Inspect Decoder | |
+// ================ | |
+// | |
+// This is a simple decoder loop that writes JSON stats to stdout. This tool | |
+// can also be compiled with Emscripten and used as a library. | |
+ | |
+#include <stdio.h> | |
+#include <stdlib.h> | |
+#include <string.h> | |
+ | |
+#include "./args.h" | |
+#ifdef __EMSCRIPTEN__ | |
+#include <emscripten.h> | |
+#else | |
+#define EMSCRIPTEN_KEEPALIVE | |
+#endif | |
+ | |
+#include "vpx/vpx_decoder.h" | |
+#include "vpx/vp8dx.h" | |
+ | |
+#include "../tools_common.h" | |
+#include "../video_reader.h" | |
+#include "./vpx_config.h" | |
+#include "vp9/vp9_dx_iface.c" | |
+// #include "../vp9/common/onyxc_int.h" | |
+ | |
+#include "../video_common.h" | |
+ | |
+// Max JSON buffer size. | |
+const int MAX_BUFFER = 1024 * 1024 * 32; | |
+ | |
+typedef enum { | |
+ ACCOUNTING_LAYER = 1, | |
+ BLOCK_SIZE_LAYER = 1 << 1, | |
+ TRANSFORM_SIZE_LAYER = 1 << 2, | |
+ TRANSFORM_TYPE_LAYER = 1 << 3, | |
+ MODE_LAYER = 1 << 4, | |
+ SKIP_LAYER = 1 << 5, | |
+ FILTER_LAYER = 1 << 6, | |
+ CDEF_LAYER = 1 << 7, | |
+ REFERENCE_FRAME_LAYER = 1 << 8, | |
+ MOTION_VECTORS_LAYER = 1 << 9, | |
+ ALL_LAYERS = (1 << 10) - 1 | |
+} LayerType; | |
+ | |
+static LayerType layers = 0; | |
+ | |
+static int stop_after = 0; | |
+ | |
+static const arg_def_t limit_arg = | |
+ ARG_DEF(NULL, "limit", 1, "Stop decoding after n frames"); | |
+static const arg_def_t dump_all_arg = ARG_DEF("A", "all", 0, "Dump All"); | |
+static const arg_def_t dump_accounting_arg = | |
+ ARG_DEF("a", "accounting", 0, "Dump Accounting"); | |
+static const arg_def_t dump_block_size_arg = | |
+ ARG_DEF("bs", "blockSize", 0, "Dump Block Size"); | |
+static const arg_def_t dump_motion_vectors_arg = | |
+ ARG_DEF("mv", "motionVectors", 0, "Dump Motion Vectors"); | |
+static const arg_def_t dump_transform_size_arg = | |
+ ARG_DEF("ts", "transformSize", 0, "Dump Transform Size"); | |
+static const arg_def_t dump_transform_type_arg = | |
+ ARG_DEF("tt", "transformType", 0, "Dump Transform Type"); | |
+static const arg_def_t dump_mode_arg = ARG_DEF("m", "mode", 0, "Dump Mode"); | |
+static const arg_def_t dump_skip_arg = ARG_DEF("s", "skip", 0, "Dump Skip"); | |
+static const arg_def_t dump_filter_arg = | |
+ ARG_DEF("f", "filter", 0, "Dump Filter"); | |
+static const arg_def_t dump_cdef_arg = ARG_DEF("c", "cdef", 0, "Dump CDEF"); | |
+static const arg_def_t dump_reference_frame_arg = | |
+ ARG_DEF("r", "referenceFrame", 0, "Dump Reference Frame"); | |
+static const arg_def_t usage_arg = ARG_DEF("h", "help", 0, "Help"); | |
+ | |
+static const arg_def_t *main_args[] = { &limit_arg, | |
+ &dump_all_arg, | |
+#if CONFIG_ACCOUNTING | |
+ &dump_accounting_arg, | |
+#endif | |
+ &dump_block_size_arg, | |
+ &dump_transform_size_arg, | |
+ &dump_transform_type_arg, | |
+ &dump_mode_arg, | |
+ &dump_skip_arg, | |
+ &dump_filter_arg, | |
+ &dump_cdef_arg, | |
+ &dump_reference_frame_arg, | |
+ &dump_motion_vectors_arg, | |
+ &usage_arg, | |
+ NULL }; | |
+#define ENUM(name) \ | |
+ { #name, name } | |
+#define LAST_ENUM \ | |
+ { NULL, 0 } | |
+typedef struct map_entry { | |
+ const char *name; | |
+ int value; | |
+} map_entry; | |
+ | |
+const map_entry refs_map[] = { ENUM(INTRA_FRAME), ENUM(LAST_FRAME), | |
+#if CONFIG_EXT_REFS | |
+ ENUM(LAST2_FRAME), ENUM(LAST3_FRAME), | |
+ ENUM(GOLDEN_FRAME), ENUM(BWDREF_FRAME), | |
+ ENUM(ALTREF_FRAME), | |
+#else | |
+ ENUM(GOLDEN_FRAME), ENUM(ALTREF_FRAME), | |
+#endif | |
+ LAST_ENUM }; | |
+ | |
+const map_entry block_size_map[] = { | |
+#if CONFIG_CB4X4 | |
+ ENUM(BLOCK_2X2), ENUM(BLOCK_2X4), ENUM(BLOCK_4X2), | |
+#endif | |
+ ENUM(BLOCK_4X4), ENUM(BLOCK_4X8), ENUM(BLOCK_8X4), | |
+ ENUM(BLOCK_8X8), ENUM(BLOCK_8X16), ENUM(BLOCK_16X8), | |
+ ENUM(BLOCK_16X16), ENUM(BLOCK_16X32), ENUM(BLOCK_32X16), | |
+ ENUM(BLOCK_32X32), ENUM(BLOCK_32X64), ENUM(BLOCK_64X32), | |
+ ENUM(BLOCK_64X64), | |
+#if CONFIG_EXT_PARTITION | |
+ ENUM(BLOCK_64X128), ENUM(BLOCK_128X64), ENUM(BLOCK_128X128), | |
+#endif | |
+ LAST_ENUM | |
+}; | |
+ | |
+const map_entry tx_size_map[] = { | |
+ ENUM(TX_4X4), | |
+ ENUM(TX_8X8), | |
+ ENUM(TX_16X16), | |
+ ENUM(TX_32X32), | |
+ LAST_ENUM | |
+}; | |
+ | |
+const map_entry tx_type_map[] = { ENUM(DCT_DCT), | |
+ ENUM(ADST_DCT), | |
+ ENUM(DCT_ADST), | |
+ ENUM(ADST_ADST), | |
+#if CONFIG_EXT_TX | |
+ ENUM(FLIPADST_DCT), | |
+ ENUM(DCT_FLIPADST), | |
+ ENUM(FLIPADST_FLIPADST), | |
+ ENUM(ADST_FLIPADST), | |
+ ENUM(FLIPADST_ADST), | |
+ ENUM(IDTX), | |
+ ENUM(V_DCT), | |
+ ENUM(H_DCT), | |
+ ENUM(V_ADST), | |
+ ENUM(H_ADST), | |
+ ENUM(V_FLIPADST), | |
+ ENUM(H_FLIPADST), | |
+#endif | |
+ LAST_ENUM }; | |
+ | |
+const map_entry prediction_mode_map[] = { ENUM(DC_PRED), | |
+ ENUM(V_PRED), | |
+ ENUM(H_PRED), | |
+ ENUM(D45_PRED), | |
+ ENUM(D135_PRED), | |
+ ENUM(D117_PRED), | |
+ ENUM(D153_PRED), | |
+ ENUM(D207_PRED), | |
+ ENUM(D63_PRED), | |
+#if CONFIG_ALT_INTRA | |
+ ENUM(SMOOTH_PRED), | |
+#endif | |
+ ENUM(TM_PRED), | |
+ ENUM(NEARESTMV), | |
+ ENUM(NEARMV), | |
+ ENUM(ZEROMV), | |
+ ENUM(NEWMV), | |
+#if CONFIG_EXT_INTER | |
+ ENUM(NEWFROMNEARMV), | |
+ ENUM(NEAREST_NEARESTMV), | |
+ ENUM(NEAREST_NEARMV), | |
+ ENUM(NEAR_NEARESTMV), | |
+ ENUM(NEAR_NEARMV), | |
+ ENUM(NEAREST_NEWMV), | |
+ ENUM(NEW_NEARESTMV), | |
+ ENUM(NEAR_NEWMV), | |
+ ENUM(NEW_NEARMV), | |
+ ENUM(ZERO_ZEROMV), | |
+ ENUM(NEW_NEWMV), | |
+#endif | |
+ LAST_ENUM }; | |
+ | |
+#define NO_SKIP 0 | |
+#define SKIP 1 | |
+ | |
+const map_entry skip_map[] = { ENUM(SKIP), ENUM(NO_SKIP), LAST_ENUM }; | |
+ | |
+const map_entry config_map[] = { ENUM(MI_SIZE), LAST_ENUM }; | |
+ | |
+static const char *exec_name; | |
+ | |
+insp_frame_data frame_data; | |
+int frame_count = 0; | |
+int decoded_frame_count = 0; | |
+vpx_codec_ctx_t codec; | |
+VpxVideoReader *reader = NULL; | |
+const VpxVideoInfo *info = NULL; | |
+vpx_image_t *img = NULL; | |
+ | |
+void on_frame_decoded_dump(char *json) { | |
+#ifdef __EMSCRIPTEN__ | |
+ EM_ASM_({ Module.on_frame_decoded_json($0); }, json); | |
+#else | |
+ printf("%s", json); | |
+#endif | |
+} | |
+ | |
+// Writing out the JSON buffer using snprintf is very slow, especially when | |
+// compiled with emscripten, these functions speed things up quite a bit. | |
+int put_str(char *buffer, const char *str) { | |
+ int i; | |
+ for (i = 0; str[i] != '\0'; i++) { | |
+ buffer[i] = str[i]; | |
+ } | |
+ return i; | |
+} | |
+ | |
+int put_num(char *buffer, char prefix, int num, char suffix) { | |
+ int i = 0; | |
+ char *buf = buffer; | |
+ int is_neg = 0; | |
+ if (prefix) { | |
+ buf[i++] = prefix; | |
+ } | |
+ if (num == 0) { | |
+ buf[i++] = '0'; | |
+ } else { | |
+ if (num < 0) { | |
+ num = -num; | |
+ is_neg = 1; | |
+ } | |
+ int s = i; | |
+ while (num != 0) { | |
+ buf[i++] = '0' + (num % 10); | |
+ num = num / 10; | |
+ } | |
+ if (is_neg) { | |
+ buf[i++] = '-'; | |
+ } | |
+ int e = i - 1; | |
+ while (s < e) { | |
+ int t = buf[s]; | |
+ buf[s] = buf[e]; | |
+ buf[e] = t; | |
+ s++; | |
+ e--; | |
+ } | |
+ } | |
+ if (suffix) { | |
+ buf[i++] = suffix; | |
+ } | |
+ return i; | |
+} | |
+ | |
+int put_map(char *buffer, const map_entry *map) { | |
+ char *buf = buffer; | |
+ const map_entry *entry = map; | |
+ while (entry->name != NULL) { | |
+ *(buf++) = '"'; | |
+ buf += put_str(buf, entry->name); | |
+ *(buf++) = '"'; | |
+ buf += put_num(buf, ':', entry->value, 0); | |
+ entry++; | |
+ if (entry->name != NULL) { | |
+ *(buf++) = ','; | |
+ } | |
+ } | |
+ return buf - buffer; | |
+} | |
+ | |
+int put_reference_frame(char *buffer) { | |
+ const int mi_rows = frame_data.mi_rows; | |
+ const int mi_cols = frame_data.mi_cols; | |
+ char *buf = buffer; | |
+ int r, c; | |
+ buf += put_str(buf, " \"referenceFrameMap\": {"); | |
+ buf += put_map(buf, refs_map); | |
+ buf += put_str(buf, "},\n"); | |
+ buf += put_str(buf, " \"referenceFrame\": ["); | |
+ for (r = 0; r < mi_rows; ++r) { | |
+ *(buf++) = '['; | |
+ for (c = 0; c < mi_cols; ++c) { | |
+ insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c]; | |
+ if (c) *(buf++) = ','; | |
+ buf += put_num(buf, '[', mi->ref_frame[0], 0); | |
+ buf += put_num(buf, ',', mi->ref_frame[1], ']'); | |
+ } | |
+ *(buf++) = ']'; | |
+ if (r < mi_rows - 1) *(buf++) = ','; | |
+ } | |
+ buf += put_str(buf, "],\n"); | |
+ return buf - buffer; | |
+} | |
+ | |
+int put_motion_vectors(char *buffer) { | |
+ const int mi_rows = frame_data.mi_rows; | |
+ const int mi_cols = frame_data.mi_cols; | |
+ char *buf = buffer; | |
+ int r, c; | |
+ buf += put_str(buf, " \"motionVectors\": ["); | |
+ for (r = 0; r < mi_rows; ++r) { | |
+ *(buf++) = '['; | |
+ for (c = 0; c < mi_cols; ++c) { | |
+ insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c]; | |
+ if (c) *(buf++) = ','; | |
+ buf += put_num(buf, '[', mi->mv[0].col, 0); | |
+ buf += put_num(buf, ',', mi->mv[0].row, 0); | |
+ buf += put_num(buf, ',', mi->mv[1].col, 0); | |
+ buf += put_num(buf, ',', mi->mv[1].row, ']'); | |
+ } | |
+ *(buf++) = ']'; | |
+ if (r < mi_rows - 1) *(buf++) = ','; | |
+ } | |
+ buf += put_str(buf, "],\n"); | |
+ return buf - buffer; | |
+} | |
+ | |
+int put_block_info(char *buffer, const map_entry *map, const char *name, | |
+ size_t offset) { | |
+ const int mi_rows = frame_data.mi_rows; | |
+ const int mi_cols = frame_data.mi_cols; | |
+ char *buf = buffer; | |
+ int r, c, v; | |
+ if (map) { | |
+ buf += snprintf(buf, MAX_BUFFER, " \"%sMap\": {", name); | |
+ buf += put_map(buf, map); | |
+ buf += put_str(buf, "},\n"); | |
+ } | |
+ buf += snprintf(buf, MAX_BUFFER, " \"%s\": [", name); | |
+ for (r = 0; r < mi_rows; ++r) { | |
+ *(buf++) = '['; | |
+ for (c = 0; c < mi_cols; ++c) { | |
+ insp_mi_data *mi = &frame_data.mi_grid[r * mi_cols + c]; | |
+ v = *(((int8_t *)mi) + offset); | |
+ if (c) *(buf++) = ','; | |
+ buf += put_num(buf, 0, v, 0); | |
+ } | |
+ *(buf++) = ']'; | |
+ if (r < mi_rows - 1) *(buf++) = ','; | |
+ } | |
+ buf += put_str(buf, "],\n"); | |
+ return buf - buffer; | |
+} | |
+ | |
+#if CONFIG_ACCOUNTING | |
+int put_accounting(char *buffer) { | |
+ char *buf = buffer; | |
+ int i; | |
+ const Accounting *accounting = frame_data.accounting; | |
+ if (accounting == NULL) { | |
+ printf("XXX\n"); | |
+ return 0; | |
+ } | |
+ const int num_syms = accounting->syms.num_syms; | |
+ const int num_strs = accounting->syms.dictionary.num_strs; | |
+ buf += put_str(buf, " \"symbolsMap\": ["); | |
+ for (i = 0; i < num_strs; i++) { | |
+ buf += snprintf(buf, MAX_BUFFER, "\"%s\"", | |
+ accounting->syms.dictionary.strs[i]); | |
+ if (i < num_strs - 1) *(buf++) = ','; | |
+ } | |
+ buf += put_str(buf, "],\n"); | |
+ buf += put_str(buf, " \"symbols\": [\n "); | |
+ AccountingSymbolContext context; | |
+ context.x = -2; | |
+ context.y = -2; | |
+ AccountingSymbol *sym; | |
+ for (i = 0; i < num_syms; i++) { | |
+ sym = &accounting->syms.syms[i]; | |
+ if (memcmp(&context, &sym->context, sizeof(AccountingSymbolContext)) != 0) { | |
+ buf += put_num(buf, '[', sym->context.x, 0); | |
+ buf += put_num(buf, ',', sym->context.y, ']'); | |
+ } else { | |
+ buf += put_num(buf, '[', sym->id, 0); | |
+ buf += put_num(buf, ',', sym->bits, 0); | |
+ buf += put_num(buf, ',', sym->samples, ']'); | |
+ } | |
+ context = sym->context; | |
+ if (i < num_syms - 1) *(buf++) = ','; | |
+ } | |
+ buf += put_str(buf, "],\n"); | |
+ return buf - buffer; | |
+} | |
+#endif | |
+ | |
+void inspect(void *pbi, void *data) { | |
+ /* Fetch frame data. */ | |
+ ifd_inspect(&frame_data, pbi); | |
+ (void)data; | |
+ // We allocate enough space and hope we don't write out of bounds. Totally | |
+ // unsafe but this speeds things up, especially when compiled to Javascript. | |
+ char *buffer = vpx_malloc(MAX_BUFFER); | |
+ char *buf = buffer; | |
+ buf += put_str(buf, "{\n"); | |
+ if (layers & BLOCK_SIZE_LAYER) { | |
+ buf += put_block_info(buf, block_size_map, "blockSize", | |
+ offsetof(insp_mi_data, sb_type)); | |
+ } | |
+ if (layers & TRANSFORM_SIZE_LAYER) { | |
+ buf += put_block_info(buf, tx_size_map, "transformSize", | |
+ offsetof(insp_mi_data, tx_size)); | |
+ } | |
+ if (layers & TRANSFORM_TYPE_LAYER) { | |
+ buf += put_block_info(buf, tx_type_map, "transformType", | |
+ offsetof(insp_mi_data, tx_type)); | |
+ } | |
+ if (layers & MODE_LAYER) { | |
+ buf += put_block_info(buf, prediction_mode_map, "mode", | |
+ offsetof(insp_mi_data, mode)); | |
+ } | |
+ if (layers & SKIP_LAYER) { | |
+ buf += put_block_info(buf, skip_map, "skip", offsetof(insp_mi_data, skip)); | |
+ } | |
+ if (layers & FILTER_LAYER) { | |
+ buf += put_block_info(buf, NULL, "filter", offsetof(insp_mi_data, filter)); | |
+ } | |
+ if (layers & CDEF_LAYER) { | |
+ // buf += put_block_info(buf, NULL, "clpf", | |
+ // offsetof(insp_mi_data, clpf)); | |
+ // buf += sprintf(buf, " \"clpfStrengthY\": %d,\n", | |
+ // frame_data.clpf_strength_y); | |
+ // buf += sprintf(buf, " \"clpfSize\": %d,\n", frame_data.clpf_size); | |
+ } | |
+ if (layers & MOTION_VECTORS_LAYER) { | |
+ buf += put_motion_vectors(buf); | |
+ } | |
+ if (layers & REFERENCE_FRAME_LAYER) { | |
+ buf += put_reference_frame(buf); | |
+ } | |
+#if CONFIG_ACCOUNTING | |
+ if (layers & ACCOUNTING_LAYER) { | |
+ buf += put_accounting(buf); | |
+ } | |
+#endif | |
+ buf += snprintf(buf, MAX_BUFFER, " \"frame\": %d,\n", decoded_frame_count); | |
+ buf += snprintf(buf, MAX_BUFFER, " \"showFrame\": %d,\n", | |
+ frame_data.show_frame); | |
+ buf += snprintf(buf, MAX_BUFFER, " \"frameType\": %d,\n", | |
+ frame_data.frame_type); | |
+ buf += snprintf(buf, MAX_BUFFER, " \"baseQIndex\": %d,\n", | |
+ frame_data.base_qindex); | |
+ buf += put_str(buf, " \"config\": {"); | |
+ buf += put_map(buf, config_map); | |
+ buf += put_str(buf, "},\n"); | |
+ buf += snprintf(buf, MAX_BUFFER, " \"configString\": \"%s\"\n", | |
+ vpx_codec_build_config()); | |
+ decoded_frame_count++; | |
+ buf += put_str(buf, "},\n"); | |
+ *(buf++) = 0; | |
+ on_frame_decoded_dump(buffer); | |
+ vpx_free(buffer); | |
+} | |
+ | |
+void ifd_init_cb() { | |
+ aom_inspect_init ii; | |
+ ii.inspect_cb = inspect; | |
+ ii.inspect_ctx = NULL; | |
+ vpx_codec_control(&codec, VP9_SET_INSPECTION_CALLBACK, &ii); | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int open_file(char *file) { | |
+ if (file == NULL) { | |
+ // The JS analyzer puts the .ivf file at this location. | |
+ file = "/tmp/input.ivf"; | |
+ } | |
+ reader = vpx_video_reader_open(file); | |
+ if (!reader) die("Failed to open %s for reading.", file); | |
+ info = vpx_video_reader_get_info(reader); | |
+ const VpxInterface *decoder = get_vpx_decoder_by_fourcc(info->codec_fourcc); | |
+ if (!decoder) die("Unknown input codec."); | |
+ fprintf(stderr, "Using %s\n", | |
+ vpx_codec_iface_name(decoder->codec_interface())); | |
+ if (vpx_codec_dec_init(&codec, decoder->codec_interface(), NULL, 0)) | |
+ die_codec(&codec, "Failed to initialize decoder."); | |
+ ifd_init(&frame_data, info->frame_width, info->frame_height); | |
+ ifd_init_cb(); | |
+ return EXIT_SUCCESS; | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int read_frame() { | |
+ if (!vpx_video_reader_read_frame(reader)) return EXIT_FAILURE; | |
+ img = NULL; | |
+ vpx_codec_iter_t iter = NULL; | |
+ size_t frame_size = 0; | |
+ const unsigned char *frame = vpx_video_reader_get_frame(reader, &frame_size); | |
+ if (vpx_codec_decode(&codec, frame, (unsigned int)frame_size, NULL, 0) != | |
+ VPX_CODEC_OK) { | |
+ die_codec(&codec, "Failed to decode frame."); | |
+ } | |
+ img = vpx_codec_get_frame(&codec, &iter); | |
+ if (img == NULL) { | |
+ return EXIT_FAILURE; | |
+ } | |
+ ++frame_count; | |
+#if CONFIG_ACCOUNTING | |
+ vpx_codec_alg_priv_t *t = (vpx_codec_alg_priv_t *)codec.priv; | |
+ VpxWorker *const worker = &t->frame_workers[0]; | |
+ FrameWorkerData *const frame_worker_data = (FrameWorkerData *)worker->data1; | |
+ struct AV1Decoder *pbi = frame_worker_data->pbi; | |
+ pbi->acct_enabled = layers & ACCOUNTING_LAYER; | |
+#endif | |
+ return EXIT_SUCCESS; | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+const char *get_vpx_codec_build_config() { return vpx_codec_build_config(); } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_bit_depth() { return img->bit_depth; } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+unsigned char *get_plane(int plane) { return img->planes[plane]; } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_plane_stride(int plane) { return img->stride[plane]; } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_plane_width(int plane) { return vpx_img_plane_width(img, plane); } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_plane_height(int plane) { return vpx_img_plane_height(img, plane); } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_frame_width() { return info->frame_width; } | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int get_frame_height() { return info->frame_height; } | |
+ | |
+static void parse_args(char **argv) { | |
+ char **argi, **argj; | |
+ struct arg arg; | |
+ (void)dump_accounting_arg; | |
+ for (argi = argj = argv; (*argj = *argi); argi += arg.argv_step) { | |
+ arg.argv_step = 1; | |
+ if (arg_match(&arg, &dump_block_size_arg, argi)) layers |= BLOCK_SIZE_LAYER; | |
+#if CONFIG_ACCOUNTING | |
+ else if (arg_match(&arg, &dump_accounting_arg, argi)) | |
+ layers |= ACCOUNTING_LAYER; | |
+#endif | |
+ else if (arg_match(&arg, &dump_transform_size_arg, argi)) | |
+ layers |= TRANSFORM_SIZE_LAYER; | |
+ else if (arg_match(&arg, &dump_transform_type_arg, argi)) | |
+ layers |= TRANSFORM_TYPE_LAYER; | |
+ else if (arg_match(&arg, &dump_mode_arg, argi)) | |
+ layers |= MODE_LAYER; | |
+ else if (arg_match(&arg, &dump_skip_arg, argi)) | |
+ layers |= SKIP_LAYER; | |
+ else if (arg_match(&arg, &dump_filter_arg, argi)) | |
+ layers |= FILTER_LAYER; | |
+ else if (arg_match(&arg, &dump_cdef_arg, argi)) | |
+ layers |= CDEF_LAYER; | |
+ else if (arg_match(&arg, &dump_reference_frame_arg, argi)) | |
+ layers |= REFERENCE_FRAME_LAYER; | |
+ else if (arg_match(&arg, &dump_motion_vectors_arg, argi)) | |
+ layers |= MOTION_VECTORS_LAYER; | |
+ else if (arg_match(&arg, &dump_all_arg, argi)) | |
+ layers |= ALL_LAYERS; | |
+ else if (arg_match(&arg, &usage_arg, argi)) | |
+ usage_exit(); | |
+ else if (arg_match(&arg, &limit_arg, argi)) | |
+ stop_after = arg_parse_uint(&arg); | |
+ else | |
+ argj++; | |
+ } | |
+} | |
+ | |
+static const char *exec_name; | |
+ | |
+void usage_exit(void) { | |
+ fprintf(stderr, "Usage: %s src_filename <options>\n", exec_name); | |
+ fprintf(stderr, "\nOptions:\n"); | |
+ arg_show_usage(stderr, main_args); | |
+ exit(EXIT_FAILURE); | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+int main(int argc, char **argv) { | |
+ exec_name = argv[0]; | |
+ parse_args(argv); | |
+ if (argc >= 2) { | |
+ open_file(argv[1]); | |
+ printf("[\n"); | |
+ while (1) { | |
+ if (stop_after && (decoded_frame_count >= stop_after)) break; | |
+ if (read_frame()) break; | |
+ } | |
+ printf("null\n"); | |
+ printf("]"); | |
+ } else { | |
+ usage_exit(); | |
+ } | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+void quit() { | |
+ if (vpx_codec_destroy(&codec)) die_codec(&codec, "Failed to destroy codec"); | |
+ vpx_video_reader_close(reader); | |
+} | |
+ | |
+EMSCRIPTEN_KEEPALIVE | |
+void set_layers(LayerType v) { layers = v; } | |
diff --git a/vp9/decoder/inspection.c b/vp9/decoder/inspection.c | |
new file mode 100644 | |
index 0000000..a67d66b | |
--- /dev/null | |
+++ b/vp9/decoder/inspection.c | |
@@ -0,0 +1,66 @@ | |
+#include "vp9/decoder/vp9_decoder.h" | |
+#include "vp9/decoder/inspection.h" | |
+#include "vp9/common/vp9_enums.h" | |
+ | |
+void ifd_init(insp_frame_data *fd, int frame_width, int frame_height) { | |
+ fd->mi_cols = ALIGN_POWER_OF_TWO(frame_width, MI_SIZE_LOG2) >> MI_SIZE_LOG2; | |
+ fd->mi_rows = ALIGN_POWER_OF_TWO(frame_height, MI_SIZE_LOG2) >> MI_SIZE_LOG2; | |
+ fd->mi_grid = (insp_mi_data *)vpx_malloc(sizeof(insp_mi_data) * fd->mi_rows * | |
+ fd->mi_cols); | |
+} | |
+ | |
+void ifd_clear(insp_frame_data *fd) { | |
+ vpx_free(fd->mi_grid); | |
+ fd->mi_grid = NULL; | |
+} | |
+ | |
+/* TODO(negge) This function may be called by more than one thread when using | |
+ a multi-threaded decoder and this may cause a data race. */ | |
+int ifd_inspect(insp_frame_data *fd, void *decoder) { | |
+ struct VP9Decoder *pbi = (struct VP9Decoder *)decoder; | |
+ VP9_COMMON *const cm = &pbi->common; | |
+ // TODO(negge): Should this function just call ifd_clear() and ifd_init()? | |
+ if (fd->mi_rows != cm->mi_rows || fd->mi_cols != cm->mi_cols) { | |
+ return 0; | |
+ } | |
+ fd->show_frame = cm->show_frame; | |
+ fd->frame_type = cm->frame_type; | |
+ fd->base_qindex = cm->base_qindex; | |
+ int i, j; | |
+ // for (i = 0; i < MAX_SEGMENTS; i++) { | |
+ // for (j = 0; j < 2; j++) { | |
+ // fd->y_dequant[i][j] = cm->y_dequant[i][j]; | |
+ // fd->uv_dequant[i][j] = cm->uv_dequant[i][j]; | |
+ // } | |
+ // } | |
+ for (j = 0; j < cm->mi_rows; j++) { | |
+ for (i = 0; i < cm->mi_cols; i++) { | |
+ const MODE_INFO *mbmi = | |
+ cm->mi_grid_visible[j * cm->mi_stride + i]; | |
+ insp_mi_data *mi = &fd->mi_grid[j * cm->mi_cols + i]; | |
+ // Segment | |
+ mi->segment_id = mbmi->segment_id; | |
+ // Motion Vectors | |
+ mi->mv[0].row = mbmi->mv[0].as_mv.row; | |
+ mi->mv[0].col = mbmi->mv[0].as_mv.col; | |
+ mi->mv[1].row = mbmi->mv[1].as_mv.row; | |
+ mi->mv[1].col = mbmi->mv[1].as_mv.col; | |
+ // Reference Frames | |
+ mi->ref_frame[0] = mbmi->ref_frame[0]; | |
+ mi->ref_frame[1] = mbmi->ref_frame[1]; | |
+ // Prediction Mode | |
+ mi->mode = mbmi->mode; | |
+ // Block Size | |
+ mi->sb_type = mbmi->sb_type; | |
+ // Skip Flag | |
+ mi->skip = mbmi->skip; | |
+ // Filters | |
+ mi->filter = mbmi->interp_filter; | |
+ // Transform | |
+ // mi->tx_type = mbmi->tx_type; | |
+ mi->tx_type = 0; | |
+ mi->tx_size = mbmi->tx_size; | |
+ } | |
+ } | |
+ return 1; | |
+} | |
diff --git a/vp9/decoder/inspection.h b/vp9/decoder/inspection.h | |
new file mode 100644 | |
index 0000000..b4fa6bd | |
--- /dev/null | |
+++ b/vp9/decoder/inspection.h | |
@@ -0,0 +1,44 @@ | |
+#ifndef AOM_INSPECTION_H_ | |
+#define AOM_INSPECTION_H_ | |
+ | |
+typedef void (*aom_inspect_cb)(void *decoder, void *data); | |
+ | |
+typedef struct insp_mv insp_mv; | |
+ | |
+struct insp_mv { | |
+ int16_t row; | |
+ int16_t col; | |
+}; | |
+ | |
+typedef struct insp_mi_data insp_mi_data; | |
+ | |
+struct insp_mi_data { | |
+ insp_mv mv[2]; | |
+ int8_t ref_frame[2]; | |
+ int8_t mode; | |
+ int8_t sb_type; | |
+ int8_t skip; | |
+ int8_t segment_id; | |
+ int8_t filter; | |
+ int8_t tx_type; | |
+ int8_t tx_size; | |
+}; | |
+ | |
+typedef struct insp_frame_data insp_frame_data; | |
+ | |
+struct insp_frame_data { | |
+ insp_mi_data *mi_grid; | |
+ int show_frame; | |
+ int frame_type; | |
+ int base_qindex; | |
+ int mi_rows; | |
+ int mi_cols; | |
+ // int16_t y_dequant[MAX_SEGMENTS][2]; | |
+ // int16_t uv_dequant[MAX_SEGMENTS][2]; | |
+}; | |
+ | |
+void ifd_init(insp_frame_data *fd, int frame_width, int frame_height); | |
+void ifd_clear(insp_frame_data *fd); | |
+int ifd_inspect(insp_frame_data *fd, void *decoder); | |
+ | |
+#endif | |
diff --git a/vp9/decoder/vp9_decodeframe.c b/vp9/decoder/vp9_decodeframe.c | |
index f71f7d1..6b93bf9 100644 | |
--- a/vp9/decoder/vp9_decodeframe.c | |
+++ b/vp9/decoder/vp9_decodeframe.c | |
@@ -25,6 +25,9 @@ | |
#include "vpx_util/vpx_thread.h" | |
#include "vp9/common/vp9_alloccommon.h" | |
+#if CONFIG_INSPECTION | |
+#include "vp9/decoder/inspection.h" | |
+#endif | |
#include "vp9/common/vp9_common.h" | |
#include "vp9/common/vp9_entropy.h" | |
#include "vp9/common/vp9_entropymode.h" | |
@@ -2159,4 +2162,11 @@ void vp9_decode_frame(VP9Decoder *pbi, const uint8_t *data, | |
// Non frame parallel update frame context here. | |
if (cm->refresh_frame_context && !context_updated) | |
cm->frame_contexts[cm->frame_context_idx] = *cm->fc; | |
+ | |
+#if CONFIG_INSPECTION | |
+ if (pbi->inspect_cb != NULL) { | |
+ (*pbi->inspect_cb)(pbi, pbi->inspect_ctx); | |
+ } | |
+#endif | |
+ | |
} | |
diff --git a/vp9/decoder/vp9_decoder.h b/vp9/decoder/vp9_decoder.h | |
index 427baf1..cf14fb8 100644 | |
--- a/vp9/decoder/vp9_decoder.h | |
+++ b/vp9/decoder/vp9_decoder.h | |
@@ -23,6 +23,11 @@ | |
#include "vp9/common/vp9_ppflags.h" | |
#include "vp9/decoder/vp9_dthread.h" | |
+#if CONFIG_INSPECTION | |
+#include "vp9/decoder/inspection.h" | |
+#endif | |
+ | |
+ | |
#ifdef __cplusplus | |
extern "C" { | |
#endif | |
@@ -76,6 +81,12 @@ typedef struct VP9Decoder { | |
int inv_tile_order; | |
int need_resync; // wait for key/intra-only frame. | |
int hold_ref_buf; // hold the reference buffer. | |
+ | |
+#if CONFIG_INSPECTION | |
+ aom_inspect_cb inspect_cb; | |
+ void *inspect_ctx; | |
+#endif | |
+ | |
} VP9Decoder; | |
int vp9_receive_compressed_data(struct VP9Decoder *pbi, size_t size, | |
diff --git a/vp9/vp9_dx_iface.c b/vp9/vp9_dx_iface.c | |
index 3c68003..7a43670 100644 | |
--- a/vp9/vp9_dx_iface.c | |
+++ b/vp9/vp9_dx_iface.c | |
@@ -442,6 +442,11 @@ static vpx_codec_err_t decode_one(vpx_codec_alg_priv_t *ctx, | |
frame_worker_data->pbi->decrypt_cb = ctx->decrypt_cb; | |
frame_worker_data->pbi->decrypt_state = ctx->decrypt_state; | |
+#if CONFIG_INSPECTION | |
+ frame_worker_data->pbi->inspect_cb = ctx->inspect_cb; | |
+ frame_worker_data->pbi->inspect_ctx = ctx->inspect_ctx; | |
+#endif | |
+ | |
worker->had_error = 0; | |
winterface->execute(worker); | |
@@ -1023,6 +1028,22 @@ static vpx_codec_err_t ctrl_set_spatial_layer_svc(vpx_codec_alg_priv_t *ctx, | |
return VPX_CODEC_OK; | |
} | |
+static vpx_codec_err_t ctrl_set_inspection_callback(vpx_codec_alg_priv_t *ctx, | |
+ va_list args) { | |
+#if !CONFIG_INSPECTION | |
+ (void)ctx; | |
+ (void)args; | |
+ return VPX_CODEC_INCAPABLE; | |
+#else | |
+ aom_inspect_init *init = va_arg(args, aom_inspect_init *); | |
+ ctx->inspect_cb = init->inspect_cb; | |
+ ctx->inspect_ctx = init->inspect_ctx; | |
+ return VPX_CODEC_OK; | |
+#endif | |
+} | |
+ | |
+ | |
+ | |
static vpx_codec_ctrl_fn_map_t decoder_ctrl_maps[] = { | |
{ VP8_COPY_REFERENCE, ctrl_copy_reference }, | |
@@ -1034,6 +1055,7 @@ static vpx_codec_ctrl_fn_map_t decoder_ctrl_maps[] = { | |
{ VP9_SET_BYTE_ALIGNMENT, ctrl_set_byte_alignment }, | |
{ VP9_SET_SKIP_LOOP_FILTER, ctrl_set_skip_loop_filter }, | |
{ VP9_DECODE_SVC_SPATIAL_LAYER, ctrl_set_spatial_layer_svc }, | |
+ { VP9_SET_INSPECTION_CALLBACK, ctrl_set_inspection_callback }, | |
// Getters | |
{ VPXD_GET_LAST_QUANTIZER, ctrl_get_quantizer }, | |
diff --git a/vp9/vp9_dx_iface.h b/vp9/vp9_dx_iface.h | |
index c155959..8c36138 100644 | |
--- a/vp9/vp9_dx_iface.h | |
+++ b/vp9/vp9_dx_iface.h | |
@@ -64,6 +64,12 @@ struct vpx_codec_alg_priv { | |
// Allow for decoding up to a given spatial layer for SVC stream. | |
int svc_decoding; | |
int svc_spatial_layer; | |
+ | |
+#if CONFIG_INSPECTION | |
+ aom_inspect_cb inspect_cb; | |
+ void *inspect_ctx; | |
+#endif | |
+ | |
}; | |
#endif // VP9_VP9_DX_IFACE_H_ | |
diff --git a/vp9/vp9dx.mk b/vp9/vp9dx.mk | |
index 4c6fd00..f873578 100644 | |
--- a/vp9/vp9dx.mk | |
+++ b/vp9/vp9dx.mk | |
@@ -31,4 +31,10 @@ VP9_DX_SRCS-yes += decoder/vp9_decoder.h | |
VP9_DX_SRCS-yes += decoder/vp9_dsubexp.c | |
VP9_DX_SRCS-yes += decoder/vp9_dsubexp.h | |
+ifeq ($(CONFIG_INSPECTION),yes) | |
+VP9_DX_SRCS-yes += decoder/inspection.c | |
+VP9_DX_SRCS-yes += decoder/inspection.h | |
+endif | |
+ | |
+ | |
VP9_DX_SRCS-yes := $(filter-out $(VP9_DX_SRCS_REMOVE-yes),$(VP9_DX_SRCS-yes)) | |
diff --git a/vpx/vp8dx.h b/vpx/vp8dx.h | |
index 41c53e4..bee2566 100644 | |
--- a/vpx/vp8dx.h | |
+++ b/vpx/vp8dx.h | |
@@ -45,6 +45,24 @@ extern vpx_codec_iface_t vpx_codec_vp9_dx_algo; | |
extern vpx_codec_iface_t *vpx_codec_vp9_dx(void); | |
/*!@} - end algorithm interface member group*/ | |
+/** Callback that inspects decoder frame data. | |
+ */ | |
+typedef void (*aom_inspect_cb)(void *decoder, void *ctx); | |
+ | |
+/*!\brief Structure to hold inspection callback and context. | |
+ * | |
+ * Defines a structure to hold the inspection callback function and calling | |
+ * context. | |
+ */ | |
+typedef struct aom_inspect_init { | |
+ /*! Inspection callback. */ | |
+ aom_inspect_cb inspect_cb; | |
+ | |
+ /*! Inspection context. */ | |
+ void *inspect_ctx; | |
+} aom_inspect_init; | |
+ | |
+ | |
/*!\enum vp8_dec_control_id | |
* \brief VP8 decoder control functions | |
* | |
@@ -124,6 +142,12 @@ enum vp8_dec_control_id { | |
*/ | |
VPXD_GET_LAST_QUANTIZER, | |
+ /** control function to set an aom_inspect_cb callback that is invoked each | |
+ * time a frame is decoded. When compiled without --enable-inspection, this | |
+ * returns AOM_CODEC_INCAPABLE. | |
+ */ | |
+ VP9_SET_INSPECTION_CALLBACK, | |
+ | |
VP8_DECODER_CTRL_ID_MAX | |
}; | |
@@ -179,6 +203,8 @@ VPX_CTRL_USE_TYPE(VP9_INVERT_TILE_DECODE_ORDER, int) | |
#define VPX_CTRL_VP9_INVERT_TILE_DECODE_ORDER | |
#define VPX_CTRL_VP9_DECODE_SVC_SPATIAL_LAYER | |
VPX_CTRL_USE_TYPE(VP9_DECODE_SVC_SPATIAL_LAYER, int) | |
+VPX_CTRL_USE_TYPE(VP9_SET_INSPECTION_CALLBACK, aom_inspect_init *) | |
+#define AOM_CTRL_VP9_SET_INSPECTION_CALLBACK | |
/*!\endcond */ | |
/*! @} - end defgroup vp8_decoder */ | |
-- | |
2.8.1 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment