From patchwork Sun Feb 21 19:51:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Thompson X-Patchwork-Id: 25877 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id C6E9A449DB5 for ; Sun, 21 Feb 2021 21:53:53 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B26F568A9E1; Sun, 21 Feb 2021 21:53:53 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f43.google.com (mail-wr1-f43.google.com [209.85.221.43]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 1946468A8A5 for ; Sun, 21 Feb 2021 21:53:43 +0200 (EET) Received: by mail-wr1-f43.google.com with SMTP id v14so16892805wro.7 for ; Sun, 21 Feb 2021 11:53:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jkqxz-net.20150623.gappssmtp.com; s=20150623; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=4yrxjQQ1rGzsgdtKtUvOcH/RVKvdTT0/G5dh5P6NCAQ=; b=vwTKeqad+PagEBOJwgygPfN+h3mHvthC2nShWxPFlLCBH6CcRM50DFjGeOB0/r8ro5 et7DvHCPAfkoEYTBlOiIPUY+xhE1gVXDf/YIlFxYKyfZ8lr9xu59xOu7Ia7cqckOCIVz eF5fW6hCu7Dkk9Dcaq4GNQjmnNwO/bLoQp6aHT9pWGDvO7BEoYcgckiviYgA/MIUuDg/ mE2FAaQ1bT4x1YVGh2RzNRRaZBcgzzJqtKCrMgMtKRDSYTgcIep/VHpYW2YJ5wtObSif suxWW5pj/cG4rOfLXMsH9Sj71Dq8QvjivDUQzDP479VhdT4Q5MLPvXviJlNA/4txd6GR J7Xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=4yrxjQQ1rGzsgdtKtUvOcH/RVKvdTT0/G5dh5P6NCAQ=; b=r2ZkVi9qXtwiEIkCD8d7I0pNieWWTDzGo00g0OBbc7TcMbIqJdaFoEWkADpFh6UXIT BpS0/kfJDXr04+Ybppqu3QYshwCtwlqztwEcySH3soLPZaSmR0Fq3ahawNX9PnT/C/+W XL1E9StuuCldq2mD2DeE57BjmptACywNt/+UNSjoNIstuPiiKh0MAX58VcuI0valIXzA ekHcIwtbumKh2yhvmzkOHSHx4rb62Plf0KUJkbwPEPTXHnubccD1diL5O6IdrMn5ADcA 4rI8mf6+5BMbALSXLFOuGrL97SExP4kqtv5cN7Z2eRuG/HQ8oBNEHAminmyNxWEEHTz7 pK8A== X-Gm-Message-State: AOAM532kF1uXqdWXh6uDJJ05uNnvLimpNhJwzeJyBo2vTZDKFW18DPkO cAHfaUMLQRWf4JvytzAIeZtvrBk5d9t9GA== X-Google-Smtp-Source: ABdhPJwp9IslA8EPSfzcPEW+WWcfXJUUmL7nZdG8laymHz+CJ15TWdNvYiHQb/eN+GisSdXQy7Q7ew== X-Received: by 2002:a5d:63ce:: with SMTP id c14mr5096814wrw.15.1613937222547; Sun, 21 Feb 2021 11:53:42 -0800 (PST) Received: from localhost.localdomain (cpc91226-cmbg18-2-0-cust7.5-4.cable.virginm.net. [82.0.29.8]) by smtp.gmail.com with ESMTPSA id z18sm1780372wrs.19.2021.02.21.11.53.41 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 Feb 2021 11:53:42 -0800 (PST) From: Mark Thompson To: ffmpeg-devel@ffmpeg.org Date: Sun, 21 Feb 2021 19:51:25 +0000 Message-Id: <20210221195125.1901683-18-sw@jkqxz.net> X-Mailer: git-send-email 2.30.0 In-Reply-To: <20210221195125.1901683-1-sw@jkqxz.net> References: <20210221195125.1901683-1-sw@jkqxz.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 18/18] cbs: Add metadata test X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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)