1 /***************************************************************************
2     copyright            : (C) 2006 by mean
3     email                : fixounet@free.fr
4  *
5  * Some AAC extradata code borrowed from mplayer
6  ***************************************************************************/
7 
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 #include "ADM_cpp.h"
17 #include <math.h>
18 
19 #include "ADM_default.h"
20 #include "ADM_Video.h"
21 
22 #include "fourcc.h"
23 #include "ADM_codecType.h"
24 #include "ADM_videoInfoExtractor.h"
25 #include "ADM_mkv.h"
26 
27 #include "mkv_tags.h"
28 #include "ADM_aacinfo.h"
29 class entryDesc
30 {
31   public:
32           uint32_t     trackNo;
33           uint32_t     trackType;
34           uint32_t     extraDataLen;
35 
36           uint32_t fcc;
37           uint32_t w,h,fps;
38           uint32_t fq,chan,bpp;
39           uint32_t defaultDuration;
40           float    trackScale;
41           uint8_t *extraData;
42           std::string codecId;
43           std::string language;
44 
45           void dump(void);
46           uint32_t   headerRepeatSize;
47           uint8_t    headerRepeat[MKV_MAX_REPEAT_HEADER_SIZE];
entryDesc()48           entryDesc()
49           {
50               codecId=language=std::string("");
51               trackNo=0;
52               trackType=0;
53               extraDataLen=0;
54               fcc=w=h=fps=fq=chan=bpp=defaultDuration=0;
55               trackScale=0;
56               extraData=NULL;
57               headerRepeatSize=0;
58           }
59 
60 };
61 /* Prototypes */
62 static uint8_t entryWalk(ADM_ebml_file *head,uint32_t headlen,entryDesc *entry);
63 uint32_t ADM_mkvCodecToFourcc(const char *codec);
64 
65 
66 
67 #define AAC_SYNC_EXTENSION_TYPE 0x02b7
aac_get_sample_rate_index(uint32_t sample_rate)68 static int aac_get_sample_rate_index(uint32_t sample_rate)
69 {
70     static const int srates[] = {
71         92017, 75132, 55426, 46009, 37566, 27713,
72         23004, 18783, 13856, 11502, 9391, 0
73     };
74     int i = 0;
75     while (sample_rate < srates[i])
76         i++;
77     ADM_info("Found index of %d for aac fq of %d\n",i,sample_rate);
78     return i;
79 }
80 
81 
82 /**
83  *
84  * @param haystack
85  * @param needle
86  * @return
87  */
hasNeedle(const char * haystack,const char * needle)88 static bool hasNeedle(const char *haystack, const char *needle)
89 {
90     if(NULL!=strstr( (char *)(haystack+12),needle)) return true;
91     return false;
92 }
93 /**
94  * \brief recreate codec extra data as soon as possible to detect / deal with sbr
95  * strongly derived from mplayer code
96  * @param codec
97  * @param entry
98  */
createAACExtraData(const char * codec,entryDesc * entry)99 static void createAACExtraData(const char *codec,entryDesc *entry)
100 {
101     int profile=3;
102     int sampleRateIndex=aac_get_sample_rate_index(entry->fq);
103     if(hasNeedle(codec,"MAIN")) profile=0;
104     else if(hasNeedle(codec,"LC")) profile=1;
105     else if(hasNeedle(codec,"SSR")) profile=2;
106     uint8_t *e=entry->extraData;
107     e[0]=((profile + 1) << 3) | ((sampleRateIndex & 0xE) >> 1);
108     e[1]= ((sampleRateIndex & 0x1) << 7) | (entry->chan << 3);
109     entry->extraDataLen=2;
110     if(hasNeedle(codec,"SBR"))
111     {
112         entry->extraDataLen=5;
113         entry->fq*=2;
114         sampleRateIndex=aac_get_sample_rate_index(entry->fq);
115         e[2] = AAC_SYNC_EXTENSION_TYPE >> 3;
116         e[3] = ((AAC_SYNC_EXTENSION_TYPE & 0x07) << 5) | 5;
117         e[4] = (1 << 7) | (sampleRateIndex << 3);
118     }
119     ADM_info("Created %d bytes\n",entry->extraDataLen);
120     mixDump(entry->extraData,entry->extraDataLen);
121 }
122 
123 /**
124     \fn entryDesc::dump
125     \brief Dump the track entry
126 */
dump(void)127 void entryDesc::dump(void)
128 {
129       printf("*** TRACK SUMMARY **\n");
130 #define PRINT(x) printf(#x" :%u\n",x)
131       PRINT(trackNo);
132       switch(trackType)
133       {
134         case 1: // Video
135           PRINT(trackType);
136           printf("==>Video\n");
137           PRINT(extraDataLen);
138           PRINT(fcc);
139           printf("%s\n",fourCC::tostring(fcc));
140           PRINT(w);
141           PRINT(h);
142           PRINT(fps);
143           break;
144         case 2: // Video
145           printf("==>Audio\n");
146           PRINT(extraDataLen);
147           PRINT(fcc);
148           PRINT(fq);
149           PRINT(chan);
150           PRINT(bpp);
151           break;
152         default:
153           printf("Unkown track type (%d)\n",trackType);
154       }
155 }
156 
157 /**
158       \fn analyzeOneTrack
159       \brief Grab info about the track (it is a recursive function !)
160 
161 */
analyzeOneTrack(void * head,uint32_t headlen)162 uint8_t mkvHeader::analyzeOneTrack(void *head,uint32_t headlen)
163 {
164     entryDesc entry;
165 
166     /* Set some defaults value */
167     entry.chan=1;
168 
169     entryWalk(  (ADM_ebml_file *)head,headlen,&entry);
170     entry.dump();
171 
172     //***************** First video track *****************
173     if(entry.trackType==1 &&  !_isvideopresent)
174     {
175         _isvideopresent=1;
176         if(entry.defaultDuration)
177         {
178             _tracks[0]._defaultFrameDuration=entry.defaultDuration;
179             double inv=entry.defaultDuration; // in us
180             inv=1/inv;
181             inv*=1000.;
182             inv*=1000.;
183             inv*=1000.;
184             _videostream.dwScale=1000;
185             _videostream.dwRate=(uint32_t)inv;
186         }else
187         {
188             printf("[MKV] No duration, assuming 25 fps\n");
189             _videostream.dwScale=1000;
190             _videostream.dwRate=25000;
191             _tracks[0]._defaultFrameDuration=40000;
192         }
193 
194         _mainaviheader.dwMicroSecPerFrame=_tracks[0]._defaultFrameDuration;
195         _videostream.fccType=fourCC::get((uint8_t *)"vids");
196         _video_bih.biBitCount=24;
197         _videostream.dwInitialFrames= 0;
198         _videostream.dwStart= 0;
199         _video_bih.biWidth=_mainaviheader.dwWidth=entry.w;
200         _video_bih.biHeight=_mainaviheader.dwHeight=entry.h;
201         _videostream.fccHandler=_video_bih.biCompression=entry.fcc;
202 
203 #define AV1_EXTRADATA_OFFSET 4
204 
205         // if it is vfw...
206         if(fourCC::check(entry.fcc,(uint8_t *)"VFWX") && entry.extraData && entry.extraDataLen>=sizeof(ADM_BITMAPINFOHEADER))
207         {
208             ADM_info("VFW compatibility header, data=%d bytes\n",(int)entry.extraDataLen);
209             memcpy(& _video_bih,entry.extraData,sizeof(ADM_BITMAPINFOHEADER));
210 
211             _videostream.fccHandler=_video_bih.biCompression;
212             _mainaviheader.dwWidth= _video_bih.biWidth;
213             _mainaviheader.dwHeight=_video_bih.biHeight;
214             if(entry.extraDataLen>sizeof(ADM_BITMAPINFOHEADER))
215             {
216                 int l=entry.extraDataLen-sizeof(ADM_BITMAPINFOHEADER);
217                 _tracks[0].extraData=new uint8_t[l];
218                 _tracks[0].extraDataLen=l;
219                 memcpy(_tracks[0].extraData,entry.extraData +sizeof(ADM_BITMAPINFOHEADER),l);
220                 ADM_info("VFW Header+%d bytes of extradata\n",l);
221                 mixDump(_tracks[0].extraData,l);
222             }
223             delete [] entry.extraData;
224             entry.extraData=NULL;
225             entry.extraDataLen=0;
226         }else if(fourCC::check(entry.fcc,(uint8_t *)"av01") && entry.extraData && entry.extraDataLen>AV1_EXTRADATA_OFFSET)
227         {
228             _tracks[0].extraDataLen=entry.extraDataLen-AV1_EXTRADATA_OFFSET;
229             _tracks[0].extraData=new uint8_t[_tracks[0].extraDataLen];
230             memcpy(_tracks[0].extraData, entry.extraData+AV1_EXTRADATA_OFFSET, _tracks[0].extraDataLen);
231             delete [] entry.extraData;
232             entry.extraData=NULL;
233             entry.extraDataLen=0;
234         }else
235         {
236             _tracks[0].extraData=entry.extraData;
237             _tracks[0].extraDataLen=entry.extraDataLen;
238         }
239         if(isH264Compatible(entry.fcc) && _tracks[0].extraData
240             && _tracks[0].extraDataLen > 8 // FIXME
241             && (_tracks[0].extraData[5] & 0x1F) == 1) // 1x SPS
242         {
243             ADM_SPSInfo info;
244             if(extractSPSInfo_mp4Header(_tracks[0].extraData, _tracks[0].extraDataLen, &info))
245             {
246                 uint32_t sz=sizeof(ADM_SPSInfo);
247                 if(_tracks[0].infoCache)
248                     delete [] _tracks[0].infoCache;
249                 _tracks[0].infoCache=new uint8_t[sz];
250                 memcpy(_tracks[0].infoCache, &info, sz);
251                 _tracks[0].infoCacheSize=sz;
252                 // now copy SPS to the raw parameter sets cache
253                 uint8_t *p=_tracks[0].extraData;
254                 sz=(*(p+6)<<8)+*(p+7);
255                 if(_tracks[0].extraDataLen > sz+8)
256                 {
257                     if(_tracks[0].paramCache)
258                         delete [] _tracks[0].paramCache;
259                     _tracks[0].paramCache=new uint8_t[sz];
260                     memcpy(_tracks[0].paramCache,p+8,sz);
261                     _tracks[0].paramCacheSize=sz;
262                 }
263             }
264         }
265         _tracks[0].streamIndex=entry.trackNo;
266 
267         uint32_t hdr=entry.headerRepeatSize;
268         if(hdr)
269         {
270             _tracks[0].headerRepeatSize=entry.headerRepeatSize;
271             memcpy(_tracks[0].headerRepeat,entry.headerRepeat,hdr);
272             ADM_info("video has %d bytes of repeated headers\n",hdr);
273         }
274         return 1;
275     }
276     //***************** Audio tracks *****************
277     if(entry.trackType==2 && _nbAudioTrack<ADM_MKV_MAX_TRACKS)
278     {
279         mkvTrak *t=&(_tracks[1+_nbAudioTrack]);
280         t->language=entry.language;
281         t->wavHeader.bitspersample=16;
282         t->wavHeader.byterate=0; // to be set later
283         // MS/ACM : ACMX
284         if(0x100001==entry.fcc)
285         {
286             int l=entry.extraDataLen;
287             int wavSize=sizeof(WAVHeader);
288             ADM_info("Found ACM compatibility header (%d / %d)\n",l,wavSize);
289             if(l>=wavSize) // we need at least a wavheader
290             {
291                 mixDump(entry.extraData,l);
292                 memcpy(&(t->wavHeader),entry.extraData,wavSize);
293                 ADM_info("Encoding : %d\n",t->wavHeader.encoding);
294                 wavSize+=2; // size of WAVEFORMATEX
295                 int x=l-wavSize;
296 
297                 if(x>0) // If we have more than WAVEFORMATEX, it is extradata
298                 {
299                     ADM_info("Found %d bytes of extradata\n",x);
300                     t->extraData=new uint8_t[x];
301                     t->extraDataLen=x;
302                     memcpy(t->extraData,entry.extraData+wavSize,x);
303                     if(t->wavHeader.encoding==0xfffe) // WAVE_FORMAT_EXTENSIBLE + extradata: might be AAC LATM
304                         t->wavHeader.encoding=MKV_MUX_LATM;
305                 }
306                 if(t->wavHeader.encoding==MKV_MUX_LATM)
307                     t->wavHeader.byterate=0; // to be set later
308                 delete [] entry.extraData;
309                 t->streamIndex=entry.trackNo;
310                 if(entry.defaultDuration)
311                     t->_defaultFrameDuration=entry.defaultDuration;
312                 else
313                     t->_defaultFrameDuration=0;
314                 // In ACM mode we should not have the stripped header stuff..
315                 _nbAudioTrack++;
316                 return 1;
317             }
318         }
319         if(entry.fcc==WAV_AAC)
320         {
321             if(!entry.extraDataLen)
322             {
323                 ADM_info("Recreating aac extradata..\n");
324                 entry.extraData = new uint8_t[5];
325                 createAACExtraData(entry.codecId.c_str(),&entry);
326             }else
327             {
328                 // check AAC infoata
329                  AacAudioInfo info;
330                 if(ADM_getAacInfoFromConfig(entry.extraDataLen,entry.extraData,info))
331                 {
332                     ADM_info("Decoding AAC extra data gives :\n");
333                     ADM_info("Fq= %d\n",info.frequency);
334                     ADM_info("channels= %d\n",info.channels);
335                     ADM_info("SBR= %d\n",info.sbr);
336                     entry.chan=info.channels;
337                     entry.fq=info.frequency;
338                 }
339             }
340         }
341         if(entry.fcc==WAV_PCM || entry.fcc==WAV_LPCM)
342         {
343             t->wavHeader.byterate = t->wavHeader.bitspersample * entry.fq * entry.chan >> 3;
344         }
345         t->wavHeader.encoding=entry.fcc;
346         t->wavHeader.channels=entry.chan;
347         t->wavHeader.frequency=entry.fq;
348         t->streamIndex=entry.trackNo;
349         if(entry.defaultDuration)
350             t->_defaultFrameDuration=entry.defaultDuration;
351         else
352             t->_defaultFrameDuration=0;
353         uint32_t hdr=entry.headerRepeatSize;
354         if(hdr)
355         {
356             t->headerRepeatSize=entry.headerRepeatSize;
357             memcpy(t->headerRepeat,entry.headerRepeat,hdr);
358         }
359         t->extraData=entry.extraData;
360         t->extraDataLen=entry.extraDataLen;
361         ADM_info("This track has %d bytes of extradata\n",t->extraDataLen);
362 
363         _nbAudioTrack++;
364         return 1;
365     }
366     // Other tracks, ignored...
367     if(entry.extraData)
368     {
369         ADM_info("Ignoring extradata\n");
370         delete [] entry.extraData;
371     }
372     return 1;
373 
374 }
375 
376 
377 /**
378     \fn entryWalk
379     \brief walk a trackEntry atom and grabs all infos. Store them in entry
380 */
entryWalk(ADM_ebml_file * head,uint32_t headlen,entryDesc * entry)381 uint8_t entryWalk(ADM_ebml_file *head,uint32_t headlen,entryDesc *entry)
382 {
383   ADM_ebml_file father( head,headlen);
384    uint64_t id,len;
385   ADM_MKV_TYPE type;
386   const char *ss;
387 
388   while(!father.finished())
389   {
390       father.readElemId(&id,&len);
391       if(!ADM_searchMkvTag( (MKV_ELEM_ID)id,&ss,&type))
392       {
393         printf("[MKV] Tag 0x%" PRIx64" not found (len %" PRIu64")\n",id,len);
394         father.skip(len);
395         continue;
396       }
397       switch(id)
398       {
399         case  MKV_CONTENT_COMPRESSION_SETTINGS:
400 //#warning todo: check it is stripping
401                     if(len<=MKV_MAX_REPEAT_HEADER_SIZE)
402                     {
403                         father.readBin(entry->headerRepeat,len);
404                         entry->headerRepeatSize=len;
405                     };
406                     break;
407         case  MKV_TRACK_NUMBER: entry->trackNo=father.readUnsignedInt(len);break;
408         case  MKV_TRACK_TYPE: entry->trackType=father.readUnsignedInt(len);break;
409 
410         case  MKV_AUDIO_FREQUENCY: entry->fq=(uint32_t)floor(father.readFloat(len));break;
411         //case MKV_AUDIO_OUT_FREQUENCY:entry->fq=(uint32_t)floor(father.readFloat(len));break;
412         case  MKV_VIDEO_WIDTH: entry->w=father.readUnsignedInt(len);break;
413         case  MKV_VIDEO_HEIGHT: entry->h=father.readUnsignedInt(len);break;
414 
415         case  MKV_DISPLAY_HEIGHT: ADM_info("Display Height:%d\n",(int)father.readUnsignedInt(len));break;
416         case  MKV_DISPLAY_WIDTH: ADM_info("Display Width:%d\n",(int)father.readUnsignedInt(len));break;
417 
418         case  MKV_AUDIO_CHANNELS: entry->chan=father.readUnsignedInt(len);break;
419         case  MKV_TIMECODE_SCALE:
420         case  MKV_TRACK_TIMECODESCALE:
421                                 {
422                                     ADM_warning("[Mkv] TimeCodeScale=%" PRIu64"\n",father.readUnsignedInt(len));
423                                 };break; //FIXME
424 
425         case  MKV_FRAME_DEFAULT_DURATION: entry->defaultDuration=father.readUnsignedInt(len)/1000; break; // In us
426         case  MKV_CODEC_EXTRADATA:
427         {
428               uint8_t *data=new uint8_t[len];
429                     father.readBin(data,len);
430                     entry->extraData=data;
431                     entry->extraDataLen=len;
432                     break;
433         }
434         case  MKV_AUDIO_SETTINGS:
435         case  MKV_VIDEO_SETTINGS:
436         case  MKV_CONTENT_ONE_ENCODING:
437         case  MKV_CONTENT_ENCODINGS:
438         case  MKV_CONTENT_COMPRESSION:
439                   entryWalk(&father,len,entry);
440                   break;
441         case MKV_LANGUAGE:
442                 {
443                  char s[100];
444                  s[99]=0;
445                  father.readString(s,len);
446                  if(!strlen(s))
447                      strcpy(s,"eng"); // english is default
448                  if(!strcmp(s,"unknown")) // we were using "unknown", which is not a valid ISO 639 code
449                  {
450                      memset(s,'\0',100);
451                      std::string und=ADM_UNKNOWN_LANGUAGE;
452                      strcpy(s,und.c_str());
453                      ADM_info("Found 'unknown' as language code, replacing it with '%s'\n",und.c_str());
454                  }else
455                  {
456                      ADM_info("Found language  = %s\n",s);
457                  }
458                  entry->language=std::string(s);
459                 }
460             break;
461         case MKV_CODEC_ID:
462             {
463             uint8_t *codec=new uint8_t[len+1];
464                   father.readBin(codec,len);
465                   codec[len]=0;
466                   std::string codecAsString=std::string((char *)codec);
467                   entry->codecId=codecAsString;
468                   entry->fcc=ADM_mkvCodecToFourcc((char *)codec);
469 
470                   delete [] codec;
471 
472             }
473                   break;
474         default: printf("[MKV]not handled %s\n",ss);
475                   father.skip(len);
476         }
477 
478       }
479   return 1;
480 }//EOF
481