MP4Encoder.cpp

00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: MP4Encoder.cpp * 00006 * * 00007 * * 00008 * * 00009 * ITEC institute of the University of Klagenfurt (Austria) * 00010 * http://www.itec.uni-klu.ac.at * 00011 * * 00012 * * 00013 * For more information visit the ViTooKi homepage: * 00014 * http://ViTooKi.sourceforge.net * 00015 * vitooki-user@lists.sourceforge.net * 00016 * vitooki-devel@lists.sourceforge.net * 00017 * * 00018 * This file is part of ViTooKi, a free video toolkit. * 00019 * ViTooKi is free software; you can redistribute it and/or * 00020 * modify it under the terms of the GNU General Public License * 00021 * as published by the Free Software Foundation; either version 2 * 00022 * of the License, or (at your option) any later version. * 00023 * * 00024 * This program is distributed in the hope that it will be useful, * 00025 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00026 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00027 * GNU General Public License for more details. * 00028 * * 00029 * You should have received a copy of the GNU General Public License * 00030 * along with this program; if not, write to the Free Software * 00031 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, * 00032 * MA 02111-1307, USA. * 00033 * * 00034 ***********************************************************************/ 00035 00036 /*********************************************************************** 00037 * * 00038 * REVISION HISTORY: * 00039 * * 00040 * * 00041 * * 00042 ***********************************************************************/ 00043 00044 #include "MP4Encoder.hpp" 00045 #include "CompressedVideoFrame.hpp" 00046 00047 00048 /***************************************************/ 00049 MP4Encoder::MP4Encoder(VideoESInfo * pESInfo, u32 targetBitRate, u32 frameRate, 00050 u32 uiKeyFrameInterval, u32 numBFrames, 00051 UncompressedVideoFrame::ColorSpace colorSpace, 00052 MP4Decoder::meCoderType coderType, enum CodecID outFormat, char* encoderConfig) { 00053 this->mpEncoderHandle = encoderConfig; 00054 es = pESInfo; 00055 muiTargetBitrate = targetBitRate; 00056 if (frameRate > 0) 00057 this->frameRate = frameRate; 00058 else 00059 this->frameRate = (int)es->getFPS(); 00060 00061 initialized = false; 00062 muiKeyFrameInterval = uiKeyFrameInterval; 00063 this->numBFrames = numBFrames; 00064 mColorSpace = colorSpace; 00065 meCoder = coderType; 00066 if(coderType == MP4Decoder::XVID) 00067 strcpy(name,"MP4Encoder-XVID"); 00068 else 00069 strcpy(name,"MP4Encoder-FFMPEG"); 00070 outputFormat=outFormat; 00071 00072 if (targetBitRate < 30 * 1024) { 00073 dprintf_err("MP4Encoder: Constructor: targetBitRate is unreasonably low (%i bits/second?)... bailing out!\n",targetBitRate); 00074 exit(1); 00075 } 00076 } 00077 00078 00079 /***************************************************/ 00080 void MP4Encoder::initialize() { 00081 if(initialized) 00082 return; 00083 dprintf_full("MP4Encoder::initialize() Handle: %p Width: %i Height: %i BW: %i kbps FPS: %i numBFrames: %i\n", 00084 mpEncoderHandle, es->getWidth(), es->getHeight(), 00085 muiTargetBitrate / 1024, frameRate, numBFrames); 00086 initialized = true; 00087 if (EncoderInit(&mpEncoderHandle, es->getWidth(), es->getHeight(), 00088 muiTargetBitrate, frameRate, numBFrames, muiKeyFrameInterval, es) != ERR_OK) { 00089 dprintf_err("MP4Encoder::initialize: Error creating encoder instance!\n"); 00090 initialized = false; 00091 } 00092 assert(mpEncoderHandle); 00093 es->setAvgBandwidth(muiTargetBitrate); 00094 muiFrameNumber = 0; 00095 } 00096 00097 00098 /***************************************************/ 00099 MP4Encoder::~MP4Encoder() { 00100 EncoderClose(&mpEncoderHandle); 00101 } 00102 00103 00104 00105 /***************************************************/ 00106 list < Frame * >MP4Encoder::adapt(Frame * frm) { 00107 CompressedVideoFrame *mpgFrame = NULL; 00108 list < Frame * >frmList; 00109 int iFramesize = 0; 00110 if (!initialized) 00111 initialize(); 00112 if (!initialized) 00113 return frmList; 00114 00115 if ((muiFrameNumber % muiKeyFrameInterval) == 0) 00116 iFramesize = EncodeFrame(mpEncoderHandle, (char *) frm->getAU()->payload, 00117 mpOutBuf, mColorSpace, 1); 00118 else 00119 iFramesize = EncodeFrame(mpEncoderHandle, (char *) frm->getAU()->payload, 00120 mpOutBuf, mColorSpace, 0); 00121 00122 dprintf_full("MP4Encoder::adapt Encode Frame No %d compressed size: %d, first bytes: %x %x %x %x\n", 00123 muiFrameNumber, iFramesize,mpOutBuf[0],mpOutBuf[1],mpOutBuf[2],mpOutBuf[3]); 00124 if (iFramesize > 0) { 00125 00126 AU *pAU = new AU(frm->getAU()); 00127 u8 *payload = new u8[iFramesize]; 00128 00129 memcpy(payload, mpOutBuf, iFramesize); 00130 00131 // First frame is tricky: 00132 // - obtain headers to set decoderconfig in ESInfo 00133 // - remove header from payload, otherwise it will exist two times. 00134 // FIXME by mike: why should we need this?? 00135 // even worse, we need this header, if it wasn't sent via net RTSP/SDP 00136 00137 // Derive headers for ESInfo object from first frame 00138 if (muiFrameNumber == 0) { 00139 u32 uiVESHeaderLen = iFramesize; 00140 int i; 00141 u32 ch = 0; 00142 u32 startCode = 0; 00143 bool foundVOPcode = false; 00144 00145 // Search for first VOP startcode in freshly encoded frame 00146 foundVOPcode = false; 00147 i = 0; 00148 startCode = 0; 00149 while (!foundVOPcode) { 00150 if (i >= iFramesize) { 00151 dprintf_err("MP4Encoder::adapt:FATAL: couldnt find frame MPEG-4 VOP code (00 00 01 B6) in newly coded frame!!!\n"); 00152 exit(1); 00153 } 00154 ch = (u32) payload[i]; 00155 startCode = (startCode << 8) | ch; 00156 if (startCode == 0x1B6) { 00157 uiVESHeaderLen = i - 4; 00158 foundVOPcode = true; 00159 } 00160 i++; 00161 } 00162 00163 // Create new header 00164 es->setDecoderConfig(payload,uiVESHeaderLen); //deep copy first bytes... 00165 es->clearDecConfUpdated(); 00166 00167 00168 if (!foundVOPcode) { //assume, there is just the header (by xvid) 00169 // iFramesize = 0; 00170 //delete[] payload; 00171 } 00172 00173 /* 00174 // remove header from payload 00175 //FIXME by mike: why should we need this?? 00176 //even worse, we need this header, if it wasn't sent via net RTSP/SDP 00177 memmove(payload, payload + uiVESHeaderLen+1, iFramesize - uiVESHeaderLen-1); 00178 iFramesize -= uiVESHeaderLen+1; 00179 */ 00180 } 00181 00182 if (iFramesize != 0) { 00183 // All the other frames is easy, just memcopy output. 00184 pAU->size = iFramesize; 00185 pAU->payload = payload; 00186 00187 /* 00188 if ((muiFrameNumber % muiKeyFrameInterval) == 0) 00189 mpgFrame = new CompressedVideoFrame(Frame::I_VOP, es->getWidth(), es->getHeight()); 00190 else 00191 mpgFrame = new CompressedVideoFrame(Frame::P_VOP, es->getWidth(), es->getHeight()); 00192 */ 00193 mpgFrame = new CompressedVideoFrame(Frame::UNKNOWN_VOP, es->getWidth(), es->getHeight()); 00194 00195 mpgFrame->detectFrameType(); 00196 mpgFrame->setAU(pAU); 00197 mpgFrame->setMediaTimeScale(frm->getMediaTimeScale()); 00198 frmList.push_back(mpgFrame); 00199 dprintf_full("MP4Encoder::adapt: first 4 bytes of %i bytes, %s frameNo %i, CTS %u: %x %x %x %x\r\n", 00200 pAU->size, Frame::VOPTypeToChar(mpgFrame->getType()), muiFrameNumber, 00201 mpgFrame->getAU()->cts, mpgFrame->getAU()->payload[0], 00202 mpgFrame->getAU()->payload[1], 00203 mpgFrame->getAU()->payload[2], 00204 mpgFrame->getAU()->payload[3]); 00205 muiFrameNumber++; 00206 } 00207 } 00208 00209 return frmList; 00210 } 00211 00212 00213 /***************************************************/ 00214 list < Frame * >MP4Encoder::close() { 00215 list < Frame * >frmList; 00216 00217 return frmList; 00218 } 00219 00220 00221 /***************************************************/ 00222 Adaptor *MP4Encoder::clone() { 00223 char* copyEncoderConfig=NULL; 00224 if(mpEncoderHandle && meCoder != MP4Decoder::XVID) { 00225 #ifdef ENABLE_FFMPEG 00226 AVCodec *codec=NULL; 00227 /* Make sure that the required encoder exists in the ffmpeg library */ 00228 codec = avcodec_find_encoder(this->outputFormat); 00229 if(codec) { 00230 AVCodecContext* srcClone = avcodec_alloc_context(); 00231 avcodec_get_context_defaults(srcClone); 00232 00233 AVCodecContext* src=(AVCodecContext*) mpEncoderHandle; 00234 // don't do a memcpy --> pointers! 00235 srcClone->codec=codec; 00236 srcClone->b_frame_strategy=src->b_frame_strategy; 00237 srcClone->b_quant_factor=src->b_quant_factor; 00238 srcClone->b_quant_offset=src->b_quant_offset; 00239 srcClone->bit_rate=src->bit_rate; 00240 srcClone->rtp_payload_size=src->rtp_payload_size; 00241 srcClone->width=src->width; 00242 srcClone->height=src->height; 00243 srcClone->gop_size=srcClone->gop_size; 00244 srcClone->qmin=src->qmin; 00245 srcClone->qmax=src->qmax; 00246 srcClone->max_b_frames=src->max_b_frames; 00247 srcClone->sample_aspect_ratio.den=src->sample_aspect_ratio.den; 00248 srcClone->sample_aspect_ratio.num=src->sample_aspect_ratio.num; 00249 srcClone->rc_max_rate=src->rc_max_rate; 00250 srcClone->rc_min_rate=src->rc_min_rate; 00251 srcClone->mb_qmax=src->mb_qmax; 00252 srcClone->mb_qmin=src->mb_qmin; 00253 srcClone->time_base=src->time_base; 00254 // open the codec for encoding 00255 if (avcodec_open(srcClone, codec) < 0) { 00256 printf("MP4Encoder::clone(): Could not open codec\n"); 00257 av_free(srcClone); 00258 } 00259 else 00260 copyEncoderConfig=(char*)srcClone; 00261 } 00262 #else 00263 dprintf_err("MP4Encoder::clone: compiled without ffmpeg support!\n"); 00264 exit(1); 00265 #endif 00266 } 00267 00268 return new MP4Encoder(es,muiTargetBitrate,frameRate,muiKeyFrameInterval,numBFrames, 00269 mColorSpace,meCoder,outputFormat,copyEncoderConfig); 00270 } 00271 00272 00273 /***************************************************/ 00274 void MP4Encoder::setCoderType(MP4Decoder::eCoderType coderType) { 00275 meCoder = coderType; 00276 } 00277 00278 00279 /***************************************************/ 00280 void MP4Encoder::EncoderClose(char **pEncoderHandle) { 00281 00282 switch (meCoder) 00283 { 00284 case (MP4Decoder::XVID): 00285 XVIDEncoderClose(pEncoderHandle); 00286 break; 00287 #ifdef ENABLE_FFMPEG 00288 case (MP4Decoder::FFMPEG): 00289 FFMPEGEncoderClose(pEncoderHandle); 00290 break; 00291 #endif 00292 default: 00293 dprintf_err("Invalid Encoder selected\n"); 00294 break; 00295 } 00296 } 00297 00298 00299 /***************************************************/ 00300 int MP4Encoder::EncoderInit( char **pEncoderHandle, unsigned int iWidth, unsigned int iHeight, 00301 int iBitrate, int iFramerate, int numBFrames, int keyFrameInterval, VideoESInfo * es) { 00302 int ret = 0; 00303 00304 switch (meCoder) 00305 { 00306 case (MP4Decoder::XVID): 00307 ret = XVIDEncoderInit(pEncoderHandle, iWidth, iHeight, iBitrate, iFramerate, numBFrames); 00308 break; 00309 #ifdef ENABLE_FFMPEG 00310 case (MP4Decoder::FFMPEG): 00311 ret = FFMPEGEncoderInit(pEncoderHandle, outputFormat, iWidth, iHeight, iBitrate, iFramerate, numBFrames, keyFrameInterval); 00312 break; 00313 #endif 00314 default: 00315 dprintf_err("Invalid Coder selected\n"); 00316 break; 00317 } 00318 00319 return ret; 00320 } 00321 00322 00323 /***************************************************/ 00324 int MP4Encoder::EncodeFrame(char *pEncoderHandle, char *pYUVFrame, 00325 char *pCompressed, UncompressedVideoFrame::ColorSpace eColorSpace, 00326 int forceIFrame) { 00327 int ret = 0; 00328 00329 switch (meCoder) { 00330 case (MP4Decoder::XVID): 00331 dprintf_full("Encoding using XVID\n"); 00332 ret = XVIDEncodeFrame(pEncoderHandle, pYUVFrame, pCompressed, 00333 (xvidColorSpace) eColorSpace, forceIFrame); 00334 break; 00335 #ifdef ENABLE_FFMPEG 00336 case (MP4Decoder::FFMPEG): 00337 dprintf_full("Encoding using FFMPEG\n"); 00338 ret = FFMPEGEncodeFrame(pEncoderHandle, pYUVFrame, pCompressed, 00339 (ffmpegColorSpace) mapToFFMPEGColorspace(eColorSpace), 00340 forceIFrame); 00341 break; 00342 default: 00343 dprintf_err("Invalid Coder selected\n"); 00344 break; 00345 #endif 00346 } 00347 00348 return ret; 00349 } 00350 00351 00352 /***************************************************/ 00353 ffmpegColorSpace MP4Encoder::mapToFFMPEGColorspace(UncompressedVideoFrame::ColorSpace colorSpace) { 00354 ffmpegColorSpace ret = FFMPEGColorSpaceUnknown; 00355 00356 switch (colorSpace) { 00357 case UncompressedVideoFrame::ColorSpaceYV12: 00358 ret = FFMPEGColorSpaceYV12; 00359 break; 00360 case UncompressedVideoFrame::ColorSpaceRGB24: 00361 ret = FFMPEGColorSpaceRGB24; 00362 break; 00363 case UncompressedVideoFrame::ColorSpaceI420: 00364 ret = FFMPEGColorSpaceI420; 00365 break; 00366 case UncompressedVideoFrame::ColorSpaceRGB32: 00367 ret = FFMPEGColorSpaceRGB32; 00368 break; 00369 default: 00370 ret = FFMPEGColorSpaceUnknown; 00371 break; 00372 } 00373 00374 return ret; 00375 } 00376 00377 00381 u32 MP4Encoder::getTranscodingCosts() const { 00382 return (u32)(es->getWidth()*es->getHeight()*es->getFPS()*2); 00383 } 00384 00385 00386 /***************************************************/ 00387 u8* MP4Encoder::createNewHeader(VideoESInfo* vid, u32 targetBitRate, u32 width, u32 height, 00388 u32 uiKeyFrameInterval, int framerate, u32* headerSize) { 00389 00390 dprintf_full("MP4Encoder::createNewHeader\n"); 00391 assert(headerSize); 00392 if(!headerSize) 00393 return NULL; 00394 UncompressedVideoFrame* frm = new UncompressedVideoFrame(Frame::YUV_VOP, width, height); 00395 AU* au = new AU(); 00396 au->payload = new u8[(int)ceil(1.5*width*height)]; 00397 au->size = (int)(1.5*width*height); 00398 frm->setAU(au,false); 00399 VideoESInfo* pCVid = (VideoESInfo*)vid->clone(vid->getContainerInfo()); 00400 pCVid->setWidth(width); 00401 pCVid->setHeight(height); 00402 MP4Encoder* mp4 = new MP4Encoder(pCVid, targetBitRate, framerate, uiKeyFrameInterval); 00403 mp4->initialize(); 00404 list<Frame*> retFrm = mp4->adapt(frm); 00405 if(retFrm.empty()) { 00406 au->cts = au->dts = 3000; 00407 retFrm = mp4->adapt(frm); 00408 } 00409 if(retFrm.empty()) { 00410 *headerSize = 0; 00411 delete frm; 00412 return NULL; 00413 } 00414 if (*retFrm.begin() != frm) 00415 delete frm; 00416 // otherwise we get a header 00417 Frame* headerFrame = *retFrm.begin(); 00418 delete headerFrame; 00419 *headerSize = 0; 00420 00421 u8* newHeader = NULL; 00422 *headerSize = pCVid->getHeaders(&newHeader); 00423 00424 return newHeader; 00425 } 00426