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