bool mpegmuxer::putpacket(int str, const void *data, int len, pts_t pts, pts_t dts, uint32_t flags) { stream * const s=st[str]; if (!s) return false; if (len == 0) { // I'm not sure why this happens, but it does. --mr fprintf(stderr, "mpegmuxer::putpacket called with zero length, str=%d\n", str); return false; } pts+=ptsoffset; dts+=ptsoffset; au *newau=new au(data,len,pts,dts,flags); if (s->type==streamtype::mpeg2video) { uint8_t *audata=(uint8_t*) newau->getdata(); // if (0) // for (int j=0;j+11<len;) { uint8_t *d=audata+j; if (d[2]&0xfe) j+=3; else if (((*(u_int32_t*)&d[0])&mbo32(0xffffff00))==mbo32(0x00000100)) { if (d[3]==0x00) // picture header { // vbv delay := 0xffff d[5]|=0x07; d[6]=0xff; d[7]|=0xf8; break; } else if (d[3]==0xb3) // sequence header { d[8]=(VIDEOBITRATE/400) >> 10; d[9]=((VIDEOBITRATE/400) >> 2) & 0xff; d[10]=(((VIDEOBITRATE/400) << 6)&0xc0)|0x20|(((VBVBUFFERSIZE/2)>>5)&0x1f); d[11]=(d[11]&0x07)|((VBVBUFFERSIZE/2)<<3); j+=12; } else if (d[3]==0xb5) // extension { if ((d[4]&0xf0) == 0x10) // sequence extension { // set bitrate_extension and vbv_buffer_size_extension to 0 d[6]&=0xe0; d[7]=0x01; d[8]=0; j+=10; } else j+=5; } else j+=4; } else
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); } } }
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); }