[FFmpeg-devel] [PATCH v2 18/18] cbs: Add metadata test
Mark Thompson
sw at jkqxz.net
Sun Feb 21 21:51:25 EET 2021
This tests insert/extract/remove for each supported codec/type combination.
---
libavcodec/Makefile | 1 +
libavcodec/tests/cbs_metadata.c | 390 ++++++++++++++++++++++++++++++++
tests/fate/libavcodec.mak | 5 +
3 files changed, 396 insertions(+)
create mode 100644 libavcodec/tests/cbs_metadata.c
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 43a54caba8..9133afcd0e 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -1225,6 +1225,7 @@ TESTPROGS = avpacket \
utils \
TESTPROGS-$(CONFIG_CABAC) += cabac
+TESTPROGS-$(CONFIG_CBS) += cbs_metadata
TESTPROGS-$(CONFIG_DCT) += avfft
TESTPROGS-$(CONFIG_FFT) += fft fft-fixed32
TESTPROGS-$(CONFIG_GOLOMB) += golomb
diff --git a/libavcodec/tests/cbs_metadata.c b/libavcodec/tests/cbs_metadata.c
new file mode 100644
index 0000000000..260a32ce7a
--- /dev/null
+++ b/libavcodec/tests/cbs_metadata.c
@@ -0,0 +1,390 @@
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include "libavutil/mastering_display_metadata.h"
+#include "libavutil/stereo3d.h"
+
+#include "libavcodec/cbs.h"
+#include "libavcodec/cbs_metadata.h"
+
+typedef struct MetadataTestCodec {
+ enum AVCodecID codec_id;
+ enum CBSMetadataType *types;
+ const uint8_t *input;
+ size_t input_size;
+} MetadataTestCodec;
+
+typedef struct MetadataTestType {
+ enum CBSMetadataType type;
+ const void *input;
+ int (*compare)(const void *input, const void *output);
+} MetadataTestType;
+
+// These test inputs are a single frame of 64x64 black.
+
+static uint8_t h264_test_stream[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x0a, 0xac, 0xd9, 0x44, 0x26,
+ 0xc0, 0x44, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xc8,
+ 0x3c, 0x48, 0x96, 0x58, 0x00, 0x00, 0x00, 0x01, 0x68, 0xeb, 0xe3, 0xcb,
+ 0x22, 0xc0, 0x00, 0x00, 0x01, 0x65, 0x88, 0x84, 0x00, 0x2b, 0xff, 0xfe,
+ 0xd8, 0xe7, 0xf3, 0x2c, 0xa7, 0xf4, 0xda, 0xbb, 0xf0, 0xac, 0xc4, 0x99,
+ 0x8a, 0xa5, 0xc3, 0xab, 0x2f, 0x6b, 0xe0, 0x64, 0x26, 0xdd, 0x1d, 0x09,
+ 0x60, 0xb8, 0x0d, 0x60, 0xf6, 0x89
+};
+
+static uint8_t h265_test_stream[] = {
+ 0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
+ 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
+ 0x1e, 0x95, 0x98, 0x09, 0x00, 0x00, 0x00, 0x01, 0x42, 0x01, 0x01, 0x01,
+ 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
+ 0x00, 0x1e, 0xa0, 0x20, 0x81, 0x05, 0x96, 0x56, 0x69, 0x24, 0xca, 0xf0,
+ 0x16, 0x80, 0x80, 0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x0c, 0x84,
+ 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0xc1, 0x72, 0xb4, 0x22, 0x40, 0x00,
+ 0x00, 0x01, 0x28, 0x01, 0xaf, 0x1d, 0x80, 0xf7, 0xcf, 0x80, 0xff, 0xf8,
+ 0x90, 0xfa, 0x3b, 0x77, 0x87, 0x96, 0x96, 0xbc, 0x7c
+};
+
+static uint8_t av1_test_stream[] = {
+ 0x12, 0x00, 0x0a, 0x0a,
+ 0x00, 0x00, 0x00, 0x02, 0xaf, 0xff, 0x89, 0x5f, 0x20, 0x08, 0x32, 0x16,
+ 0x10, 0x00, 0xa6, 0x80, 0x10, 0x40, 0x82, 0x08, 0x00, 0x00, 0x4b, 0xb7,
+ 0x9a, 0x9b, 0x57, 0x72, 0xea, 0xe9, 0x28, 0xb8, 0x15, 0x30
+};
+
+static const MetadataTestCodec test_codecs[] = {
+#if CONFIG_CBS_H264
+ {
+ AV_CODEC_ID_H264,
+ (enum CBSMetadataType[]) {
+ CBS_METADATA_MASTERING_DISPLAY,
+ CBS_METADATA_CONTENT_LIGHT_LEVEL,
+ CBS_METADATA_DISPLAY_MATRIX,
+ CBS_METADATA_STEREO3D,
+ CBS_METADATA_NONE,
+ },
+ h264_test_stream,
+ sizeof(h264_test_stream),
+ },
+#endif
+#if CONFIG_CBS_H265
+ {
+ AV_CODEC_ID_H265,
+ (enum CBSMetadataType[]) {
+ CBS_METADATA_MASTERING_DISPLAY,
+ CBS_METADATA_CONTENT_LIGHT_LEVEL,
+ CBS_METADATA_NONE,
+ },
+ h265_test_stream,
+ sizeof(h265_test_stream),
+ },
+#endif
+#if CONFIG_CBS_AV1
+ {
+ AV_CODEC_ID_AV1,
+ (enum CBSMetadataType[]) {
+ CBS_METADATA_MASTERING_DISPLAY,
+ CBS_METADATA_CONTENT_LIGHT_LEVEL,
+ CBS_METADATA_NONE,
+ },
+ av1_test_stream,
+ sizeof(av1_test_stream),
+ },
+#endif
+};
+
+#define VALUE_RATIONAL(name) do { \
+ AVRational diff = av_sub_q(a->name, b->name); \
+ diff.num = FFABS(diff.num); \
+ if (av_cmp_q(diff, av_make_q(1, 25000)) >= 0) { \
+ av_log(NULL, AV_LOG_ERROR, \
+ "%s differs by %d/%d after reading back.\n", \
+ #name, diff.num, diff.den); \
+ return 1; \
+ } \
+ } while (0)
+
+#define VALUE_ENUM(name) do { \
+ if (a->name != b->name) { \
+ av_log(NULL, AV_LOG_ERROR, \
+ "%s differs after reading back.\n", #name); \
+ return 1; \
+ } \
+ } while (0)
+
+#define VALUE_UINT(name) do { \
+ unsigned int diff; \
+ if (a->name > b->name) \
+ diff = a->name - b->name; \
+ else \
+ diff = b->name - a->name; \
+ if (diff > 1) { \
+ av_log(NULL, AV_LOG_ERROR, \
+ "%s differs by %d after reading back.\n", \
+ #name, diff); \
+ return 1; \
+ } \
+ } while (0)
+
+static int compare_mastering_display_metadata(const void *va,
+ const void *vb)
+{
+ const AVMasteringDisplayMetadata *a = va, *b = vb;
+ VALUE_RATIONAL(display_primaries[0][0]);
+ VALUE_RATIONAL(display_primaries[0][1]);
+ VALUE_RATIONAL(display_primaries[1][0]);
+ VALUE_RATIONAL(display_primaries[1][1]);
+ VALUE_RATIONAL(display_primaries[2][0]);
+ VALUE_RATIONAL(display_primaries[2][1]);
+ VALUE_RATIONAL(white_point[0]);
+ VALUE_RATIONAL(white_point[1]);
+ VALUE_RATIONAL(min_luminance);
+ VALUE_RATIONAL(max_luminance);
+ return 0;
+}
+
+static int compare_content_light_level_metadata(const void *va,
+ const void *vb)
+{
+ const AVContentLightMetadata *a = va, *b = vb;
+ VALUE_UINT(MaxCLL);
+ VALUE_UINT(MaxFALL);
+ return 0;
+}
+
+static int compare_display_matrix(const void *va,
+ const void *vb)
+{
+ const int32_t *a = va, *b = vb;
+ for (int i = 0; i < 9; i++) {
+ int diff = FFABS(a[i] - b[i]);
+ if (diff > 1) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Matrix %d differs by %d after reading back.\n",
+ i, diff);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int compare_stereo3d(const void *va,
+ const void *vb)
+{
+ const AVStereo3D *a = va, *b = vb;
+ VALUE_ENUM(type);
+ VALUE_ENUM(view);
+ return 0;
+}
+
+static const MetadataTestType test_types[] = {
+ {
+ CBS_METADATA_MASTERING_DISPLAY,
+ &(AVMasteringDisplayMetadata) {
+ // This test colour space is BT.2020.
+ .display_primaries = {
+ { { 708, 1000 }, { 292, 1000 }, },
+ { { 170, 1000 }, { 797, 1000 }, },
+ { { 131, 1000 }, { 46, 1000 }, },
+ },
+ .white_point = {
+ { 3127, 10000 }, { 3290, 10000 },
+ },
+ // These are arbitrary numbers in a plausible range.
+ .min_luminance = { 56, 100 },
+ .max_luminance = { 1234, 1 },
+ .has_primaries = 1,
+ .has_luminance = 1,
+ },
+ &compare_mastering_display_metadata,
+ },
+ {
+ CBS_METADATA_CONTENT_LIGHT_LEVEL,
+ &(AVContentLightMetadata) {
+ .MaxCLL = 1234,
+ .MaxFALL = 567,
+ },
+ &compare_content_light_level_metadata,
+ },
+ {
+ CBS_METADATA_DISPLAY_MATRIX,
+ &(int32_t[9]) {
+ // This is a quarter-turn clockwise.
+ 0, -1 * (1 << 16), 0,
+ 1 * (1 << 16), 0, 0,
+ 0, 0, 1 << 30,
+ },
+ &compare_display_matrix,
+ },
+ {
+ CBS_METADATA_STEREO3D,
+ &(AVStereo3D) {
+ .type = AV_STEREO3D_SIDEBYSIDE,
+ .flags = 0,
+ .view = AV_STEREO3D_VIEW_PACKED,
+ },
+ &compare_stereo3d,
+ },
+};
+
+#define CHECK(op) do { \
+ if ((op) < 0) { \
+ av_log(NULL, AV_LOG_ERROR, "%s failed.\n", #op); \
+ return 1; \
+ } \
+ } while (0)
+
+int main(void)
+{
+ // None of these tests care about any persistent header information,
+ // so we use the same context for all reading/writing.
+ CodedBitstreamContext *cbc;
+ CodedBitstreamFragment frag, frag2;
+ AVPacket *pkt;
+ int c, t, i;
+
+ memset(&frag, 0, sizeof(frag));
+ memset(&frag2, 0, sizeof(frag2));
+
+ pkt = av_packet_alloc();
+ if (!pkt)
+ return 1;
+
+ for (c = 0; c < FF_ARRAY_ELEMS(test_codecs); c++) {
+ const MetadataTestCodec *test_codec;
+ const AVCodecDescriptor *codec_desc;
+
+ test_codec = &test_codecs[c];
+ codec_desc = avcodec_descriptor_get(test_codec->codec_id);
+ if (!codec_desc) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid codec %d used in test.\n",
+ test_codec->codec_id);
+ return 1;
+ }
+
+ av_log(NULL, AV_LOG_INFO,
+ "Testing codec %d (%s):\n",
+ test_codec->codec_id, codec_desc->name);
+
+ CHECK(ff_cbs_init(&cbc, test_codec->codec_id, NULL));
+
+ // Having tracing enabled on the CBS read/write operations may be
+ // helpful when debug problems found during this test.
+ if (0) {
+ cbc->trace_enable = 1;
+ cbc->trace_level = AV_LOG_INFO;
+ }
+
+ CHECK(ff_cbs_read(cbc, &frag,
+ test_codec->input, test_codec->input_size));
+
+ // Verify no change.
+ CHECK(ff_cbs_write_packet(cbc, pkt, &frag));
+ if (memcmp(pkt->data, test_codec->input, test_codec->input_size)) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Passthrough output does not match.\n");
+ return 1;
+ }
+
+ for (t = 0; t < FF_ARRAY_ELEMS(test_types); t++) {
+ const MetadataTestType *test_type;
+ const CBSMetadataTypeDescriptor *type_desc;
+ void *output;
+
+ test_type = &test_types[t];
+ type_desc = ff_cbs_metadata_find_type(test_type->type);
+ if (!type_desc) {
+ av_log(NULL, AV_LOG_ERROR, "Invalid metadata type %d "
+ "used in test.\n", test_type->type);
+ return 1;
+ }
+
+ for (i = 0; test_codec->types[i] != CBS_METADATA_NONE; i++) {
+ if (test_codec->types[i] == test_type->type)
+ break;
+ }
+ if (test_codec->types[i] != test_type->type) {
+ // Type not supported by this codec.
+ continue;
+ }
+
+ av_log(NULL, AV_LOG_INFO,
+ "Testing metadata type %d (%s) with codec %d (%s).\n",
+ test_type->type, type_desc->name,
+ test_codec->codec_id, codec_desc->name);
+
+ // Insert the metadata into the packet.
+ CHECK(ff_cbs_insert_metadata(cbc, &frag,
+ test_type->type,
+ test_type->input));
+
+ // Write packet containing the new metadata.
+ CHECK(ff_cbs_write_packet(cbc, pkt, &frag));
+
+ // Ensure that that actually did something.
+ if (pkt->size == test_codec->input_size &&
+ memcmp(pkt->data, test_codec->input,
+ test_codec->input_size) == 0) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Output not changed by metadata addition.\n");
+ return 1;
+ }
+
+ // Read packet back into a different fragment.
+ CHECK(ff_cbs_read_packet(cbc, &frag2, pkt));
+
+ // Make a new instance of the same structure to extract to.
+ output = ff_cbs_alloc_metadata(test_type->type, NULL);
+ if (!output)
+ return 1;
+
+ // Extract the metadata from the new fragment.
+ CHECK(ff_cbs_extract_metadata(cbc, &frag2,
+ test_type->type, output));
+
+ // Make sure the result is sensible.
+ if (test_type->compare(test_type->input, output)) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Output metadata does not match input.\n");
+ return 1;
+ }
+
+ av_free(output);
+ ff_cbs_fragment_free(&frag2);
+
+ // Remove the metadata from the fragment again.
+ CHECK(ff_cbs_remove_metadata(cbc, &frag, test_type->type));
+
+ // Write it back to make sure it's been removed.
+ CHECK(ff_cbs_write_packet(cbc, pkt, &frag));
+ if (pkt->size != test_codec->input_size ||
+ memcmp(pkt->data, test_codec->input,
+ test_codec->input_size)) {
+ av_log(NULL, AV_LOG_ERROR,
+ "Output changed after adding/removing metadata.\n");
+ return 1;
+ }
+ }
+
+ ff_cbs_fragment_free(&frag);
+ ff_cbs_close(&cbc);
+ }
+
+ return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index 747dae3704..e303e46b48 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -8,6 +8,11 @@ fate-cabac: libavcodec/tests/cabac$(EXESUF)
fate-cabac: CMD = run libavcodec/tests/cabac$(EXESUF)
fate-cabac: CMP = null
+FATE_LIBAVCODEC-$(CONFIG_CBS) += fate-cbs-metadata
+fate-cbs-metadata: libavcodec/tests/cbs_metadata$(EXESUF)
+fate-cbs-metadata: CMD = run libavcodec/tests/cbs_metadata$(EXESUF)
+fate-cbs-metadata: CMP = null
+
FATE_LIBAVCODEC-yes += fate-celp_math
fate-celp_math: libavcodec/tests/celp_math$(EXESUF)
fate-celp_math: CMD = run libavcodec/tests/celp_math$(EXESUF)
--
2.30.0
More information about the ffmpeg-devel
mailing list