void mpgfile::initaudiocodeccontext(int aud) { stream &S=s[audiostream(aud)]; S.infostring="Audio "; { char number[16]; snprintf(number,16,"%d",aud); S.infostring+=number; } switch (S.type) { case streamtype::mpegaudio: S.infostring+=" (MPEG)"; break; case streamtype::ac3audio: S.infostring+=" (AC3)"; break; default: S.infostring+=" (unknown)"; break; } }
void mpgfile::savempg(muxer &mux, int start, int stop, int savedpics, int savepics, logoutput *log) { if (start<0) start=0; if (start>pictures) start=pictures; if (stop<0) stop=0; if (stop>pictures) stop=pictures; if (start==stop) return; if (stop<start) { int x=start; start=stop; stop=x; } int seekpic=idx.indexnr(start); int framerate = mpgfile::frameratescr[idx[seekpic].getframerate()]; bool fixedstart=true; if (mux.isempty()) { fixedstart=false; mux.unsetempty(); pts_t startpts = framerate / 300; for (int i=0;i<MAXAVSTREAMS;++i) mux.setpts(i,startpts); } pts_t videostartpts=idx[seekpic].getpts(); pts_t videostoppts=idx[idx.indexnr(stop)].getpts(); pts_t videooffset=videostartpts-mux.getpts(VIDEOSTREAM); pts_t audiopts[MAXAUDIOSTREAMS]; pts_t audiostartpts[MAXAUDIOSTREAMS]; pts_t audiooffset[MAXAUDIOSTREAMS]; for(int a=0;a<MAXAUDIOSTREAMS;++a) { audiooffset[a]=videooffset; audiostartpts[a]=audiopts[a]=videostartpts; } pts_t shift=0; { dvbcut_off_t start_pos = idx[idx.indexnr(start)].getpos().fileposition(); dvbcut_off_t stop_pos = idx[idx.indexnr(stop)].getpos().fileposition(); dvbcut_off_t bytes = stop_pos - start_pos; pts_t delta_pts = (pts_t)(stop - start) * framerate / 300; double mux_rate = (double)bytes * 9e4 / (double)delta_pts; if (log) { //: Placeholder will be replaced with floating point number log->printinfo(QCoreApplication::translate("mpgfile", "Estimated mux rate: %1 MB/s").arg(mux_rate * 1e-6, 0, 'f', 2)); } } while (seekpic>0 && idx[seekpic].getpts()>=videostartpts-180000) --seekpic; dvbcut_off_t tpos; { int stoppic=idx.indexnr(start); while (stoppic<pictures && (idx[stoppic].getpts()<videostartpts+180000 || !idx[stoppic].getseqheader())) ++stoppic; tpos=idx[stoppic].getpos().packetposition(); } streamhandle sh(idx[seekpic].getpos().packetposition()); streamdata *vsd=sh.newstream(VIDEOSTREAM,s[VIDEOSTREAM].type,istransportstream()); for (int a=0;a<MAXAUDIOSTREAMS;++a) if (mux.streampresent(audiostream(a))) sh.newstream(audiostream(a),s[audiostream(a)].type,istransportstream()); while (sh.fileposition<tpos) if (streamreader(sh)<0) break; for (int a=0;a<MAXAUDIOSTREAMS;++a) if (streamdata *sd=sh.stream[audiostream(a)]) { pts_t tpts=videostartpts-mux.getpts(VIDEOSTREAM)+mux.getpts(audiostream(a)); sd->audio_addpts(); uint32_t startbufferpos=sd->ptsbufferpos(tpts); if (startbufferpos>sd->getoffset()) sd->discard(startbufferpos-sd->getoffset()); sd->audio_addpts(0,true); startbufferpos=sd->closestptsbufferpos(tpts); if (startbufferpos>sd->getoffset()) sd->discard(startbufferpos-sd->getoffset()); pts_t apts=sd->itemlist().front().headerpts(tpts); audiostartpts[a]=apts; if (apts>=0) { if (fixedstart) audiooffset[a]=videooffset-tpts+apts; else if (tpts-apts>shift) shift=tpts-apts; } } if (!fixedstart) { videooffset-=shift; for(int a=0;a<MAXAUDIOSTREAMS;++a) audiooffset[a]=videooffset; } pts_t audiostoppts[MAXAUDIOSTREAMS]; for(int a=0;a<MAXAUDIOSTREAMS;++a) audiostoppts[a]=videostoppts-videooffset+audiooffset[a]; int firstseqhdr=nextseqheader(start); { filepos_t copystart=idx[idx.indexnr(firstseqhdr)].getpos(); vsd->discard(vsd->fileposbufferpos(copystart)-vsd->getoffset()); } bool isfirstpic=true, isfirstseq=true; int firstseqnr=idx[idx.indexnr(firstseqhdr)].getsequencenumber(); if (firstseqhdr>start) { recodevideo(mux,start,firstseqhdr,videooffset,savedpics,savepics,log); savedpics+=firstseqhdr-start; } int copystop=stop; // first picture not to write to stream while (copystop<pictures && idx[idx.indexnr(copystop)].isbframe()) ++copystop; copystop=idx.indexnr(copystop); int streampic=idx.indexnr(firstseqhdr); while (!log || !log->cancelled()) { int packetsread; for (packetsread=0;packetsread<20;++packetsread) if (streamreader(sh)<=0) break; if (packetsread==0) break; // copy video if (vsd) for(;;) { if (streampic>=copystop) { vsd=0; sh.delstream(VIDEOSTREAM); break; } uint32_t picsize=vsd->fileposbufferpos(idx[streampic+1].getpos())-vsd->getoffset(); if (picsize>=vsd->inbytes()) break; if (!isfirstpic && idx[streampic].getseqheader()) isfirstseq=false; isfirstpic=false; int seqoff=0; if (!isfirstseq || idx[streampic].getsequencenumber()>=firstseqnr) { if (isfirstseq && firstseqnr>0) // need to subtract offset from picture sequence number { uint8_t *d=(uint8_t*) vsd->getdata(); for (unsigned int j=0;j+5<picsize;) { if (d[2]&0xfe) j+=3; else if (*(u_int32_t*)&d[j]==mbo32(0x00000100)) { int seqpic=(d[j+4]<<2)|((d[j+5]>>6)&0x03); seqpic-=firstseqnr; d[j+4]=seqpic>>2; d[j+5]=(d[j+5]&0x3f)|((seqpic<<6)&0xc0); break; } else ++j; } seqoff=firstseqnr; } pts_t vidpts=idx[streampic].getpts()-videooffset; pts_t viddts=vidpts; if (!idx[streampic].isbframe()) { viddts=mux.getdts(VIDEOSTREAM); mux.setdts(VIDEOSTREAM,vidpts); } if (idx[streampic].getseqheader()) { int tcpic=streampic; while (tcpic < copystop && idx[tcpic].getsequencenumber() != seqoff) ++tcpic; pts_t tcpts=idx[tcpic].getpts()-videooffset; fixtimecode((uint8_t*)vsd->getdata(),picsize,tcpts); } if (!mux.putpacket(VIDEOSTREAM,vsd->getdata(),picsize,vidpts,viddts, idx[streampic].isiframe() ? MUXER_FLAG_KEY:0 )) { if (log) { //: Placeholder will be replaced with streampic number log->printwarning(QCoreApplication::translate("mpgfile", "putpacket(streampic=%1) returned false").arg(streampic)); } else { fprintf(stderr,"WARN: putpacket(streampic=%d) returned false\n",streampic); } } }
void mpgfile::playaudio(int aud, int picture, int ms) { if (aud>=audiostreams || ms==0) return; pts_t startpts=idx[idx.indexnr(picture)].getpts(); pts_t stoppts=startpts; if (ms<0) startpts+=ms*90; else stoppts+=ms*90; int seekpic=idx.indexnr(picture); while (seekpic>0 && idx[seekpic].getpts()>=startpts-180000) --seekpic; int stopreadpic=idx.indexnr(picture); while (stopreadpic<pictures-1 && idx[stopreadpic].getpts()<stoppts+180000) ++stopreadpic; dvbcut_off_t stopreadpos=idx[stopreadpic].getpos().packetposition(); streamhandle sh(idx[seekpic].getpos().packetposition()); streamdata *sd=sh.newstream(audiostream(aud),s[audiostream(aud)].type,istransportstream()); while (sd->empty()) { if (sh.fileposition > stopreadpos || streamreader(sh)<=0) return; // data does not reach the point in time from which we like to start playing while (!sd->empty() && !sd->itemlist().begin()->headerhaspts()) sd->pop(); } for(;;) { if (sh.fileposition > stopreadpos || streamreader(sh)<=0) return; // data does not reach the point in time from which we like to start playing if (sd->empty()) continue; streamdata::itemlisttype::const_iterator it=sd->itemlist().begin(); int pop=1; pts_t pts=AV_NOPTS_VALUE; for(++it;it!=sd->itemlist().end();++it,++pop) if (it->headerhaspts()) //if (streamdata::headerhaspts(it->header)) { pts=it->headerpts(startpts); break; } if (pts==(pts_t)AV_NOPTS_VALUE) continue; if (pts<=startpts) sd->pop(pop); if (pts>=startpts) break; } while (streamreader(sh)>0) { streamdata::itemlisttype::const_reverse_iterator it=sd->itemlist().rbegin(); while(it!=sd->itemlist().rend()) if (it->headerhaspts()) break; else --it; if (it==sd->itemlist().rend()) continue; if (it->headerpts(stoppts)>stoppts) break; } sd->audio_addpts(); uint32_t startbufferpos=sd->closestptsbufferpos(startpts); uint32_t stopbufferpos=sd->closestptsbufferpos(stoppts); if (stopbufferpos>startbufferpos) { const stream &S=s[audiostream(aud)]; if (S.type==streamtype::ac3audio) playaudio_ac3(sd->getdata(startbufferpos),stopbufferpos-startbufferpos); else playaudio_mp2(sd->getdata(startbufferpos),stopbufferpos-startbufferpos); } }
mpegmuxer::mpegmuxer(uint32_t audiostreammask, mpgfile &mpg, const char *filename, bool dvd, int packsize_bytes, int muxrate_bitsps) : fd(-1), st(), muxrate(muxrate_bitsps/400), packsize(packsize_bytes), ptsoffset(0), aucounter(0), systemhdr(0), systemhdrlen(0), pespacket_setlength(true),scr(0) { if (packsize<MINPACKSIZE) packsize=0; scrpack=int(27.e6/double(muxrate*50)*packsize+0.9999); st[VIDEOSTREAM]=new stream(streamtype::mpeg2video,0xe0,232<<10,232<<10,true); strpres[VIDEOSTREAM]=true; int audiobuffersize=dvd?(4<<10):(48<<10); for (int i=0;i<mpg.getaudiostreams();++i) if (audiostreammask & (1u<<i)) { streamtype::type t = mpg.getstreamtype(audiostream(i)); if (t==streamtype::ac3audio) st[audiostream(i)]=new stream(t, 0x180+i, audiobuffersize,58<<10,true); /* not supported yet: else if (t==streamtype::dtsaudio) st[audiostream(i)]=new stream(t, 0x188+i, audiobuffersize,58<<10,true); */ else st[audiostream(i)]=new stream(t, 0xc0+i, audiobuffersize,audiobuffersize,false); strpres[audiostream(i)]=true; } systemhdrlen=dvd ? 2034 : 24; // include DVD navigation packets if dvd set systemhdr = (void*) calloc(1, systemhdrlen); bzero(systemhdr,systemhdrlen); { systemhdr_s *value = (systemhdr_s*) systemhdr; *value = { mbo32(0x000001bb),mbo16(18), //1,muxrate,1,mpg.getaudiostreams(),0,0, htom32(0x80000100 | ((muxrate&0x3fffff)<<9) | (mpg.getaudiostreams()<<2)), //1,1,1,1, 0xe1, //0,0x7f, 0x7f, 0xb9,mbo16(0xe000|232),0xb8,mbo16(0xc000|32), 0xbd,mbo16(0xe000|58),0xbf,mbo16(0xe000|2) }; } if (dvd) { // dvd nav packets *(uint32_t*)((char*)systemhdr+24)=mbo32(0x000001bf); *(uint16_t*)((char*)systemhdr+28)=mbo16(980); *(uint32_t*)((char*)systemhdr+1010)=mbo32(0x000001bf); *(uint16_t*)((char*)systemhdr+1014)=mbo16(1018); } // total size of all buffers: 232kB video + 4kB per mpeg audio stream double allbuffers(double((232<<10)+mpg.getaudiostreams()*(4<<10))); allbuffers*=1.05; // 5% assumed muxing overhead ptsoffset=pts_t(90000.*(allbuffers/50./muxrate))+90; if (!strncmp(filename, "pipe:", 5)) fd = atoi(filename+5); else fd=::open(filename,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY,0666); }