libextractor

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

mkv_extractor.c (10411B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2004, 2005, 2006, 2009 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 2, 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  * Made by Gabriel Peixoto
     23  * Using AVInfo 1.x code. Copyright (c) 2004 George Shuklin.
     24  *
     25  */
     26 
     27 #include "platform.h"
     28 #include "extractor.h"
     29 #include <stdint.h>
     30 
     31 #define ADD(s,t) do { if (0 != (ret = proc (proc_cls, "mkv", t, \
     32                                             EXTRACTOR_METAFORMAT_UTF8, \
     33                                             "text/plain", s, strlen (s) \
     34                                             + 1))) goto EXIT; \
     35 } while (0)
     36 
     37 /**
     38  * FIXME: document
     39  */
     40 #define BUFFER_SIZE 0x4000
     41 
     42 /**
     43  * FIXME: document
     44  */
     45 #define MAX_STRING_SIZE 1024
     46 
     47 /**
     48  * FIXME: document
     49  */
     50 #define MAX_STREAMS 9
     51 
     52 /**
     53  * FIXME: document
     54  */
     55 enum MKV_TrackType
     56 {
     57   MKV_Track_video = 1,
     58   MKV_Track_audio = 2,
     59   MKV_Track_subtitle = 3,
     60   MKV_Track_subtitle_orig = 0x11
     61 };
     62 
     63 /**
     64  * FIXME: document
     65  */
     66 enum
     67 {
     68   MKVID_OutputSamplingFrequency = 0x78B5,
     69   MKVID_FILE_BEGIN = 0x1A,
     70   MKVID_EBML = 0x1A45DFA3,
     71   MKVID_Segment = 0x18538067,
     72   MKVID_Info = 0x1549A966,
     73   MKVID_Tracks = 0x1654AE6B,
     74   MKVID_TrackEntry = 0xAE,
     75   MKVID_TrackType = 0x83,
     76   MKVID_DefaultDuration = 0x23E383,
     77   MKVID_Language = 0x22B59C,
     78   MKVID_CodecID = 0x86,
     79   MKVID_CodecPrivate = 0x63A2,
     80   MKVID_PixelWidth = 0xB0,
     81   MKVID_PixelHeight = 0xBA,
     82   MKVID_TimeCodeScale = 0x2AD7B1,
     83   MKVID_Duration = 0x4489,
     84   MKVID_Channels = 0x9F,
     85   MKVID_BitDepth = 0x6264,
     86   MKVID_SamplingFrequency = 0xB5,
     87   MKVID_Title = 0x7BA9,
     88   MKVID_Tags = 0x1254C367,
     89   MKVID_SeekHead = 0x114D9B74,
     90   MKVID_Video = 0xE0,
     91   MKVID_Audio = 0xE1,
     92   MKVID_CodecName = 0x258688,
     93   MKVID_DisplayHeight = 0x54BA,
     94   MKVID_DisplayWidth = 0x54B0
     95 };
     96 
     97 
     98 /**
     99  * FIXME: document 'flag', should 'temp'/'result' really be signed?
    100  *
    101  * @return 0 on error, otherwise number of bytes read from buffer
    102  */
    103 static size_t
    104 VINTparse (const unsigned char *buffer, size_t start, size_t end,
    105            int64_t *result, int flag)
    106 {
    107   static const unsigned char mask[8] = { 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2,
    108                                          0x1 };
    109   static const unsigned char imask[8] = { 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1,
    110                                           00 };
    111   int vint_width;
    112   unsigned int c;
    113   int64_t temp;
    114   unsigned char tempc;
    115 
    116   if (end - start < 2)
    117     return 0;                   /*ops */
    118 
    119   vint_width = 0;
    120   for (c = 0; c < 8; c++)
    121     if (! (buffer[start] & mask[c]))
    122       vint_width++;
    123     else
    124       break;
    125   if ( (vint_width >= 8) || (vint_width + start + 1 >= end) )
    126     return 0;
    127 
    128   *result = 0;
    129   for (c = 0; c < vint_width; c++)
    130   {
    131     tempc = buffer[start + vint_width - c];
    132     temp = tempc << (c * 8);
    133     *result += temp;
    134   }
    135   if (flag)
    136     *result += (buffer[start] & imask[vint_width]) << (vint_width * 8);
    137   else
    138     *result += (buffer[start]) << (vint_width * 8);
    139   return vint_width + 1;
    140 }
    141 
    142 
    143 /**
    144  * FIXME: document arguments, return value...
    145  */
    146 static unsigned int
    147 elementRead (const unsigned char *buffer, size_t start, size_t end,
    148              uint32_t *id, int64_t *size)
    149 {
    150   int64_t tempID;
    151   int64_t tempsize;
    152   size_t id_offset;
    153   size_t size_offset;
    154 
    155   tempID = 0;
    156 
    157   id_offset = VINTparse (buffer, start, end, &tempID, 0);
    158   if (! id_offset)
    159     return 0;
    160   size_offset = VINTparse (buffer, start + id_offset, end, &tempsize, 1);
    161   if (! size_offset)
    162     return 0;
    163   *id = (uint32_t) tempID;
    164   *size = tempsize;
    165   return id_offset + size_offset;
    166 }
    167 
    168 
    169 /**
    170  * FIXME: signed or unsigned return value?
    171  */
    172 static int64_t
    173 getInt (const unsigned char *buffer, size_t start, size_t size)
    174 {
    175   /*return a int [8-64], from buffer, Big Endian*/
    176   int64_t result;
    177   size_t c;
    178 
    179   result = 0;
    180   for (c = 1; c <= size; c++)
    181     result += ((uint64_t) buffer[start + c - 1]) << (8 * (size - c));
    182   return result;
    183 }
    184 
    185 
    186 static float
    187 getFloat (const unsigned char *buffer, size_t start, size_t size)
    188 {
    189   unsigned char tmp[4];
    190 
    191   if (size != sizeof (float))
    192     return 0.0;
    193   if (size == sizeof (float))
    194   {
    195     tmp[0] = buffer[start + 3];
    196     tmp[1] = buffer[start + 2];
    197     tmp[2] = buffer[start + 1];
    198     tmp[3] = buffer[start];
    199     return *((float *) (tmp));
    200   }
    201   return 0.0;
    202 }
    203 
    204 
    205 static const unsigned int MKV_Parse_list[] = {
    206   /*Elements, containing requed information (sub-elements), see enum in mkv.h for values */
    207   MKVID_Segment,
    208   MKVID_Info,
    209   MKVID_Video,
    210   MKVID_Audio,
    211   MKVID_TrackEntry,
    212   MKVID_Tracks
    213 };
    214 
    215 static const char stream_type_letters[] = "?vat";      /*[0]-no, [1]-video,[2]-audio,[3]-text */
    216 
    217 
    218 /* video/mkv */
    219 int
    220 EXTRACTOR_mkv_extract (const unsigned char *data, size_t size,
    221                        EXTRACTOR_MetaDataProcessor proc, void *proc_cls,
    222                        const char *options)
    223 {
    224   int ret;
    225   char buffer[256];
    226   size_t p;
    227   int c;
    228   uint32_t eID;
    229   int64_t eSize; /* signed? */
    230   unsigned int offs;
    231   int64_t timescale = 1000000;
    232   float duration = -1.0;
    233   enum MKV_TrackType trackType;
    234   int have_audio = 0;
    235   int have_video = 0;
    236   unsigned int value_width = 0;
    237   unsigned int value_height = 0;
    238   int64_t value;
    239   size_t size1;
    240   const unsigned char *start;
    241   int is_mkv = 0;
    242   unsigned int fps = 0;
    243   unsigned int bit_depth = 0;
    244   const unsigned char *codec = NULL;
    245   int codec_strlen = 0;
    246 
    247   if (size > 32 * 1024)
    248     size1 = 32 * 1024;
    249   else
    250     size1 = size;
    251   start = memchr (data, MKVID_FILE_BEGIN, size1);
    252   if (NULL == start)
    253     return 0;
    254   p = start - data;
    255 
    256 /*main loop*/
    257   ret = 0;
    258   do
    259   {
    260     offs = elementRead (data, p, size, &eID, &eSize);
    261     p += offs;
    262     if (! offs || (p >= size))
    263       break;
    264     if (MKVID_EBML == eID)
    265     {
    266       ADD ("video/mkv", EXTRACTOR_METATYPE_MIMETYPE);
    267       is_mkv = 1;
    268       continue;
    269     }
    270     if (! is_mkv)
    271       return 0;
    272     for (c = 0; c < sizeof (MKV_Parse_list) / sizeof (*MKV_Parse_list); c++)
    273       if (MKV_Parse_list[c] == eID)
    274         break;
    275     if (c < sizeof (MKV_Parse_list) / sizeof (*MKV_Parse_list))
    276       continue;
    277 
    278     if (p + eSize > size)
    279       break;
    280 
    281     if ( (eSize == 4) || (eSize == 8) || (eSize == 1) || (eSize == 2))
    282       value = getInt (data, p, eSize);
    283 
    284     switch (eID)
    285     {
    286     case MKVID_TrackType:      /*detect a stream type (video/audio/text) */
    287       trackType = (enum MKV_TrackType) value;
    288       switch (trackType)
    289       {
    290       case MKV_Track_video:
    291         have_video = 1;
    292         break;
    293       case MKV_Track_audio:
    294         have_audio = 1;
    295         break;
    296       case MKV_Track_subtitle:
    297         break;
    298       case MKV_Track_subtitle_orig:
    299         break;
    300       }
    301       break;
    302     case MKVID_DefaultDuration:
    303       if (value > 0)
    304         fps = (unsigned int) (1000000000 / value);
    305       else
    306         fps = 0;
    307       break;
    308     case MKVID_Language:
    309       snprintf (buffer,
    310                 sizeof (buffer),
    311                 "%.*s",
    312                 (int) eSize,
    313                 data + p);
    314       ADD (buffer, EXTRACTOR_METATYPE_LANGUAGE);
    315       break;
    316     case MKVID_CodecName:
    317     case MKVID_CodecID:
    318       codec = data + p;
    319       codec_strlen = eSize;
    320       break;
    321     case MKVID_CodecPrivate:
    322       break;
    323     case MKVID_PixelWidth:     /*pasthough *//*bug with aspect differ from 1:1 */
    324     case MKVID_DisplayWidth:
    325       value_width = value;
    326       break;
    327     case MKVID_PixelHeight:    /*pasthough */
    328     case MKVID_DisplayHeight:
    329       value_height = value;
    330       break;
    331     case MKVID_TimeCodeScale:
    332       timescale = getInt (data, p, eSize);
    333       break;
    334     case MKVID_Duration:
    335       duration = getFloat (data, p, eSize);
    336       break;
    337     case MKVID_Channels:
    338       /* num_channels = (unsigned int) value; */
    339       break;
    340     case MKVID_BitDepth:
    341       bit_depth = (unsigned int) value;
    342       break;
    343     case MKVID_OutputSamplingFrequency:
    344     case MKVID_SamplingFrequency:
    345       /* FIXME: what unit has 'value'? */
    346       break;
    347     case MKVID_Title:
    348       if (eSize > MAX_STRING_SIZE)
    349         break;
    350       snprintf (buffer,
    351                 sizeof (buffer),
    352                 "%.*s",
    353                 (int) eSize,
    354                 (const char*) data + p);
    355       ADD (buffer, EXTRACTOR_METATYPE_TITLE);
    356       break;
    357     default:
    358       break;
    359     }
    360     p += eSize;                 /*skip unknown or uninteresting */
    361   }
    362   while (1);
    363 
    364   snprintf (buffer,
    365             sizeof (buffer),
    366             "%u s (%s%s%s)",
    367             (unsigned int) (duration / 1e+9 * (float) timescale),
    368             (have_audio ? "audio" : ""),
    369             ((have_audio && have_video) ? "/" : ""),
    370             (have_video ? "video" : ""));
    371   if ( (have_audio || have_video) && (duration >= 0.0) )
    372     ADD (buffer, EXTRACTOR_METATYPE_DURATION);
    373   if ( (value_width != 0) && (value_height != 0) )
    374   {
    375     snprintf (buffer,
    376               sizeof(buffer),
    377               "%ux%u",
    378               value_width, value_height);
    379     ADD (buffer, EXTRACTOR_METATYPE_IMAGE_DIMENSIONS);
    380   }
    381 
    382   if (NULL != codec)
    383   {
    384     if ( (fps != 0) && (bit_depth != 0) )
    385       snprintf (buffer,
    386                 sizeof (buffer),
    387                 "%.*s (%u fps, %u bit)",
    388                 codec_strlen,
    389                 codec,
    390                 fps,
    391                 bit_depth);
    392     else if (fps != 0)
    393       snprintf (buffer,
    394                 sizeof (buffer),
    395                 "%.*s (%u fps)",
    396                 codec_strlen,
    397                 codec,
    398                 fps);
    399     else if (bit_depth != 0)
    400       snprintf (buffer,
    401                 sizeof (buffer),
    402                 "%.*s (%u bit)",
    403                 codec_strlen,
    404                 codec,
    405                 bit_depth);
    406     else
    407       snprintf (buffer,
    408                 sizeof (buffer),
    409                 "%.*s",
    410                 codec_strlen,
    411                 codec);
    412     ADD (buffer,
    413          EXTRACTOR_METATYPE_FORMAT);
    414   }
    415 EXIT:
    416   return ret;
    417 }