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