/*
Copyright (C) 2011 Arnaud Champenois arthelion92@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QTime>
#include <QErrorMessage>
#include <QMessageBox>
#include <QSettings>
#include <sstream>
#include <math.h>
#include <QDropEvent>
#include <QUrl>
#include <QMimeData>
#include <windows.h>
#include <winbase.h>
#include <QInputDialog>
#include "managetemplatelist.h"
#include "videosegment.h"
#include "kernel.hpp"
#include "filecopier.h"
#include "launchprocess.h"
#include "videosegmentlistitem.h"
#include "utils.h"
#include "about.h"
#include "logotemplate.h"
const double theMinStdDev = 5;
const double theCorrelScore = 0.5;
const int64_t theSecondMS = 1000;
const int64_t the5SecondsMS = 5000;
const int64_t theMinuteMS = 60000;
using kernel::FileNameString;
#ifdef DEBUG
#include <stdio.h>
#include <stdlib.h>
static FILE*theFilePt = NULL;
void print_log(const QString&aString)
{
if(theFilePt == NULL)
{ unlink("debug.txt"); theFilePt = fopen("debug.txt","a"); }
fprintf(theFilePt,"%s\n",aString.toStdString().c_str());
}
#endif
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mDecoderPt = NULL;
mInfoLabel = new QLabel();
mFileLabel = new QLabel();
mPosLabel = new QLabel();
mAspectLabel = new QLabel();
mCorrLabel = new QLabel();
mSelLabel = new QLabel();
ui->statusBar->addWidget(mFileLabel);
ui->statusBar->addWidget(mInfoLabel);
ui->statusBar->addWidget(mPosLabel);
ui->statusBar->addWidget(mAspectLabel);
ui->statusBar->addWidget(mCorrLabel);
ui->statusBar->addWidget(mSelLabel);
ui->VideoFrame->SetMainWindow(this);
mInSlider = false;
SetClosedState();
mInfoLabel->setText(QString("VidePub ")+QString(VIDEPUB_VERSION));
mMarginBefore = mMarginAfter = the5SecondsMS;
mMinJump = theMinuteMS;
mLastCorrelation = -1;
mJumpInc = the5SecondsMS;
setAcceptDrops(true);
ReadSettings();
}
MainWindow::~MainWindow()
{
Close();
SaveSettings();
delete ui;
#ifdef DEBUG
if(theFilePt) fclose(theFilePt);
#endif
}
void MainWindow::SetClosedState()
{
ui->actionDetect_Segments->setEnabled(false);
ui->TimeSlider->setEnabled(false);
ui->PrevButton->setEnabled(false);
ui->NextButton->setEnabled(false);
ui->FastPrevButton->setEnabled(false);
ui->FastNextButton->setEnabled(false);
ui->AddBt->setEnabled(false);
ui->NextLogoOn->setEnabled(false);
ui->NextLogoOff->setEnabled(false);
ui->actionSaveCurrentIcon->setEnabled(false);
SetSaveState(false);
}
void MainWindow::SetOpenedState()
{
ui->actionDetect_Segments->setEnabled(true);
ui->TimeSlider->setEnabled(true);
ui->PrevButton->setEnabled(true);
ui->NextButton->setEnabled(true);
ui->FastPrevButton->setEnabled(true);
ui->FastNextButton->setEnabled(true);
ui->PrevButton->setEnabled(true);
ui->AddBt->setEnabled(true);
ui->NextLogoOn->setEnabled(false);
ui->NextLogoOff->setEnabled(false);
ui->actionSaveCurrentIcon->setEnabled(false);
SetSaveState(false);
}
void MainWindow::SetLogoState()
{
ui->actionDetect_Segments->setEnabled(true);
ui->TimeSlider->setEnabled(true);
ui->PrevButton->setEnabled(true);
ui->NextButton->setEnabled(true);
ui->FastPrevButton->setEnabled(true);
ui->FastNextButton->setEnabled(true);
ui->NextLogoOn->setEnabled(true);
ui->NextLogoOff->setEnabled(true);
ui->actionSaveCurrentIcon->setEnabled(true);
SetSaveState(false);
}
void MainWindow::SetDetectedState()
{
SetSaveState(false);
ui->actionDetect_Segments->setEnabled(true);
ui->TimeSlider->setEnabled(true);
ui->PrevButton->setEnabled(true);
ui->NextButton->setEnabled(true);
ui->FastPrevButton->setEnabled(true);
ui->FastNextButton->setEnabled(true);
ui->NextLogoOn->setEnabled(true);
ui->NextLogoOff->setEnabled(true);
ui->actionSaveCurrentIcon->setEnabled(true);
}
void MainWindow::SetSaveState(bool aSelected)
{
ui->GotoBeginBt->setEnabled(aSelected);
ui->GoToEndBt->setEnabled(aSelected);
ui->SetBeginBt->setEnabled(aSelected);
ui->SetEndBt->setEnabled(aSelected);
ui->DeleteBt->setEnabled(aSelected);
ui->MergeBt->setEnabled(aSelected);
ui->actionSave_selection->setEnabled(aSelected);
}
void MainWindow::Error(QString aMessage)
{
QMessageBox::critical(this,tr("Error"),aMessage,QMessageBox::Ok,
QMessageBox::NoButton);
}
void MainWindow::Close()
{
if(mDecoderPt)
{
delete mDecoderPt; mDecoderPt = NULL;
mPosLabel->setText("");
mInfoLabel->setText("");
mFileLabel->setText("");
mAspectLabel->setText("");
ui->RegionList->clear();
mLogo = QImage();
SetClosedState();
mLastCorrelation = -1;
ui->VideoFrame->SetOptimalSize(0,0);
ui->VideoFrame->repaint();
}
}
void MainWindow::PrintPosition()
{
int64_t aPos = mDecoderPt->GetMSPosition();
QString aString = tr("Pos :");
aString += std::MsToString(aPos);
mPosLabel->setText(aString);
mInSlider = true;
ui->TimeSlider->setValue(aPos/theSecondMS);
int64_t aNum,aDen;
mDecoderPt->GetAspectRatio(aNum,aDen);
aString = QString("%1/%2").arg(aNum).arg(aDen);
mAspectLabel->setText(aString);
if(HasLogo())
{
const QImage & anImage = GetCurrentImage();
if(!anImage.isNull())
{
mLastCorrelation = ComputeCorrelation(anImage,false);
aString = tr("Score : %1").arg(mLastCorrelation,4,'g',2);
mCorrLabel->setText(aString);
}
}
mInSlider = false;
}
void MainWindow::UseFileInfo()
{
FileNameString fns(mInputFileName.toStdString());
mFileLabel->setText(QString(fns.GetFileName().c_str()));
QString aString = tr("Length :");
aString += std::MsToString(mFileInfo.mLength);
mInfoLabel->setText(aString);
aString = tr("Pos :");
aString += "00:00:00";
mPosLabel->setText(aString);
aString = QString("%1/%2").arg(mFileInfo.mAspectRatio[0]).arg(mFileInfo.mAspectRatio[1]);
mAspectLabel->setText(aString);
ui->TimeSlider->setMaximum(mFileInfo.mLength/theSecondMS);
ui->VideoFrame->SetOptimalSize(mFileInfo.mOptWidth,mFileInfo.mOptHeight);
ui->VideoFrame->SetZoom(1.0);
}
bool MainWindow::Open(const QString&aFileName)
{
if(aFileName.isEmpty()) return false;
Close();
mDecoderPt = new FFMPEGDecoder();
if(!mDecoderPt->Open(aFileName))
{ Close(); return false; }
mInputFileName = aFileName;
if(mDecoderPt->GetFileInfo(mFileInfo))
UseFileInfo();
ShowNextFrame();
SetOpenedState();
QList<LogoTemplate> templateList = LogoTemplate::readTemplates();
if(!templateList.empty() && FindLogo(templateList)) {
ExtractSegments();
}
return true;
}
static inline double sqr(double a) { return a*a; }
void MainWindow::ComputeStats(const QImage& anImage,
const QRect& anArea,
double*aMeanArray,
double*aStdDevArray)
{
double aSum[3] = { 0,0,0 };
double aSqrSum[3] = { 0,0,0 };
double aCount = anArea.height()*anArea.width();
for(int y=anArea.top(),t=0;t<anArea.height();y++,t++)
{
const uchar*aBits = anImage.scanLine(y);
for(int x=anArea.left(),u=0;u<anArea.width();x++,u++)
{
for(int i=0;i<3;i++)
{
aSum[i] += aBits[3*x+i];
aSqrSum[i] += sqr(aBits[3*x+i]);
}
}
}
for(int i=0;i<3;i++)
{
aMeanArray[i] = aSum[i]/aCount;
aStdDevArray[i] = sqrt(aSqrSum[i]/aCount-sqr(aMeanArray[i]));
}
}
double MainWindow::ComputeCorrelation(const QImage &anImageIn, bool aStopWhenFound)
{
double aMax = -1;
QRect aRect = mLogoRect;
const int MaxOffset = 2; // Bagottement possible du logo
// On cherche un peu au dessus et en dessous
for(int anYOffset = -MaxOffset;anYOffset<=MaxOffset;anYOffset++)
{
// Et un peu sur les bords aussi
for(int anXOffset = -MaxOffset;anXOffset<=MaxOffset;anXOffset++)
{
aRect.moveTo(mLogoRect.left()+anXOffset,mLogoRect.top()+anYOffset);
if(aRect.intersected(anImageIn.rect()) != aRect)
continue;
double anImaMean[3],anImaStd[3];
double aCorrSum[3] = { 0,0,0 };
ComputeStats(anImageIn,aRect,anImaMean,anImaStd);
double aCount = aRect.height()*aRect.width();
for(int y=aRect.top(),t=0;t<aRect.height();y++,t++)
{
const uchar*anIma = anImageIn.scanLine(y);
const uchar*aLogo = mLogo.scanLine(t);
for(int x=aRect.left(),u=0;u<aRect.width();x++,u++)
{
for(int i=0;i<3;i++)
{
aCorrSum[i] += ((anIma[3*x+i]-anImaMean[i])*(aLogo[3*u+i]-mMean[i]));
}
}
}
double aMin = -1;
for(int i=0;i<3;i++)
{
// Filtrage des plans unis
if(anImaStd[i]<theMinStdDev || mStdDev[i]<theMinStdDev) continue;
aCorrSum[i] /= (aCount*mStdDev[i]*anImaStd[i]);
if(aCorrSum[i]<aMin || aMin == -1)
aMin = aCorrSum[i];
}
if(aMin>aMax)
{
aMax = aMin;
if(aStopWhenFound && aMax>theCorrelScore) return aMax; // On peut abr�ger...
}
}
}
return aMax;
}
bool MainWindow::SetLogo(const QRect&anArea,
const QImage&anImage) {
mLogo = anImage;
mLogoRect = anArea;
ComputeStats(mLogo,mLogo.rect(),mMean,mStdDev);
int aCount = 0;
for(int i=0;i<3;i++)
{
if(mStdDev[i]<theMinStdDev) aCount++;
}
if(aCount == 3)
{
mLogo = QImage();
mLogoRect = QRect();
return false;
}
return true;
}
void MainWindow::SetLogoAndSearch(const QRect &anArea, const QImage &anImage)
{
if(!SetLogo(anArea,anImage))
{
Error(tr("Logo too uniform"));
return;
}
PrintPosition();
SetLogoState();
ExtractSegments();
}
void MainWindow::ShowNextFrame()
{
if(IsClosed()) return;
QImage anImage;
if(mDecoderPt->ReadNextImage(anImage,true))
{
ui->VideoFrame->SetImage(anImage);
PrintPosition();
}
}
void MainWindow::FlushAll()
{
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
QCoreApplication::flush();
}
const QImage& MainWindow::GetCurrentImage() const
{
return ui->VideoFrame->GetImage();
}
bool MainWindow::ExtractSegments()
{
const double aCorrelScore = theCorrelScore;
if(IsClosed() || !HasLogo()) return false;
LaunchProcess aProcessDialog(mMarginBefore,mMarginAfter,mMinJump,mJumpInc,mLogo,this);
if(aProcessDialog.exec() == QDialog::Rejected)
return false;
ui->RegionList->clear();
mMarginBefore = aProcessDialog.GetMarginBefore();
mMarginAfter = aProcessDialog.GetMarginAfter();
mMinJump = aProcessDialog.GetMinJump();
mJumpInc = aProcessDialog.GetJumpInc();
mInSlider = true;
mDecoderPt->Rewind();
VPosition anInc = mDecoderPt->GetVPosition(mJumpInc);
VPosition aMarginBefore = mDecoderPt->GetVPosition(mMarginBefore);
VPosition aMarginAfter = mDecoderPt->GetVPosition(mMarginAfter);
VPosition aMinJump = mDecoderPt->GetVPosition(mMinJump);
VPosition aLastPos = mDecoderPt->GetPosition();
int64_t aPos2 = 0;
VideoSegment aSegment;
int64_t aTickLength = mFileInfo.mLength;
VPosition aPos;
aPos.mMsPos = 0;
bool aInCorrelation = false;
bool aJumpCorrelation = false;
QProgressDialog aProgress(this);
aProgress.setWindowModality(Qt::WindowModal);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching..."));
aProgress.setRange(ui->TimeSlider->minimum(),ui->TimeSlider->maximum());
aProgress.show();
for(;aPos.mMsPos < aTickLength;)
{
if(aProgress.wasCanceled())
break;
if(aPos2 >= 10)
{
aProgress.setValue(aPos.mMsPos/theSecondMS);
FlushAll();
aPos2 = 0;
}
QImage aNewImage;
if(!mDecoderPt->ReadNextImage(aNewImage,true)) break;
aPos = mDecoderPt->GetPosition();
aPos2++;
double aCorrelation = ComputeCorrelation(aNewImage);
if(aCorrelation>=aCorrelScore)
{
if(aInCorrelation && aSegment.IsValid())
{
aSegment.mEnd = aPos; // continue la s�quence
aLastPos = aPos;
}
else
{
if(!aInCorrelation)
{
// on raffine le d�but � la trame pr�s
mDecoderPt->SeekFrame(aLastPos,FFMPEGDecoder::FRAME_SEEK_SET);
VPosition aNewPos = mDecoderPt->GetPosition();
// On cherche le "vrai" d�but (en esp�rant qu'il n'y en ait pas 36 entre les deux...)
while(aNewPos<aPos && ComputeCorrelation(aNewImage)<aCorrelScore &&
mDecoderPt->ReadNextImage(aNewImage,true))
aNewPos = mDecoderPt->GetPosition();
if(aNewPos<aPos)
aPos = aNewPos;
else mDecoderPt->SeekPosition(aPos.mFilePos);
}
if(aSegment.IsValid())
{
if(aPos>aSegment.mEnd+aMinJump)
{
if(aSegment.GetLength()>anInc.mMsPos)
{
aSegment.mBegin -= aMarginBefore;
aSegment.mEnd += aMarginAfter;
new VideoSegmentListItem(aSegment,ui->RegionList);
}
aSegment.Clear(); // On recommence un nouveau segment
}
else // On fusionne : le saut est trop petit
{
aSegment.mEnd = aPos;
aLastPos = aPos;
}
}
if(!aSegment.IsValid())
{ // On commence un nouveau segment
aSegment.mBegin = aPos;
aSegment.mEnd = aPos+mDecoderPt->GetVPosition(1);
aLastPos = aPos;
}
}
aInCorrelation = true;
aJumpCorrelation = false;
}
else
{
if(aInCorrelation && aSegment.IsValid() && !aJumpCorrelation)
{
mDecoderPt->SeekFrame(aLastPos,FFMPEGDecoder::FRAME_SEEK_SET);
while(mDecoderPt->ReadNextImage(aNewImage,true) &&
ComputeCorrelation(aNewImage)>=aCorrelScore);
aPos = mDecoderPt->GetPosition();
aSegment.mEnd = aPos;
aLastPos = aPos;
double aScore = -1;
// Verif qu'on a rien dans l'intervalle qui suit
while(mDecoderPt->ReadNextImage(aNewImage,true) &&
(aPos-aSegment.mEnd)<anInc && aScore<aCorrelScore)
{
aScore=ComputeCorrelation(aNewImage);
aPos = mDecoderPt->GetPosition();
}
if(aScore>=aCorrelScore)
{
aSegment.mEnd = aPos;
aInCorrelation = true;
aJumpCorrelation = true;
aLastPos = aPos;
}
else
aInCorrelation = false;
}
else
{
aInCorrelation = false;
aLastPos = aPos;
}
}
if(!mDecoderPt->SeekFrame(anInc,FFMPEGDecoder::FRAME_SEEK_CUR)) break;
}
if(aSegment.IsValid())
new VideoSegmentListItem(aSegment,ui->RegionList);
mInSlider = false;
if(ui->RegionList->count()>0)
SetDetectedState();
return true;
}
void MainWindow::Rewind()
{
if(!mDecoderPt) return;
mDecoderPt->Rewind();
ShowNextFrame();
}
bool MainWindow::FindLogo(QList<LogoTemplate> &logoList)
{
static int64_t percentSearch[10] = { 7, 15 , 21, 37, 46, 54, 66, 78, 84, 93 };
FFMPEGFileInfo info;
mDecoderPt->GetFileInfo(info);
mDecoderPt->Rewind();
QList<LogoTemplate>::iterator anIter;
QImage curImage;
QProgressDialog aProgress(this);
aProgress.setWindowModality(Qt::WindowModal);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching for saved logos..."));
aProgress.setRange(0,10);
aProgress.show();
for(int search=0;search<10;search++) {
aProgress.setValue(search);
FlushAll();
if(aProgress.wasCanceled()) {
break;
}
int64_t frame = (info.mLength*percentSearch[search])/100;
if(mDecoderPt->SeekFrame(frame,FFMPEGDecoder::FRAME_SEEK_SET)) {
mDecoderPt->ReadNextImage(curImage,true);
for(anIter = logoList.begin();anIter!=logoList.end();anIter++) {
if(!SetLogo((*anIter).getRect(),(*anIter).getImage())) continue;
if(ComputeCorrelation(curImage,false)>0.9)
{
Rewind();
return true;
}
}
}
}
Rewind();
mLogo = QImage();
mLogoRect = QRect();
return false;
}
/*===============================================================
Toutes les callbacks IHM Qt
=================================================================*/
void MainWindow::on_actionQuitter_triggered()
{
QCoreApplication * anApptPt = QCoreApplication::instance ();
anApptPt->exit();
}
void MainWindow::on_actionOuvrir_triggered()
{
QString aFileName = QFileDialog::getOpenFileName(this,
tr("Open video file"),
mInputFileName,
tr("MPEG stream files (*.ts)"));
Open(aFileName);
}
void MainWindow::on_actionDetect_Segments_triggered()
{
if(mLogo.isNull()) {
QList<LogoTemplate> templateList = LogoTemplate::readTemplates();
if(templateList.empty()) {
Error(tr("No stored logo to search for.\nPlease grab it on the movie to start processing."));
return;
}
if(!FindLogo(templateList)) {
Error(tr("None of the stored logo could be found on the movie.\nPlease grab it manually on the movie."));
return;
}
}
ExtractSegments();
}
void MainWindow::on_GotoBeginBt_clicked()
{
VideoSegment aSegment;
if(!ui->RegionList->GetSelection(aSegment)) return;
mDecoderPt->SeekPosition(aSegment.mBegin.mFilePos);
ShowNextFrame();
}
void MainWindow::on_GoToEndBt_clicked()
{
VideoSegment aSegment;
if(!ui->RegionList->GetSelection(aSegment)) return;
mDecoderPt->SeekPosition(aSegment.mEnd.mFilePos);
ShowNextFrame();
}
void MainWindow::on_actionSave_selection_triggered()
{
if(!mDecoderPt) return;
QList<VideoSegment> aList = ui->RegionList->GetSelectedItems();
if(aList.empty()) return;
qSort(aList);
QFile aFile(mLastOutputFile);
FileNameString aFileNameString = mLastOutputFile.toStdString();
int i = aFileNameString.GetNumber();
while(aFile.exists())
aFile.setFileName(aFileNameString.GetPath(i++));
QString aFileName = QFileDialog::getSaveFileName(this,
tr("OuputFileName"),
aFile.fileName(),
tr("Video stream (*.ts)"));
if(aFileName.isEmpty()) return;
aFile.setFileName(aFileName);
aFileNameString = FileNameString(aFileName.toStdString());
mLastOutputFile = aFile.fileName();
FileCopier aCopier;
if(!aCopier.Open(mInputFileName,mLastOutputFile))
{
Error(tr("Impossible to open output file"));
return;
}
QProgressDialog aProgress(this);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setAutoClose(false);
aProgress.setWindowModality(Qt::WindowModal);
aProgress.show();
QList<VideoSegment>::iterator anIter;
for(anIter = aList.begin();
anIter != aList.end();
anIter++)
{
VideoSegment &aSegment = (*anIter);
aProgress.setLabelText(QString("%1%2").arg(tr("Saving segment ")).arg(aSegment.GetString()));
aProgress.setRange(0,aSegment.mEnd.mMsPos-aSegment.mBegin.mMsPos);
aProgress.setValue(0);
FlushAll();
if(!aCopier.Write(aSegment,&aProgress))
{
Error(tr("Saving interrupted"));
break;
}
aSegment.getListItem()->setFileName(QString(aFileNameString.GetFileName().c_str()));
}
}
void MainWindow::on_RegionList_itemSelectionChanged()
{
QList<VideoSegment> aList = ui->RegionList->GetSelectedItems();
int64_t aLength = 0;
QList<VideoSegment>::iterator anIter;
for(anIter = aList.begin();
anIter != aList.end();
anIter++)
{
VideoSegment aSegment = (*anIter);
aLength += aSegment.GetLength();
}
if(aLength>0)
{
QString aString = QString("%1%2").arg(tr("Selection :")).arg(std::MsToString(aLength));
mSelLabel->setText(aString);
SetSaveState(true);
}
else
{
SetSaveState(false);
mSelLabel->setText(tr("No selection"));
}
}
void MainWindow::dropEvent(QDropEvent *event)
{
const QMimeData *mimeData = event->mimeData();
QUrl anURL = mimeData->urls().front();
Close();
QString aString = anURL.toLocalFile();
Open(aString);
}
void MainWindow::dragEnterEvent(QDragEnterEvent *event)
{
const QMimeData *mimeData = event->mimeData();
if(mimeData->hasUrls())
{
QUrl anURL = mimeData->urls().front();
QString aString = anURL.toLocalFile();
FileNameString aFNS = aString.toLatin1().data();
if(aFNS.TestExtension("ts"))
event->acceptProposedAction();
}
}
void MainWindow::on_TimeSlider_valueChanged(int aValue)
{
if(!mDecoderPt || mInSlider) return;
int64_t aMSValue = aValue*theSecondMS;
if(mDecoderPt->SeekFrame(aMSValue,FFMPEGDecoder::FRAME_SEEK_SET))
ShowNextFrame();
else Error(tr("Impossible to seek in video"));
}
void MainWindow::on_PrevButton_clicked()
{
int64_t aDeltaMSValue = -500;
int64_t aPos = mDecoderPt->GetMSPosition();
FFWD_AGAIN:
if(!mDecoderPt->SeekFrame(aDeltaMSValue,FFMPEGDecoder::FRAME_SEEK_CUR)) return;
QImage anImage;
if(!mDecoderPt->ReadNextImage(anImage,true)) return;
if(mDecoderPt->GetMSPosition() >= aPos && aPos>-aDeltaMSValue)
{
aDeltaMSValue -= 250;
goto FFWD_AGAIN;
}
else
{
ui->VideoFrame->SetImage(anImage);
PrintPosition();
}
}
void MainWindow::on_FastPrevButton_clicked()
{
int64_t aDeltaMSValue = -mJumpInc;
int64_t aPos = mDecoderPt->GetMSPosition();
FFWD_AGAIN:
if(!mDecoderPt->SeekFrame(aDeltaMSValue,FFMPEGDecoder::FRAME_SEEK_CUR)) return;
ShowNextFrame();
if(mDecoderPt->GetMSPosition() >= aPos && aPos>-aDeltaMSValue)
{
aDeltaMSValue -= mJumpInc;
goto FFWD_AGAIN;
}
}
void MainWindow::on_FastNextButton_clicked()
{
const int64_t aDeltaMSValue = mJumpInc;
mDecoderPt->SeekFrame(aDeltaMSValue,FFMPEGDecoder::FRAME_SEEK_CUR);
ShowNextFrame();
}
void MainWindow::on_NextButton_clicked()
{
ShowNextFrame();
}
void MainWindow::on_MergeBt_clicked()
{
ui->RegionList->MergeSelection();
}
void MainWindow::on_DeleteBt_clicked()
{
ui->RegionList->DeleteSelection();
}
void MainWindow::on_SetBeginBt_clicked()
{
VPosition aPos = mDecoderPt->GetPosition();
ui->RegionList->SetSelectionBegin(aPos);
}
void MainWindow::on_SetEndBt_clicked()
{
VPosition aPos = mDecoderPt->GetPosition();
ui->RegionList->SetSelectionEnd(aPos);
}
void MainWindow::on_AddBt_clicked()
{
VPosition aPos = mDecoderPt->GetPosition();
VPosition anEnd = aPos+mDecoderPt->GetVPosition(theSecondMS);
ui->RegionList->AddNewRegion(aPos,anEnd);
}
void MainWindow::SaveSettings()
{
QSettings aSet(QSettings::IniFormat,QSettings::UserScope,"Arthelion","VidePub");
aSet.setValue("Version",1);
aSet.setValue("LastInputFile",mInputFileName);
aSet.setValue("LastOutputFile",mLastOutputFile);
aSet.setValue("MarginBefore",mMarginBefore);
aSet.setValue("MarginAfter",mMarginAfter);
aSet.setValue("MinJump",mMinJump);
aSet.setValue("JumpInc",mJumpInc);
}
void MainWindow::ReadSettings()
{
QSettings aSet(QSettings::IniFormat,QSettings::UserScope,"Arthelion","VidePub");
QFile aFile(aSet.fileName());
if(!aFile.exists()) return;
mInputFileName = aSet.value("LastInputFile").toString();
mLastOutputFile = aSet.value("LastOutputFile").toString();
mMarginBefore = aSet.value("MarginBefore").toInt();
mMarginAfter = aSet.value("MarginAfter").toInt();
mMinJump = aSet.value("MinJump").toInt();
mJumpInc = aSet.value("JumpInc").toInt();
if(mMinJump<=0) mMinJump = theMinuteMS;
if(mJumpInc<=0) mJumpInc = the5SecondsMS;
}
void MainWindow::on_NextLogoOn_clicked()
{
setCursor (Qt::BusyCursor);
setEnabled(false);
if(mLastCorrelation>=theCorrelScore)
SearchLogo(1);
SearchLogo(-1);
unsetCursor();
setEnabled(true);
}
void MainWindow::on_NextLogoOff_clicked()
{
setCursor (Qt::BusyCursor);
setEnabled(false);
if(mLastCorrelation<theCorrelScore)
SearchLogo(-1);
SearchLogo(1);
unsetCursor();
setEnabled(true);
}
void MainWindow::SearchLogo(double aSign)
{
if(!mDecoderPt || !HasLogo()) return;
const double aCorrelScore = theCorrelScore*aSign;
QProgressDialog aProgress(this);
aProgress.setWindowModality(Qt::WindowModal);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching..."));
aProgress.setRange(ui->TimeSlider->value(),ui->TimeSlider->maximum());
VPosition anInc = mDecoderPt->GetVPosition(mJumpInc);
QImage anImage;
int i=6;
VPosition aNewPos = mDecoderPt->GetPosition();
VPosition aPos = aNewPos+anInc;
double aCorrel = 1;
while(aNewPos<aPos && mDecoderPt->ReadNextImage(anImage,true) &&
(aCorrel = ComputeCorrelation(anImage)*aSign)>=aCorrelScore)
aNewPos = mDecoderPt->GetPosition();
if(aCorrel>=aCorrelScore)
{
while(mDecoderPt->ReadNextImage(anImage,true) &&
(aCorrel = ComputeCorrelation(anImage)*aSign)>=aCorrelScore)
{
i++;
if(i==6)
{
aProgress.setValue(mDecoderPt->GetMSPosition()*theSecondMS);
FlushAll();
if(aProgress.wasCanceled()) break;
i=0;
}
aNewPos = mDecoderPt->GetPosition();
mDecoderPt->SeekFrame(anInc,FFMPEGDecoder::FRAME_SEEK_CUR);
}
if(aCorrel<aCorrelScore)
{
mDecoderPt->SeekFrame(aNewPos,FFMPEGDecoder::FRAME_SEEK_SET);
while(mDecoderPt->ReadNextImage(anImage,true) &&
ComputeCorrelation(anImage)*aSign>=aCorrelScore);
}
}
ui->VideoFrame->SetImage(anImage);
PrintPosition();
}
void MainWindow::on_actionAbout_VidePub_triggered()
{
About anAbout(this);
anAbout.exec();
}
void MainWindow::on_actionZoom_plus_triggered()
{
if(!mDecoderPt) return;
ui->actionZoom_plus->setEnabled(ui->VideoFrame->IncZoom());
ui->actionZoom_moins->setEnabled(true);
}
void MainWindow::on_actionZoom_moins_triggered()
{
if(!mDecoderPt) return;
ui->actionZoom_moins->setEnabled(ui->VideoFrame->DecZoom());
ui->actionZoom_plus->setEnabled(true);
}
void MainWindow::on_actionZoom_1_triggered()
{
if(!mDecoderPt) return;
ui->VideoFrame->SetZoom(1.0);
ui->actionZoom_moins->setEnabled(true);
ui->actionZoom_plus->setEnabled(true);
}
void MainWindow::on_actionClose_triggered()
{
Close();
}
void MainWindow::on_actionHelp_triggered()
{
QString anHelpFile = QDir::current().absolutePath();
anHelpFile += "/VidePub.pdf";
ShellExecuteA(NULL,"open",anHelpFile.toLatin1().data(),0,0,SW_NORMAL);
}
void MainWindow::on_actionManage_stored_icons_triggered()
{
ManageTemplateList templateList(this);
templateList.setModal(true);
templateList.exec();
}
void MainWindow::on_actionSaveCurrentIcon_triggered()
{
if(mLogo.isNull()) return;
QString name = QInputDialog::getText(this,tr("Save logo"),tr("Name :"));
if(name.isEmpty()) return;
LogoTemplate tmpl(mLogo,name,mLogoRect);
LogoTemplate::write(tmpl);
}