[FFmpeg-devel] [PATCH 3/3] tests/dict2: Add tests and benchmark for AVDictionary2
softworkz
ffmpegagent at gmail.com
Sat Apr 12 18:11:58 EEST 2025
From: softworkz <softworkz at hotmail.com>
Signed-off-by: softworkz <softworkz at hotmail.com>
---
libavutil/tests/dict2.c | 185 +++++++++++++++++++++++++++++
tests/api/Makefile | 1 +
tests/api/api-dict2-test.c | 122 +++++++++++++++++++
tests/fate/api.mak | 15 +++
tools/Makefile | 2 +-
tools/dict2_benchmark.c | 237 +++++++++++++++++++++++++++++++++++++
6 files changed, 561 insertions(+), 1 deletion(-)
create mode 100644 libavutil/tests/dict2.c
create mode 100644 tests/api/api-dict2-test.c
create mode 100644 tools/dict2_benchmark.c
diff --git a/libavutil/tests/dict2.c b/libavutil/tests/dict2.c
new file mode 100644
index 0000000000..31c9f568f6
--- /dev/null
+++ b/libavutil/tests/dict2.c
@@ -0,0 +1,185 @@
+/*
+ * AVDictionary2 test utility
+ * This file is part of FFmpeg.
+ */
+
+#include "libavutil/dict2.h"
+#include "libavutil/dict.h"
+#include "libavutil/time.h"
+#include "libavutil/avassert.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+
+static void basic_functionality_test(void)
+{
+ printf("\n=== Basic Functionality Test ===\n");
+
+ AVDictionary2 *dict = NULL;
+ AVDictionaryEntry2 *entry;
+ int ret;
+
+ // Test setting keys
+ ret = av_dict2_set(&dict, "key1", "value1", 0);
+ printf("Adding key1: %s\n", ret >= 0 ? "OK" : "FAILED");
+ av_assert0(ret >= 0);
+
+ ret = av_dict2_set(&dict, "key2", "value2", 0);
+ printf("Adding key2: %s\n", ret >= 0 ? "OK" : "FAILED");
+ av_assert0(ret >= 0);
+
+ // Test lookup
+ entry = av_dict2_get(dict, "key1", NULL, 0);
+ printf("Lookup key1: %s (value: %s)\n",
+ entry ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value1"));
+
+ // Test count
+ int count = av_dict2_count(dict);
+ printf("Dictionary count: %d (expected 2)\n", count);
+ av_assert0(count == 2);
+
+ // Test iteration
+ printf("Dictionary contents:\n");
+ const AVDictionaryEntry2 *iter = NULL;
+ while ((iter = av_dict2_iterate(dict, iter))) {
+ printf(" %s: %s\n", iter->key, iter->value);
+ }
+
+ // Free dictionary
+ av_dict2_free(&dict);
+ printf("Dictionary freed successfully\n");
+}
+
+static void overwrite_test(void)
+{
+ printf("\n=== Overwrite Test ===\n");
+
+ AVDictionary2 *dict = NULL;
+ AVDictionaryEntry2 *entry;
+
+ // Test normal overwrite
+ av_dict2_set(&dict, "key", "value1", 0);
+ av_dict2_set(&dict, "key", "value2", 0);
+
+ entry = av_dict2_get(dict, "key", NULL, 0);
+ printf("Overwrite test: %s (value: %s, expected: value2)\n",
+ entry && !strcmp(entry->value, "value2") ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value2"));
+
+ // Test DONT_OVERWRITE flag
+ av_dict2_set(&dict, "key", "value3", AV_DICT2_DONT_OVERWRITE);
+
+ entry = av_dict2_get(dict, "key", NULL, 0);
+ printf("DONT_OVERWRITE flag test: %s (value: %s, expected: value2)\n",
+ entry && !strcmp(entry->value, "value2") ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value2"));
+
+ av_dict2_free(&dict);
+}
+
+static void case_sensitivity_test(void)
+{
+ printf("\n=== Case Sensitivity Test ===\n");
+
+ // Test case-sensitive dictionary with AV_DICT2_MATCH_CASE flag
+ AVDictionary2 *dict1 = NULL;
+ av_dict2_set(&dict1, "Key", "value1", AV_DICT2_MATCH_CASE);
+
+ AVDictionaryEntry2 *entry1 = av_dict2_get(dict1, "key", NULL, AV_DICT2_MATCH_CASE);
+ printf("Case-sensitive lookup: %s (expected NULL)\n",
+ entry1 ? "FAILED" : "OK");
+ av_assert0(entry1 == NULL);
+
+ // Test case-insensitive dictionary (default behavior)
+ AVDictionary2 *dict2 = NULL;
+ av_dict2_set(&dict2, "Key", "value1", 0);
+
+ AVDictionaryEntry2 *entry2 = av_dict2_get(dict2, "key", NULL, 0);
+ printf("Case-insensitive lookup: %s (value: %s)\n",
+ entry2 ? "OK" : "FAILED",
+ entry2 ? entry2->value : "NULL");
+ av_assert0(entry2 && !strcmp(entry2->value, "value1"));
+
+ av_dict2_free(&dict1);
+ av_dict2_free(&dict2);
+}
+
+static void stress_test(void)
+{
+ printf("\n=== Stress Test ===\n");
+
+ AVDictionary2 *dict = NULL;
+ char key[32], value[32];
+ int i, count, lookup_successful = 0;
+ int64_t start_time, elapsed;
+
+ // Create a large number of entries
+ const int num_entries = 10000;
+ printf("Creating %d entries...\n", num_entries);
+
+ start_time = av_gettime();
+ for (i = 0; i < num_entries; i++) {
+ sprintf(key, "key%d", i);
+ sprintf(value, "value%d", i);
+ av_dict2_set(&dict, key, value, 0);
+ }
+ elapsed = av_gettime() - start_time;
+ printf("Insertion time: %" PRId64 " us (%.2f us per entry)\n",
+ elapsed, (double)elapsed / num_entries);
+
+ // Test lookup of all keys
+ printf("Looking up all keys...\n");
+ start_time = av_gettime();
+ for (i = 0; i < num_entries; i++) {
+ sprintf(key, "key%d", i);
+ AVDictionaryEntry2 *entry = av_dict2_get(dict, key, NULL, 0);
+ if (entry) lookup_successful++;
+ }
+ elapsed = av_gettime() - start_time;
+ printf("Lookup time: %" PRId64 " us (%.2f us per lookup)\n",
+ elapsed, (double)elapsed / num_entries);
+ printf("Found %d of %d entries\n", lookup_successful, num_entries);
+ av_assert0(lookup_successful == num_entries);
+
+ // Check count
+ count = av_dict2_count(dict);
+ printf("Dictionary count: %d (expected %d)\n", count, num_entries);
+ av_assert0(count == num_entries);
+
+ // Free dictionary and measure cleanup time
+ start_time = av_gettime();
+ av_dict2_free(&dict);
+ elapsed = av_gettime() - start_time;
+ printf("Cleanup time: %" PRId64 " us\n", elapsed);
+ printf("Stress test completed successfully\n");
+}
+
+int main(int argc, char **argv)
+{
+ printf("AVDictionary2 Test Suite\n");
+ printf("========================\n");
+
+ // Check if specific test is requested
+ int run_stress = 0;
+ if (argc >= 2 && !strcmp(argv[1], "stress")) {
+ run_stress = 1;
+ }
+
+ // Always run basic tests
+ basic_functionality_test();
+ overwrite_test();
+ case_sensitivity_test();
+
+ // Run stress test if requested
+ if (run_stress) {
+ stress_test();
+ }
+
+ printf("\nAll tests PASSED!\n");
+ return 0;
+}
diff --git a/tests/api/Makefile b/tests/api/Makefile
index c96e636756..4d069f7bae 100644
--- a/tests/api/Makefile
+++ b/tests/api/Makefile
@@ -2,6 +2,7 @@ APITESTPROGS-$(call ENCDEC, FLAC, FLAC) += api-flac
APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264
APITESTPROGS-$(call DEMDEC, H264, H264) += api-h264-slice
APITESTPROGS-yes += api-seek
+APITESTPROGS-yes += api-dict2
APITESTPROGS-$(call DEMDEC, H263, H263) += api-band
APITESTPROGS-$(HAVE_THREADS) += api-threadmessage
APITESTPROGS += $(APITESTPROGS-yes)
diff --git a/tests/api/api-dict2-test.c b/tests/api/api-dict2-test.c
new file mode 100644
index 0000000000..a120a3488c
--- /dev/null
+++ b/tests/api/api-dict2-test.c
@@ -0,0 +1,122 @@
+/*
+ * AVDictionary2 test utility
+ * This file is part of FFmpeg.
+ */
+
+#include "libavutil/dict2.h"
+#include "libavutil/dict.h"
+#include "libavutil/time.h"
+#include "libavutil/avassert.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void basic_functionality_test(void)
+{
+ printf("\n=== Basic Functionality Test ===\n");
+
+ AVDictionary2 *dict = NULL;
+ AVDictionaryEntry2 *entry;
+ int ret;
+
+ // Test setting keys
+ ret = av_dict2_set(&dict, "key1", "value1", 0);
+ printf("Adding key1: %s\n", ret >= 0 ? "OK" : "FAILED");
+ av_assert0(ret >= 0);
+
+ ret = av_dict2_set(&dict, "key2", "value2", 0);
+ printf("Adding key2: %s\n", ret >= 0 ? "OK" : "FAILED");
+ av_assert0(ret >= 0);
+
+ // Test lookup
+ entry = av_dict2_get(dict, "key1", NULL, 0);
+ printf("Lookup key1: %s (value: %s)\n",
+ entry ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value1"));
+
+ // Test count
+ int count = av_dict2_count(dict);
+ printf("Dictionary count: %d (expected 2)\n", count);
+ av_assert0(count == 2);
+
+ // Test iteration
+ printf("Dictionary contents:\n");
+ const AVDictionaryEntry2 *iter = NULL;
+ while ((iter = av_dict2_iterate(dict, iter))) {
+ printf(" %s: %s\n", iter->key, iter->value);
+ }
+
+ // Free dictionary
+ av_dict2_free(&dict);
+ printf("Dictionary freed successfully\n");
+}
+
+static void overwrite_test(void)
+{
+ printf("\n=== Overwrite Test ===\n");
+
+ AVDictionary2 *dict = NULL;
+ AVDictionaryEntry2 *entry;
+
+ // Test normal overwrite
+ av_dict2_set(&dict, "key", "value1", 0);
+ av_dict2_set(&dict, "key", "value2", 0);
+
+ entry = av_dict2_get(dict, "key", NULL, 0);
+ printf("Overwrite test: %s (value: %s expected: value2)\n",
+ entry && !strcmp(entry->value, "value2") ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value2"));
+
+ // Test DONT_OVERWRITE flag
+ av_dict2_set(&dict, "key", "value3", AV_DICT2_DONT_OVERWRITE);
+
+ entry = av_dict2_get(dict, "key", NULL, 0);
+ printf("DONT_OVERWRITE flag test: %s (value: %s expected: value2)\n",
+ entry && !strcmp(entry->value, "value2") ? "OK" : "FAILED",
+ entry ? entry->value : "NULL");
+ av_assert0(entry && !strcmp(entry->value, "value2"));
+
+ av_dict2_free(&dict);
+}
+
+static void case_sensitivity_test(void)
+{
+ printf("\n=== Case Sensitivity Test ===\n");
+
+ // Test case-sensitive dictionary with AV_DICT2_MATCH_CASE flag
+ AVDictionary2 *dict1 = NULL;
+ av_dict2_set(&dict1, "Key", "value1", AV_DICT2_MATCH_CASE);
+
+ AVDictionaryEntry2 *entry1 = av_dict2_get(dict1, "key", NULL, AV_DICT2_MATCH_CASE);
+ printf("Case-sensitive lookup: %s (expected NULL)\n",
+ entry1 ? "FAILED" : "OK");
+ av_assert0(entry1 == NULL);
+
+ // Test case-insensitive dictionary (default behavior)
+ AVDictionary2 *dict2 = NULL;
+ av_dict2_set(&dict2, "Key", "value1", 0);
+
+ AVDictionaryEntry2 *entry2 = av_dict2_get(dict2, "key", NULL, 0);
+ printf("Case-insensitive lookup: %s (value: %s)\n",
+ entry2 ? "OK" : "FAILED",
+ entry2 ? entry2->value : "NULL");
+ av_assert0(entry2 && !strcmp(entry2->value, "value1"));
+
+ av_dict2_free(&dict1);
+ av_dict2_free(&dict2);
+}
+
+int main(void)
+{
+ printf("AVDictionary2 Test Suite\n");
+ printf("========================\n");
+
+ basic_functionality_test();
+ overwrite_test();
+ case_sensitivity_test();
+
+ printf("\nAll tests PASSED!\n");
+ return 0;
+}
diff --git a/tests/fate/api.mak b/tests/fate/api.mak
index d2868e57ac..18219eab1d 100644
--- a/tests/fate/api.mak
+++ b/tests/fate/api.mak
@@ -27,6 +27,21 @@ fate-api-threadmessage: $(APITESTSDIR)/api-threadmessage-test$(EXESUF)
fate-api-threadmessage: CMD = run $(APITESTSDIR)/api-threadmessage-test$(EXESUF) 3 10 30 50 2 20 40
fate-api-threadmessage: CMP = null
+FATE_API-yes += fate-api-dict2
+fate-api-dict2: $(APITESTSDIR)/api-dict2-test$(EXESUF)
+fate-api-dict2: CMD = run $(APITESTSDIR)/api-dict2-test$(EXESUF)
+fate-api-dict2: CMP = null
+
+# Dict2 implementation tests
+FATE_DICT2 = fate-dict2-basic fate-dict2-stress
+FATE_AVUTIL += $(FATE_DICT2)
+
+fate-dict2-basic: libavutil/tests/dict2$(EXESUF)
+fate-dict2-basic: CMD = run libavutil/tests/dict2 basic
+
+fate-dict2-stress: libavutil/tests/dict2$(EXESUF)
+fate-dict2-stress: CMD = run libavutil/tests/dict2 stress
+
FATE_API_SAMPLES-$(CONFIG_AVFORMAT) += $(FATE_API_SAMPLES_LIBAVFORMAT-yes)
ifdef SAMPLES
diff --git a/tools/Makefile b/tools/Makefile
index 7ae6e3cb75..d0a5c45c80 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,4 +1,4 @@
-TOOLS = enc_recon_frame_test enum_options qt-faststart scale_slice_test trasher uncoded_frame
+TOOLS = dict2_benchmark enc_recon_frame_test enum_options qt-faststart scale_slice_test trasher uncoded_frame
TOOLS-$(CONFIG_LIBMYSOFA) += sofa2wavs
TOOLS-$(CONFIG_ZLIB) += cws2fws
diff --git a/tools/dict2_benchmark.c b/tools/dict2_benchmark.c
new file mode 100644
index 0000000000..bdf34440b9
--- /dev/null
+++ b/tools/dict2_benchmark.c
@@ -0,0 +1,237 @@
+/*
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * AVDictionary vs AVDictionary2 Benchmark
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "libavutil/dict.h"
+#include "libavutil/dict2.h"
+#include "libavutil/time.h"
+
+#define RAND_STR_LEN 16
+#define TEST_ITERATIONS 5000
+
+/* Generate random string */
+static void gen_random_str(char *s, int len) {
+ static const char alphanum[] =
+ "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
+ }
+ s[len - 1] = '\0';
+}
+
+/* Fill a dictionary with random key-value pairs */
+static void fill_dict(AVDictionary **dict, int count) {
+ int i;
+ char key[RAND_STR_LEN];
+ char val[RAND_STR_LEN];
+
+ for (i = 0; i < count; i++) {
+ gen_random_str(key, RAND_STR_LEN);
+ gen_random_str(val, RAND_STR_LEN);
+ av_dict_set(dict, key, val, 0);
+ }
+}
+
+/* Fill a dictionary2 with random key-value pairs */
+static void fill_dict2(AVDictionary2 **dict, int count) {
+ int i;
+ char key[RAND_STR_LEN];
+ char val[RAND_STR_LEN];
+
+ for (i = 0; i < count; i++) {
+ gen_random_str(key, RAND_STR_LEN);
+ gen_random_str(val, RAND_STR_LEN);
+ av_dict2_set(dict, key, val, 0);
+ }
+}
+
+/* Generate lookup keys: some existing and some new */
+static char **gen_lookup_keys(int count, AVDictionary *dict, int hit_ratio_percent) {
+ int i, hits = count * hit_ratio_percent / 100;
+ char **keys = malloc(count * sizeof(char *));
+ if (!keys) return NULL;
+
+ // First add some keys that exist in the dictionary
+ AVDictionaryEntry *entry = NULL;
+ for (i = 0; i < hits && i < count; i++) {
+ entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX);
+ if (!entry) break; // Not enough entries
+
+ keys[i] = malloc(RAND_STR_LEN);
+ if (!keys[i]) {
+ while (--i >= 0) free(keys[i]);
+ free(keys);
+ return NULL;
+ }
+ strcpy(keys[i], entry->key);
+ }
+
+ // Fill the rest with random keys (likely misses)
+ for (; i < count; i++) {
+ keys[i] = malloc(RAND_STR_LEN);
+ if (!keys[i]) {
+ while (--i >= 0) free(keys[i]);
+ free(keys);
+ return NULL;
+ }
+ gen_random_str(keys[i], RAND_STR_LEN);
+ }
+
+ return keys;
+}
+
+/* Free lookup keys */
+static void free_lookup_keys(char **keys, int count) {
+ int i;
+ for (i = 0; i < count; i++) {
+ free(keys[i]);
+ }
+ free(keys);
+}
+
+int main(int argc, char *argv[])
+{
+ int count = 1000; // Default dictionary size
+ double time_start, time_end, time_dict, time_dict2;
+
+ // Parse command line for count
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ if (count <= 0) count = 1000;
+ }
+
+ printf("Benchmarking AVDictionary vs AVDictionary2 with %d entries\n\n", count);
+
+ srand(1234); // Fixed seed for reproducibility
+
+ // Setup dictionaries for insertion test
+ AVDictionary *dict1 = NULL;
+ AVDictionary2 *dict2 = NULL;
+
+ // Benchmark 1: Insertion
+ printf("1. Insertion Performance:\n");
+
+ time_start = av_gettime_relative() / 1000.0;
+ fill_dict(&dict1, count);
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict = time_end - time_start;
+ printf(" AVDictionary: %.3f ms\n", time_dict);
+
+ time_start = av_gettime_relative() / 1000.0;
+ fill_dict2(&dict2, count);
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict2 = time_end - time_start;
+ printf(" AVDictionary2: %.3f ms (%.1f%% of original time)\n",
+ time_dict2, time_dict2*100.0/time_dict);
+
+ // Benchmark 2: Lookup (existing keys - 100% hit rate)
+ printf("\n2. Lookup Performance (100%% existing keys):\n");
+
+ char **lookup_keys = gen_lookup_keys(TEST_ITERATIONS, dict1, 100);
+ if (!lookup_keys) {
+ fprintf(stderr, "Failed to generate lookup keys\n");
+ return 1;
+ }
+
+ time_start = av_gettime_relative() / 1000.0;
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ av_dict_get(dict1, lookup_keys[i], NULL, 0);
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict = time_end - time_start;
+ printf(" AVDictionary: %.3f ms\n", time_dict);
+
+ time_start = av_gettime_relative() / 1000.0;
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ av_dict2_get(dict2, lookup_keys[i], NULL, 0);
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict2 = time_end - time_start;
+ printf(" AVDictionary2: %.3f ms (%.1f%% of original time)\n",
+ time_dict2, time_dict2*100.0/time_dict);
+
+ free_lookup_keys(lookup_keys, TEST_ITERATIONS);
+
+ // Benchmark 3: Lookup (mixed keys - 50% hit rate)
+ printf("\n3. Lookup Performance (50%% existing keys):\n");
+
+ lookup_keys = gen_lookup_keys(TEST_ITERATIONS, dict1, 50);
+ if (!lookup_keys) {
+ fprintf(stderr, "Failed to generate lookup keys\n");
+ return 1;
+ }
+
+ time_start = av_gettime_relative() / 1000.0;
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ av_dict_get(dict1, lookup_keys[i], NULL, 0);
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict = time_end - time_start;
+ printf(" AVDictionary: %.3f ms\n", time_dict);
+
+ time_start = av_gettime_relative() / 1000.0;
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ av_dict2_get(dict2, lookup_keys[i], NULL, 0);
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict2 = time_end - time_start;
+ printf(" AVDictionary2: %.3f ms (%.1f%% of original time)\n",
+ time_dict2, time_dict2*100.0/time_dict);
+
+ free_lookup_keys(lookup_keys, TEST_ITERATIONS);
+
+ // Benchmark 4: Iteration
+ printf("\n4. Iteration Performance:\n");
+
+ time_start = av_gettime_relative() / 1000.0;
+ AVDictionaryEntry *entry = NULL;
+ while ((entry = av_dict_get(dict1, "", entry, AV_DICT_IGNORE_SUFFIX))) {
+ // Just iterate
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict = time_end - time_start;
+ printf(" AVDictionary: %.3f ms\n", time_dict);
+
+ time_start = av_gettime_relative() / 1000.0;
+ const AVDictionaryEntry2 *entry2 = NULL;
+ while ((entry2 = av_dict2_iterate(dict2, entry2))) {
+ // Just iterate
+ }
+ time_end = av_gettime_relative() / 1000.0;
+ time_dict2 = time_end - time_start;
+ printf(" AVDictionary2: %.3f ms (%.1f%% of original time)\n",
+ time_dict2, time_dict2*100.0/time_dict);
+
+ // Cleanup
+ av_dict_free(&dict1);
+ av_dict2_free(&dict2);
+
+ printf("\nBenchmark completed successfully\n");
+ return 0;
+}
--
ffmpeg-codebot
More information about the ffmpeg-devel
mailing list