avifenc, avifdec: Allow "-j all" to automatically use all of the cores on the machine

Fixes: #645
diff --git a/apps/avifdec.c b/apps/avifdec.c
index 313439c..81de197 100644
--- a/apps/avifdec.c
+++ b/apps/avifdec.c
@@ -29,7 +29,7 @@
     printf("Options:\n");
     printf("    -h,--help         : Show syntax help\n");
     printf("    -V,--version      : Show the version number\n");
-    printf("    -j,--jobs J       : Number of jobs (worker threads, default: 1)\n");
+    printf("    -j,--jobs J       : Number of jobs (worker threads, default: 1. Use \"all\" to use all available cores)\n");
     printf("    -c,--codec C      : AV1 codec to use (choose from versions list below)\n");
     printf("    -d,--depth D      : Output depth [8,16]. (PNG only; For y4m, depth is retained, and JPEG is always 8bpc)\n");
     printf("    -q,--quality Q    : Output quality [0-100]. (JPEG only, default: %d)\n", DEFAULT_JPEG_QUALITY);
@@ -74,9 +74,13 @@
             return 0;
         } else if (!strcmp(arg, "-j") || !strcmp(arg, "--jobs")) {
             NEXTARG();
-            jobs = atoi(arg);
-            if (jobs < 1) {
-                jobs = 1;
+            if (!strcmp(arg, "all")) {
+                jobs = avifQueryMaxThreads();
+            } else {
+                jobs = atoi(arg);
+                if (jobs < 1) {
+                    jobs = 1;
+                }
             }
         } else if (!strcmp(arg, "-c") || !strcmp(arg, "--codec")) {
             NEXTARG();
diff --git a/apps/avifenc.c b/apps/avifenc.c
index 3e8a3b0..4415bf8 100644
--- a/apps/avifenc.c
+++ b/apps/avifenc.c
@@ -51,7 +51,7 @@
     printf("Options:\n");
     printf("    -h,--help                         : Show syntax help\n");
     printf("    -V,--version                      : Show the version number\n");
-    printf("    -j,--jobs J                       : Number of jobs (worker threads, default: 1)\n");
+    printf("    -j,--jobs J                       : Number of jobs (worker threads, default: 1. Use \"all\" to use all available cores)\n");
     printf("    -o,--output FILENAME              : Instead of using the last filename given as output, use this filename\n");
     printf("    -l,--lossless                     : Set all defaults to encode losslessly, and emit warnings when settings/input don't allow for it\n");
     printf("    -d,--depth D                      : Output depth [8,10,12]. (JPEG/PNG only; For y4m or stdin, depth is retained)\n");
@@ -487,9 +487,13 @@
             goto cleanup;
         } else if (!strcmp(arg, "-j") || !strcmp(arg, "--jobs")) {
             NEXTARG();
-            jobs = atoi(arg);
-            if (jobs < 1) {
-                jobs = 1;
+            if (!strcmp(arg, "all")) {
+                jobs = avifQueryMaxThreads();
+            } else {
+                jobs = atoi(arg);
+                if (jobs < 1) {
+                    jobs = 1;
+                }
             }
         } else if (!strcmp(arg, "--stdin")) {
             input.useStdin = AVIF_TRUE;
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c
index e99d8f0..4ddc406 100644
--- a/apps/shared/avifutil.c
+++ b/apps/shared/avifutil.c
@@ -220,3 +220,74 @@
     printf("Diagnostics:\n");
     printf(" * %s\n", diag->error);
 }
+
+// ---------------------------------------------------------------------------
+// avifQueryMaxThreads (separated into OS implementations)
+
+#if defined(_WIN32)
+
+// Windows
+
+#pragma warning(disable : 5031)
+#pragma warning(disable : 5032)
+#include <windows.h>
+
+int avifQueryMaxThreads(void)
+{
+    int numCPU;
+    SYSTEM_INFO sysinfo;
+    GetSystemInfo(&sysinfo);
+    numCPU = sysinfo.dwNumberOfProcessors;
+    return numCPU;
+}
+
+#elif defined(__APPLE__)
+
+// Apple
+
+#include <sys/sysctl.h>
+
+int avifQueryMaxThreads()
+{
+    int mib[4];
+    int numCPU;
+    size_t len = sizeof(numCPU);
+
+    /* set the mib for hw.ncpu */
+    mib[0] = CTL_HW;
+    mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU;
+
+    /* get the number of CPUs from the system */
+    sysctl(mib, 2, &numCPU, &len, NULL, 0);
+
+    if (numCPU < 1) {
+        mib[1] = HW_NCPU;
+        sysctl(mib, 2, &numCPU, &len, NULL, 0);
+        if (numCPU < 1)
+            numCPU = 1;
+    }
+    return numCPU;
+}
+
+#elif defined(__EMSCRIPTEN__)
+
+// Emscripten
+
+int avifQueryMaxThreads()
+{
+    return 1;
+}
+
+#else
+
+// POSIX
+
+#include <unistd.h>
+
+int avifQueryMaxThreads()
+{
+    int numCPU = (int)sysconf(_SC_NPROCESSORS_ONLN);
+    return (numCPU > 0) ? numCPU : 1;
+}
+
+#endif
diff --git a/apps/shared/avifutil.h b/apps/shared/avifutil.h
index cd26b3a..a8fed45 100644
--- a/apps/shared/avifutil.h
+++ b/apps/shared/avifutil.h
@@ -23,6 +23,7 @@
 void avifContainerDump(avifDecoder * decoder);
 void avifPrintVersions(void);
 void avifDumpDiagnostics(const avifDiagnostics * diag);
+int avifQueryMaxThreads(void);
 
 typedef enum avifAppFileFormat
 {