libextractor

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

flv_extractor.c (30724B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright Copyright (C) 2007, 2009 Heikki Lindholm
      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  * see http://osflash.org/flv
     23  *     http://osflash.org/documentation/amf
     24  */
     25 #include "platform.h"
     26 #include "extractor.h"
     27 #include "convert_numeric.h"
     28 #include <string.h>
     29 
     30 #define DEBUG 0
     31 
     32 #define FLV_SIGNATURE "FLV"
     33 
     34 /*
     35  * AMF parser
     36  */
     37 
     38 /* Actionscript types */
     39 #define ASTYPE_NUMBER       0x00
     40 #define ASTYPE_BOOLEAN      0x01
     41 #define ASTYPE_STRING       0x02
     42 #define ASTYPE_OBJECT       0x03
     43 #define ASTYPE_MOVIECLIP    0x04
     44 #define ASTYPE_NULL         0x05
     45 #define ASTYPE_UNDEFINED    0x06
     46 #define ASTYPE_REFERENCE    0x07
     47 #define ASTYPE_MIXEDARRAY   0x08
     48 #define ASTYPE_ENDOFOBJECT  0x09
     49 #define ASTYPE_ARRAY        0x0a
     50 #define ASTYPE_DATE         0x0b
     51 #define ASTYPE_LONGSTRING   0x0c
     52 #define ASTYPE_UNSUPPORTED  0x0d
     53 #define ASTYPE_RECORDSET    0x0e
     54 #define ASTYPE_XML          0x0f
     55 #define ASTYPE_TYPEDOBJECT  0x10
     56 #define ASTYPE_AMF3DATA     0x11
     57 
     58 typedef struct
     59 {
     60   void *userdata;
     61   void (*as_begin_callback)(unsigned char type, void *userdata);
     62   void (*as_key_callback)(char *key, void *userdata);
     63   void (*as_end_callback)(unsigned char type, void *value, void *userdata);
     64 } AMFParserHandler;
     65 
     66 /* core datatypes */
     67 
     68 static uint32_t
     69 readInt32 (const unsigned char **data)
     70 {
     71   const unsigned char *ptr = *data;
     72   uint32_t val;
     73 
     74   val = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
     75   ptr += 4;
     76   *data = ptr;
     77   return val;
     78 }
     79 
     80 
     81 static uint32_t
     82 readInt24 (const unsigned char **data)
     83 {
     84   const unsigned char *ptr = *data;
     85   uint32_t val;
     86 
     87   val = (ptr[0] << 16) | (ptr[1] << 8) | ptr[2];
     88   ptr += 3;
     89   *data = ptr;
     90   return val;
     91 }
     92 
     93 
     94 static uint16_t
     95 readInt16 (const unsigned char **data)
     96 {
     97   const unsigned char *ptr = *data;
     98   uint16_t val;
     99 
    100   val = (ptr[0] << 8) | ptr[1];
    101   ptr += 2;
    102   *data = ptr;
    103   return val;
    104 }
    105 
    106 
    107 static double
    108 readDouble (const unsigned char **data)
    109 {
    110   const unsigned char *ptr = *data;
    111   double val;
    112 
    113   EXTRACTOR_common_floatformat_to_double (
    114     &EXTRACTOR_floatformat_ieee_double_big,
    115     (const void *) ptr,
    116     &val);
    117   ptr += 8;
    118   *data = ptr;
    119   return val;
    120 }
    121 
    122 
    123 /* actionscript types */
    124 
    125 static int
    126 readASNumber (const unsigned char **data,
    127               size_t *len,
    128               double *retval)
    129 {
    130   const unsigned char *ptr = *data;
    131   double val;
    132 
    133   if (*len < 8)
    134     return -1;
    135 
    136   val = readDouble (&ptr);
    137   *len -= 8;
    138 
    139   *retval = val;
    140   *data = ptr;
    141   return 0;
    142 }
    143 
    144 
    145 static int
    146 readASBoolean (const unsigned char **data,
    147                size_t *len,
    148                int *retval)
    149 {
    150   const unsigned char *ptr = *data;
    151   int val;
    152 
    153   if (*len < 1)
    154     return -1;
    155 
    156   val = (*ptr != 0x00);
    157   ptr += 1;
    158   *len -= 1;
    159 
    160   *retval = val;
    161   *data = ptr;
    162   return 0;
    163 }
    164 
    165 
    166 static int
    167 readASDate (const unsigned char **data,
    168             size_t *len,
    169             double *millis,
    170             short *zone)
    171 {
    172   const unsigned char *ptr = *data;
    173 
    174   if (*len < 10)
    175     return -1;
    176 
    177   *millis = readDouble (&ptr);
    178   *len -= 8;
    179 
    180   *zone = readInt16 (&ptr);
    181   *len -= 2;
    182 
    183   *data = ptr;
    184   return 0;
    185 }
    186 
    187 
    188 static int
    189 readASString (const unsigned char **data,
    190               size_t *len,
    191               char **retval)
    192 {
    193   const unsigned char *ptr = *data;
    194   char *ret;
    195   int slen;
    196 
    197   if (*len < 2)
    198     return -1;
    199 
    200   slen = readInt16 (&ptr);
    201 
    202   if (*len < (2 + slen))
    203     return -1;
    204 
    205   ret = malloc (slen + 1);
    206   if (ret == NULL)
    207     return -1;
    208   memcpy (ret, ptr, slen);
    209   ret[slen] = '\0';
    210   ptr += slen;
    211   *len -= (2 + slen);
    212 
    213   *retval = ret;
    214   *data = ptr;
    215   return 0;
    216 }
    217 
    218 
    219 static int
    220 parse_amf (const unsigned char **data,
    221            size_t *len,
    222            AMFParserHandler *handler)
    223 {
    224   const unsigned char *ptr = *data;
    225   unsigned char astype;
    226   int ret;
    227 
    228   ret = 0;
    229   astype = *ptr++;
    230   (*(handler->as_begin_callback))(astype, handler->userdata);
    231   switch (astype)
    232   {
    233   case ASTYPE_NUMBER:
    234     {
    235       double val;
    236       ret = readASNumber (&ptr, len, &val);
    237       if (ret == 0)
    238         (*(handler->as_end_callback))(astype,
    239                                       &val,
    240                                       handler->userdata);
    241       break;
    242     }
    243   case ASTYPE_BOOLEAN:
    244     {
    245       int val;
    246       ret = readASBoolean (&ptr, len, &val);
    247       if (ret == 0)
    248         (*(handler->as_end_callback))(astype,
    249                                       &val,
    250                                       handler->userdata);
    251       break;
    252     }
    253   case ASTYPE_STRING:
    254     {
    255       char *val;
    256       ret = readASString (&ptr, len, &val);
    257       if (ret == 0)
    258       {
    259         (*(handler->as_end_callback))(astype,
    260                                       val,
    261                                       handler->userdata);
    262         free (val);
    263       }
    264       break;
    265     }
    266   case ASTYPE_DATE:
    267     {
    268       void *tmp[2];
    269       double millis;
    270       short tz;
    271       ret = readASDate (&ptr, len, &millis, &tz);
    272       tmp[0] = &millis;
    273       tmp[1] = &tz;
    274       if (ret == 0)
    275         (*(handler->as_end_callback))(astype,
    276                                       &tmp,
    277                                       handler->userdata);
    278       break;
    279     }
    280   case ASTYPE_NULL:
    281   case ASTYPE_UNDEFINED:
    282   case ASTYPE_UNSUPPORTED:
    283   case ASTYPE_ENDOFOBJECT:
    284     (*(handler->as_end_callback))(astype, NULL, handler->userdata);
    285     break;
    286   case ASTYPE_ARRAY:
    287     {
    288       long i, alen;
    289       if (*len < 4)
    290       {
    291         ret = -1;
    292         break;
    293       }
    294       alen = readInt32 (&ptr);
    295       *len -= 4;
    296       for (i = 0; i < alen; i++)
    297       {
    298         ret = parse_amf (&ptr, len, handler);
    299         if (ret == -1)
    300           break;
    301       }
    302       (*(handler->as_end_callback))(ASTYPE_ARRAY,
    303                                     NULL,
    304                                     handler->userdata);
    305       break;
    306     }
    307   case ASTYPE_OBJECT:
    308     {
    309       char *key;
    310       unsigned char type;
    311 
    312       ret = readASString (&ptr, len, &key);
    313       if (ret == -1)
    314         break;
    315       (*(handler->as_key_callback))(key,
    316                                     handler->userdata);
    317       free (key);
    318       type = *ptr;
    319       while (type != ASTYPE_ENDOFOBJECT)
    320       {
    321         ret = parse_amf (&ptr, len, handler);
    322         if (ret == -1)
    323           break;
    324         ret = readASString (&ptr, len, &key);
    325         if (ret == -1)
    326           break;
    327         (*(handler->as_key_callback))(key,
    328                                       handler->userdata);
    329         free (key);
    330         type = *ptr;
    331       }
    332       if (ret == 0)
    333         (*(handler->as_end_callback))(ASTYPE_OBJECT,
    334                                       NULL,
    335                                       handler->userdata);
    336       break;
    337     }
    338   case ASTYPE_MIXEDARRAY:
    339     {
    340       char *key;
    341       unsigned char type;
    342 
    343       if (*len < 4)
    344       {
    345         ret = -1;
    346         break;
    347       }
    348       /* max_index */ readInt32 (&ptr);
    349       *len -= 4;
    350       ret = readASString (&ptr, len, &key);
    351       if (ret == -1)
    352         break;
    353       (*(handler->as_key_callback))(key,
    354                                     handler->userdata);
    355       free (key);
    356       type = *ptr;
    357       while (type != ASTYPE_ENDOFOBJECT)
    358       {
    359         ret = parse_amf (&ptr, len, handler);
    360         if (ret == -1)
    361           break;
    362         ret = readASString (&ptr, len, &key);
    363         if (ret == -1)
    364           break;
    365         (*(handler->as_key_callback))(key,
    366                                       handler->userdata);
    367         free (key);
    368         type = *ptr;
    369       }
    370       if (ret == 0)
    371         (*(handler->as_end_callback))(astype,
    372                                       NULL,
    373                                       handler->userdata);
    374       break;
    375     }
    376   default:
    377     ret = -1;
    378     (*(handler->as_end_callback))(astype,
    379                                   NULL,
    380                                   handler->userdata);
    381 #if DEBUG
    382     printf ("parse_amf: Unknown type %02x", astype);
    383 #endif
    384     break;
    385   }
    386 
    387   *data = ptr;
    388   return ret;
    389 }
    390 
    391 
    392 /*
    393  * FLV parser
    394  */
    395 
    396 /* from tarextractor, modified to take timezone */
    397 /* TODO: check that the output date is correct */
    398 static int
    399 flv_to_iso_date (double timeval, short timezone,
    400                  char *rtime, unsigned int rsize)
    401 {
    402   int retval = 0;
    403 
    404   /*
    405    * shift epoch to proleptic times
    406    * to make subsequent modulo operations safer.
    407    */
    408   long long my_timeval = (timeval / 1000)
    409                          + ((long long) ((1970 * 365) + 478) * (long
    410                                                                 long) 86400);
    411 
    412   unsigned int seconds = (unsigned int) (my_timeval % 60);
    413   unsigned int minutes = (unsigned int) ((my_timeval / 60) % 60);
    414   unsigned int hours = (unsigned int) ((my_timeval / 3600) % 24);
    415 
    416   int zone_sign;
    417   int zone_hours;
    418   unsigned int zone_minutes;
    419 
    420   unsigned int year = 0;
    421   unsigned int month = 1;
    422 
    423   unsigned int days = (unsigned int) (my_timeval / (24 * 3600));
    424 
    425   unsigned int days_in_month[] =
    426   { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    427   unsigned int diff = 0;
    428 
    429   if ((long long) 0 > my_timeval)
    430     return EDOM;
    431 
    432   /*
    433    * 400-year periods
    434    */
    435   year += (400 * (days / ((365 * 400) + 97)));
    436   days %= ((365 * 400) + 97);
    437 
    438   /*
    439    * 100-year periods
    440    */
    441   diff = (days / ((365 * 100) + 24));
    442   if (4 <= diff)
    443   {
    444     year += 399;
    445     days = 364;
    446   }
    447   else
    448   {
    449     year += (100 * diff);
    450     days %= ((365 * 100) + 24);
    451   }
    452 
    453   /*
    454    * remaining leap years
    455    */
    456   year += (4 * (days / ((365 * 4) + 1)));
    457   days %= ((365 * 4) + 1);
    458 
    459   while (1)
    460   {
    461     if ((0 == (year % 400)) || ((0 == (year % 4)) && (0 != (year % 100))))
    462     {
    463       if (366 > days)
    464       {
    465         break;
    466       }
    467       else
    468       {
    469         days -= 366;
    470         year++;
    471       }
    472     }
    473     else
    474     {
    475       if (365 > days)
    476       {
    477         break;
    478       }
    479       else
    480       {
    481         days -= 365;
    482         year++;
    483       }
    484     }
    485   }
    486 
    487   if ((0 == (year % 400)) || ((0 == (year % 4)) && (0 != (year % 100))))
    488     days_in_month[1] = 29;
    489 
    490   for (month = 0; (month < 12) && (days >= days_in_month[month]); month += 1)
    491     days -= days_in_month[month];
    492 
    493   zone_sign = 0;
    494   if (timezone < 0)
    495   {
    496     zone_sign = -1;
    497     timezone = -timezone;
    498   }
    499   zone_hours = timezone / 60;
    500   zone_minutes = timezone - zone_hours * 60;
    501 
    502   retval = snprintf (rtime, rsize, "%04u-%02u-%02uT%02u:%02u:%02u%c%02d:%02u",
    503                      year, month + 1, days + 1, hours, minutes, seconds,
    504                      ((zone_sign < 0) ? '-' : '+'), zone_hours, zone_minutes);
    505 
    506   return (retval < rsize) ? 0 : EOVERFLOW;
    507 }
    508 
    509 
    510 typedef struct
    511 {
    512   char signature[3];
    513   unsigned char version;
    514   unsigned char flags;
    515   unsigned long offset;
    516 } FLVHeader;
    517 
    518 #define FLV_HEADER_SIZE 9
    519 
    520 #define FLV_TAG_TYPE_AUDIO 0x08
    521 #define FLV_TAG_TYPE_VIDEO 0x09
    522 #define FLV_TAG_TYPE_META  0x12
    523 
    524 typedef struct
    525 {
    526   unsigned char type;
    527   unsigned long bodyLength;
    528   uint32_t timestamp;
    529   unsigned long streamId;
    530 } FLVTagHeader;
    531 
    532 #define FLV_TAG_HEADER_SIZE 11
    533 
    534 static int
    535 readFLVHeader (const unsigned char **data,
    536                const unsigned char *end,
    537                FLVHeader *hdr)
    538 {
    539   const unsigned char *ptr = *data;
    540 
    541   if ((ptr + FLV_HEADER_SIZE) > end)
    542     return -1;
    543 
    544   memcpy (hdr->signature, ptr, 3);
    545   ptr += 3;
    546   hdr->version = *ptr++;
    547   hdr->flags = *ptr++;
    548   hdr->offset = readInt32 (&ptr);
    549   if (hdr->offset != FLV_HEADER_SIZE)
    550     return -1;
    551 
    552   *data = ptr;
    553   return 0;
    554 }
    555 
    556 
    557 static int
    558 readPreviousTagSize (const unsigned char **data,
    559                      const unsigned char *end,
    560                      unsigned long *prev_size)
    561 {
    562   const unsigned char *ptr = *data;
    563 
    564   if ((ptr + 4) > end)
    565     return -1;
    566 
    567   *prev_size = readInt32 (&ptr);
    568 
    569   *data = ptr;
    570   return 0;
    571 }
    572 
    573 
    574 static int
    575 readFLVTagHeader (const unsigned char **data,
    576                   const unsigned char *end,
    577                   FLVTagHeader *hdr)
    578 {
    579   const unsigned char *ptr = *data;
    580 
    581   if ((ptr + FLV_TAG_HEADER_SIZE) > end)
    582     return -1;
    583 
    584   hdr->type = *ptr++;
    585   hdr->bodyLength = readInt24 (&ptr);
    586   hdr->timestamp = readInt32 (&ptr);
    587   hdr->streamId = readInt24 (&ptr);
    588 
    589   *data = ptr;
    590   return 0;
    591 }
    592 
    593 
    594 typedef struct
    595 {
    596   int videoCodec;
    597   char *videoCodecStr;
    598   int videoWidth;
    599   int videoHeight;
    600   double videoDataRate;
    601   double videoFrameRate;
    602 
    603   int audioCodec;
    604   char *audioCodecStr;
    605   double audioDataRate;
    606   int audioChannels;
    607   int audioSampleBits;
    608   int audioRate;
    609 } FLVStreamInfo;
    610 
    611 typedef enum
    612 {
    613   FLV_NONE = 0,
    614   FLV_WIDTH,
    615   FLV_HEIGHT,
    616   FLV_FRAMERATE,
    617   FLV_STEREO,
    618   FLV_ACHANNELS,
    619   FLV_VDATARATE,
    620   FLV_ADATARATE,
    621   FLV_VCODECID,
    622   FLV_ACODECID
    623 } FLVStreamAttribute;
    624 
    625 typedef struct
    626 {
    627   const char *key;
    628   FLVStreamAttribute attribute;
    629 } MetaKeyToStreamAttribute;
    630 
    631 static MetaKeyToStreamAttribute key_to_attribute_map[] = {
    632   { "width", FLV_WIDTH },
    633   { "height", FLV_HEIGHT },
    634   { "framerate", FLV_FRAMERATE },
    635   { "videoframerate", FLV_FRAMERATE },
    636   { "stereo", FLV_STEREO },
    637   { "audiochannels", FLV_ACHANNELS },
    638   { "videodatarate", FLV_VDATARATE },
    639   { "audiodatarate", FLV_ADATARATE },
    640   { "videocodecid", FLV_VCODECID },
    641   { "audiocodecid", FLV_ACODECID },
    642   { NULL, FLV_NONE }
    643 };
    644 
    645 typedef struct
    646 {
    647   const char *key;
    648   enum EXTRACTOR_MetaType type;
    649 } MetaKeyToExtractorItem;
    650 
    651 static MetaKeyToExtractorItem key_to_extractor_map[] = {
    652   { "duration", EXTRACTOR_METATYPE_DURATION },
    653   { "creator", EXTRACTOR_METATYPE_CREATOR },
    654   { "metadatacreator", EXTRACTOR_METATYPE_CREATOR },
    655   { "creationdate", EXTRACTOR_METATYPE_CREATION_DATE },
    656   { "metadatadate", EXTRACTOR_METATYPE_MODIFICATION_DATE },
    657   { NULL, EXTRACTOR_METATYPE_RESERVED }
    658 };
    659 
    660 typedef struct
    661 {
    662   int onMetaData;
    663   int parsingDepth;
    664   int ret;
    665   /* mixed array keys mapped to something readily usable */
    666   enum EXTRACTOR_MetaType currentKeyType;
    667   FLVStreamAttribute currentAttribute;
    668 
    669   EXTRACTOR_MetaDataProcessor proc;
    670   void *proc_cls;
    671   FLVStreamInfo *streamInfo;
    672 } FLVMetaParserState;
    673 
    674 static void
    675 handleASBegin (unsigned char type, void *userdata)
    676 {
    677   FLVMetaParserState *state = (FLVMetaParserState *) userdata;
    678 
    679   if (state->onMetaData && (state->parsingDepth == 0) &&
    680       (type != ASTYPE_MIXEDARRAY) )
    681     state->onMetaData = 0;
    682 
    683   if ((type == ASTYPE_ARRAY) || (type == ASTYPE_MIXEDARRAY) ||
    684       (type == ASTYPE_OBJECT) )
    685     state->parsingDepth++;
    686 }
    687 
    688 
    689 static void
    690 handleASKey (char *key, void *userdata)
    691 {
    692   FLVMetaParserState *state = (FLVMetaParserState *) userdata;
    693   int i;
    694 
    695   if (key == NULL)
    696     return;
    697 
    698   i = 0;
    699   while ((key_to_extractor_map[i].key != NULL) &&
    700          (strcasecmp (key, key_to_extractor_map[i].key) != 0))
    701     i++;
    702   state->currentKeyType = key_to_extractor_map[i].type;
    703 
    704   i = 0;
    705   while ((key_to_attribute_map[i].key != NULL) &&
    706          (strcasecmp (key, key_to_attribute_map[i].key) != 0))
    707     i++;
    708   state->currentAttribute = key_to_attribute_map[i].attribute;
    709 }
    710 
    711 
    712 static void
    713 handleASEnd (unsigned char type, void *value, void *userdata)
    714 {
    715   FLVMetaParserState *state = (FLVMetaParserState *) userdata;
    716   const char *s;
    717   char tmpstr[30];
    718 
    719   if ((state->parsingDepth == 0) && (type == ASTYPE_STRING))
    720   {
    721     s = (const char *) value;
    722     if (! strcmp (s, "onMetaData"))
    723       state->onMetaData = 1;
    724   }
    725 
    726   /* we expect usable metadata to reside in a MIXEDARRAY container
    727    * right after a "onMetaData" STRING */
    728 
    729   /* stream info related metadata */
    730   if (state->onMetaData && (state->parsingDepth == 1) &&
    731       (state->currentAttribute != FLV_NONE) &&
    732       (type == ASTYPE_NUMBER))
    733   {
    734     double n = *((double *) value);
    735     switch (state->currentAttribute)
    736     {
    737     case FLV_NONE:   /* make gcc happy */
    738       break;
    739     case FLV_STEREO:
    740       break;
    741     case FLV_ACHANNELS:
    742       state->streamInfo->audioChannels = n;
    743       break;
    744     case FLV_WIDTH:
    745       if (state->streamInfo->videoWidth == -1)
    746         state->streamInfo->videoWidth = n;
    747       break;
    748     case FLV_HEIGHT:
    749       if (state->streamInfo->videoHeight == -1)
    750         state->streamInfo->videoHeight = n;
    751       break;
    752     case FLV_FRAMERATE:
    753       state->streamInfo->videoFrameRate = n;
    754       break;
    755     case FLV_VDATARATE:
    756       state->streamInfo->videoDataRate = n;
    757       break;
    758     case FLV_ADATARATE:
    759       state->streamInfo->audioDataRate = n;
    760       break;
    761     case FLV_VCODECID:
    762       if (state->streamInfo->videoCodec == -1)
    763         state->streamInfo->videoCodec = n;
    764       /* prefer codec ids to fourcc codes */
    765       if (state->streamInfo->videoCodecStr != NULL)
    766       {
    767         free (state->streamInfo->videoCodecStr);
    768         state->streamInfo->videoCodecStr = NULL;
    769       }
    770       break;
    771     case FLV_ACODECID:
    772       if (state->streamInfo->audioCodec == -1)
    773         state->streamInfo->audioCodec = n;
    774       /* prefer codec ids to fourcc codes */
    775       if (state->streamInfo->audioCodecStr != NULL)
    776       {
    777         free (state->streamInfo->audioCodecStr);
    778         state->streamInfo->audioCodecStr = NULL;
    779       }
    780       break;
    781     }
    782   }
    783 
    784   /* sometimes a/v codecs are as fourcc strings */
    785   if (state->onMetaData && (state->parsingDepth == 1) &&
    786       (state->currentAttribute != FLV_NONE) &&
    787       (type == ASTYPE_STRING))
    788   {
    789     s = (const char *) value;
    790     switch (state->currentAttribute)
    791     {
    792     case FLV_VCODECID:
    793       if ((s != NULL) && (state->streamInfo->videoCodecStr == NULL) &&
    794           (state->streamInfo->videoCodec == -1) )
    795         state->streamInfo->videoCodecStr = strdup (s);
    796       break;
    797     case FLV_ACODECID:
    798       if ((s != NULL) && (state->streamInfo->audioCodecStr == NULL) &&
    799           (state->streamInfo->audioCodec == -1) )
    800         state->streamInfo->audioCodecStr = strdup (s);
    801       break;
    802     default:
    803       break;
    804     }
    805   }
    806 
    807   if (state->onMetaData && (state->parsingDepth == 1) &&
    808       (state->currentAttribute == FLV_STEREO) &&
    809       (type == ASTYPE_BOOLEAN))
    810   {
    811     int n = *((int *) value);
    812     if (state->streamInfo->audioChannels == -1)
    813       state->streamInfo->audioChannels = (n == 0) ? 1 : 2;
    814   }
    815 
    816   /* metadata that maps straight to extractor keys */
    817   if (state->onMetaData && (state->parsingDepth == 1) &&
    818       (state->currentKeyType != EXTRACTOR_METATYPE_RESERVED))
    819   {
    820     s = NULL;
    821     switch (type)
    822     {
    823     case ASTYPE_NUMBER:
    824       {
    825         double n = *((double *) value);
    826         s = tmpstr;
    827         if (state->currentKeyType == EXTRACTOR_METATYPE_DURATION)
    828           snprintf (tmpstr, sizeof(tmpstr), "%.2f s", n);
    829         else
    830           snprintf (tmpstr, sizeof(tmpstr), "%f", n);
    831         break;
    832       }
    833     case ASTYPE_STRING:
    834       {
    835         s = (char *) value;
    836         break;
    837       }
    838     case ASTYPE_DATE:
    839       {
    840         void **tmp = (void **) value;
    841         double *millis;
    842         short *tz;
    843         millis = (double *) tmp[0];
    844         tz = (short *) tmp[1];
    845         if (0 == flv_to_iso_date (*millis, *tz, tmpstr, sizeof(tmpstr)))
    846           s = tmpstr;
    847         break;
    848       }
    849     }
    850     if ( (s != NULL) &&
    851          (state->ret == 0) )
    852       state->ret = state->proc (state->proc_cls,
    853                                 "flv",
    854                                 state->currentKeyType,
    855                                 EXTRACTOR_METAFORMAT_UTF8,
    856                                 "text/plain",
    857                                 s,
    858                                 strlen (s) + 1);
    859   }
    860   state->currentKeyType = EXTRACTOR_METATYPE_RESERVED;
    861   state->currentAttribute = FLV_NONE;
    862 
    863   if ((type == ASTYPE_ARRAY) || (type == ASTYPE_MIXEDARRAY) ||
    864       (type == ASTYPE_OBJECT) )
    865     state->parsingDepth--;
    866 }
    867 
    868 
    869 static int
    870 handleMetaBody (const unsigned char *data, size_t len,
    871                 FLVStreamInfo *stinfo,
    872                 EXTRACTOR_MetaDataProcessor proc,
    873                 void *proc_cls)
    874 {
    875   AMFParserHandler handler;
    876   FLVMetaParserState pstate;
    877 
    878   pstate.onMetaData = 0;
    879   pstate.currentKeyType = EXTRACTOR_METATYPE_RESERVED;
    880   pstate.parsingDepth = 0;
    881   pstate.streamInfo = stinfo;
    882   pstate.ret = 0;
    883   pstate.proc = proc;
    884   pstate.proc_cls = proc_cls;
    885   handler.userdata = &pstate;
    886   handler.as_begin_callback = &handleASBegin;
    887   handler.as_key_callback = &handleASKey;
    888   handler.as_end_callback = &handleASEnd;
    889 
    890   while (len > 0 && parse_amf (&data, &len, &handler) == 0)
    891     ;
    892   if (pstate.ret != 0)
    893     return 1;
    894   return 0;
    895 }
    896 
    897 
    898 static char *FLVAudioCodecs[] = {
    899   "Uncompressed",
    900   "ADPCM",
    901   "MP3",
    902   NULL,
    903   NULL,
    904   "Nellymoser 8kHz mono",
    905   "Nellymoser",
    906   NULL,
    907   NULL,
    908   NULL,
    909   "AAC",
    910   "Speex"
    911 };
    912 
    913 static char *FLVAudioChannels[] = {
    914   "mono",
    915   "stereo"
    916 };
    917 
    918 static char *FLVAudioSampleSizes[] = {
    919   "8-bit",
    920   "16-bit"
    921 };
    922 
    923 static char *FLVAudioSampleRates[] = {
    924   "5512.5",
    925   "11025",
    926   "22050",
    927   "44100"
    928 };
    929 
    930 static void
    931 handleAudioBody (const unsigned char *data, size_t len,
    932                  FLVStreamInfo *stinfo)
    933 {
    934   stinfo->audioChannels = (*data & 0x01) + 1;
    935   stinfo->audioSampleBits = (*data & 0x02) >> 1;
    936   stinfo->audioRate = (*data & 0x0C) >> 2;
    937   stinfo->audioCodec = (*data & 0xF0) >> 4;
    938   if (stinfo->audioCodecStr != NULL)
    939   {
    940     free (stinfo->audioCodecStr);
    941     stinfo->audioCodecStr = NULL;
    942   }
    943 }
    944 
    945 
    946 static char *FLVVideoCodecs[] = {
    947   NULL,
    948   NULL,
    949   "Sorenson Spark",
    950   "ScreenVideo",
    951   "On2 TrueMotion VP6",
    952   "On2 TrueMotion VP6 Alpha",
    953   "ScreenVideo 2",
    954   "H.264" /* XXX not found in docs */
    955 };
    956 
    957 static int sorenson_predefined_res[][2] = {
    958   { -1, -1 },
    959   { -1, -1 },
    960   { 352, 288 },
    961   { 176, 144 },
    962   { 128, 96 },
    963   { 320, 240 },
    964   { 160, 120 },
    965   { -1, -1 }
    966 };
    967 
    968 static void
    969 handleVideoBody (const unsigned char *data, size_t len,
    970                  FLVStreamInfo *stinfo)
    971 {
    972   int codecId, frameType;
    973 
    974   codecId = *data & 0x0F;
    975   frameType = (*data & 0xF0) >> 4;
    976   data++;
    977 
    978   /* try to get video dimensions */
    979   switch (codecId)
    980   {
    981   case 0x02:   /* Sorenson */
    982     if (len < 9)
    983       break;
    984     if (frameType == 1)
    985     {
    986       int start_code = (data[0] << 9) | (data[1] << 1) | ((data[2] >> 7) & 0x1);
    987       int version = (data[2] & 0x7C) >> 2;
    988       int frame_size = ((data[3] & 0x03) << 1) | (data[4] >> 7);
    989       if (start_code != 0x00000001)
    990         break;
    991       if (! ((version == 0) || (version == 1) ))
    992         break;
    993       if (frame_size == 0)
    994       {
    995         stinfo->videoWidth = ((data[4] & 0x7F) >> 1) | (data[5] >> 7);
    996         stinfo->videoHeight = ((data[5] & 0x7F) >> 1) | (data[6] >> 7);
    997       }
    998       else if (frame_size == 1)
    999       {
   1000         stinfo->videoWidth = ((data[4] & 0x7F) << 9) | (data[5] << 1)
   1001                              | (data[6] >> 7);
   1002         stinfo->videoHeight = ((data[6] & 0x7F) << 9) | (data[7] << 1)
   1003                               | (data[8] >> 7);
   1004       }
   1005       else
   1006       {
   1007         stinfo->videoWidth = sorenson_predefined_res[frame_size][0];
   1008         stinfo->videoHeight = sorenson_predefined_res[frame_size][1];
   1009       }
   1010     }
   1011     break;
   1012   case 0x03:   /* ScreenVideo */
   1013     if (len < 5)
   1014       break;
   1015     stinfo->videoWidth = readInt16 (&data) & 0x0FFF;
   1016     stinfo->videoHeight = readInt16 (&data) & 0x0FFF;
   1017     break;
   1018   case 0x04:   /* On2 VP6 */
   1019   case 0x05:
   1020     {
   1021       unsigned char dim_adj;
   1022       if (len < 10)
   1023         break;
   1024       dim_adj = *data++;
   1025       if ((frameType == 1) && ((data[0] & 0x80) == 0))
   1026       {
   1027         /* see ffmpeg vp6 decoder */
   1028         int separated_coeff = data[0] & 0x01;
   1029         int filter_header = data[1] & 0x06;
   1030         /*int interlaced = data[1] & 0x01; TODO: used in flv ever? */
   1031         if (separated_coeff || ! filter_header)
   1032         {
   1033           data += 2;
   1034         }
   1035         /* XXX encoded/displayed dimensions might vary, but which are the
   1036          * right ones? */
   1037         stinfo->videoWidth = (data[3] * 16) - (dim_adj >> 4);
   1038         stinfo->videoHeight = (data[2] * 16) - (dim_adj & 0x0F);
   1039       }
   1040       break;
   1041     }
   1042   default:
   1043     break;
   1044   }
   1045 
   1046   stinfo->videoCodec = codecId;
   1047   if (stinfo->videoCodecStr != NULL)
   1048   {
   1049     free (stinfo->videoCodecStr);
   1050     stinfo->videoCodecStr = NULL;
   1051   }
   1052 }
   1053 
   1054 
   1055 static int
   1056 readFLVTag (const unsigned char **data,
   1057             const unsigned char *end,
   1058             FLVStreamInfo *stinfo,
   1059             EXTRACTOR_MetaDataProcessor proc,
   1060             void *proc_cls)
   1061 {
   1062   const unsigned char *ptr = *data;
   1063   FLVTagHeader header;
   1064   int ret = 0;
   1065 
   1066   if (readFLVTagHeader (&ptr, end, &header) == -1)
   1067     return -1;
   1068 
   1069   if ((ptr + header.bodyLength) > end)
   1070     return -1;
   1071 
   1072   switch (header.type)
   1073   {
   1074   case FLV_TAG_TYPE_AUDIO:
   1075     handleAudioBody (ptr, header.bodyLength, stinfo);
   1076     break;
   1077   case FLV_TAG_TYPE_VIDEO:
   1078     handleVideoBody (ptr, header.bodyLength, stinfo);
   1079     break;
   1080   case FLV_TAG_TYPE_META:
   1081     ret = handleMetaBody (ptr, header.bodyLength, stinfo, proc, proc_cls);
   1082     break;
   1083   default:
   1084     break;
   1085   }
   1086 
   1087   ptr += header.bodyLength;
   1088   *data = ptr;
   1089   return ret;
   1090 }
   1091 
   1092 
   1093 #define MAX_FLV_FORMAT_LINE 80
   1094 static char *
   1095 printVideoFormat (FLVStreamInfo *stinfo)
   1096 {
   1097   char s[MAX_FLV_FORMAT_LINE + 1];
   1098   int n;
   1099   size_t len = MAX_FLV_FORMAT_LINE;
   1100 
   1101   n = 0;
   1102   /* some files seem to specify only the width or the height, print '?' for
   1103    * the unknown dimension */
   1104   if ((stinfo->videoWidth != -1) || (stinfo->videoHeight != -1))
   1105   {
   1106     if (n < len)
   1107     {
   1108       if (stinfo->videoWidth != -1)
   1109         n += snprintf (s + n, len - n, "%d", stinfo->videoWidth);
   1110       else
   1111         n += snprintf (s + n, len - n, "?");
   1112     }
   1113 
   1114     if (n < len)
   1115     {
   1116       if (stinfo->videoHeight != -1)
   1117         n += snprintf (s + n, len - n, "x%d", stinfo->videoHeight);
   1118       else
   1119         n += snprintf (s + n, len - n, "x?");
   1120     }
   1121   }
   1122 
   1123   if ((stinfo->videoFrameRate != 0.0) && (n < len))
   1124   {
   1125     if (n > 0)
   1126       n += snprintf (s + n, len - n, ", ");
   1127     if (n < len)
   1128       n += snprintf (s + n, len - n, "%0.2f fps", stinfo->videoFrameRate);
   1129   }
   1130 
   1131   if ((stinfo->videoCodec > -1) && (stinfo->videoCodec < 8) &&
   1132       (FLVVideoCodecs[stinfo->videoCodec] != NULL) && (n < len) )
   1133   {
   1134     if (n > 0)
   1135       n += snprintf (s + n, len - n, ", ");
   1136     if (n < len)
   1137       n += snprintf (s + n, len - n, "%s", FLVVideoCodecs[stinfo->videoCodec]);
   1138   }
   1139   else if ((stinfo->videoCodecStr != NULL) && (n < len))
   1140   {
   1141     if (n > 0)
   1142       n += snprintf (s + n, len - n, ", ");
   1143     if (n < len)
   1144       n += snprintf (s + n, len - n, "%s", stinfo->videoCodecStr);
   1145   }
   1146 
   1147   if ((stinfo->videoDataRate != 0.0) && (n < len))
   1148   {
   1149     if (n > 0)
   1150       n += snprintf (s + n, len - n, ", ");
   1151     if (n < len)
   1152       n += snprintf (s + n, len - n, "%.4f kbps", stinfo->videoDataRate);
   1153   }
   1154 
   1155   if (n == 0)
   1156     return NULL;
   1157   return strdup (s);
   1158 }
   1159 
   1160 
   1161 static char *
   1162 printAudioFormat (FLVStreamInfo *stinfo)
   1163 {
   1164   char s[MAX_FLV_FORMAT_LINE + 1];
   1165   int n;
   1166   size_t len = MAX_FLV_FORMAT_LINE;
   1167 
   1168   n = 0;
   1169   if ( (stinfo->audioRate != -1) && (n < len))
   1170   {
   1171     n += snprintf (s + n, len - n, "%s Hz",
   1172                    FLVAudioSampleRates[stinfo->audioRate]);
   1173   }
   1174 
   1175   if ((stinfo->audioSampleBits != -1) && (n < len))
   1176   {
   1177     if (n > 0)
   1178       n += snprintf (s + n, len - n, ", ");
   1179     if (n < len)
   1180       n += snprintf (s + n, len - n, "%s",
   1181                      FLVAudioSampleSizes[stinfo->audioSampleBits]);
   1182   }
   1183 
   1184   if ((stinfo->audioChannels != -1) && (n < len))
   1185   {
   1186     if (n > 0)
   1187       n += snprintf (s + n, len - n, ", ");
   1188     if (n < len)
   1189     {
   1190       if ((stinfo->audioChannels >= 1) && (stinfo->audioChannels <= 2))
   1191         n += snprintf (s + n, len - n, "%s",
   1192                        FLVAudioChannels[stinfo->audioChannels - 1]);
   1193       else
   1194         n += snprintf (s + n, len - n, "%d",
   1195                        stinfo->audioChannels);
   1196     }
   1197   }
   1198 
   1199   if ((stinfo->audioCodec > -1) && (stinfo->audioCodec < 12) &&
   1200       (FLVAudioCodecs[stinfo->audioCodec] != NULL) && (n < len))
   1201   {
   1202     if (n > 0)
   1203       n += snprintf (s + n, len - n, ", ");
   1204     if (n < len)
   1205       n += snprintf (s + n, len - n, "%s", FLVAudioCodecs[stinfo->audioCodec]);
   1206   }
   1207   else if ((stinfo->audioCodecStr != NULL) && (n < len))
   1208   {
   1209     if (n > 0)
   1210       n += snprintf (s + n, len - n, ", ");
   1211     if (n < len)
   1212       n += snprintf (s + n, len - n, "%s", stinfo->audioCodecStr);
   1213   }
   1214 
   1215   if ((stinfo->audioDataRate != 0.0) && (n < len))
   1216   {
   1217     if (n > 0)
   1218       n += snprintf (s + n, len - n, ", ");
   1219     if (n < len)
   1220       n += snprintf (s + n, len - n, "%.4f kbps", stinfo->audioDataRate);
   1221   }
   1222 
   1223   if (n == 0)
   1224     return NULL;
   1225   return strdup (s);
   1226 }
   1227 
   1228 
   1229 int
   1230 EXTRACTOR_flv_extract (const unsigned char *data,
   1231                        size_t size,
   1232                        EXTRACTOR_MetaDataProcessor proc,
   1233                        void *proc_cls,
   1234                        const char *options)
   1235 {
   1236   const unsigned char *ptr;
   1237   const unsigned char *end;
   1238   FLVStreamInfo stinfo;
   1239   FLVHeader header;
   1240   unsigned long prev_tag_size;
   1241   char *s;
   1242   int ret;
   1243 
   1244   ptr = data;
   1245   end = ptr + size;
   1246 
   1247   if (readFLVHeader (&ptr, end, &header) == -1)
   1248     return 0;
   1249 
   1250   if (memcmp (header.signature, FLV_SIGNATURE, 3) != 0)
   1251     return 0;
   1252 
   1253   if (0 != proc (proc_cls,
   1254                  "flv",
   1255                  EXTRACTOR_METATYPE_MIMETYPE,
   1256                  EXTRACTOR_METAFORMAT_UTF8,
   1257                  "text/plain",
   1258                  "video/x-flv",
   1259                  strlen ("video/x-flv") + 1))
   1260     return 0;
   1261   if (header.version != 1)
   1262     return 0;
   1263   if (readPreviousTagSize (&ptr, end, &prev_tag_size) == -1)
   1264     return 0;
   1265 
   1266   stinfo.videoCodec = -1;
   1267   stinfo.videoCodecStr = NULL;
   1268   stinfo.videoWidth = -1;
   1269   stinfo.videoHeight = -1;
   1270   stinfo.videoFrameRate = 0.0;
   1271   stinfo.videoDataRate = 0.0;
   1272   stinfo.audioCodec = -1;
   1273   stinfo.audioCodecStr = NULL;
   1274   stinfo.audioRate = -1;
   1275   stinfo.audioSampleBits = -1;
   1276   stinfo.audioChannels = -1;
   1277   stinfo.audioDataRate = 0.0;
   1278   ret = 0;
   1279   while (ptr < end)
   1280   {
   1281     if (-1 == (ret = readFLVTag (&ptr, end, &stinfo, proc, proc_cls)))
   1282       break;
   1283     if (readPreviousTagSize (&ptr, end, &prev_tag_size) == -1)
   1284       break;
   1285   }
   1286   if (1 == ret)
   1287     return 1;
   1288   s = printVideoFormat (&stinfo);
   1289   if (s != NULL)
   1290   {
   1291     if (0 != proc (proc_cls,
   1292                    "flv",
   1293                    EXTRACTOR_METATYPE_RESOURCE_TYPE,
   1294                    EXTRACTOR_METAFORMAT_UTF8,
   1295                    "text/plain",
   1296                    s,
   1297                    strlen (s) + 1))
   1298     {
   1299       free (s);
   1300       return 1;
   1301     }
   1302     free (s);
   1303   }
   1304   s = printAudioFormat (&stinfo);
   1305   if (s != NULL)
   1306   {
   1307     if (0 != proc (proc_cls,
   1308                    "flv",
   1309                    EXTRACTOR_METATYPE_RESOURCE_TYPE,
   1310                    EXTRACTOR_METAFORMAT_UTF8,
   1311                    "text/plain",
   1312                    s,
   1313                    strlen (s) + 1))
   1314     {
   1315       free (s);
   1316       return 1;
   1317     }
   1318     free (s);
   1319   }
   1320   return 0;
   1321 }