/** \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; }
/** \fn updateRefVideo \brief Update start time */ bool ADM_EditorSegment::halfFps(void) { int n=videos.size(); ADM_assert(n); _VIDEOS *ref=getRefVideo(n-1); ref->timeIncrementInUs*=2; ref->_aviheader->getVideoStreamHeader()->dwRate/=2; return true; }
/** \fn isKeyFrameByTime \brief Return true if frame with PTS==seektime is a keyframe */ bool ADM_EditorSegment::isKeyFrameByTime(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)) return false; if(flags & AVI_KEY_FRAME) return true; return false; }
/** \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); } }
/** \fn dtsFromPts \brief guestimate DTS from PTS. For the wanted frame, we go back until we find a valid DTS then the wanted DTS=~ valid DTS + timeIncrement * number of frames in between */ bool ADM_EditorSegment::dtsFromPts(uint32_t refVideo,uint64_t pts,uint64_t *dts) { uint32_t frame,flags; _VIDEOS *vid=getRefVideo(refVideo); vidHeader *demuxer=vid->_aviheader; if(false==TimeToFrame(vid,pts,&frame,&flags)) { ADM_warning("Cannot get frame with pts=%"PRIu64" ms\n",pts/1000); return false; } // Now get DTS.. uint64_t p,d; if(!frame) // The very first frame { demuxer->getPtsDts(0,&p,&d); if(d==ADM_NO_PTS) { ADM_warning("No DTS available for first frame, putting pts, probably incorrect\n"); *dts=pts; }else { *dts=d; } return true; } int32_t deltaFrame=frame; while(deltaFrame>0) { demuxer->getPtsDts(deltaFrame,&p,&d); if(d!=ADM_NO_PTS) break; deltaFrame--; } if(deltaFrame<0) { ADM_warning("Cannot find a valid DTS for pts=%"PRIu64"ms\n",pts/1000); *dts=pts; return false; } deltaFrame=frame-deltaFrame; *dts=d+deltaFrame*vid->timeIncrementInUs; return true; }
/** \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 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 truncateVideo \brief Remove a part of the video from the given time to the end */ bool ADM_EditorSegment::truncateVideo(uint64_t from) { uint32_t startSeg; uint64_t startOffset; ADM_info("Truncating from %" PRIu64" ms\n",from/1000); dump(); if(false==convertLinearTimeToSeg(from,&startSeg,&startOffset)) { ADM_warning("Cannot get starting point for linear time %" PRIu64" ms\n",from/1000); return false; } ADM_info("Start in segment %" PRIu32" at offset :%" PRIu64" ms\n",startSeg,startOffset); ListOfSegments tmp=segments; _SEGMENT *first=getSegment(startSeg); _VIDEOS *vid=getRefVideo(first->_reference); uint64_t padding=vid->timeIncrementInUs/2; // shorten the start segment first->_durationUs=startOffset+padding; // remove following segments int n=segments.size(); for(int i=startSeg+1;i<n;i++) { segments.erase(segments.begin()+startSeg+1); } removeEmptySegments(); updateStartTime(); if(isEmpty()) { GUI_Error_HIG(QT_TRANSLATE_NOOP("adm","Error"),QT_TRANSLATE_NOOP("adm","You cannot remove *all* the video\n")); segments=tmp; updateStartTime(); return false; } dump(); return true; }