1 /**********************************************************************
2             \file            muxerMp4v2
3             \brief           libmp4v2 muxer
4                              -------------------
5 
6     copyright            : (C) 2011 by mean
7     email                : fixounet@free.fr
8     Strongly inspired by handbrake code
9 
10  ***************************************************************************/
11 
12 /***************************************************************************
13  *                                                                         *
14  *   This program is free software; you can redistribute it and/or modify  *
15  *   it under the terms of the GNU General Public License as published by  *
16  *   the Free Software Foundation; either version 2 of the License, or     *
17  *   (at your option) any later version.                                   *
18  *                                                                         *
19  ***************************************************************************/
20 
21 #include "ADM_default.h"
22 #include "fourcc.h"
23 #include "muxerMp4v2.h"
24 #include "ADM_codecType.h"
25 #include "ADM_imageFlags.h"
26 #include "ADM_videoInfoExtractor.h"
27 #if 1
28 #define aprintf(...) {}
29 #else
30 #define aprintf printf
31 #endif
32 
33 /**
34     \fn loadNextVideoFrame
35     \brief Load buffer, convert to annexB if needed
36 */
loadNextVideoFrame(ADMBitstream * bs)37 bool muxerMp4v2::loadNextVideoFrame(ADMBitstream *bs)
38 {
39     if(true==needToConvertFromAnnexB)
40         {
41             ADMBitstream tmp;
42             tmp.data=scratchBuffer;
43             tmp.bufferSize=videoBufferSize;
44             if(false==vStream->getPacket(&tmp))
45                 return false;
46             bs->dts=tmp.dts;
47             bs->pts=tmp.pts;
48             bs->flags=tmp.flags;
49             bs->len=ADM_convertFromAnnexBToMP4(scratchBuffer,tmp.len, bs->data,videoBufferSize);
50             goto goOn;
51         }
52     if(false==vStream->getPacket(bs))
53         {
54             return false;
55         }
56 goOn:
57     if(bs->dts==ADM_NO_PTS)
58     {
59         bs->dts=lastVideoDts+vStream->getFrameIncrement();
60     }
61     lastVideoDts=bs->dts;
62     return true;
63 }
64 /**
65     \fn setEsdsAtom
66     \brief extract esds atom from extradata or for first frame. In all cases read the first frame
67 */
initMpeg4(void)68 bool muxerMp4v2::initMpeg4(void)
69 {
70     bool removeVol=false;
71     // Preload first image
72     if(false==loadNextVideoFrame(&(in[0])))
73         {
74             ADM_error("Cannot read 1st video frame\n");
75             return false;
76         }
77         nextWrite=1;
78 
79     videoTrackId=MP4AddVideoTrack(handle,90000,MP4_INVALID_DURATION,
80     vStream->getWidth(),vStream->getHeight(),MP4_MPEG4_VIDEO_TYPE);
81     if(MP4_INVALID_TRACK_ID==videoTrackId)
82         {
83             ADM_error("Cannot add mpeg4 video Track \n");
84             return false;
85         }
86     ADM_info("Setting mpeg4 (a)SP ESDS...\n");
87     if(0) //false==vStream->getPacket(&in) )
88      {
89         ADM_error("Cannot read first frame\n");
90         return false;
91      }
92      uint8_t *esdsData=NULL;
93      uint32_t esdsLen=0;
94         if(false==vStream->getExtraData(&esdsLen,&esdsData))
95         {
96             ADM_info("No extradata, geting ESDS from first frame...\n");
97         }else
98         {
99             ADM_info("Got esds from extradata\n");
100         }
101         if(!esdsLen) // We dont have extraData, look into the 1st frame
102         {
103             ADM_info("Trying to get VOL header from first frame...\n");
104             if(!extractVolHeader(in[0].data,in[0].len,&esdsData,&esdsLen))
105             {
106                 ADM_error("Cannot get ESDS, aborting\n");
107                 return false;
108             }
109             // Remove VOL Header from Fist frame...
110             removeVol=true;
111         }
112         //
113         if(!esdsLen)
114         {
115             ADM_error("ESDS not found, aborting\n");
116             return false;
117         }
118         if(!esdsData[0] && !esdsData[1] && esdsData[2]==1)
119         {
120             // Remove startcode
121             if(esdsLen<4)
122             {
123                 ADM_error("ESDS too short\n");
124                 return false;
125             }
126             esdsData+=4;
127             esdsLen-=4;
128         }
129 
130         ADM_info("Esds:\n"); mixDump(esdsData,esdsLen);
131         if(false==MP4SetTrackESConfiguration(handle,videoTrackId,esdsData,esdsLen))
132         {
133             ADM_error("SetTracEsConfiguration failed\n");
134             return false;
135         }
136         ADM_info("ESDS atom set\n");
137         if(removeVol)
138         {
139             uint32_t size=(uint32_t)((in[0].data+in[0].len)-(esdsData+esdsLen));
140             memmove(in[0].data,esdsData+esdsLen,size);
141             in[0].len=size;
142         }
143         return true;
144 }
145 /**
146        \fn initH264
147        \brief format header for H264
148 */
initH264(void)149 bool muxerMp4v2::initH264(void)
150 {
151 //
152             bool result=false;
153             uint32_t spsLen;
154             uint8_t  *spsData=NULL;
155             uint32_t ppsLen;
156             uint8_t  *ppsData=NULL;
157             // Extract sps & pps
158             uint8_t *extra=NULL;
159             uint32_t extraLen=0;
160             if(false==vStream->getExtraData(&extraLen,&extra))
161             {
162                 ADM_error("Cannot get extradata\n");
163                 return false;
164             }
165             if(extraLen)
166                 mixDump(extra,extraLen);
167             if(false==ADM_getH264SpsPpsFromExtraData(extraLen,extra,&spsLen,&spsData,&ppsLen,&ppsData))
168             {
169                 ADM_error("Wrong extra data for h264\n");
170                 return false;
171             }
172 
173             // if we dont have extraData, it is annexB 100 % sure
174             needToConvertFromAnnexB=true;
175             if(extraLen)
176                 if(extra[0]==1) needToConvertFromAnnexB=false;
177             if(false==loadNextVideoFrame(&(in[0])))
178             {
179                 ADM_error("Cannot read 1st video frame\n");
180                 return false;
181             }
182             nextWrite=1;
183             //
184             videoTrackId=MP4AddH264VideoTrack(handle,90000,MP4_INVALID_DURATION,
185                     vStream->getWidth(),vStream->getHeight(),spsData[1],spsData[2],spsData[3],3);
186             if(MP4_INVALID_TRACK_ID==videoTrackId)
187             {
188                 ADM_error("Cannot add h264 video Track \n");
189                 return false;
190             }
191             ADM_info("SPS (%d) :",spsLen);
192             mixDump(spsData,spsLen);
193             ADM_info("PPS (%d) :",ppsLen);
194             mixDump(ppsData,ppsLen);
195 
196             MP4AddH264SequenceParameterSet(handle,videoTrackId, spsData,spsLen );
197             MP4AddH264PictureParameterSet( handle,videoTrackId, ppsData,ppsLen);
198             // MP4AddIPodUUID
199             result=true;
200 clnup:
201             if(spsData) delete [] spsData;
202             if(ppsData) delete [] ppsData;
203             spsData=NULL;
204             ppsData=NULL;
205             return result;
206 }
207 /**
208     \fn initVideo
209 */
initVideo(void)210 bool muxerMp4v2::initVideo(void)
211 {
212         uint32_t fcc=vStream->getFCC();
213 
214         ADM_info("Setting video..\n");
215         if(isMpeg4Compatible(fcc))
216         {
217 
218             if(false==initMpeg4())
219             {
220                 ADM_error("Cannot set ESDS atom\n");
221                 return false;
222             }
223         }
224         if(isH264Compatible(fcc))
225         {
226             if(false==initH264())
227             {
228                 ADM_error("Cannot add h264 track\n");
229                 return false;
230             }
231         }
232         // Refine audio delay, encoders may adjust videoDelay once the first compressed frame has been produced.
233         uint64_t refinedAudioDelay=vStream->getVideoDelay();
234         if(refinedAudioDelay!=audioDelay)
235         {
236             ADM_info("[muxerMp4v2] Adjusting audio delay, was %" PRIu64" ms, now %" PRIu64" ms.\n",audioDelay/1000,refinedAudioDelay/1000);
237             audioDelay=refinedAudioDelay;
238         }
239         double inc=vStream->getFrameIncrement();
240         inc=inc/1000000;
241         ADM_info("Frame increment =%d ms\n",(int)(inc*1000));
242         inc*=90000;
243         setMaxDurationPerChunk(videoTrackId, inc);
244         ADM_info("[MP4V2] Video correctly initalized\n");
245         return true;
246 }
247 //EOF
248 
249 
250 
251