/** \fn refill */ bool ADM_audioStreamBuffered::refill(void) { // Shrink buffer... if(limit>ADM_AUDIOSTREAM_BUFFER_SIZE && start> 10*1024) { //printf("[Shrink]\n"); memmove(buffer.at(0), buffer.at(start),limit-start); limit-=start; start=0; } uint64_t newDts; uint32_t size; ADM_assert(limit<(2*ADM_AUDIOSTREAM_BUFFER_SIZE-16)); uint32_t toRead=2*ADM_AUDIOSTREAM_BUFFER_SIZE-limit-16; if(true!=access->getPacket(buffer.at(limit), &size, toRead,&newDts)) return false; // We introduce a small error as there might be some bytes left in the buffer // By construction, the error should be minimal if(newDts!=ADM_AUDIO_NO_DTS) { if( abs(newDts-lastDts)>ADM_MAX_SKEW) { printf("[AudioStream] Warning skew in dts =%"PRId64", \n",(int64_t)newDts-(uint64_t)lastDts); printf("[AudioStream] Warning skew lastDts=%s \n",ADM_us2plain(lastDts)); printf("[AudioStream] Warning skew newDts=%s \n",ADM_us2plain(newDts)); setDts(newDts); } // If we have a DTS and the buffer is empty, set the dts inconditionnaly if(!start) setDts(newDts); // Fixme allow a bit of error, not accumulating } limit+=size; ADM_assert(limit<ADM_AUDIOSTREAM_BUFFER_SIZE*2); return true; }
/** \fn shiftAudioVideoBy \brief shift audio and video so that they start close to zero */ bool asfHeader::shiftAudioVideoBy(uint64_t s) { int n=_index.size(); ADM_info("Shifting by %s\n",ADM_us2plain(s)); for(int i=0;i<n;i++) { if(_index[i].pts!=ADM_NO_PTS) { if(_index[i].pts<s) { ADM_error("Shifting too big for frame %d PTS: %s\n",i,ADM_us2plain(_index[i].pts)); }else _index[i].pts-=s; } _index[i].dts=ADM_NO_PTS; /* if(_index[i].dts!=ADM_NO_PTS) { if(_index[i].dts<s) { ADM_error("Shifting too big for frame %d DTS: %s\n",i,ADM_us2plain(_index[i].dts)); _index[i].dts=ADM_NO_PTS; }else _index[i].dts-=s; } */ } _shiftUs=s; return true; }
/** * \fn getConfiguration * */ const char *AVDM_Fade::getConfiguration(void) { static char conf[256]; std::string start=std::string(ADM_us2plain(param.startFade*1000LL)); std::string end=std::string(ADM_us2plain(param.endFade*1000LL)); snprintf(conf,255," Fade : Start %s End %s",start.c_str(),end.c_str()); return conf; }
/** \fn checkCutsAreOnIntra \brief In copy mode, if the cuts are not on intra we will run into trouble : * We include skipped ref frames: we will have DTS going back error * We skip them, we have borked video at cut points due to missing ref framesz \return true if everything ok */ bool ADM_Composer::checkCutsAreOnIntra(void) { bool fail=false; int nbSeg=_segments.getNbSegments(); ADMCompressedImage img; uint8_t *buffer=new uint8_t[1920*1080*3]; img.data=buffer; ADM_info("Checking cuts start on keyframe..\n"); for(int i=0; i<nbSeg; i++) { _SEGMENT *seg=_segments.getSegment(i); _VIDEOS *vid=_segments.getRefVideo(seg->_reference); vidHeader *demuxer=vid->_aviheader; if(false==switchToSegment(i,true)) { fail=true; break; } if(false==demuxer->getFrame (vid->lastSentFrame,&img)) { ADM_info("Cannot get 1st frame of segment %d\n",i); fail=true; break; } if(!img.flags & AVI_KEY_FRAME) { ADM_warning("Segment %d does not start on a keyframe (%s)\n",i,ADM_us2plain(img.demuxerPts)); fail=true; break; } // After a seg switch we are at the keyframe before or equal to where we want to go // if the dts do not match, it means we went back too much // When re-encoding, it's not a problem, it is when copying. ADM_info("seg:%d refDTS=%"PRIu64"\n",seg->_reference,seg->_refStartDts); ADM_info("seg:%d imgDTS=%"PRIu64"\n",seg->_reference,img.demuxerDts); if(!seg->_refStartDts && !seg->_reference) { ADM_info("Ignoring first seg (unreliable DTS)\n"); } else if(img.demuxerDts!=ADM_NO_PTS && seg->_refStartDts!=ADM_NO_PTS && img.demuxerDts!=seg->_refStartDts) { ADM_warning("Segment %d does not start on a known DTS (%s)\n",i,ADM_us2plain(img.demuxerPts)); ADM_warning("expected (%s)\n",ADM_us2plain(seg->_refStartDts)); fail=true; break; } ADM_info("Segment %d ok\n",i); } delete [] buffer; buffer=NULL; if(fail) return false; return true; }
void ADM_EditorSegment::dumpSegmentsInternal(ListOfSegments &l) { int n=l.size(); for(int i=0;i<n;i++) { _SEGMENT s=l.at(i); printf("Segment :%d/%d\n",i,n); printf("\tReference :%"PRIu32" %s\n",s._reference,ADM_us2plain(s._reference)); printf("\tstartLinear :%08"PRIu64" %s\n",s._startTimeUs,ADM_us2plain(s._startTimeUs)); printf("\tduration :%08"PRIu64" %s\n",s._durationUs,ADM_us2plain(s._durationUs)); printf("\trefStartPts :%08"PRIu64" %s\n",s._refStartTimeUs,ADM_us2plain(s._refStartTimeUs)); printf("\trefStartDts :%08"PRIu64" %s\n",s._refStartDts,ADM_us2plain(s._refStartDts)); } }
/** * \fn switchToNextAudioSegment * */ bool ADM_edAudioTrackFromVideo::switchToNextAudioSegment(void) { // Try to switch segment if(_audioSeg+1>=parent->_segments.getNbSegments()) return false; ADM_warning("Switching to segment %"PRIu32"\n",_audioSeg+1); _audioSeg++; _SEGMENT *seg=parent->_segments.getSegment(_audioSeg); ADM_audioStreamTrack *trk=getTrackAtVideoNumber(seg->_reference); // ADM_Audiocodec *codec=NULL; if(trk) if(trk->codec) codec=trk->codec; if(codec) { codec->resetAfterSeek(); } // Go to beginning of the stream if(false==trk->stream->goToTime(seg->_refStartTimeUs)) { ADM_warning("Fail to seek audio to %"PRIu64"ms\n",seg->_refStartTimeUs/1000); return false; } ADM_info("Switched ok to audio segment %"PRIu32", with a ref time=%s\n", _audioSeg,ADM_us2plain(seg->_refStartTimeUs)); return true; }
/** \fn getPacket */ uint8_t ADM_audioStreamConstantChunk::getPacket(uint8_t *buffer,uint32_t *size, uint32_t sizeMax,uint32_t *nbSample,uint64_t *dts) { *size=0; *nbSample=0; if(sizeMax>=chunkSize) { uint32_t mSize; uint64_t mDts; if(!access->getPacket(buffer,&mSize,sizeMax,&mDts)) { ADM_warning("Cant get packet\n"); return 0; } ADM_info("Got packet : chunk=%d size=%d dts=%s\n",chunkSize,mSize,ADM_us2plain(mDts)); if(!*size) *dts=mDts; *size+=mSize; *nbSample+=samplesPerChunk; if(mSize!=chunkSize) { ADM_warning("Expected chunk of size =%d, got %d\n",chunkSize,mSize); } buffer+=mSize; sizeMax-=mSize; } if(!*size) return 0; return 1; }
/** \fn dumpRefVideos \brief Dump the refVideo content */ void ADM_EditorSegment::dumpRefVideos(void) { int n=videos.size(); printf("We have %d reference videos\n",n); for(int i=0;i<n;i++) { _VIDEOS *s=getRefVideo(i); printf("Videos :%d/%d\n",i,n); printf("\tfirstFramePts :%08"PRIu64" %s\n",s->firstFramePts,ADM_us2plain(s->firstFramePts)); printf("\ttimeIncrementInUs :%08"PRIu64" %s\n",s->timeIncrementInUs,ADM_us2plain(s->timeIncrementInUs)); printf("\tnb frames :%04"PRIu32"\n",s->_nb_video_frames); } }
uint8_t MP4Header::getFrame(uint32_t framenum,ADMCompressedImage *img) { aprintf("[MP4] frame %d requested (nbFrame=%d)\n",framenum,VDEO.nbIndex); if(framenum>=VDEO.nbIndex) { return 0; } MP4Index *idx=&(VDEO.index[framenum]); uint64_t offset=idx->offset; //+_mdatOffset; fseeko(_fd,offset,SEEK_SET); fread(img->data, idx->size, 1, _fd); img->dataLength=idx->size; img->flags = idx->intra; img->demuxerDts=idx->dts; img->demuxerPts=idx->pts; aprintf("[MP4] Pts=%s\n",ADM_us2plain(idx->pts)); /* if(img->demuxerPts==ADM_COMPRESSED_NO_PTS) img->demuxerPts=img->demuxerDts; */ return 1; }
/** \fn fillAudio \brief Put audio datas until targetDts is reached */ bool muxerAvi::fillAudio(uint64_t targetDts) { // Now send audio until they all have DTS > lastVideoDts+increment for(int audioIndex=0;audioIndex<nbAStreams;audioIndex++) { ADM_audioStream*a=aStreams[audioIndex]; uint32_t fq=a->getInfo()->frequency; int nb=0; audioClock *clk=clocks[audioIndex]; aviAudioPacket *aPacket=audioPackets+audioIndex; if(true==aPacket->eos) return true; while(1) { if(false==aPacket->present) { if(!a->getPacket(aPacket->buffer, &(aPacket->sizeInBytes), AUDIO_BUFFER_SIZE, &(aPacket->nbSamples), &(aPacket->dts))) { ADM_warning("Cannot get audio packet for stream %d\n",audioIndex); aPacket->eos=true; break; } if(aPacket->dts!=ADM_NO_PTS) { aPacket->dts+=audioDelay; aPacket->dts-=firstPacketOffset; } aprintf("[Audio] Packet size %"PRIu32" sample:%"PRIu32" dts:%"PRIu64" target :%"PRIu64"\n", aPacket->sizeInBytes,aPacket->nbSamples,aPacket->dts,targetDts); if(aPacket->dts!=ADM_NO_PTS) if( abs(aPacket->dts-clk->getTimeUs())>32000) { ADM_warning("[AviMuxer] Audio skew!\n"); clk->setTimeUs(aPacket->dts); #warning FIXME add padding } aPacket->present=true; } // We now have a packet stored aprintf("Audio packet dts =%s\n",ADM_us2plain(aPacket->dts)); if(aPacket->dts!=ADM_NO_PTS) if(aPacket->dts>targetDts) { aprintf("In the future..\n"); break; // this one is in the future } nb=writter.saveAudioFrame(audioIndex,aPacket->sizeInBytes,aPacket->buffer) ; encoding->pushAudioFrame(aPacket->sizeInBytes); aprintf("writting audio packet\n"); clk->advanceBySample(aPacket->nbSamples); aPacket->present=false; //printf("%u vs %u\n",audioDts/1000,(lastVideoDts+videoIncrement)/1000); } } return true; }
/** \fn updateRefVideo \brief Update start time */ bool ADM_EditorSegment::updateRefVideo(void) { int n=videos.size(); ADM_assert(n); _VIDEOS *ref=getRefVideo(n-1); vidHeader *demuxer=ref->_aviheader; uint64_t pts,dts; demuxer->getPtsDts(0,&pts,&dts); if(pts!=ADM_NO_PTS && pts >0) { ADM_warning("Updating firstFramePTS, The first frame has a PTS >0, adjusting to %"PRIu64" ms\n",pts/1000); ref->firstFramePts=pts; }else { ADM_info("First PTS is %s\n",ADM_us2plain(pts)); } updateStartTime(); // n=segments.size(); if(n) { _SEGMENT *seg=getSegment(n-1); uint64_t dur=ref->_aviheader->getVideoDuration(); printf("Current duration %"PRIu64" ms real one %"PRIu64" ms\n",dur/1000,seg->_durationUs/1000); } return true; }
void ADM_EditorSegment::dumpSegment(int i) { int n=segments.size(); if(i>=n) { ADM_error("Segment %d too big (%d)\n",i,(int)n); return ; } _SEGMENT *s=getSegment(i); printf("Segment :%d/%d\n",i,n); printf("\tReference :%"PRIu32" %s\n",s->_reference,ADM_us2plain(s->_reference)); printf("\tstartLinear :%08"PRIu64" %s\n",s->_startTimeUs,ADM_us2plain(s->_startTimeUs)); printf("\tduration :%08"PRIu64" %s\n",s->_durationUs,ADM_us2plain(s->_durationUs)); printf("\trefStartPts :%08"PRIu64" %s\n",s->_refStartTimeUs,ADM_us2plain(s->_refStartTimeUs)); printf("\trefStartDts :%08"PRIu64" %s\n",s->_refStartDts,ADM_us2plain(s->_refStartDts)); }
/** \fn getNextFrame \brief */ bool vdpauVideoFilterDeint::getNextFrame(uint32_t *fn,ADMImage *image) { bool r=true; if(eof) { ADM_warning("[VdpauDeint] End of stream\n"); return false; } #define FAIL {r=false;goto endit;} if(passThrough) return previousFilter->getNextFrame(fn,image); // top field has already been sent, grab bottom field if((secondField)&&(configuration.deintMode==ADM_KEEP_BOTH)) { secondField=false; *fn=nextFrame*2+1; if(false==getResult(image)) return false; if(ADM_NO_PTS==nextPts) image->Pts=nextPts; else image->Pts=nextPts-info.frameIncrement; aprintf("2ndField : Pts=%s\n",ADM_us2plain(image->Pts)); return true; } // shift frames;... free slot[0] rotateSlots(); // our first frame, we need to preload one frame if(!nextFrame) { aprintf("This is our first image, filling slot 1\n"); ADMImage *prev= vidCache->getImageAs(ADM_HW_VDPAU,0); if(false==fillSlot(1,prev)) { vidCache->unlockAll(); return false; } if(false==fillSlot(0,prev)) { vidCache->unlockAll(); return false; } } // regular image, in fact we get the next image here ADMImage *next= vidCache->getImageAs(ADM_HW_VDPAU,nextFrame+1); if(next) { if(false==fillSlot(2,next)) { vidCache->unlockAll(); FAIL } }
/** * \fn copyToClipBoard * \Brief copy the section between startTime and endTime to clipboard * @param startTime * @param endTime * @return */ bool ADM_EditorSegment::copyToClipBoard(uint64_t startTime, uint64_t endTime) { ADM_info("Copy to clipboard from %s",ADM_us2plain(startTime)); ADM_info("to %s\n",ADM_us2plain(endTime)); uint32_t startSeg,endSeg; uint64_t startSegTime,endSegTime; convertLinearTimeToSeg( startTime, &startSeg,&startSegTime); convertLinearTimeToSeg( endTime, &endSeg,&endSegTime); dump(); clipboard.clear(); for(int seg=startSeg;seg<=endSeg;seg++) { _SEGMENT s=segments[seg]; aprintf("Adding segment %d to clipboard\n",seg); if(s._startTimeUs<=startTime && (s._startTimeUs+s._durationUs)>startTime) { // need to refine 1st seg uint64_t offset=startTime-s._startTimeUs; s._refStartTimeUs+=offset; s._durationUs-=offset; // take into account the part we chopped aprintf("Marker A is here offset=%d\n",(int)offset); } if(s._startTimeUs<=endTime && (s._startTimeUs+s._durationUs)>endTime) { // need to refine last seg uint64_t offset=endTime-s._startTimeUs; s._durationUs=offset; aprintf("Marker B is here offset=%d\n",(int)offset); } // TODO refine timing for 1st/last/duration/... clipboard.push_back(s); } dumpClipBoard(); return false; }
/** * \fn pasteFromClipBoard * \brief instert clipboard at currentTime position * @param currentTime * @return */ bool ADM_EditorSegment::pasteFromClipBoard(uint64_t currentTime) { if(clipboardEmpty()) { ADM_info("The clipboard is empty, nothing to do\n"); return true; } ADM_info("Pasting from clipboard to %s\n",ADM_us2plain(currentTime)); uint32_t startSeg; uint64_t startSegTime; convertLinearTimeToSeg( currentTime, &startSeg,&startSegTime); ListOfSegments tmp=segments; ListOfSegments newSegs; int n=segments.size(); for(int i=0;i<n;i++) { _SEGMENT s=segments[i]; if(i==startSeg) { // insert clipboard // Do we need to split it ? if(currentTime==s._startTimeUs) { // nope for(int j=0;j<clipboard.size();j++) newSegs.push_back(clipboard[j]); }else { _SEGMENT pre=s,post=s; uint64_t offset=currentTime-s._startTimeUs; pre._durationUs=offset; post._refStartTimeUs+=offset; post._durationUs-=offset; newSegs.push_back(pre); for(int j=0;j<clipboard.size();j++) newSegs.push_back(clipboard[j]); newSegs.push_back(post); continue; } } newSegs.push_back(s); } segments=newSegs; // If a video doesn't start at zero and we paste to its first frame, // we end up with an empty segment at the beginning. Remove it. removeEmptySegments(); updateStartTime(); dump(); return true; }
/** * \fn prefill * \brief load first audio & video packets to get the 1st packet offset * Needed to avoid adding fillers for both audio & video at the beginning * @return */ bool muxerAvi::prefill(ADMBitstream *in) { uint64_t dts=0; if(false==vStream->getPacket(in)) { ADM_error("Cannot get first video frame\n"); return false; } dts=in->dts; for(int audioIndex=0;audioIndex<nbAStreams;audioIndex++) { ADM_audioStream*a=aStreams[audioIndex]; audioClock *clk=clocks[audioIndex]; aviAudioPacket *aPacket=audioPackets+audioIndex; if(!a->getPacket(aPacket->buffer, &(aPacket->sizeInBytes), AUDIO_BUFFER_SIZE, &(aPacket->nbSamples), &(aPacket->dts))) { ADM_warning("Cannot get audio packet for stream %d\n",audioIndex); aPacket->eos=true; aPacket->present=false; continue; } aPacket->present=true; if(aPacket->dts!=ADM_NO_PTS) { aPacket->dts+=audioDelay; } if(dts==ADM_NO_PTS) dts=aPacket->dts; if(aPacket->dts!=ADM_NO_PTS && dts!=ADM_NO_PTS) if(aPacket->dts<dts) dts=aPacket->dts; } ADM_info("Min 1st packet time :%s\n",ADM_us2plain(dts)); if(dts!=ADM_NO_PTS) firstPacketOffset=dts; rescaleVideo(in,firstPacketOffset); for(int audioIndex=0;audioIndex<nbAStreams;audioIndex++) { aviAudioPacket *aPacket=audioPackets+audioIndex; if(!aPacket->present) continue; if(aPacket->dts!=ADM_NO_PTS) aPacket->dts-=firstPacketOffset; } return true; }
/** \fn refillPacketBuffer */ bool ADM_edAudioTrackExternal::refillPacketBuffer(void) { packetBufferSize=0; uint64_t dts; if(!internalAudioStream->getPacket(packetBuffer,&packetBufferSize,ADM_EDITOR_PACKET_BUFFER_SIZE, &packetBufferSamples,&dts)) { return false; } // // Ok we have a packet, rescale audio //if(dts==ADM_NO_PTS) packetBufferDts=ADM_NO_PTS; packetBufferDts=dts; // Could have a small error here.. vprintf("Refilling buffer dts=%s\n",ADM_us2plain(packetBufferDts)); return true; }
/** \fn initUI \brief initialize the progress bar */ bool ADM_muxer::initUI(const char *title) { bool useTray = false; if (!prefs->get(FEATURES_USE_SYSTRAY, &useTray)) useTray = false; videoIncrement=vStream->getFrameIncrement(); // Video increment in AVI-Tick videoDuration=vStream->getVideoDuration(); ADM_info("Muxer, creating UI, video duration is %s\n",ADM_us2plain(videoDuration)); encoding=createEncoding(videoDuration,useTray); // Set video stream etc... encoding->setVideoCodec(fourCC::tostring(vStream->getFCC())); if(!nbAStreams) encoding->setAudioCodec("None"); else encoding->setAudioCodec(getStrFromAudioCodec(aStreams[0]->getInfo()->encoding)); return true; }
/** * \fn pasteFromClipBoard * \brief instert clipboard at currentTime position * @param currentTime * @return */ bool ADM_EditorSegment::pasteFromClipBoard(uint64_t currentTime) { ADM_info("Pasting from clipboard to %s\n",ADM_us2plain(currentTime)); uint32_t startSeg; uint64_t startSegTime; convertLinearTimeToSeg( currentTime, &startSeg,&startSegTime); ListOfSegments newSegs; int n=segments.size(); for(int i=0;i<n;i++) { _SEGMENT s=segments[i]; if(i==startSeg) { // insert clipboard // Do we need to split it ? if(currentTime==s._startTimeUs) { // nope for(int j=0;j<clipboard.size();j++) newSegs.push_back(clipboard[j]); }else { _SEGMENT pre=s,post=s; uint64_t offset=currentTime-s._startTimeUs; pre._durationUs=offset; post._refStartTimeUs+=offset; post._durationUs-=offset; newSegs.push_back(pre); for(int j=0;j<clipboard.size();j++) newSegs.push_back(clipboard[j]); newSegs.push_back(post); continue; } } newSegs.push_back(s); } segments=newSegs; updateStartTime(); return true; }
/** \fn loadAndToggleAudioSlot */ bool muxerMp4v2::loadAndToggleAudioSlot(int index) { ADM_audioStream *a=aStreams[index]; mp4v2AudioPacket *pkt=&(audioPackets[index]); mp4v2AudioPacket::mp4v2AudioBlock *blk=&(pkt->blocks[pkt->nextWrite]); if(!a->getPacket(blk->buffer, &(blk->sizeInBytes), AUDIO_BUFFER_SIZE, &(blk->nbSamples), &(blk->dts))) { ADM_warning("Cannot get audio packet for stream %d\n",index); pkt->eos=true; return false; } if(blk->dts!=ADM_NO_PTS) blk->dts+=audioDelay; blk->present=true; pkt->nextWrite=!pkt->nextWrite; aprintf("Read audio block, size=%d bytes, dts=%s\n",blk->sizeInBytes,ADM_us2plain(blk->dts)); return true; }
/** \fn ADM_audioStreamConstantChunk \brief constructor */ ADM_audioStreamConstantChunk::ADM_audioStreamConstantChunk(WAVHeader *header,ADM_audioAccess *access) : ADM_audioStream(header,access) { // chunkSize=header->blockalign; if(!chunkSize) { ADM_warning("[ADM_audioStreamConstantChunk] Blockalign is null expect problems\n"); chunkSize=8192; // dummy value } ADM_info("[ADM_audioStreamConstantChunk] Chunk size %" PRIu32"\n",chunkSize); ADM_info("[ADM_audioStreamConstantChunk] Byterate %" PRIu32"\n",header->byterate); // Compute sample per chunk from wavHeader... float f; f=chunkSize; f/=header->byterate; // F is in seconds f*=header->frequency; // in sample samplesPerChunk=(uint32_t)f; ADM_info("[ADM_audioStreamConstantChunk] About %" PRIu32" samples per chunk\n",samplesPerChunk); //samplesPerChunk=16; // If hinted..., compute the duration ourselves if(access->isCBR()==true && access->canSeekOffset()==true) { // We can compute the duration from the length float size=access->getLength(); size/=header->byterate; // Result is in second size*=1000; size*=1000; // s->us durationInUs=(uint64_t)size; ADM_info("Computed duration %s\n",ADM_us2plain(durationInUs)); return; } // Time based durationInUs=access->getDurationInUs(); }
/** \fn intraTimeToFrame \brief Return the frame whosePTS==seektime, assert if does not exist */ uint32_t ADM_EditorSegment::intraTimeToFrame(uint32_t refVideo,uint64_t seekTime) { uint32_t frame; uint32_t flags; _VIDEOS *v=getRefVideo(refVideo); ADM_assert(v); if(false==TimeToFrame(v,seekTime,&frame,&flags)) { ADM_error("Cannot find frame with time %"PRIu64"ms\n",seekTime/1000); ADM_assert(0); } uint32_t next; v->_aviheader->getFlags(frame+1,&next); if(!((next | flags) & AVI_KEY_FRAME)) // The 2nd field might be keyframe { ADM_warning("Seeking to a non keyframe (time=%s), flags=%x, flagsNext=%x\n",ADM_us2plain(seekTime),flags,next); ADM_warning("This is not normal unless you start frame is not a keyframe\n"); } return frame; }
/** \fn getPCMPacket */ bool ADM_edAudioTrackExternal::getPCMPacket(float *dest, uint32_t sizeMax, uint32_t *samples,uint64_t *odts) { uint32_t fillerSample=0; // FIXME : Store & fix the DTS error correctly!!!! uint32_t inSize; bool drop=false; uint32_t outFrequency=codec->getOutputFrequency(); vprintf("[PCMPacketExt] request TRK %d:%x\n",0,(long int)0); again: *samples=0; // Do we already have a packet ? if(!packetBufferSize) { if(!refillPacketBuffer()) { ADM_warning("Cannot refill\n"); return false; } } // We do now vprintf("[PCMPacketExt] Packet size %d, Got %d samples, time code %08lu lastDts=%08lu delta =%08ld\n", packetBufferSize,packetBufferSamples,packetBufferDts,lastDts,packetBufferDts-lastDts); // If lastDts is not initialized.... if(lastDts==ADM_AUDIO_NO_DTS) setDts(packetBufferDts); vprintf("Last Dts=%s\n",ADM_us2plain(lastDts)); // // The packet is ok, decode it... // uint32_t nbOut=0; // Nb sample as seen by codec vprintf("externalPCM: Sending %d bytes to codec\n",packetBufferSize); if(!codec->run(packetBuffer, packetBufferSize, dest, &nbOut)) { packetBufferSize=0; // consume ADM_warning("[PCMPacketExt::getPCMPacket] Track %d:%x : codec failed failed\n", 0,0); return false; } packetBufferSize=0; // consume // Compute how much decoded sample to compare with what demuxer said uint32_t decodedSample=nbOut; decodedSample/=wavHeader.channels; if(!decodedSample) goto again; #define ADM_MAX_JITTER 5000 // in samples, due to clock accuracy, it can be +er, -er, + er, -er etc etc if(labs((int64_t)decodedSample-(int64_t)packetBufferSamples)>ADM_MAX_JITTER) { ADM_warning("[PCMPacketExt::getPCMPacket] Track %d:%x Demuxer was wrong %d vs %d samples!\n", 0,0,packetBufferSamples,decodedSample); } // Update infos *samples=(decodedSample); *odts=lastDts; vprintf("externalPCM: got %d samples, PTS is now %s\n",decodedSample,ADM_us2plain(*odts)); advanceDtsByCustomSample(decodedSample,outFrequency); vprintf("[Composer::getPCMPacket] Track %d:%x Adding %u decoded, Adding %u filler sample," " dts is now %lu\n", 0,(long int)0, decodedSample,fillerSample,lastDts); ADM_assert(sizeMax>=(fillerSample+decodedSample)*wavHeader.channels); vprintf("[getPCMext] %d samples, dts=%s\n",*samples,ADM_us2plain(*odts)); return true; }
/** \fn addReferenceVideo \brief Add a new source video, fill in the missing info + create automatically the matching seg */ bool ADM_EditorSegment::addReferenceVideo(_VIDEOS *ref) { uint32_t l; uint8_t *d; aviInfo info; _SEGMENT seg; ref->dontTrustBFramePts=ref->_aviheader->unreliableBFramePts(); ref->_aviheader->getVideoInfo (&info); ref->_aviheader->getExtraHeaderData (&l, &d); ref->decoder = ADM_getDecoder (info.fcc, info.width, info.height, l, d,info.bpp); ref->_videoCache = new EditorCache(8,info.width,info.height) ; float frameD=info.fps1000; frameD=frameD/1000; frameD=1/frameD; frameD*=1000000; ref->timeIncrementInUs=(uint64_t)frameD; // Probe the real time increment as the value determined from FPS may be incorrect due to interlace uint64_t firstNonZeroDts=ADM_NO_PTS,pts,dts; int firstNonZeroDtsFrame; ADM_info("[editor] Original frame increment %s\n",ADM_us2plain(ref->timeIncrementInUs)); uint64_t minDelta=100000; uint64_t maxDelta=0; for (int frame=0; frame<info.nb_frames; frame++) { if (ref->_aviheader->getPtsDts(frame,&pts,&dts) && dts!=ADM_NO_PTS && dts!=0) { if (firstNonZeroDts==ADM_NO_PTS) { firstNonZeroDts=dts; firstNonZeroDtsFrame=frame; continue; } uint64_t probedTimeIncrement=(dts-firstNonZeroDts)/(frame-firstNonZeroDtsFrame); if(probedTimeIncrement<minDelta) minDelta=probedTimeIncrement; if(probedTimeIncrement>maxDelta) maxDelta=probedTimeIncrement; firstNonZeroDts=dts; firstNonZeroDtsFrame=frame; } } ADM_info("[Editor] min increment %s\n",ADM_us2plain(minDelta)); ADM_info("[Editor] max increment %s\n",ADM_us2plain(maxDelta)); //if (minDelta==ref->timeIncrementInUs*2) //ref->timeIncrementInUs=minDelta; ADM_info("[Editor] About %"PRIu64" microseconds per frame\n",ref->timeIncrementInUs); ref->_nb_video_frames = info.nb_frames; // // And automatically create the segment // seg._reference=videos.size(); if(!videos.size()) { seg._startTimeUs=0; }else { // #warning todo compute cumulative time } seg._durationUs=ref->_aviheader->getVideoDuration(); // Set the default startTime to the pts of first Pic vidHeader *demuxer= ref->_aviheader; uint32_t flags; demuxer->getFlags(0,&flags); demuxer->getPtsDts(0,&pts,&dts); ref->firstFramePts=0; if(pts==ADM_NO_PTS) ADM_warning("First frame has unknown PTS\n"); if(pts!=ADM_NO_PTS &&pts) { ADM_warning("The first frame has a PTS >0, adjusting to %"PRIu64" ms\n",pts/1000); ref->firstFramePts=pts; } if(!segments.empty()) undoSegments.push_back(segments); segments.push_back(seg); videos.push_back(*ref); updateStartTime(); return true; }
/** \fn getCompressedPicture \brief bypass decoder and directly get the source image The dropBframe is as follow : 0 : Dont drop b frame 1 : Follow the next bframes 2 : Drop */ bool ADM_Composer::getCompressedPicture(uint64_t videoDelay,ADMCompressedImage *img) { uint64_t tail; // int64_t signedPts; int64_t signedDts; againGet: static uint32_t fn; fn++; _SEGMENT *seg=_segments.getSegment(_currentSegment); ADM_assert(seg); _VIDEOS *vid=_segments.getRefVideo(seg->_reference); ADM_assert(vid); vidHeader *demuxer=vid->_aviheader; ADM_assert(demuxer); // Get next pic? if(false==demuxer->getFrame (vid->lastSentFrame,img)) { ADM_info("Failed to get next frame for ref %"PRIu32"\n",seg->_reference); goto nextSeg; } vid->lastSentFrame++; // aviInfo info; vid->_aviheader->getVideoInfo (&info); // if(bFrameDroppable(info.fcc)) { if(img->flags & AVI_B_FRAME) { if(seg->_dropBframes==2) { ADM_warning("%"PRIu32" Dropping bframes\n",fn); goto againGet; } } else { // not a bframe switch(seg->_dropBframes) { case 2: seg->_dropBframes=0; break; case 1: seg->_dropBframes=2; break; default: break; } } } aprintf("Bframe droppable=%d, lastSentFrame=%d\n",seg->_dropBframes,vid->lastSentFrame); // after a segment switch, we may have some frames from "the past" // if the cut point is not a keyframe, drop them #if 1 if( img->demuxerDts!=ADM_NO_PTS) { bool drop=false; if(_currentSegment && img->demuxerDts<seg->_refStartDts) { ADM_info("Frame %d is in the past for this segment (%s)",vid->lastSentFrame,ADM_us2plain(img->demuxerDts)); ADM_info("vs refstartdts %s\n",ADM_us2plain(seg->_refStartDts)); ADM_info(" dts=%llu, ref=%llu\n",img->demuxerDts,seg->_refStartDts); drop=true; } // Seeking is not accurate when cutting on non intra // we might have some frames that are clearly too early , even in seg0 if(img->demuxerPts!=ADM_NO_PTS) { if(img->demuxerPts+seg->_startTimeUs<seg->_refStartTimeUs) { ADM_info("Frame %d is in the past for this segment -bis (%s)",vid->lastSentFrame,ADM_us2plain(img->demuxerPts)); ADM_info("vs refstartdts %s\n",ADM_us2plain(seg->_refStartTimeUs)); ADM_info(" pts=%llu, startTime=%llu, _refStartTimeUs=%llu\n",img->demuxerPts,seg->_startTimeUs,seg->_refStartTimeUs); drop=true; } } if(drop) goto againGet; } #endif // Need to switch seg ? tail=seg->_refStartTimeUs+seg->_durationUs; // Guess DTS // //** // ADM_info("Frame : Flags :%X, DTS:%"PRId64" PTS=%"PRId64" nextDts=%"PRId64" tail=%"PRId64"\n",img->flags,img->demuxerDts/1000,img->demuxerPts/1000,_nextFrameDts,tail); if(img->demuxerDts!= ADM_NO_PTS && img->demuxerDts>=tail) { aprintf("DTS is too late, switching (%s)\n",ADM_us2plain(img->demuxerDts)); goto nextSeg; } if(img->demuxerPts!= ADM_NO_PTS && img->demuxerPts>=tail) { aprintf("PTS is too late, switching (%s)\n",ADM_us2plain(img->demuxerDts)); goto nextSeg; } // Since we rely on PTS for seeking, frame 0 might be at PTS 0, in that case the matching dts would be <0 // so the caller can delay everything but recalibrate will clamp the value // so we use correctedDts so that the value is ok if(img->demuxerDts==ADM_NO_PTS) signedDts=ADM_NO_PTS; else { signedDts=(int64_t)img->demuxerDts; recalibrateSigned(&(signedDts),seg); } if(img->demuxerPts==ADM_NO_PTS) signedPts=ADM_NO_PTS; else { signedPts=(int64_t)img->demuxerPts; recalibrateSigned(&(signedPts),seg); } // From here we are in linear time, guess DTS if missing... if(signedDts==ADM_NO_PTS) { // border case due to rounding we can have pts slighly above dts if(signedPts!=ADM_NO_PTS && _nextFrameDts!=ADM_NO_PTS) { signedDts=_nextFrameDts; if(signedPts != ADM_NO_PTS && signedDts>signedPts) { // not sure it is correct. We may want to do it the other way around, i.e. bumping pts ADM_warning("Compensating for rounding error with PTS=%"PRId64"ms DTS=%"PRId64"ms \n",signedPts,signedDts); signedPts=signedDts; } } } else { // It means that the incoming image is earlier than the expected time. // we add a bit of timeIncrement to compensate for rounding if(_nextFrameDts!=ADM_NO_PTS) { if(_nextFrameDts>signedDts+vid->timeIncrementInUs/10) { ADM_error("Frame %"PRIu32" DTS is going back in time : expected : %"PRId64" ms got : %"PRId64" ms\n", fn,_nextFrameDts/1000,signedDts/1000); } } _nextFrameDts=signedDts; } // Increase for next one if(ADM_NO_PTS!=_nextFrameDts) _nextFrameDts+=vid->timeIncrementInUs; // Check the DTS is not too late compared to next seg beginning... if(_currentSegment+1<_segments.getNbSegments() && img->demuxerDts!=ADM_NO_PTS) { _SEGMENT *nextSeg=_segments.getSegment(_currentSegment+1); int64_t nextDts=nextSeg->_startTimeUs+nextSeg->_refStartDts; if(nextDts<nextSeg->_refStartTimeUs) { ADM_warning("%"PRIu32" next DTS is negative %"PRIu64" %"PRIu64" ms\n",fn,nextDts,nextSeg->_refStartTimeUs); } else { nextDts-=nextSeg->_refStartTimeUs; if(signedDts>=nextDts) { ADM_warning("%"PRIu32" have to switch segment, DTS limit reached %"PRIu64" %"PRIu64"\n",fn,img->demuxerDts/1000,nextDts/1000); goto nextSeg; } } } // ** // ADM_info("Frame after RECAL: Flags :%X, DTS:%"PRId64" PTS=%"PRId64" tail=%"PRId64"\n",img->flags,img->demuxerDts/1000,img->demuxerPts/1000,tail); img->demuxerDts=signedDts+videoDelay; img->demuxerPts=signedPts+videoDelay; return true; nextSeg: if(false==switchToNextSegment(true)) { ADM_warning("Cannot update to new segment\n"); return false; } // Mark it as drop b frames... _SEGMENT *thisseg=_segments.getSegment(_currentSegment); thisseg->_dropBframes=1; ADM_info("Retrying for next segment\n"); return getCompressedPicture(videoDelay,img); }
/** \fn fillAudio \brief push audio packets until nextDts is reached */ bool muxerMp4v2::fillAudio(uint64_t targetDts) { for(int audioIndex=0;audioIndex<nbAStreams;audioIndex++) { ADM_audioStream *a=aStreams[audioIndex]; uint32_t fq=a->getInfo()->frequency; mp4v2AudioPacket *pkt=&(audioPackets[audioIndex]); audioClock *clock=pkt->clock; if(pkt->eos) continue; uint64_t extraSamples=0; while(1) { int current=!pkt->nextWrite; int other=pkt->nextWrite; mp4v2AudioPacket::mp4v2AudioBlock *currentBlock=&(pkt->blocks[current]); mp4v2AudioPacket::mp4v2AudioBlock *otherBlock=&(pkt->blocks[other]); // Get our currentDts uint64_t currentDts=clock->getTimeUs(); uint64_t blockDts=currentBlock->dts; if(pkt->eos) break; extraSamples=0; // Take either block Dts or our own if no DTS is provided if(currentBlock->dts!=ADM_NO_PTS) { bprintf("Current audio Dts=%"PRId64"\n",currentDts); bprintf("Incoming block, dts=%"PRId64"\n",currentBlock->dts); bprintf("Delta =%d ms\n",(int)(currentDts-currentBlock->dts)); if( labs((long int)currentBlock->dts-(long int)currentDts)>MP4V2_MAX_JITTER) { if(currentBlock->dts<currentDts) { ADM_warning("Audio going back in time audio track %d\n",audioIndex); ADM_warning("expected %d ms, got %d ms",currentDts/1000,currentBlock->dts/1000); ADM_warning("Dropping packet\n"); goto nextOne; } // We have a hole, increase duration of current packet double holeDurationUs=currentBlock->dts-currentDts; ADM_warning("Hole detected in audio of %d ms, track %d\n",(int)(holeDurationUs/1000),audioIndex); ADM_warning("we got a timing of %s",ADM_us2plain(currentBlock->dts)); ADM_warning("and expected %s\n",ADM_us2plain(currentDts)); holeDurationUs*=fq; holeDurationUs/=1000*1000; ADM_warning("Increasing hole duration by %d samples\n",(int)holeDurationUs); extraSamples=(uint64_t)holeDurationUs; } }else blockDts=currentDts; if(blockDts>targetDts) // In the future break; if(false==writeAudioBlock(audioIndex,currentBlock,currentBlock->nbSamples+extraSamples)) { ADM_error("Cannot write audio sample for track %d\n",audioIndex); pkt->eos=true; return false; } // load next clock->advanceBySample(currentBlock->nbSamples+extraSamples); nextOne: if(false==loadAndToggleAudioSlot(audioIndex)) { ADM_warning("End of audio stream %d\n",audioIndex); #warning Purge other slot pkt->eos=true; } } } return true; }
bool admSaver::save(void) { int ret=false; ADM_info("Audio starting time %s\n",ADM_us2plain(startAudioTime)); ADM_info("[A_Save] Saving..\n"); const char *defaultExtension=ADM_MuxerGetDefaultExtension(muxerIndex); EditableAudioTrack *ed=NULL; ADM_info("Muxer default extension %s\n",defaultExtension); if(!videoEncoderIndex) { if(false==video_body-> checkCutsAreOnIntra()) { if(!GUI_Question("The video is in copy mode but the cut points are not on keyframes.\n" "The video will be saved but there will corruption at cut point(s).\n" "Do you want to continue anyway ?")) { return false; } } } if(!(muxer=ADM_MuxerSpawnFromIndex(muxerIndex))) { GUI_Error_HIG("Muxer","Cannot instantiante muxer"); return 0; } ADM_videoStream *video=setupVideo(); if(!video) { return false; } // adjust audio starting time for(int i=0;i<nbAudioTracks;i++) { ADM_audioStream *stream=video_body->getAudioStreamAt(i); stream->goToTime(startAudioTime); } if(false==setupAudio()) { if(video) delete video; if(muxer) delete muxer; muxer=NULL; return false; } // Check if we need to add an extension.... if(defaultExtension) { if(!strstr(fileName.c_str(),".")) { fileName=fileName+std::string(".")+std::string(defaultExtension); ADM_info("Adding extension, filename is now %s\n",fileName.c_str()); } } if(!muxer->open(fileName.c_str(),video,nbAudioTracks,audioAccess)) { GUI_Error_HIG("Muxer","Cannot open "); }else { ret=muxer->save(); muxer->close(); } abort123: if(video) delete video; video=NULL; for(int i=0;i<nbAudioTracks;i++) { delete audioAccess[i]; audioAccess[i]=NULL; } return ret; }
/** \fn save */ bool muxerAvi::save(void) { printf("[AviMuxer] Saving\n"); uint32_t bufSize=vStream->getWidth()*vStream->getHeight()*3; bool result=true; uint64_t rawDts; uint64_t lastVideoDts=0; int ret; int written=0; audioPackets=new aviAudioPacket[nbAStreams]; videoBuffer=new uint8_t[bufSize]; ADM_info("[AviMuxer]avg fps=%u\n",vStream->getAvgFps1000()); ADMBitstream in(bufSize); in.data=videoBuffer; uint64_t aviTime=0; if(in.dts==ADM_NO_PTS) in.dts=0; lastVideoDts=in.dts; initUI("Saving Avi"); encoding->setContainer("AVI/OpenDML"); if(false==prefill(&in)) goto abt; while(1) { aprintf("Current clock=%s\n",ADM_us2plain(aviTime)); aprintf("Video in dts=%s\n",ADM_us2plain(in.dts)); if(in.dts>aviTime+videoIncrement) { aprintf("Too far in the future, writting dummy frame\n"); writter.saveVideoFrame( 0, 0,videoBuffer); // Insert dummy video frame encoding->pushVideoFrame(0,0,in.dts); }else { aprintf("Writting video frame\n"); if(!writter.saveVideoFrame( in.len, in.flags,videoBuffer)) // Put our real video { ADM_warning("[AviMuxer] Error writting video frame\n"); result=false; goto abt; } encoding->pushVideoFrame(in.len,in.out_quantizer,in.dts); if(false==vStream->getPacket(&in)) goto abt; if(in.dts==ADM_NO_PTS) { in.dts=lastVideoDts+videoIncrement; }else rescaleVideo(&in,firstPacketOffset); lastVideoDts=in.dts; } fillAudio(aviTime+videoIncrement); // and matching audio if(updateUI()==false) { result=false; goto abt; } written++; aviTime+=videoIncrement; } abt: closeUI(); writter.setEnd(); delete [] videoBuffer; videoBuffer=NULL; delete [] audioPackets; audioPackets=NULL; ADM_info("[AviMuxer] Wrote %d frames, nb audio streams %d\n",written,nbAStreams); return result; }
/** \fn updateStartTime \brief Recompute the start time of the video */ bool ADM_EditorSegment::updateStartTime(void) { int n=segments.size(); if(!n) return true; uint64_t t=0; t=segments[0]._startTimeUs; for(int i=0;i<n;i++) { segments[i]._startTimeUs=t; t+=segments[i]._durationUs; } // Now set the _refStartDts field for(int i=0;i<n;i++) { _VIDEOS *vid=getRefVideo(segments[i]._reference); _SEGMENT *seg=getSegment(i); vidHeader *demuxer=vid->_aviheader; uint64_t pts,dts; pts=seg->_refStartTimeUs; // Special case : If pts=0 it might mean beginning of seg i, but the PTS might be not 0 // in such a case the DTS is wrong if(!pts) { uint64_t pts2,dts2; demuxer->getPtsDts(0,&pts2,&dts2); if(pts2!=ADM_NO_PTS) { ADM_info("Using pts2=%s to get dts, as this video does not start at zero\n",ADM_us2plain(pts2)); pts=pts2; } } dtsFromPts(seg->_reference,pts,&dts); seg->_refStartDts=dts; } return true; }
/** \fn setup */ bool x265Encoder::setup(void) { ADM_info("=============x265, setting up==============\n"); MMSET(param); x265_param_default( ¶m); firstIdr=true; image=new ADMImageDefault(getWidth(),getHeight()); // -------------- preset, tune, idc ------------ if(!x265Settings.useAdvancedConfiguration) { char tune[200] = {0}; strcat(tune, x265Settings.general.tuning); x265_param_default_preset(¶m, x265Settings.general.preset, tune); } param.logLevel=x265Settings.level; // Threads.. #if X265_BUILD < 47 switch(x265Settings.general.poolThreads) { case 0: case 1: case 2: param.poolNumThreads = x265Settings.general.poolThreads; break; case 99: break; //auto default: ADM_error("UNKNOWN NB OF THREADS\n"); break; } #endif switch(x265Settings.general.frameThreads) { case 0: case 1: case 2: param.frameNumThreads = x265Settings.general.frameThreads; break; case 99: break; //auto default: ADM_error("UNKNOWN NB OF THREADS\n"); break; } param.sourceWidth = getWidth(); param.sourceHeight = getHeight(); param.internalCsp = X265_CSP_I420; param.internalBitDepth = 8; param.logLevel=X265_LOG_INFO; //DEBUG; //INFO; //Framerate int n,d; uint64_t f=source->getInfo()->frameIncrement; usSecondsToFrac(f,&n,&d); param.fpsNum = d; param.fpsDenom = n; // -------------- vui------------ #undef MKPARAM #undef MKPARAMD #undef MKPARAMB #define MKPARAM(x,y) {param.vui.x = x265Settings.vui.y;aprintf("[x265] vui."#x" = %d\n",param.vui.x);} #define MKPARAMD(x,y) {param.vui.x = (double)x265Settings.vui.y; aprintf("[x265] vui."#x" = %.2f\n",param.vui.x);} #define MKPARAMB(x,y) {param.vui.x = x265Settings.vui.y ;aprintf("[x265] vui."#x" = %s\n",TrueFalse[param.vui.x&1]);} MKPARAM (sarWidth,sar_width) MKPARAM (sarHeight,sar_height) // -------------- rate control------------ switch(x265Settings.general.params.mode) { case COMPRESS_2PASS: case COMPRESS_2PASS_BITRATE: uint32_t bitrate; if(passNumber!=1 && passNumber!=2) { ADM_error("No pass number specified! (%d)\n",(int)passNumber); return false; } ADM_info("Starting pass :%d\n",passNumber); if(x265Settings.general.params.mode==COMPRESS_2PASS) { uint64_t duration=source->getInfo()->totalDuration; // in us ADM_info("Source duration :%s\n",ADM_us2plain(duration)); ADM_info("Target size :%d\n",(int)x265Settings.general.params.finalsize); uint32_t avg; if(false==ADM_computeAverageBitrateFromDuration(duration, x265Settings.general.params.finalsize, &avg)) { ADM_error("[x265] No source duration!\n"); return false; } bitrate=(uint32_t)avg; } else bitrate=x265Settings.general.params.avg_bitrate; ADM_info("Using average bitrate of %d kb/s\n",(int)bitrate); param.rc.rateControlMode = X265_RC_ABR; param.rc.bitrate = bitrate; if(passNumber==1) { param.rc.bStatWrite=1; param.rc.bStatRead=0; param.rc.statFileName=strdup(logFile); } else { param.rc.bStatWrite=0; param.rc.bStatRead=1; param.rc.statFileName=strdup(logFile); if(!ADM_fileExist(logFile)) { ADM_error("Logfile %s does not exist \n",logFile); return false; } } break; case COMPRESS_AQ: param.rc.rateControlMode = X265_RC_CRF; param.rc.rfConstant = x265Settings.general.params.qz; break; case COMPRESS_CQ: param.rc.rateControlMode = X265_RC_CQP; param.rc.qp = x265Settings.general.params.qz; break; case COMPRESS_CBR: param.rc.rateControlMode = X265_RC_ABR; param.rc.bitrate = x265Settings.general.params.bitrate; param.rc.qp = 0; param.rc.rfConstant = 0; break; default: GUI_Error_HIG("Not coded","this mode has notbeen implemented\n"); return false; break; } if(globalHeader) param.bRepeatHeaders=0; else param.bRepeatHeaders=1; if(x265Settings.useAdvancedConfiguration) { #undef MKPARAM #undef MKPARAMD #undef MKPARAMB #define MKPARAM(x,y) {param.x = x265Settings.y;aprintf("[x265] "#x" = %d\n",param.x);} #define MKPARAMD(x,y) {param.x = (double)x265Settings.y; aprintf("[x265] "#x" = %.2f\n",param.x);} #define MKPARAMB(x,y) {param.x = x265Settings.y ;aprintf("[x265] "#x" = %s\n",TrueFalse[param.x&1]);} MKPARAM(maxNumReferences,MaxRefFrames); MKPARAM(keyframeMin,MinIdr); MKPARAM(keyframeMax,MaxIdr); MKPARAM(scenecutThreshold,i_scenecut_threshold); MKPARAM(bframes,MaxBFrame); MKPARAM(bFrameAdaptive,i_bframe_adaptive); MKPARAM(bFrameBias,i_bframe_bias); MKPARAM(bBPyramid,i_bframe_pyramid); MKPARAMB(bEnableLoopFilter,b_deblocking_filter); MKPARAMB(interlaceMode,interlaced_mode); MKPARAMB(bEnableConstrainedIntra,constrained_intra); MKPARAM(lookaheadDepth,lookahead); MKPARAMB(bEnableWeightedBiPred,weighted_bipred) MKPARAM (bEnableWeightedPred,weighted_pred) MKPARAM (cbQpOffset,cb_chroma_offset) MKPARAM (crQpOffset,cr_chroma_offset) MKPARAM (searchMethod,me_method) MKPARAM (searchRange,me_range) MKPARAM (subpelRefine,subpel_refine) MKPARAM (bFrameAdaptive,trellis) MKPARAMB(bEnableEarlySkip,fast_pskip) MKPARAMB(bEnableTSkipFast,dct_decimate) MKPARAMD(psyRd,psy_rd) #if X265_BUILD >= 40 MKPARAM (noiseReductionIntra,noise_reduction_intra) MKPARAM (noiseReductionInter,noise_reduction_inter) #else MKPARAM (noiseReduction,noise_reduction) #endif //---------------- ratecontrol ------------------- #undef MKPARAM #undef MKPARAMD #undef MKPARAMB #define MKPARAM(x,y) {param.rc.x = x265Settings.ratecontrol.y;aprintf("[x265] rc."#x" = %d\n",param.rc.x);} #define MKPARAMD(x,y) {param.rc.x = (double)x265Settings.ratecontrol.y; aprintf("[x265] rc."#x" = %.2f\n",param.rc.x);} #define MKPARAMB(x,y) {param.rc.x = x265Settings.ratecontrol.y ;aprintf("[x265] rc."#x" = %s\n",TrueFalse[param.rc.x&1]);} MKPARAM(qpStep,qp_step); #if X265_BUILD >= 41 MKPARAMB(bStrictCbr,strict_cbr); #else MKPARAM(rateTolerance,rate_tolerance); #endif MKPARAM(ipFactor,ip_factor); MKPARAM(pbFactor,pb_factor); MKPARAMB(cuTree,cu_tree); MKPARAM(aqMode,aq_mode); MKPARAMD(aqStrength,aq_strength); } if(!param.bframes) encoderDelay=0; else { if(2>=param.maxNumReferences) { encoderDelay=f*2*2; } else { encoderDelay=2*f*(x265Settings.MaxRefFrames-1); } } if(!x265Settings.useAdvancedConfiguration) { x265_param_apply_profile(¶m, x265Settings.general.profile); } dumpx265Setup(¶m); ADM_info("Creating x265 encoder\n"); handle = x265_encoder_open (¶m); if (!handle) { ADM_error("Cannot initialize x265\n"); return 0; } ADM_info("x265, setup ok\n"); if (globalHeader) { ADM_info("Creating global header\n"); return createHeader (); } else ADM_info("No need for global header\n"); return true; }