libextractor

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

real_extractor.c (14930B)


      1 /*
      2  * This file is part of libextractor.
      3  * Copyright (C) 2021 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 /**
     22  * @file plugins/real_extractor.c
     23  * @brief plugin to support REAL files
     24  * @author Christian Grothoff
     25  */
     26 #include "platform.h"
     27 #include "extractor.h"
     28 #include "le_architecture.h"
     29 
     30 /* The structs below describe on-disk layout and are overlaid directly
     31    on the (unaligned) read buffer, so they must be packed: without that
     32    the compiler is entitled to assume natural alignment and emit loads
     33    that are undefined for the addresses they are actually used at -- and
     34    fatal on a strict-alignment target.  The attribute goes on the struct
     35    rather than on each member because gcc ignores it on aggregate and
     36    array members. */
     37 LE_NETWORK_STRUCT_BEGIN
     38 struct MediaProperties
     39 {
     40   uint32_t object_id;
     41   uint32_t size;
     42   uint16_t object_version;        /* must be 0 */
     43   uint16_t stream_number;
     44   uint32_t max_bit_rate;
     45   uint32_t avg_bit_rate;
     46   uint32_t max_packet_size;
     47   uint32_t avg_packet_size;
     48   uint32_t start_time;
     49   uint32_t preroll;
     50   uint32_t duration;
     51   uint8_t stream_name_size;
     52   uint8_t data[0];                /* variable length section */
     53   /*
     54      uint8_t[stream_name_size]     stream_name;
     55      uint8_t                       mime_type_size;
     56      uint8_t[mime_type_size]       mime_type;
     57      uint32_t                      type_specific_len;
     58      uint8_t[type_specific_len]    type_specific_data;
     59    */
     60 } LE_PACKED;
     61 LE_NETWORK_STRUCT_END
     62 
     63   LE_NETWORK_STRUCT_BEGIN
     64 struct ContentDescription
     65 {
     66   uint32_t object_id;
     67   uint32_t size;
     68   uint16_t object_version;        /* must be 0 */
     69   uint16_t title_len;
     70   uint8_t data[0];                /* variable length section */
     71   /*
     72      uint8_t[title_len]  title;
     73      uint16_t    author_len;
     74      uint8_t[author_len]  author;
     75      uint16_t    copyright_len;
     76      uint8_t[copyright_len]  copyright;
     77      uint16_t    comment_len;
     78      uint8_t[comment_len]  comment;
     79    */
     80 } LE_PACKED;
     81 LE_NETWORK_STRUCT_END
     82 /* author, copyright and comment are supposed to be ASCII */
     83 
     84 
     85 /**
     86  * Read a big-endian 16 bit value from @a p, which need not be aligned.
     87  *
     88  * @param p (unaligned) source
     89  * @return the value in host byte order
     90  */
     91 static uint16_t
     92 read_be16 (const void *p)
     93 {
     94   uint16_t v;
     95 
     96   memcpy (&v, p, sizeof (v));
     97   return ntohs (v);
     98 }
     99 
    100 
    101 /**
    102  * Read a big-endian 32 bit value from @a p, which need not be aligned.
    103  *
    104  * @param p (unaligned) source
    105  * @return the value in host byte order
    106  */
    107 static uint32_t
    108 read_be32 (const void *p)
    109 {
    110   uint32_t v;
    111 
    112   memcpy (&v, p, sizeof (v));
    113   return ntohl (v);
    114 }
    115 
    116 
    117 #define REAL_HEADER 0x2E524d46
    118 #define MDPR_HEADER 0x4D445052
    119 #define CONT_HEADER 0x434F4e54
    120 #define RAFF4_HEADER 0x2E7261FD
    121 
    122 
    123 /**
    124  * Give meta data to LE.
    125  *
    126  * @param s utf-8 string meta data value
    127  * @param t type of the meta data
    128  */
    129 #define ADD(s,t) do { \
    130           if (0 != ec->proc (ec->cls, "real", t, \
    131                              EXTRACTOR_METAFORMAT_C_STRING, \
    132                              "text/plain", s, strlen (s) + 1)) \
    133           { return; } \
    134 } while (0)
    135 
    136 
    137 static void
    138 processMediaProperties (const struct MediaProperties *prop,
    139                         struct EXTRACTOR_ExtractContext *ec)
    140 {
    141   uint8_t mime_type_size;
    142   uint32_t prop_size;
    143 
    144   prop_size = ntohl (prop->size);
    145   if (prop_size <= sizeof (struct MediaProperties))
    146     return;
    147   if (0 != prop->object_version)
    148     return;
    149   if (prop_size <= prop->stream_name_size + sizeof (uint8_t)
    150       + sizeof (struct MediaProperties))
    151     return;
    152   mime_type_size = prop->data[prop->stream_name_size];
    153   if (prop_size > prop->stream_name_size + sizeof (uint8_t)
    154       + mime_type_size + sizeof (struct MediaProperties))
    155   {
    156     char data[mime_type_size + 1];
    157 
    158     memcpy (data,
    159             &prop->data[prop->stream_name_size + 1],
    160             mime_type_size);
    161     data[mime_type_size] = '\0';
    162     ADD (data,
    163          EXTRACTOR_METATYPE_MIMETYPE);
    164   }
    165 }
    166 
    167 
    168 static void
    169 processContentDescription (const struct ContentDescription *prop,
    170                            struct EXTRACTOR_ExtractContext *ec)
    171 {
    172   uint16_t author_len;
    173   uint16_t copyright_len;
    174   uint16_t comment_len;
    175   uint16_t title_len;
    176   uint32_t prop_size;
    177 
    178   prop_size = ntohl (prop->size);
    179   if (prop_size <= sizeof (struct ContentDescription))
    180     return;
    181   if (0 != prop->object_version)
    182     return;
    183   title_len = ntohs (prop->title_len);
    184   if (prop_size <=
    185       title_len
    186       + sizeof (struct ContentDescription))
    187     return;
    188   if (title_len > 0)
    189   {
    190     char title[title_len + 1];
    191 
    192     memcpy (title,
    193             &prop->data[0],
    194             title_len);
    195     title[title_len] = '\0';
    196     ADD (title,
    197          EXTRACTOR_METATYPE_TITLE);
    198   }
    199   if (prop_size <=
    200       title_len
    201       + sizeof (uint16_t)
    202       + sizeof (struct ContentDescription))
    203     return;
    204   author_len = read_be16 (&prop->data[title_len]);
    205   if (prop_size <=
    206       title_len
    207       + sizeof (uint16_t)
    208       + author_len
    209       + sizeof (struct ContentDescription))
    210     return;
    211   if (author_len > 0)
    212   {
    213     char author[author_len + 1];
    214 
    215     memcpy (author,
    216             &prop->data[title_len
    217                         + sizeof (uint16_t)],
    218             author_len);
    219     author[author_len] = '\0';
    220     ADD (author,
    221          EXTRACTOR_METATYPE_AUTHOR_NAME);
    222   }
    223   if (prop_size <=
    224       title_len
    225       + sizeof (uint16_t)
    226       + author_len
    227       + sizeof (uint16_t)
    228       + sizeof (struct ContentDescription))
    229     return;
    230   copyright_len = read_be16 (&prop->data[title_len
    231                                          + author_len
    232                                          + sizeof (uint16_t)]);
    233   if (prop_size <=
    234       title_len
    235       + sizeof (uint16_t)
    236       + author_len
    237       + sizeof (uint16_t)
    238       + copyright_len
    239       + sizeof (struct ContentDescription))
    240     return;
    241   if (copyright_len > 0)
    242   {
    243     char copyright[copyright_len + 1];
    244 
    245     memcpy (copyright,
    246             &prop->data[title_len
    247                         + sizeof (uint16_t) * 2
    248                         + author_len],
    249             copyright_len);
    250     copyright[copyright_len] = '\0';
    251     ADD (copyright,
    252          EXTRACTOR_METATYPE_COPYRIGHT);
    253   }
    254 
    255   if (prop_size <=
    256       title_len
    257       + sizeof (uint16_t)
    258       + author_len
    259       + sizeof (uint16_t)
    260       + copyright_len
    261       + sizeof (uint16_t)
    262       + sizeof (struct ContentDescription))
    263     return;
    264   comment_len = read_be16 (&prop->data[title_len
    265                                        + author_len
    266                                        + copyright_len
    267                                        + 2 * sizeof (uint16_t)]);
    268   if (prop_size <
    269       title_len
    270       + sizeof (uint16_t)
    271       + author_len
    272       + sizeof (uint16_t)
    273       + copyright_len
    274       + sizeof (uint16_t)
    275       + comment_len
    276       + sizeof (struct ContentDescription))
    277     return;
    278 
    279   if (comment_len > 0)
    280   {
    281     char comment[comment_len + 1];
    282 
    283     memcpy (comment,
    284             &prop->data[title_len
    285                         + sizeof (uint16_t) * 3
    286                         + author_len
    287                         + copyright_len],
    288             comment_len);
    289     comment[comment_len] = '\0';
    290     ADD (comment,
    291          EXTRACTOR_METATYPE_COMMENT);
    292   }
    293 }
    294 
    295 
    296 LE_NETWORK_STRUCT_BEGIN
    297 struct RAFF_Header
    298 {
    299   uint16_t version;
    300 } LE_PACKED;
    301 LE_NETWORK_STRUCT_END
    302 
    303   LE_NETWORK_STRUCT_BEGIN
    304 struct RAFF3_Header
    305 {
    306   uint8_t unknown[10];
    307   uint32_t data_size;
    308   /*
    309      uint8_t tlen;
    310      uint8_t title[tlen];
    311      uint8_t alen;
    312      uint8_t author[alen];
    313      uint8_t clen;
    314      uint8_t copyright[clen];
    315      uint8_t aplen;
    316      uint8_t app[aplen]; */
    317 } LE_PACKED;
    318 LE_NETWORK_STRUCT_END
    319 
    320 
    321 #define RAFF3_HDR_SIZE 14
    322 
    323 
    324 LE_NETWORK_STRUCT_BEGIN
    325 struct RAFF4_Header
    326 {
    327   uint16_t version;
    328   uint16_t revision;
    329   uint16_t header_length;
    330   uint16_t compression_type;
    331   uint32_t granularity;
    332   uint32_t total_bytes;
    333   uint32_t bytes_per_minute;
    334   uint32_t bytes_per_minute2;
    335   uint16_t interleave_factor;
    336   uint16_t interleave_block_size;
    337   uint32_t user_data;
    338   float sample_rate;
    339   uint16_t sample_size;
    340   uint16_t channels;
    341   uint8_t interleave_code[5];
    342   uint8_t compression_code[5];
    343   uint8_t is_interleaved;
    344   uint8_t copy_byte;
    345   uint8_t stream_type;
    346   /*
    347      uint8_t tlen;
    348      uint8_t title[tlen];
    349      uint8_t alen;
    350      uint8_t author[alen];
    351      uint8_t clen;
    352      uint8_t copyright[clen];
    353      uint8_t aplen;
    354      uint8_t app[aplen]; */
    355 } LE_PACKED;
    356 LE_NETWORK_STRUCT_END
    357 
    358 #define RAFF4_HDR_SIZE 53
    359 
    360 
    361 static void
    362 extract_raff3 (struct EXTRACTOR_ExtractContext *ec,
    363                const void *ptr,
    364                size_t size)
    365 {
    366   const uint8_t *data = ptr;
    367   uint8_t tlen;
    368   uint8_t alen;
    369   uint8_t clen;
    370   uint8_t aplen;
    371 
    372   if (size <= RAFF3_HDR_SIZE + 8)
    373     return;
    374   tlen = data[8 + RAFF3_HDR_SIZE];
    375   if (tlen + RAFF3_HDR_SIZE + 12 > size)
    376     return;
    377   if (tlen > 0)
    378   {
    379     char x[tlen + 1];
    380 
    381     memcpy (x,
    382             &data[9 + RAFF3_HDR_SIZE],
    383             tlen);
    384     x[tlen] = '\0';
    385     ADD (x,
    386          EXTRACTOR_METATYPE_TITLE);
    387   }
    388   alen = data[9 + tlen + RAFF3_HDR_SIZE];
    389   if (tlen + alen + RAFF3_HDR_SIZE + 12 > size)
    390     return;
    391   if (alen > 0)
    392   {
    393     char x[alen + 1];
    394 
    395     memcpy (x,
    396             &data[10 + RAFF3_HDR_SIZE + tlen],
    397             alen);
    398     x[alen] = '\0';
    399     ADD (x,
    400          EXTRACTOR_METATYPE_AUTHOR_NAME);
    401   }
    402   clen = data[10 + tlen + alen + RAFF3_HDR_SIZE];
    403   if (tlen + alen + clen + RAFF3_HDR_SIZE + 12 > size)
    404     return;
    405   if (clen > 0)
    406   {
    407     char x[clen + 1];
    408 
    409     memcpy (x,
    410             &data[11 + RAFF3_HDR_SIZE + tlen + alen],
    411             clen);
    412     x[clen] = '\0';
    413     ADD (x,
    414          EXTRACTOR_METATYPE_COPYRIGHT);
    415   }
    416   aplen = data[11 + tlen + clen + alen + RAFF3_HDR_SIZE];
    417   if (tlen + alen + clen + aplen + RAFF3_HDR_SIZE + 12 > size)
    418     return;
    419   if (aplen > 0)
    420   {
    421     char x[aplen + 1];
    422 
    423     memcpy (x,
    424             &data[12 + RAFF3_HDR_SIZE + tlen + alen + clen],
    425             aplen);
    426     x[aplen] = '\0';
    427     ADD (x,
    428          EXTRACTOR_METATYPE_UNKNOWN);
    429   }
    430 }
    431 
    432 
    433 static void
    434 extract_raff4 (struct EXTRACTOR_ExtractContext *ec,
    435                const void *ptr,
    436                size_t size)
    437 {
    438   const uint8_t *data = ptr;
    439   uint8_t tlen;
    440   uint8_t alen;
    441   uint8_t clen;
    442   uint8_t aplen;
    443 
    444   if (size <= RAFF4_HDR_SIZE + 16 + 4)
    445     return;
    446   tlen = data[16 + RAFF4_HDR_SIZE];
    447   if (tlen + RAFF4_HDR_SIZE + 20 > size)
    448     return;
    449   alen = data[17 + tlen + RAFF4_HDR_SIZE];
    450   if (tlen + alen + RAFF4_HDR_SIZE + 20 > size)
    451     return;
    452   clen = data[18 + tlen + alen + RAFF4_HDR_SIZE];
    453   if (tlen + alen + clen + RAFF4_HDR_SIZE + 20 > size)
    454     return;
    455   aplen = data[19 + tlen + clen + alen + RAFF4_HDR_SIZE];
    456   if (tlen + alen + clen + aplen + RAFF4_HDR_SIZE + 20 > size)
    457     return;
    458   if (tlen > 0)
    459   {
    460     char x[tlen + 1];
    461 
    462     memcpy (x,
    463             &data[17 + RAFF4_HDR_SIZE],
    464             tlen);
    465     x[tlen] = '\0';
    466     ADD (x,
    467          EXTRACTOR_METATYPE_TITLE);
    468   }
    469   if (alen > 0)
    470   {
    471     char x[alen + 1];
    472 
    473     memcpy (x,
    474             &data[18 + RAFF4_HDR_SIZE + tlen],
    475             alen);
    476     x[alen] = '\0';
    477     ADD (x,
    478          EXTRACTOR_METATYPE_AUTHOR_NAME);
    479   }
    480   if (clen > 0)
    481   {
    482     char x[clen + 1];
    483 
    484     memcpy (x,
    485             &data[19 + RAFF4_HDR_SIZE + tlen + alen],
    486             clen);
    487     x[clen] = '\0';
    488     ADD (x,
    489          EXTRACTOR_METATYPE_COPYRIGHT);
    490   }
    491   if (aplen > 0)
    492   {
    493     char x[aplen + 1];
    494 
    495     memcpy (x,
    496             &data[20 + RAFF4_HDR_SIZE + tlen + alen + clen],
    497             aplen);
    498     x[aplen] = '\0';
    499     ADD (x,
    500          EXTRACTOR_METATYPE_UNKNOWN);
    501   }
    502 }
    503 
    504 
    505 static void
    506 extract_raff (struct EXTRACTOR_ExtractContext *ec,
    507               const void *ptr,
    508               size_t size)
    509 {
    510   const uint8_t *data = ptr;
    511   const struct RAFF_Header *hdr;
    512 
    513   /* HELIX */
    514   if (size <= sizeof (*hdr) + 4)
    515     return;
    516   ADD ("audio/vnd.rn-realaudio",
    517        EXTRACTOR_METATYPE_MIMETYPE);
    518   hdr = (const struct RAFF_Header *) &data[4];
    519   switch (ntohs (hdr->version))
    520   {
    521   case 3:
    522     extract_raff3 (ec,
    523                    ptr,
    524                    size);
    525     break;
    526   case 4:
    527     extract_raff4 (ec,
    528                    ptr,
    529                    size);
    530     break;
    531   }
    532 }
    533 
    534 
    535 /* old real format */
    536 static void
    537 extract_real (struct EXTRACTOR_ExtractContext *ec,
    538               const void *data,
    539               size_t size)
    540 {
    541   uint64_t off = 0;
    542   size_t pos = 0;
    543 
    544   while (1)
    545   {
    546     uint32_t length;
    547 
    548     if ( (pos + 8 > size) ||
    549          (pos + 8 < pos) ||
    550          (pos + (length = read_be32 ((const char *) data + pos + 4)) > size) )
    551     {
    552       uint64_t noff;
    553       void *in;
    554       ssize_t isize;
    555 
    556       noff = ec->seek (ec->cls,
    557                        off + pos,
    558                        SEEK_SET);
    559       if (-1 == noff)
    560         return;
    561       isize = ec->read (ec->cls,
    562                         &in,
    563                         32 * 1024);
    564       if (isize < 8)
    565         return;
    566       data = in;
    567       size = isize;
    568       off = noff;
    569       pos = 0;
    570       /* re-read the atom header from the freshly filled buffer;
    571          the previous value of 'length' refers to the old buffer
    572          (or was never assigned due to short-circuit evaluation) */
    573       length = read_be32 ((const char *) data + pos + 4);
    574     }
    575     if (length <= 8)
    576       return;
    577     if ( (pos + length > size) ||
    578          (pos + length < pos) )
    579       return;
    580     switch (read_be32 ((const char *) data + pos))
    581     {
    582     case MDPR_HEADER:
    583       processMediaProperties (data + pos,
    584                               ec);
    585       pos += length;
    586       break;
    587     case CONT_HEADER:
    588       processContentDescription (data + pos,
    589                                  ec);
    590       pos += length;
    591       break;
    592     case REAL_HEADER:          /* treat like default */
    593     default:
    594       pos += length;
    595       break;
    596     }
    597   }
    598 }
    599 
    600 
    601 /**
    602  * "extract" metadata from a REAL file
    603  *
    604  * @param ec extraction context
    605  */
    606 void
    607 EXTRACTOR_real_extract_method (struct EXTRACTOR_ExtractContext *ec)
    608 {
    609   void *data;
    610   ssize_t n;
    611 
    612   n = ec->read (ec->cls,
    613                 &data,
    614                 sizeof (struct RAFF4_Header) + 4 * 256);
    615   if (n < (ssize_t) sizeof (uint32_t))
    616     return;
    617   switch (read_be32 (data))
    618   {
    619   case RAFF4_HEADER:
    620     extract_raff (ec,
    621                   data,
    622                   n);
    623     break;
    624   case REAL_HEADER:
    625     extract_real (ec,
    626                   data,
    627                   n);
    628     break;
    629   }
    630 }
    631 
    632 
    633 /* end of real_extractor.c */