/*
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 "videosegment.h"
#include "kernel.hpp"
#include "filecopier.h"
#include "launchprocess.h"
#include "videosegmentlistitem.h"
#include "utils.h"
#include "about.h"
const double theMinStdDev = 5;
#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();
mPosLabel = new QLabel();
mAspectLabel = new QLabel();
mCorrLabel = new QLabel();
mSelLabel = new QLabel();
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 = 10000;
mMinJump = 60000;
mLastCorrelation = -1;
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);
SetSaveState(false);
}
void MainWindow::SetOpenedState()
{
ui->actionDetect_Segments->setEnabled(false);
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);
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);
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);
}
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("");
mPosLabel->setText("");
ui->RegionList->clear();
mLogo = QImage();
SetClosedState();
mLastCorrelation = -1;
}
}
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/1000);
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);
aString = tr("Score : %1").arg(mLastCorrelation,4,'g',2);
mCorrLabel->setText(aString);
}
}
mInSlider = false;
}
void MainWindow::UseFileInfo()
{
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/1000);
ui->VideoFrame->resize(mFileInfo.mWidth,mFileInfo.mHeight);
}
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();
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)
{
double aMax = -1;
QRect aRect = mLogoRect;
const int MaxOffset = 2; // Bagottement sauce TF1
// 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.intersect(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;
}
}
return aMax;
}
void 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)
{
Error(tr("Logo too uniform"));
mLogo = QImage();
mLogoRect = QRect();
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::processEvents();
QCoreApplication::sendPostedEvents();
QCoreApplication::flush();
}
const QImage& MainWindow::GetCurrentImage() const
{
return ui->VideoFrame->GetImage();
}
bool MainWindow::ExtractSegments()
{
const double aCorrelScore = 0.5;
if(IsClosed() || !HasLogo()) return false;
ui->RegionList->clear();
LaunchProcess aProcessDialog(mMarginBefore,mMarginAfter,mMinJump,this);
if(aProcessDialog.exec() == QDialog::Rejected)
return false;
mMarginBefore = aProcessDialog.GetMarginBefore();
mMarginAfter = aProcessDialog.GetMarginAfter();
mMinJump = aProcessDialog.GetMinJump();
mInSlider = true;
mDecoderPt->Rewind();
VPosition anInc = mDecoderPt->GetVPosition(10000);
VPosition aMarginBefore = mDecoderPt->GetVPosition(mMarginBefore);
VPosition aMarginAfter = mDecoderPt->GetVPosition(mMarginAfter);
VPosition aMinJump = mDecoderPt->GetVPosition(mMinJump);
int64_t aPos2 = 0;
VideoSegment aSegment;
int64_t aTickLength = mFileInfo.mLength;
VPosition aPos;
aPos.mMsPos = 0;
bool aInCorrelation = false;
bool aJumpCorrelation = false;
VPosition aFullMargin = aMarginBefore+aMarginAfter+anInc;
QProgressDialog aProgress(this);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching..."));
aProgress.setRange(ui->TimeSlider->minimum(),ui->TimeSlider->maximum());
for(;aPos.mMsPos < aTickLength;)
{
if(aProgress.wasCanceled())
break;
if(aPos2 >= 10)
{
aProgress.setValue(aPos.mMsPos/1000);
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
else
{
// on raffine le début à la trame près
if(!aSegment.IsValid() || aPos>aSegment.mEnd+anInc)
mDecoderPt->SeekFrame(aPos-anInc,FFMPEGDecoder::FRAME_SEEK_SET);
else
mDecoderPt->SeekFrame(aSegment.mEnd,FFMPEGDecoder::FRAME_SEEK_SET);
// Si par hasard on retombait dans une (ancienne) bonne corrélation, on avance
VPosition aNewPos = mDecoderPt->GetPosition();
while(aNewPos<aPos && mDecoderPt->ReadNextImage(aNewImage,true) &&
ComputeCorrelation(aNewImage)>=aCorrelScore)
aNewPos = mDecoderPt->GetPosition();
// Puis 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
{
aSegment.mEnd = aPos;
}
}
if(!aSegment.IsValid())
{
aSegment.mBegin = aPos;
aSegment.mEnd = aPos+mDecoderPt->GetVPosition(1);
}
}
aInCorrelation = true;
aJumpCorrelation = false;
}
else
{
if(aInCorrelation && aSegment.IsValid() && !aJumpCorrelation)
{
if(aPos>aSegment.mEnd+anInc)
mDecoderPt->SeekFrame(aSegment.mEnd-anInc,FFMPEGDecoder::FRAME_SEEK_SET);
else
mDecoderPt->SeekFrame(aSegment.mEnd,FFMPEGDecoder::FRAME_SEEK_SET);
while(mDecoderPt->ReadNextImage(aNewImage,true) &&
ComputeCorrelation(aNewImage)>=aCorrelScore);
aPos = mDecoderPt->GetPosition();
aSegment.mEnd = 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;
}
else
aInCorrelation = false;
}
else
aInCorrelation = false;
}
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();
}
/*===============================================================
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()
{
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 = 0;
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);
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"));
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(aSegment.mBegin.mFilePos,aSegment.mEnd.mFilePos);
if(!aCopier.Write(aSegment,&aProgress))
{
Error(tr("Saving interrupted"));
break;
}
}
}
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.toAscii().data();
if(aFNS.TestExtension("ts"))
event->acceptProposedAction();
}
}
void MainWindow::on_TimeSlider_valueChanged(int aValue)
{
if(!mDecoderPt || mInSlider) return;
int64_t aMSValue = aValue*1000;
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 = -10000;
int64_t aPos = mDecoderPt->GetMSPosition();
FFWD_AGAIN:
if(!mDecoderPt->SeekFrame(aDeltaMSValue,FFMPEGDecoder::FRAME_SEEK_CUR)) return;
ShowNextFrame();
if(mDecoderPt->GetMSPosition() >= aPos && aPos>-aDeltaMSValue)
{
aDeltaMSValue -= 10000;
goto FFWD_AGAIN;
}
}
void MainWindow::on_FastNextButton_clicked()
{
const int64_t aDeltaMSValue = 10000;
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(1000);
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);
}
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();
if(mMinJump<=0) mMinJump = 60000;
}
void MainWindow::on_NextLogoOn_clicked()
{
if(!mDecoderPt || !HasLogo()) return;
const double aCorrelScore = 0.4;
QProgressDialog aProgress(this);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching..."));
aProgress.setRange(ui->TimeSlider->value(),ui->TimeSlider->maximum());
FlushAll();
QImage anImage;
int i=150;
while(mDecoderPt->ReadNextImage(anImage,true) &&
ComputeCorrelation(anImage)<aCorrelScore)
{
i++;
aProgress.setValue(mDecoderPt->GetMSPosition()*1000);
if(i==300)
{
FlushAll();
if(aProgress.wasCanceled()) break;
i=0;
}
}
ui->VideoFrame->SetImage(anImage);
PrintPosition();
}
void MainWindow::on_NextLogoOff_clicked()
{
if(!mDecoderPt || !HasLogo()) return;
const double aCorrelScore = 0.4;
QProgressDialog aProgress(this);
aProgress.setCancelButtonText(tr("Cancel"));
aProgress.setLabelText(tr("Searching..."));
aProgress.setRange(ui->TimeSlider->value(),ui->TimeSlider->maximum());
FlushAll();
QImage anImage;
int i=150;
while(mDecoderPt->ReadNextImage(anImage,true) &&
ComputeCorrelation(anImage)>=aCorrelScore)
{
i++;
aProgress.setValue(mDecoderPt->GetMSPosition()*1000);
if(i==300)
{
FlushAll();
if(aProgress.wasCanceled()) break;
i=0;
}
}
ui->VideoFrame->SetImage(anImage);
PrintPosition();
}
void MainWindow::on_actionAbout_VidePub_triggered()
{
About anAbout(this);
anAbout.exec();
}