/** \fn removeChunk \brief */ bool ADM_EditorSegment::removeChunk(uint64_t from, uint64_t to) { uint32_t startSeg,endSeg; uint64_t startOffset,endOffset; ADM_info("Cutting from %"PRIu64" to %"PRIu64" ms\n",from/1000,to/1000); dump(); if(false==convertLinearTimeToSeg( from,&startSeg,&startOffset)) { ADM_warning("Cannot get starting point (%"PRIu64" ms\n",from/1000); return false; } if(false==convertLinearTimeToSeg( to,&endSeg,&endOffset)) { ADM_warning("Cannot get starting point (%"PRIu64" ms\n",from/1000); return false; } ADM_info("Start, seg %"PRIu32" Offset :%"PRIu64" ms\n",startSeg,startOffset); ADM_info("End , seg %"PRIu32" Offset :%"PRIu64" ms\n",endSeg,endOffset); ListOfSegments tmp=segments; if(startSeg==endSeg) { // Split the seg int two.. segments.insert(segments.begin()+startSeg+1,*getSegment(startSeg)); endSeg=startSeg+1; } _SEGMENT *first=getSegment(startSeg); // Span over several seg... // 1- shorten the start segment.. first->_durationUs=startOffset; // 3- Shorten last segment _SEGMENT *last=getSegment(endSeg); last->_refStartTimeUs+=endOffset; last->_durationUs-=endOffset; // 2- Kill the segment in between for(int i=startSeg+1;i<endSeg;i++) { segments.erase(segments.begin()+startSeg+1); } updateStartTime(); removeEmptySegments(); 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; } undoSegments.push_back(tmp); dump(); 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 FCamera::update() { log(Trace, this, "updating ..."); updateStartTime(); updateTimeOfDay(); updateInterval(); }
/** \fn resetSegment \brief Redo a 1:1 mapping between videos and segments */ bool ADM_EditorSegment::resetSegment(void) { // aviInfo info; segments.clear(); int n=videos.size(); for(int i=0;i<n;i++) { _SEGMENT seg; _VIDEOS *vid=&(videos[i]); seg._durationUs=vid->_aviheader->getVideoDuration(); #ifdef ADM_ZERO_OFFSET uint64_t pts=vid->firstFramePts; if(pts!=ADM_NO_PTS && pts) { ADM_warning("The first frame in ref video %d has PTS = %" PRIu64" us, adjusting the segment\n",i,pts); seg._refStartTimeUs=pts; seg._durationUs-=pts; } #endif seg._reference=i; vid->_aviheader->getVideoInfo(&info); segments.push_back(seg); } updateStartTime(); return true; }
/** \fn addSegment \brief Add a segment to the segments list */ bool ADM_EditorSegment::addSegment(_SEGMENT *seg) { ADM_info("Adding a new segment\n"); segments.push_back(*seg); updateStartTime(); return true; }
/** \fn undo \brief */ bool ADM_EditorSegment::undo(void) { if(undoSegments.empty()) return false; clipboard.clear(); segments=undoSegments.back(); undoSegments.pop_back(); updateStartTime(); 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; }
/** * \fn appendFromClipBoard * \brief append clipboard at the end of video * @return */ bool ADM_EditorSegment::appendFromClipBoard(void) { if(clipboardEmpty()) { ADM_info("The clipboard is empty, nothing to do\n"); return true; } ADM_info("Appending from clipboard\n"); ListOfSegments tmp=segments; for(int i=0;i<clipboard.size();i++) segments.push_back(clipboard[i]); updateStartTime(); dump(); return true; }
/** * \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 resetSegment \brief Redo a 1:1 mapping between videos and segments */ bool ADM_EditorSegment::resetSegment(void) { // aviInfo info; segments.clear(); undoSegments.clear(); int n=videos.size(); for(int i=0;i<n;i++) { _SEGMENT seg; _VIDEOS *vid=&(videos[i]); seg._durationUs=vid->_aviheader->getVideoDuration(); seg._reference=i; vid->_aviheader->getVideoInfo(&info); segments.push_back(seg); } updateStartTime(); 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 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; }