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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".