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