1 /***************************************************************************
2
3 copyright : (C) 2007 by mean
4 email : fixounet@free.fr
5 ***************************************************************************/
6
7 /***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16
17 #include <string.h>
18 #include <math.h>
19 #include <map>
20
21 #include "ADM_default.h"
22 #include "ADM_Video.h"
23
24 #include "fourcc.h"
25 #include "ADM_mp4.h"
26 #include "DIA_coreToolkit.h"
27
28 #if 1
29 #define aprintf(...) {}
30 #else
31 #define aprintf printf
32 #endif
33
34
35 #define MAX_CHUNK_SIZE (4*1024)
36 uint32_t sample2byte(WAVHeader *hdr,uint32_t sample);
37 /**
38 * \fn splitAudio
39 * \brief Split audio chunks into small enough pieces
40 * @param track
41 * @param vinfo
42 * @return
43 */
splitAudio(MP4Track * track,MPsampleinfo * info,uint32_t trackScale)44 bool MP4Header::splitAudio(MP4Track *track,MPsampleinfo *info, uint32_t trackScale)
45 {
46 uint32_t maxChunkSize=(MAX_CHUNK_SIZE>>5)<<5;
47 // Probe if it is needed
48 int extra=0;
49 int sizeOfAudio=0;
50 for(int i=0;i<track->nbIndex;i++)
51 {
52 int x=track->index[i].size/(maxChunkSize+1);
53 extra+=x;
54 sizeOfAudio+=track->index[i].size;
55 }
56 if(!extra)
57 {
58 ADM_info("No very large blocks found, %d bytes present over %d blocks\n",sizeOfAudio,track->nbIndex);
59 return true;
60 }
61 ADM_info("%d large blocks found, splitting into %d bytes block\n",extra,maxChunkSize);
62
63 uint32_t newNbCo=track->nbIndex+extra*2; // *2 is enough, should be.
64 MP4Index *newindex=new MP4Index[newNbCo];
65 int w=0;
66
67 for(int i=0;i<track->nbIndex;i++)
68 {
69 uint32_t sz;
70 sz=track->index[i].size;
71 if(sz<=maxChunkSize)
72 {
73 memcpy(&(newindex[w]),&(track->index[i]),sizeof(MP4Index));
74 w++;
75 continue;
76 }
77 // We have to split it...
78 int part=0;
79
80 uint64_t offset=track->index[i].offset;
81 uint32_t samples=track->index[i].dts;
82 uint32_t totalSamples=samples;
83 uint32_t originalSize=sz;
84 while(sz>maxChunkSize)
85 {
86 newindex[w].offset=offset+part*maxChunkSize;
87 newindex[w].size=maxChunkSize;
88 newindex[w].dts=(samples*maxChunkSize)/originalSize;
89 newindex[w].pts=ADM_COMPRESSED_NO_PTS; // No seek
90 totalSamples-=newindex[w].dts;
91 ADM_assert(w<newNbCo);
92 w++;
93 part++;
94 sz-=maxChunkSize;
95 }
96 // The last one...
97 newindex[w].offset=offset+part*maxChunkSize;
98 newindex[w].size=sz;
99 newindex[w].dts=totalSamples;
100 newindex[w].pts=ADM_COMPRESSED_NO_PTS;
101 w++;
102 }
103 delete [] track->index;
104 track->index=newindex;
105 track->nbIndex=w;
106 uint32_t total=0;
107 for(int i=0;i<track->nbIndex;i++)
108 total+=track->index[i].size;
109 ADM_info("After split, we have %u bytes across %d blocks\n",total,w);
110
111 return true;
112 }
113 /**
114 * \fn processAudio
115 * \brief used when all samples have the same size. We make some assumptions here,
116 * might not work with all mp4/mov files.
117 * @param track
118 * @param trackScale
119 * @param info
120 * @param outNbChunk
121 * @return
122 */
processAudio(MP4Track * track,uint32_t trackScale,MPsampleinfo * info,uint32_t * nbOut)123 bool MP4Header::processAudio( MP4Track *track, uint32_t trackScale,
124 MPsampleinfo *info,uint32_t *nbOut)
125 {
126 uint64_t totalBytes=info->SzIndentical*info->nbSz;
127 uint32_t totalSamples=0;
128 double skewFactor=1;
129 ADM_info("All the same size: %u (total size %" PRIu64" bytes)\n",info->SzIndentical,totalBytes);
130 ADM_info("Byte per frame =%d\n",(int)info->bytePerFrame);
131 ADM_info("SttsC[0] = %d, sttsN[0]=%d\n",info->SttsC[0],info->SttsN[0]);
132
133 track->totalDataSize=totalBytes;
134
135 if(info->nbStts!=1)
136 {
137 ADM_info("WARNING: Same size, different duration\n");
138 return 1;
139 }
140
141 if(info->SttsC[0]!=1)
142 {
143 ADM_warning("Not regular (time increment is not 1=%d)\n",(int)info->SttsC[0]);
144 return 1;
145 }
146 //
147 // Each chunk contains N samples=N bytes
148 int *samplePerChunk=(int *)malloc(info->nbCo*sizeof(int));
149 memset(samplePerChunk,0,info->nbCo*sizeof(int));
150 int total=0;
151 for(int i=0;i<info->nbSc;i++)
152 {
153 for(int j=info->Sc[i]-1;j<info->nbCo;j++)
154 {
155 aprintf("For chunk %lu, %lu \n",j,info->Sn[i] );
156 samplePerChunk[j]=info->Sn[i];
157 }
158 }
159 /**/
160 for(int i=0;i<info->nbCo;i++)
161 {
162 aprintf("Chunk %d Samples=%d\n",i,samplePerChunk[i]);
163 total+=samplePerChunk[i];
164 }
165
166 ADM_info("Total size in sample : %u\n",total);
167 ADM_info("Sample size : %u\n",info->SzIndentical);
168
169 if(info->SttsN[0]!=total)
170 {
171 ADM_warning("Not regular (Nb sequential samples (%d)!= total samples (%d))\n",info->SttsN[0],total);
172 //free(samplePerChunk);
173 //return 1;
174 }
175
176 track->index=new MP4Index[info->nbCo];
177 memset(track->index,0,info->nbCo*sizeof(MP4Index));
178 track->nbIndex=info->nbCo;;
179
180 totalBytes=0;
181 totalSamples=0;
182 #if 0
183 #define ADM_PER info->bytePerPacket
184 #else
185 #define ADM_PER info->bytePerFrame
186 #endif
187 for(int i=0;i<info->nbCo;i++)
188 {
189 uint32_t sz;
190
191 track->index[i].offset=info->Co[i];
192 sz=samplePerChunk[i];
193 sz=sz/info->samplePerPacket;
194 sz*=ADM_PER; //*track->_rdWav.channels;;
195
196 track->index[i].size=sz;
197 track->index[i].dts=samplePerChunk[i]; // No seek
198 track->index[i].pts=ADM_NO_PTS; // No seek
199
200 totalBytes+=track->index[i].size;
201 totalSamples+=samplePerChunk[i];
202 aprintf("Block %d , size=%d,total=%d,samples=%d,total samples=%d\n",i,track->index[i].size,totalBytes,samplePerChunk[i],totalSamples);
203 }
204 free(samplePerChunk);
205 if(info->nbCo)
206 track->index[0].pts=0;
207 ADM_info("Found %u bytes, spread over %d blocks\n",totalBytes,info->nbCo);
208 track->totalDataSize=totalBytes;
209
210 // split large chunk into smaller ones if needed
211 splitAudio(track,info, trackScale);
212
213
214 // Now time to update the time...
215 // Normally they have all the same duration with a time increment of
216 // 1 per sample
217 // so we have so far all samples with a +1 time increment
218 uint32_t samplesSoFar=0;
219 double scale=trackScale*track->_rdWav.channels;
220 switch(track->_rdWav.encoding)
221 {
222 default:break;
223 case WAV_PCM: // wtf ?
224 case WAV_LPCM: // wtf ?
225 case WAV_ULAW: // Wtf ?
226 case WAV_IMAADPCM:
227 case WAV_MSADPCM:
228 scale/=track->_rdWav.channels;
229 break;
230 }
231 if(info->bytePerPacket!=info->samplePerPacket)
232 {
233 ADM_info("xx Byte per packet =%d\n",info->bytePerPacket);
234 ADM_info("xx Sample per packet =%d\n",info->samplePerPacket);
235 }
236 for(int i=0;i< track->nbIndex;i++)
237 {
238 uint32_t thisSample=track->index[i].dts;
239 double v=samplesSoFar; // convert offset in sample to regular time (us)
240 v=(v)/(scale);
241 v*=1000LL*1000LL;
242 #if 1
243 track->index[i].dts=track->index[i].pts=(uint64_t)v;
244 #else
245 track->index[i].dts=track->index[i].pts=ADM_NO_PTS;
246 #endif
247 samplesSoFar+=thisSample;
248 aprintf("Block %d, size=%d, dts=%d\n",i,track->index[i].size,track->index[i].dts);
249 }
250 // track->index[0].dts=0;
251 ADM_info("Index done (sample same size)\n");
252 return 1;
253 }
254 /**
255 \fn indexify
256 \brief build the index from the stxx atoms
257 */
indexify(MP4Track * track,uint32_t trackScale,MPsampleinfo * info,uint32_t isAudio,uint32_t * outNbChunk)258 uint8_t MP4Header::indexify(
259 MP4Track *track,
260 uint32_t trackScale,
261 MPsampleinfo *info,
262 uint32_t isAudio,
263 uint32_t *outNbChunk)
264
265 {
266
267 uint32_t i,j,cur;
268
269 ADM_info("Build Track index, track timescale: %u\n",trackScale);
270 *outNbChunk=0;
271 aprintf("+_+_+_+_+_+\n");
272 aprintf("co : %lu sz: %lu sc: %lu co[0] %" PRIu64"\n",info->nbCo,info->nbSz,info->nbSc,info->Co[0]);
273 aprintf("+_+_+_+_+_+\n");
274
275 ADM_assert(info->Sc);
276 ADM_assert(info->Sn);
277 ADM_assert(info->Co);
278 if(!info->SzIndentical)
279 {
280 ADM_assert(info->Sz);
281 }
282
283 // Audio with all samples of the same size and regular
284 if(info->SzIndentical && isAudio && info->nbStts==1 && info->SttsC[0]==1)
285 return processAudio(track,trackScale,info,outNbChunk);
286
287 // Audio with variable sample size or video
288 track->index=new MP4Index[info->nbSz];
289 memset(track->index,0,info->nbSz*sizeof(MP4Index));
290
291 if(info->SzIndentical) // Video, all same size (DV ?)
292 {
293 aprintf("\t size for all %u frames : %u\n",info->nbSz,info->SzIndentical);
294 for(i=0;i<info->nbSz;i++)
295 track->index[i].size=info->SzIndentical;
296 track->totalDataSize+=info->nbSz*info->SzIndentical;
297 }else // Different size
298 {
299 for(i=0;i<info->nbSz;i++)
300 {
301 track->index[i].size=info->Sz[i];
302 aprintf("\t size : %d : %u\n",i,info->Sz[i]);
303 track->totalDataSize+=info->Sz[i];
304 }
305 }
306 // if no sample to chunk we map directly
307 // first build the # of sample per chunk table
308 uint32_t totalchunk=0;
309
310 // Search the maximum
311 if(info->nbSc)
312 {
313 for(i=0;i<info->nbSc-1;i++)
314 {
315 totalchunk+=(info->Sc[i+1]-info->Sc[i])*info->Sn[i];
316 }
317 totalchunk+=(info->nbCo-info->Sc[info->nbSc-1]+1)*info->Sn[info->nbSc-1];
318 }
319 aprintf("# of chunks %d, max # of samples %d\n",info->nbCo, totalchunk);
320
321 uint32_t *chunkCount = new uint32_t[totalchunk+1];
322 #if 0
323 for(i=0;i<info->nbSc;i++)
324 {
325 for(j=info->Sc[i]-1;j<info->nbCo;j++)
326 {
327 chunkCount[j]=info->Sn[i];
328 ADM_assert(j<=totalchunk);
329 }
330 aprintf("(%d) sc: %lu sn:%lu\n",i,info->Sc[i],info->Sn[i]);
331 }
332 #else
333 if(info->nbSc)
334 {
335 for(i=0;i<info->nbSc-1;i++)
336 {
337 int mn=info->Sc[i]-1;
338 int mx=info->Sc[i+1]-1;
339 if(mn<0 || mx<0 || mn>totalchunk || mx > totalchunk || mx<mn)
340 {
341 ADM_warning("Corrupted file\n");
342 return false;
343 }
344 for(j=mn;j<mx;j++)
345 {
346 chunkCount[j]=info->Sn[i];
347 ADM_assert(j<=totalchunk);
348 }
349 aprintf("(%d) sc: %lu sn:%lu\n",i,info->Sc[i],info->Sn[i]);
350 }
351 // Last one
352 for(j=info->Sc[info->nbSc-1]-1;j<info->nbCo;j++)
353 {
354 chunkCount[j]=info->Sn[i];
355 ADM_assert(j<=totalchunk);
356 }
357 }
358 #endif
359
360 // now we have for each chunk the number of sample in it
361 cur=0;
362 for(j=0;j<info->nbCo;j++)
363 {
364 uint64_t tail=0;
365 aprintf("--starting at %lu , %lu to go\n",info->Co[j],chunkCount[j]);
366 for(uint32_t k=0;k<chunkCount[j];k++)
367 {
368 track->index[cur].offset=info->Co[j]+tail;
369 tail+=track->index[cur].size;
370 aprintf(" sample : %d offset : %lu\n",cur,track->index[cur].offset);
371 aprintf("Tail : %lu\n",tail);
372 cur++;
373 }
374 }
375
376 delete [] chunkCount;
377
378
379 track->nbIndex=cur;;
380
381
382 // Now deal with duration
383 // the unit is us FIXME, probably said in header
384 // we put each sample duration in the time entry
385 // then sum them up to get the absolute time position
386 if(!info->nbStts)
387 {
388 ADM_warning("No time-to-sample table (stts) found.\n");
389 return 0;
390 }
391
392 uint32_t nbChunk=track->nbIndex;
393 uint32_t start=0;
394 if(info->nbStts>1 || info->SttsC[0]!=1)
395 {
396 for(uint32_t i=0;i<info->nbStts;i++)
397 {
398 for(uint32_t j=0;j<info->SttsN[i];j++)
399 {
400 track->index[start].dts=(uint64_t)info->SttsC[i];
401 track->index[start].pts=ADM_COMPRESSED_NO_PTS;
402 start++;
403 ADM_assert(start<=nbChunk);
404 }
405 }
406 }else // All same duration
407 {
408 for(uint32_t i=0;i<nbChunk;i++)
409 {
410 track->index[i].dts=(uint64_t)info->SttsC[0]; // this is not an error!
411 track->index[i].pts=ADM_COMPRESSED_NO_PTS;
412 }
413 }
414
415 if(isAudio)
416 splitAudio(track,info, trackScale);
417 // now collapse
418 uint64_t total=0;
419 double ftot;
420 uint32_t thisone,previous=0;
421 uint32_t step=0xFFFFFFFF;
422 bool constantFps=true;
423
424 // try to correct jitter from rounding errors first
425 if(!isAudio)
426 {
427 std::map <uint32_t, uint32_t> hist;
428 for(uint32_t i=0;i<nbChunk;i++)
429 {
430 thisone=track->index[i].dts;
431 if(!thisone) continue;
432 if(thisone<step) step=thisone;
433 if(thisone<100) continue; // ignore too low durations
434 if(hist.find(thisone)==hist.end())
435 hist.insert({thisone,1});
436 else
437 hist[thisone]++;
438 }
439 ADM_info("Histogram map has %u elements.\n",hist.size());
440 std::map <uint32_t, uint32_t>::iterator it;
441 for(it=hist.begin(); it!=hist.end(); it++)
442 {
443 printf("Frame duration %u count: %u\n",it->first,it->second);
444 }
445 if(hist.size()==3) // we look for pattern x-1, x, x+1
446 {
447 ADM_info("Checking whether we need to fix jitter from rounding errors...\n");
448 uint32_t a,b,c;
449 uint32_t acount,ccount;
450 it=hist.begin();
451 a=it->first;
452 acount=it->second;
453 it++;
454 b=it->first;
455 bool restored=false;
456 if(b==a+1)
457 {
458 it++;
459 c=it->first;
460 ccount=it->second;
461 if(c==b+1 && ccount+2>acount && acount+2>ccount)
462 {
463 for(uint32_t i=0;i<nbChunk;i++)
464 track->index[i].dts=b;
465 ADM_info("Yes, enforcing CFR, frame duration %u ticks.\n",b);
466 step=b;
467 restored=true;
468 }
469 }
470 if(!restored)
471 ADM_info("No, nothing we can do.\n");
472 }
473 }
474 if(step==0xFFFFFFFF) step=1;
475
476 for(uint32_t i=0;i<nbChunk;i++)
477 {
478 thisone=track->index[i].dts;
479 if(!isAudio && i+1<nbChunk)
480 {
481 while(thisone%step)
482 step=thisone%step;
483 if(constantFps && i && thisone!=previous && thisone && previous)
484 constantFps=false;
485 previous=thisone;
486 }
487 ftot=total;
488 ftot*=1000.*1000.;
489 ftot/=trackScale;
490 track->index[i].dts=(uint64_t)floor(ftot);
491 track->index[i].pts=ADM_COMPRESSED_NO_PTS;
492 total+=thisone;
493 aprintf("Audio chunk : %lu time :%lu\n",i,track->index[i].dts);
494 }
495 if(isAudio)
496 {
497 ADM_info("Audio index done.\n");
498 return true;
499 }
500 if(!nbChunk)
501 {
502 ADM_warning("Empty index!\n");
503 return false;
504 }
505 // Time is now built, it is in us
506 ADM_info("Video index done.\n");
507 _videoFound++;
508 ADM_info("Setting video timebase to %u / %u\n",step,_videoScale);
509 _videostream.dwScale=step;
510 if(constantFps)
511 {
512 _mainaviheader.dwMicroSecPerFrame=0; // force usage of fraction for fps
513 return true;
514 }
515 ftot=total;
516 ftot/=nbChunk;
517 ftot*=1000.*1000.;
518 ftot/=trackScale;
519 ftot+=0.49;
520 /* If the frame increment calculated from the time base is close to the average,
521 the stream may be a constant fps stream with a mixture of frames and fields or
522 simply have holes. The average is meaningless then. */
523 if(step && _videoScale)
524 {
525 double ti=1000.*1000.;
526 ti/=_videoScale;
527 ti*=step;
528 ti+=0.49;
529 if(ftot<ti*2)
530 {
531 _mainaviheader.dwMicroSecPerFrame=(int32_t)ti;
532 ADM_info("Using time base for frame increment %d us instead of average %d\n",(int32_t)ti,(int32_t)ftot);
533 return true;
534 }
535 }
536 _mainaviheader.dwMicroSecPerFrame=(int32_t)ftot;
537 ADM_info("Variable frame rate, %d us per frame on average.\n",_mainaviheader.dwMicroSecPerFrame);
538
539 return true;
540 }
541 /**
542 \fn sample2byte
543 \brief Convert the # of samples into the # of bytes needed
544 */
sample2byte(WAVHeader * hdr,uint32_t sample)545 uint32_t sample2byte(WAVHeader *hdr,uint32_t sample)
546 {
547 float f;
548 f=hdr->frequency; // 1 sec worth of data
549 f=sample/f; // in seconds
550 f*=hdr->byterate; // in byte
551 return (uint32_t)floor(f);
552 }
553 // EOF
554