libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

test_media_lib.c (7604B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2026 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/test_media_lib.c
     22  * @brief helpers for testing the video thumbnail and audio preview plugins
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "test_media_lib.h"
     27 
     28 #if TEST_MEDIA_HAVE_PNG
     29 #include <zlib.h>
     30 #endif
     31 
     32 /**
     33  * How far a decoded colour may be off before we call it a mismatch.
     34  */
     35 #define COLOR_TOLERANCE 48
     36 
     37 
     38 /**
     39  * Read a big endian 32 bit number.
     40  *
     41  * @param p bytes to read
     42  * @return the number
     43  */
     44 static uint32_t
     45 get_u32 (const uint8_t *p)
     46 {
     47   return (((uint32_t) p[0]) << 24)
     48          | (((uint32_t) p[1]) << 16)
     49          | (((uint32_t) p[2]) << 8)
     50          | ((uint32_t) p[3]);
     51 }
     52 
     53 
     54 #if TEST_MEDIA_HAVE_PNG
     55 /**
     56  * The Paeth predictor of the PNG specification.
     57  *
     58  * @param a pixel to the left
     59  * @param b pixel above
     60  * @param c pixel above left
     61  * @return the predicted value
     62  */
     63 static uint8_t
     64 paeth (uint8_t a,
     65        uint8_t b,
     66        uint8_t c)
     67 {
     68   int p = (int) a + (int) b - (int) c;
     69   int pa = abs (p - (int) a);
     70   int pb = abs (p - (int) b);
     71   int pc = abs (p - (int) c);
     72 
     73   if ( (pa <= pb) &&
     74        (pa <= pc) )
     75     return a;
     76   if (pb <= pc)
     77     return b;
     78   return c;
     79 }
     80 
     81 
     82 /**
     83  * Undo the per-scanline filters of a decompressed PNG image.
     84  *
     85  * @param[in,out] raw the filtered scanlines, overwritten with the image
     86  * @param width width of the image in pixels
     87  * @param height height of the image in pixels
     88  * @param bpp number of bytes per pixel
     89  * @return 1 on success
     90  */
     91 static int
     92 unfilter (uint8_t *raw,
     93           uint32_t width,
     94           uint32_t height,
     95           uint32_t bpp)
     96 {
     97   uint32_t stride = width * bpp;
     98   uint8_t *prev = NULL;
     99   uint32_t y;
    100 
    101   for (y = 0; y < height; y++)
    102   {
    103     uint8_t *row = &raw[y * (stride + 1)];
    104     uint8_t ft = row[0];
    105     uint32_t x;
    106 
    107     row++;
    108     for (x = 0; x < stride; x++)
    109     {
    110       uint8_t a = (x >= bpp) ? row[x - bpp] : 0;
    111       uint8_t b = (NULL != prev) ? prev[x] : 0;
    112       uint8_t c = ( (x >= bpp) && (NULL != prev) ) ? prev[x - bpp] : 0;
    113 
    114       switch (ft)
    115       {
    116       case 0:
    117         break;
    118       case 1:
    119         row[x] += a;
    120         break;
    121       case 2:
    122         row[x] += b;
    123         break;
    124       case 3:
    125         row[x] += (uint8_t) (((int) a + (int) b) / 2);
    126         break;
    127       case 4:
    128         row[x] += paeth (a, b, c);
    129         break;
    130       default:
    131         return 0;
    132       }
    133     }
    134     prev = row;
    135   }
    136   return 1;
    137 }
    138 
    139 
    140 #endif
    141 
    142 
    143 int
    144 TEST_media_png_center (const void *data,
    145                        size_t data_len,
    146                        uint8_t rgb[3])
    147 {
    148 #if ! TEST_MEDIA_HAVE_PNG
    149   (void) data;
    150   (void) data_len;
    151   (void) rgb;
    152   return 0;
    153 #else
    154   static const uint8_t magic[] = { 137, 'P', 'N', 'G', '\r', '\n', 26, '\n' };
    155   const uint8_t *d = data;
    156   uint8_t *idat = NULL;
    157   uint8_t *raw = NULL;
    158   size_t idat_len = 0;
    159   size_t pos = sizeof (magic);
    160   uLongf raw_len;
    161   uint32_t width = 0;
    162   uint32_t height = 0;
    163   uint32_t bpp = 0;
    164   uint32_t stride;
    165   const uint8_t *px;
    166   int ret = 0;
    167 
    168   if ( (data_len < sizeof (magic) + 12) ||
    169        (0 != memcmp (d, magic, sizeof (magic))) )
    170     return 0;
    171   while (pos + 12 <= data_len)
    172   {
    173     uint32_t clen = get_u32 (&d[pos]);
    174     const char *ctype = (const char *) &d[pos + 4];
    175     const uint8_t *cdata = &d[pos + 8];
    176 
    177     if ( (clen > data_len) ||
    178          (pos + 12 + clen > data_len) )
    179       break;
    180     if (0 == memcmp (ctype, "IHDR", 4))
    181     {
    182       if (clen < 13)
    183         goto cleanup;
    184       width = get_u32 (&cdata[0]);
    185       height = get_u32 (&cdata[4]);
    186       if ( (8 != cdata[8]) ||
    187            (0 != cdata[12]) )
    188         goto cleanup;   /* not 8 bits per channel, or interlaced */
    189       if (2 == cdata[9])
    190         bpp = 3;
    191       else if (6 == cdata[9])
    192         bpp = 4;
    193       else
    194         goto cleanup;   /* palette or greyscale; our encoders emit neither */
    195       if ( (0 == width) ||
    196            (0 == height) ||
    197            (width > 4096) ||
    198            (height > 4096) )
    199         goto cleanup;
    200     }
    201     else if (0 == memcmp (ctype, "IDAT", 4))
    202     {
    203       uint8_t *n = realloc (idat, idat_len + clen);
    204 
    205       if (NULL == n)
    206         goto cleanup;
    207       idat = n;
    208       memcpy (&idat[idat_len],
    209               cdata,
    210               clen);
    211       idat_len += clen;
    212     }
    213     else if (0 == memcmp (ctype, "IEND", 4))
    214     {
    215       break;
    216     }
    217     pos += 12 + clen;
    218   }
    219   if ( (0 == bpp) ||
    220        (NULL == idat) )
    221     goto cleanup;
    222   stride = width * bpp;
    223   raw_len = (uLongf) height * (stride + 1);
    224   if (NULL == (raw = malloc (raw_len)))
    225     goto cleanup;
    226   if (Z_OK != uncompress (raw,
    227                           &raw_len,
    228                           idat,
    229                           (uLong) idat_len))
    230     goto cleanup;
    231   if (raw_len != (uLongf) height * (stride + 1))
    232     goto cleanup;
    233   if (! unfilter (raw, width, height, bpp))
    234     goto cleanup;
    235   px = &raw[(height / 2) * (stride + 1) + 1 + (width / 2) * bpp];
    236   rgb[0] = px[0];
    237   rgb[1] = px[1];
    238   rgb[2] = px[2];
    239   ret = 1;
    240 cleanup:
    241   free (raw);
    242   free (idat);
    243   return ret;
    244 #endif
    245 }
    246 
    247 
    248 int
    249 TEST_media_png_center_is (const void *data,
    250                           size_t data_len,
    251                           uint8_t r,
    252                           uint8_t g,
    253                           uint8_t b)
    254 {
    255   uint8_t rgb[3];
    256 
    257   if (! TEST_media_png_center (data,
    258                                data_len,
    259                                rgb))
    260   {
    261 #if TEST_MEDIA_HAVE_PNG
    262     fprintf (stderr,
    263              "Failed to decode the PNG the plugin produced\n");
    264     return 0;
    265 #else
    266     /* without zlib we cannot look inside; the magic number check that
    267        the caller already did will have to do */
    268     return 1;
    269 #endif
    270   }
    271   if ( (abs ((int) rgb[0] - (int) r) > COLOR_TOLERANCE) ||
    272        (abs ((int) rgb[1] - (int) g) > COLOR_TOLERANCE) ||
    273        (abs ((int) rgb[2] - (int) b) > COLOR_TOLERANCE) )
    274   {
    275     fprintf (stderr,
    276              "Thumbnail centre is rgb(%u,%u,%u), expected rgb(%u,%u,%u); "
    277              "the plugin most likely sampled the wrong position\n",
    278              rgb[0],
    279              rgb[1],
    280              rgb[2],
    281              r,
    282              g,
    283              b);
    284     return 0;
    285   }
    286   return 1;
    287 }
    288 
    289 
    290 int
    291 TEST_media_is_ogg_opus (const void *data,
    292                         size_t data_len)
    293 {
    294   const uint8_t *d = data;
    295   uint8_t segments;
    296   size_t payload;
    297 
    298   /* an Ogg page header is 27 bytes plus one byte per segment */
    299   if (data_len < 28 + 8)
    300     return 0;
    301   if (0 != memcmp (d, "OggS", 4))
    302     return 0;
    303   if (0 != d[4])
    304     return 0;   /* stream structure version */
    305   segments = d[26];
    306   payload = 27 + (size_t) segments;
    307   if (payload + 8 > data_len)
    308     return 0;
    309   if (0 != memcmp (&d[payload],
    310                    "OpusHead",
    311                    8))
    312   {
    313     fprintf (stderr,
    314              "Ogg stream does not start with an OpusHead header\n");
    315     return 0;
    316   }
    317   return 1;
    318 }
    319 
    320 
    321 /* end of test_media_lib.c */