Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members
ffMP4IO.cpp
00001 /*********************************************************************** 00002 * * 00003 * ViTooKi * 00004 * * 00005 * title: ffMP4IO.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 #ifdef ENABLE_FFMPEG 00045 00046 #include "ffMP4IO.hpp" 00047 #include "CompressedVideoFrame.hpp" 00048 #include "CompressedAudioFrame.hpp" 00049 #include "Frame.hpp" 00050 #include "VideoESInfo.hpp" 00051 #include "AudioESInfo.hpp" 00052 00053 /***************************************************************/ 00054 ffMP4IO::ffMP4IO(ESInfo * esi, const char *mp4File, bool write) { 00055 es = esi; 00056 state = CLOSED; 00057 input = new char[strlen(mp4File) + 1]; 00058 strcpy(input, mp4File); 00059 writeOnly = write; 00060 DTS = 0; 00061 currentFrameNumber = 0; 00062 endFrameNumber = 0; 00063 priofp = NULL; 00064 ic=NULL; 00065 00066 ap = &ap1; 00067 if (es->isVisualStream()) { //set up timely distribution priority table 00068 tableSize = (int)ceil(((VideoESInfo*)es)->getFPS()); 00069 int maxdepth=(int)((log((double)tableSize)/log(2.0)) + 1); 00070 00071 dprintf_full("ffMP4IO: timelyDistTable: calculating maxdepth %i for patSize %i (=fps)\n", maxdepth,tableSize); 00072 int actpos=tableSize; //pseudo-global helper var 00073 for (int i=0; i<maxdepth; i++){ 00074 patSplit(prioTable,tableSize,i,&actpos); 00075 } 00076 00077 #ifdef VITOOKI_DEBUG 00078 dprintf_full("ffMP4IO: timelyDistTable: "); 00079 printPat(prioTable,tableSize); 00080 #endif 00081 } 00082 } 00083 00084 00085 /***************************************************************/ 00086 ffMP4IO::~ffMP4IO() { 00087 delete[] input; 00088 close(); 00089 // dfclose(fp); 00090 if(priofp) 00091 fclose(priofp); 00092 } 00093 00094 00095 void ffMP4IO::printPat(int *pat, int len) { 00096 int i; 00097 for (i=0; i<len; i++) { 00098 if (pat[i] > -1) 00099 printf("%2i-", pat[i]); 00100 else 00101 printf("..-"); 00102 } 00103 printf("\n"); 00104 } 00105 00106 00107 00108 /***************************************************************/ 00109 int ffMP4IO::patSplitRec(int *pat, int len, int actdepth, int maxdepth, int gowhere, int *actpos) { 00110 int half=len/2; 00111 int *middle = &pat[half]; 00112 int ret; 00113 00114 // printf("depth %i (of %i) pos %i len %i left %i right %i --> ",actdepth,maxdepth,*actpos,len,half,len-half); 00115 //printPat(pat,len,-1,0); 00116 00117 if (actdepth < maxdepth) { 00118 if (gowhere) { 00119 ret = patSplitRec(pat, half, actdepth+1, maxdepth, gowhere,actpos); 00120 if (ret == 0) 00121 ret = patSplitRec(pat+half,len-half, actdepth+1, maxdepth, gowhere,actpos); 00122 return ret; 00123 } else { 00124 ret = patSplitRec(pat+half,len-half, actdepth+1, maxdepth, gowhere,actpos); 00125 if (ret == 0) 00126 ret = patSplitRec(pat, half, actdepth+1, maxdepth, gowhere,actpos); 00127 return ret; 00128 } 00129 } else { 00130 if (*middle == -1) { 00131 *middle = (*actpos)--; 00132 return 1; 00133 } else { 00134 // printf("FULL\n"); 00135 return 0; 00136 } 00137 } 00138 00139 } 00140 00141 00142 /***************************************************************/ 00143 void ffMP4IO::patSplit(int *pat, int len, int maxdepth, int *actpos) { 00144 int i=1, j=1; 00145 00146 if (*actpos == len) { //init 00147 for (int k=0; k<len;k++) 00148 pat[k]=-1; 00149 patSplitRec(pat, len, 0, 0, 0, actpos); 00150 } 00151 00152 while ((i!=0) || (j!=0)) { 00153 // printf(">>>next node RIGHT\n"); 00154 //printPat(pat,len); 00155 i = patSplitRec(pat, len, 0, maxdepth, 1, actpos); 00156 // printf(">>>next node LEFT \n"); 00157 //printPat(pat,len); 00158 j = patSplitRec(pat, len, 0, maxdepth, 0, actpos); 00159 } 00160 } 00161 00162 00163 /***************************************************************/ 00164 Frame *ffMP4IO::getFrame() { 00165 if (state != OPEN || writeOnly) { // FIXME: change this when adding buffering 00166 return NULL; 00167 } 00168 if( (this->currentFrameNumber > this->endFrameNumber) && (endFrameNumber != 0)) { 00169 close(true); 00170 state=STREAMEOF; 00171 return NULL; 00172 } 00173 00174 Frame *frm = NULL; 00175 AU *aus = new AU(); 00176 AVPacket pkt1, *pkt = &pkt1; 00177 int err; 00178 do { 00179 err = av_read_frame(ic, pkt); 00180 00181 //dprintf_full("ffMP4IO::getFrame() av_read_frame: streamID: %i, size %i, err = %i\n",ic->streams[pkt->stream_index]->id, pkt->size, err); 00182 if(err != 0) 00183 break; 00184 00185 if (pkt->size == 0) 00186 dprintf_full("ffMP4IO::getFrame() ignoring empty frame...\n"); 00187 00188 if(pkt->size>0 && ic->streams[pkt->stream_index]->id != streamID) 00189 av_free_packet(pkt); 00190 00191 } while(ic->streams[pkt->stream_index]->id != streamID || (pkt->size == 0)); 00192 00193 00194 if (!err) 00195 dprintf_full("ffMP4IO::getFrame() av_read_frame: streamID = %d, size = %d, pts = %u, dts = %u, dur = %d\n", 00196 pkt->stream_index, pkt->size, (u32) pkt->pts, (u32) pkt->dts, pkt->duration); 00197 00198 00199 if(pkt->size==0) { 00200 dprintf_err("ffMP4IO::getFrame: Read empty packet (ret %i) (-->EOF!?)\n",err); 00201 delete aus; 00202 // if(err==AVERROR_UNKNOWN) //EOF 00203 state = STREAMEOF; 00204 return NULL; 00205 } 00206 00207 aus->size = pkt->size; 00208 //aus->sampleFlags = ; 00209 aus->cts = DTS; //XXX for the time being 00210 aus->dts = DTS; 00211 00212 if(es->getCodecID() == CODEC_ID_MPEG4 || es->getCodecID() == CODEC_ID_MSMPEG4V1 || 00213 es->getCodecID() == CODEC_ID_MSMPEG4V2 || es->getCodecID() == CODEC_ID_MSMPEG4V3 || 00214 es->getCodecID() == CODEC_ID_MPEG1VIDEO 00215 || es->getCodecID() == CODEC_ID_MPEG2VIDEO || es->getCodecID() == CODEC_ID_THEORA) { 00216 DTS += es->getVOPTimeIncrement(); 00217 } else { 00218 if (es->getVOPTimeIncrement() == 1 ) //there was a zero number of samples in ffMP4ContainerFile 00219 DTS += (1152*es->getMediaTimeScale())/((AudioESInfo *)es)->getSampleRate(); /* for MP3 ONLY */ 00220 else 00221 DTS += es->getVOPTimeIncrement(); 00222 } 00223 00224 aus->duration = es->getVOPTimeIncrement(); //XXX currently true only for video 00225 00226 switch(err) { 00227 case 0 : 00228 aus->err = SA_OK; 00229 break; 00230 case AVERROR_NOMEM : 00231 case AVERROR_UNKNOWN : //this is what av_read... returns when EOF is reached. 00232 aus->err = SA_EOF; 00233 state = STREAMEOF; 00234 delete aus; 00235 return NULL; 00236 case AVERROR_IO : 00237 case AVERROR_NUMEXPECTED : 00238 case AVERROR_INVALIDDATA : 00239 case AVERROR_NOFMT : 00240 default : 00241 aus->err = SA_Error; 00242 } 00243 00244 00245 // has to do deep-copy, otherwise collision with MP4lib mem handler 00246 aus->payload = new u8[aus->size]; 00247 //memcpy(aus->payload, (*(au)), aus->size); 00248 memcpy(aus->payload, pkt->data, aus->size); 00249 00250 //Packet pkt no longer needed so free it 00251 av_free_packet(pkt); 00252 if(es->isVisualStream()) 00253 frm = new CompressedVideoFrame(Frame::NN_VOP, ((VideoESInfo*)es)->getWidth(), 00254 ((VideoESInfo*)es)->getHeight()); 00255 else if(es->isAudioStream()) 00256 frm=new CompressedAudioFrame(Frame::NN_VOP); 00257 else //hack for any other stream type 00258 frm = new CompressedVideoFrame(Frame::NN_VOP,0,0); 00259 00260 if (!frm->setAU(aus)) { //automatically detects frame type 00261 dfprintf_err(fp, "Failed to set AU at frame?"); 00262 } 00263 frm->setMediaTimeScale(es->getMediaTimeScale()); 00264 frm->setStreamID(pkt->stream_index); 00265 frm->setCodecID(es->getCodecID()); 00266 00267 if (!priofp) { //no prio-File given, use TIMELY DISTRIBUTION 00268 if (es->isVisualStream() && (frm->getType() == Frame::B_VOP)) { 00269 frm->getAU()->prio = prioTable[currentFrameNumber % tableSize]; 00270 } else // reference frames or AudioFrames 00271 frm->getAU()->prio = IO_NETWORK_HIGHEST_PRIORITY; //highest priority is 0 00272 } else { //parse given prio-File 00273 u32 fno=0; 00274 char *tmp = new char[MAX_STR_LEN]; 00275 fgets(tmp,MAX_STR_LEN,priofp); 00276 dprintf_full("READING %s\n",tmp); 00277 int ret = sscanf(tmp,"Prio %i Frame %i",(int*)&frm->getAU()->prio, &fno); 00278 if ( (ret <= 0) || feof(priofp)) { 00279 dprintf_err("ffMP4IO::getFrame: FATAL: parse error or EOF of prio-File! switching back to " 00280 "TIMELY DISTRIBUTION!\n"); 00281 fclose(priofp); 00282 priofp = NULL; 00283 } else { 00284 if (currentFrameNumber != fno) { 00285 dprintf_err("ffMP4IO::getFrame: FATAL: prio-File framenr %i doesnt match expected %i!" 00286 " switching back to TIMELY DISTRIBUTION!\n", fno, currentFrameNumber); 00287 fclose(priofp); 00288 priofp = NULL; 00289 } else 00290 if ((frm->getType() == Frame::I_VOP) || (frm->getType() == Frame::P_VOP)) 00291 assert(frm->getAU()->prio == 0); 00292 } 00293 delete [] tmp; 00294 } 00295 dprintf_full("ffMP4IO::getFrame %5i: (%p, AU %p) setting VOP type %s TS %i prio %i size %i Duration %i\n", 00296 currentFrameNumber, frm, frm->getAU(), Frame::VOPTypeToChar(frm->getType()),frm->getAU()->dts, 00297 frm->getAU()->prio, aus->size, aus->duration); 00298 00299 currentFrameNumber++; 00300 framesSeen++; 00301 00302 #ifdef _POSIX_PRIORITY_SCHEDULING 00303 sched_yield(); //this is necessary to give parallel getFrames a chance 00304 #endif 00305 00306 return frm; 00307 } 00308 00309 00310 /***************************************************************/ 00311 bool ffMP4IO::open() { 00312 dprintf_full("ffMP4IO::open()\r\n"); 00313 if(!writeOnly) 00314 return openForReading(); 00315 return openForWriting(); 00316 } 00317 00318 00319 /***************************************************************/ 00320 IO::State ffMP4IO::play(double prefetchTime) { 00321 00322 dprintf_full("ffMP4IO::play prefetch %f (old state %i)\n",prefetchTime,state); 00323 00324 if (state==CLOSED) { 00325 open(); 00326 return state; 00327 } else 00328 if ( (state==PAUSED) || (state==MUTED) ) 00329 setState(OPEN); 00330 else { 00331 dprintf_err("ffMP4IO::play tried to OPEN from state %i\n",state); 00332 return state; 00333 } 00334 return state; 00335 } 00336 00337 00338 /***************************************************************/ 00339 bool ffMP4IO::openForReading() { 00340 state = OPENING; 00341 int err; 00342 AVInputFormat *informat = NULL; 00343 00344 dprintf_full("ffMP4IO::openForReading : input is %s\n", input); 00345 if (strlen(input)==0) { //no Input given, assume video4linux 00346 00347 memset(ap, 0, sizeof(*ap)); 00348 if (es->isVisualStream()) { 00349 dprintf_full("ffMP4IO::openForReading : using video4linux as input: VIDEO\n"); 00350 // ap->frame_rate = 25; 00351 ap->width = 720; 00352 ap->height = 576; 00353 00354 informat = av_find_input_format("video4linux"); 00355 ap->device = NULL; 00356 ap->channel = 0; 00357 ap->standard = "ntsc"; 00358 } 00359 else if (es->isAudioStream()) { 00360 dprintf_full("ffMP4IO::openForReading : using audio grabbing as input: AUDIO\n"); 00361 ap->sample_rate = 44100; 00362 ap->channels = 2; 00363 informat = av_find_input_format("audio_device"); 00364 } 00365 else 00366 dprintf_err("ffMP4IO::openForReading : Video 4 Linux: Unknown stream type!\n"); 00367 } 00368 00369 informat=NULL; 00370 err = av_open_input_file(&ic, input, informat, 0, ap); 00371 if(err < 0) { 00372 //HACK FOR DVB-HTTP STREAM: assume&force mpeg 00373 informat = av_find_input_format("mpeg"); 00374 dprintf_full("informat is %p\n",informat); 00375 00376 err = av_open_input_file(&ic, input, informat, 0, ap); 00377 if(err < 0) { 00378 dprintf_full("ffMP4IO::openForReading : Error in opening file!! ffmpeg error %i\n",err); 00379 state = CLOSED; 00380 return false; 00381 } 00382 } 00383 dprintf_full("ffMP4IO::openForReading : av_open_input_file %s is ok\n", input); 00384 00385 //If not enough info to get the stream parameters, we decode the 00386 //first frames to get it. (used in cases when file has no headers eg. mpeg1) 00387 #ifndef WIN32 //This gives error on windows in case of audio 00388 err = av_find_stream_info(ic); 00389 if (err < 0) { 00390 fprintf(stderr, "%s: could not find codec parameters\n", input); 00391 state = CLOSED; 00392 return false; 00393 } 00394 #endif 00395 00396 00397 streamID = es->getStreamId(); 00398 char *tmp = new char[MAX_STR_LEN]; 00399 strncpy(tmp,input,MAX_STR_LEN); 00400 if (es->isVisualStream()) 00401 strcat(tmp,".video"); 00402 else if (es->isAudioStream()) 00403 strcat(tmp,".audio"); 00404 else { 00405 dprintf_err("ffMP4IO::openForReading requesting streamID neither video nor audio!\n"); 00406 delete [] tmp; 00407 return false; 00408 } 00409 sprintf(tmp,"%s.%i.prio",tmp,streamID); 00410 00411 if ((priofp=fopen(tmp,"r")) != NULL) { 00412 dprintf_full("ffMP4IO::openForReading: successfully opened prio-File %s\n",tmp); 00413 } else { 00414 dprintf_full("ffMP4IO::openForReading: prio-File %s not found. using timely distribution!\n",tmp); 00415 } 00416 00417 state = OPEN; 00418 dprintf_full("ffMP4IO::openForReading : State is open\n"); 00419 delete [] tmp; 00420 return true; 00421 00422 } 00423 00424 00425 /***************************************************************/ 00426 bool ffMP4IO::openForWriting() { 00427 00428 return false; 00429 } 00430 00431 00432 /***************************************************************/ 00433 bool ffMP4IO::close(bool immediate) { 00434 if (priofp) 00435 fclose(priofp); 00436 priofp = NULL; 00437 if (state == CLOSED) 00438 return true; 00439 00440 state = CLOSED; 00441 int err; 00442 if (ic) 00443 { 00444 av_close_input_file(ic); 00445 ic = NULL; 00446 00447 } 00448 00449 err = 0; //ASSUMING no error has occured because 00450 //av_close_.... does not return anything; ISONoErr = 0 00451 return err; 00452 } 00453 00454 00455 /***************************************************************/ 00456 int ffMP4IO::writeFrame(Frame * frm, ESInfo *out_es) { 00457 #ifdef _POSIX_PRIORITY_SCHEDULING 00458 sched_yield(); //this is necessary to give parallel getFrames a chance 00459 #endif 00460 00461 return 0; 00462 } 00463 00464 00465 /***************************************************************/ 00468 int ffMP4IO::getBufferFillLevel() const { 00469 if (state == OPEN) 00470 return 50; 00471 else if (state == STREAMEOF) 00472 return -1; 00473 else 00474 return 0; 00475 } 00476 00477 00478 /***************************************************************/ 00479 bool ffMP4IO::destroy() { 00480 close(); 00481 return remove(input) == 0; 00482 } 00483 00484 00488 bool ffMP4IO::setToFrameNumber(u32 frameNumber) { 00489 if(state!=OPEN) 00490 open(); 00491 if(ic) { 00492 if( av_seek_frame(ic, -1, frameNumber*es->getVOPTimeIncrement()*(1000000/90000), 0) ) 00493 return false; 00494 } 00495 else 00496 return false; 00497 return true; 00498 } 00499 00500 00501 #endif