Here they come - thumbnails.
Also fixing some resizing issues in pagevideos - now it resizes nicer.
Wait for a announcement on hedgewars.org, I hope to make it soon.
--- a/QTfrontend/ui/page/pagevideos.cpp Tue Jun 26 23:23:02 2012 +0400
+++ b/QTfrontend/ui/page/pagevideos.cpp Tue Jun 26 23:29:41 2012 +0400
@@ -35,6 +35,7 @@
#include <QHeaderView>
#include <QKeyEvent>
#include <QVBoxLayout>
+#include <QHBoxLayout>
#include <QFileSystemWatcher>
#include "hwconsts.h"
@@ -44,6 +45,8 @@
#include "gameuiconfig.h"
#include "recorder.h"
+const int ThumbnailSize = 400;
+
// columns in table with list of video files
enum VideosColumns
{
@@ -90,12 +93,14 @@
{
QGridLayout * pPageLayout = new QGridLayout();
pPageLayout->setColumnStretch(0, 1);
- pPageLayout->setColumnStretch(1, 1);
+ pPageLayout->setColumnStretch(1, 2);
+ pPageLayout->setRowStretch(0, 1);
+ pPageLayout->setRowStretch(1, 1);
{
IconedGroupBox* pOptionsGroup = new IconedGroupBox(this);
pOptionsGroup->setIcon(QIcon(":/res/graphicsicon.png"));
- pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ pOptionsGroup->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
pOptionsGroup->setTitle(QGroupBox::tr("Video recording options"));
QGridLayout * pOptLayout = new QGridLayout(pOptionsGroup);
@@ -199,7 +204,7 @@
QStringList columns;
columns << tr("Name");
columns << tr("Size");
- columns << tr("...");
+ columns << "";
filesTable = new QTableWidget(pTableGroup);
filesTable->setColumnCount(vcNumColumns);
@@ -209,11 +214,10 @@
filesTable->verticalHeader()->hide();
QHeaderView * header = filesTable->horizontalHeader();
- int length = header->length(); // FIXME
- // header->setResizeMode(QHeaderView::ResizeToContents);
- header->resizeSection(vcName, length/2);
- header->resizeSection(vcSize, length/4);
- header->resizeSection(vcProgress, length/4);
+ header->setResizeMode(vcName, QHeaderView::ResizeToContents);
+ header->setResizeMode(vcSize, QHeaderView::Fixed);
+ header->resizeSection(vcSize, 100);
+ header->setStretchLastSection(true);
btnOpenDir = new QPushButton(QPushButton::tr("Open videos directory"), pTableGroup);
@@ -228,21 +232,38 @@
IconedGroupBox* pDescGroup = new IconedGroupBox(this);
pDescGroup->setIcon(QIcon(":/res/graphicsicon.png"));
pDescGroup->setTitle(QGroupBox::tr("Description"));
- QGridLayout* pDescLayout = new QGridLayout(pDescGroup);
+
+ QVBoxLayout* pDescLayout = new QVBoxLayout(pDescGroup);
+ QHBoxLayout* pTopDescLayout = new QHBoxLayout(0); // picture and text
+ QHBoxLayout* pBottomDescLayout = new QHBoxLayout(0); // buttons
+ // label with thumbnail picture
labelThumbnail = new QLabel(pDescGroup);
- pDescLayout->addWidget(labelThumbnail, 0, 0);
+ labelThumbnail->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
+ labelThumbnail->setStyleSheet(
+ "QFrame {"
+ "border: solid;"
+ "border-width: 3px;"
+ "border-color: #ffcc00;"
+ "border-radius: 4px;"
+ "}" );
+ pTopDescLayout->addWidget(labelThumbnail);
// label with file description
labelDesc = new QLabel(pDescGroup);
labelDesc->setAlignment(Qt::AlignLeft | Qt::AlignTop);
- pDescLayout->addWidget(labelDesc, 0, 1);
+ pTopDescLayout->addWidget(labelDesc);
// buttons: play and delete
btnPlay = new QPushButton(QPushButton::tr("Play"), pDescGroup);
- pDescLayout->addWidget(btnPlay, 1, 0);
+ pBottomDescLayout->addWidget(btnPlay);
btnDelete = new QPushButton(QPushButton::tr("Delete"), pDescGroup);
- pDescLayout->addWidget(btnDelete, 1, 1);
+ pBottomDescLayout->addWidget(btnDelete);
+
+ pDescLayout->addStretch(1);
+ pDescLayout->addLayout(pTopDescLayout, 0);
+ pDescLayout->addStretch(1);
+ pDescLayout->addLayout(pBottomDescLayout, 0);
pPageLayout->addWidget(pDescGroup, 0, 0);
}
@@ -543,10 +564,19 @@
VideoItem * item = nameItem(row);
QString oldName = item->name;
QString newName = item->text();
+ if (!newName.contains('.'))
+ {
+ // user forgot an extension
+ int pt = oldName.lastIndexOf('.');
+ if (pt != -1)
+ newName += oldName.right(oldName.length() - pt);
+ }
item->name = newName;
if (item->ready())
{
- if(!cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
+ if(cfgdir->rename("Videos/" + oldName, "Videos/" + newName))
+ updateDescription();
+ else
{
// unable to rename for some reason (maybe user entered incorrect name),
// therefore restore old name in cell
@@ -586,6 +616,8 @@
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
filesTable->setItem(row, vcProgress, item);
+ // filesTable->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents);
+
return row;
}
@@ -597,27 +629,65 @@
void PageVideos::updateDescription()
{
VideoItem * item = nameItem(filesTable->currentRow());
+ if (!item)
+ {
+ labelDesc->clear();
+ labelThumbnail->clear();
+ return;
+ }
+
QString desc = "";
- if (item)
+ desc += item->name + "\n";
+
+ QString thumbName = "";
+
+ if (item->ready())
{
- QString t = item->name;
- desc += item->name + "\n";
- // t.replace(".mp4", ".bmp");
- // QMessageBox::information(this, "1", cfgdir->absoluteFilePath("Screenshots/" + t));
- // m_pic.load(cfgdir->absoluteFilePath("Screenshots/" + t));
- // m_pic = m_pic.scaledToWidth(400);
- // m_thumbnail.setPixmap(m_pic);
+ QString path = item->path();
+ desc += tr("\nSize: ") + FileSizeStr(path) + "\n";
+ if (item->desc == "")
+ item->desc = LibavIteraction::instance().getFileInfo(path);
+ desc += item->desc;
+
+ // extract thumbnail name fron description
+ int prefixBegin = desc.indexOf("prefix[");
+ int prefixEnd = desc.indexOf("]prefix");
+ if (prefixBegin != -1 && prefixEnd != -1)
+ {
+ QString prefix = desc.mid(prefixBegin + 7, prefixEnd - (prefixBegin + 7));
+ desc.remove(prefixBegin, prefixEnd + 7 - prefixBegin);
+ thumbName = prefix;
+ }
+ }
+ else
+ desc += tr("(in progress...)");
+ if (thumbName.isEmpty())
+ {
if (item->ready())
+ thumbName = item->name;
+ else
+ thumbName = item->pRecorder->name;
+ // remove extension
+ int pt = thumbName.lastIndexOf('.');
+ if (pt != -1)
+ thumbName.truncate(pt);
+ }
+
+ if (!thumbName.isEmpty())
+ {
+ thumbName = cfgdir->absoluteFilePath("VideoTemp/" + thumbName);
+ if (picThumbnail.load(thumbName + ".png") || picThumbnail.load(thumbName + ".bmp"))
{
- QString path = item->path();
- desc += tr("\nSize: ") + FileSizeStr(path) + "\n";
- if (item->desc == "")
- item->desc = LibavIteraction::instance().getFileInfo(path);
- desc += item->desc;
+ if (picThumbnail.width() > picThumbnail.height())
+ picThumbnail = picThumbnail.scaledToWidth(ThumbnailSize);
+ else
+ picThumbnail = picThumbnail.scaledToHeight(ThumbnailSize);
+ labelThumbnail->setMaximumSize(picThumbnail.size());
+ labelThumbnail->setPixmap(picThumbnail);
}
else
- desc += tr("(in progress...)");
+ labelThumbnail->clear();
}
labelDesc->setText(desc);
}
--- a/hedgewars/ArgParsers.inc Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/ArgParsers.inc Tue Jun 26 23:29:41 2012 +0400
@@ -72,7 +72,7 @@
begin
internalStartGameWithParameters();
GameType:= gmtRecord;
- cRecPrefix:= ParamStr(20);
+ RecPrefix:= ParamStr(20);
cAVFormat:= ParamStr(21);
cVideoCodec:= ParamStr(22);
cVideoQuality:= StrToInt(ParamStr(23));
--- a/hedgewars/avwrapper.c Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/avwrapper.c Tue Jun 26 23:29:41 2012 +0400
@@ -90,7 +90,7 @@
#endif
if(!g_pAStream)
{
- Log("Could not allocate audio stream");
+ Log("Could not allocate audio stream\n");
return;
}
g_pAStream->id = 1;
@@ -121,7 +121,7 @@
// open it
if (avcodec_open2(g_pAudio, g_pACodec, NULL) < 0)
{
- Log("Could not open audio codec %s", g_pACodec->long_name);
+ Log("Could not open audio codec %s\n", g_pACodec->long_name);
return;
}
@@ -137,7 +137,7 @@
g_pAFrame = avcodec_alloc_frame();
if (!g_pAFrame)
{
- Log("Could not allocate frame");
+ Log("Could not allocate frame\n");
return;
}
}
@@ -400,7 +400,7 @@
AddAudioStream();
}
else
- Log("Could not open %s", pSoundFile);
+ Log("Could not open %s\n", pSoundFile);
}
else
Log("Audio codec \"%s\" was not found; audio will be ignored.\n", pACodecName);
@@ -415,7 +415,7 @@
if (!(g_pFormat->flags & AVFMT_NOFILE))
{
if (avio_open(&g_pContainer->pb, g_pContainer->filename, AVIO_FLAG_WRITE) < 0)
- FatalError("Could not open output file (%s)", pFilename);
+ FatalError("Could not open output file (%s)", g_pContainer->filename);
}
// write the stream header, if any
--- a/hedgewars/hwengine.pas Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/hwengine.pas Tue Jun 26 23:29:41 2012 +0400
@@ -111,14 +111,18 @@
begin
flagMakeCapture:= false;
{$IFDEF PAS2C}
- s:= 'hw';
+ s:= '/Screenshots/hw';
{$ELSE}
- s:= 'hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
+ s:= '/Screenshots/hw_' + FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now()) + inttostr(GameTicks);
{$ENDIF}
+ // flash
playSound(sndShutter);
-
- if MakeScreenshot(s) then
+ ScreenFade:= sfFromWhite;
+ ScreenFadeValue:= sfMax;
+ ScreenFadeSpeed:= 5;
+
+ if MakeScreenshot(s, 1) then
WriteLnToConsole('Screenshot saved: ' + s)
else
begin
--- a/hedgewars/uMisc.pas Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/uMisc.pas Tue Jun 26 23:29:41 2012 +0400
@@ -28,7 +28,7 @@
procedure movecursor(dx, dy: LongInt);
function doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
-function MakeScreenshot(filename: shortstring): boolean;
+function MakeScreenshot(filename: shortstring; k: LongInt): boolean;
function GetTeamStatString(p: PTeam): shortstring;
{$IFDEF SDL13}
function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
@@ -186,19 +186,48 @@
{$ENDIF} // no PNG_SCREENSHOTS
+{$IFDEF USE_VIDEO_RECORDING}
+// make image k times smaller (useful for saving thumbnails)
+procedure ReduceImage(img: PByte; width, height, k: LongInt);
+var i, j, i0, j0, w, h, r, g, b: LongInt;
+begin
+ w:= width div k;
+ h:= height div k;
+
+ // rescale inplace
+ if k <> 1 then
+ begin
+ for i:= 0 to h-1 do
+ for j:= 0 to w-1 do
+ begin
+ r:= 0;
+ g:= 0;
+ b:= 0;
+ for i0:= 0 to k-1 do
+ for j0:= 0 to k-1 do
+ begin
+ r+= img[4*(width*(i*k+i0) + j*k+j0)+0];
+ g+= img[4*(width*(i*k+i0) + j*k+j0)+1];
+ b+= img[4*(width*(i*k+i0) + j*k+j0)+2];
+ end;
+ img[4*(w*i + j)+0]:= r div (k*k);
+ img[4*(w*i + j)+1]:= g div (k*k);
+ img[4*(w*i + j)+2]:= b div (k*k);
+ img[4*(w*i + j)+3]:= 0;
+ end;
+ end;
+end;
+{$ENDIF}
+
// captures and saves the screen. returns true on success.
-function MakeScreenshot(filename: shortstring): Boolean;
+// saved image will be k times smaller than original (useful for saving thumbnails).
+function MakeScreenshot(filename: shortstring; k: LongInt): Boolean;
var p: Pointer;
size: QWord;
image: PScreenshot;
format: GLenum;
ext: string[4];
begin
-// flash
-ScreenFade:= sfFromWhite;
-ScreenFadeValue:= sfMax;
-ScreenFadeSpeed:= 5;
-
{$IFDEF PNG_SCREENSHOTS}
format:= GL_RGBA;
ext:= '.png';
@@ -218,14 +247,18 @@
exit;
end;
-// read pixel from the front buffer
+// read pixels from the front buffer
glReadPixels(0, 0, cScreenWidth, cScreenHeight, format, GL_UNSIGNED_BYTE, p);
+{$IFDEF USE_VIDEO_RECORDING}
+ReduceImage(p, cScreenWidth, cScreenHeight, k);
+{$ENDIF}
+
// allocate and fill structure that will be passed to new thread
New(image); // will be disposed in SaveScreenshot()
-image^.filename:= UserPathPrefix + '/Screenshots/' + filename + ext;
-image^.width:= cScreenWidth;
-image^.height:= cScreenHeight;
+image^.filename:= UserPathPrefix + filename + ext;
+image^.width:= cScreenWidth div k;
+image^.height:= cScreenHeight div k;
image^.size:= size;
image^.buffer:= p;
--- a/hedgewars/uVariables.pas Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/uVariables.pas Tue Jun 26 23:29:41 2012 +0400
@@ -53,7 +53,7 @@
cStereoMode : TStereoMode = smNone;
cOnlyStats : boolean = False;
{$IFDEF USE_VIDEO_RECORDING}
- cRecPrefix : shortstring;
+ RecPrefix : shortstring;
cAVFormat : shortstring;
cVideoCodec : shortstring;
cVideoFramerateNum : LongInt = 25;
--- a/hedgewars/uVideoRec.pas Tue Jun 26 23:23:02 2012 +0400
+++ b/hedgewars/uVideoRec.pas Tue Jun 26 23:29:41 2012 +0400
@@ -51,7 +51,7 @@
implementation
-uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO;
+uses uVariables, uUtils, GLunit, SDLh, SysUtils, uIO, uMisc, uTypes;
{$IFDEF WIN32}
const AVWrapperLibName = 'libavwrapper.dll';
@@ -88,6 +88,7 @@
numPixels: LongWord;
startTime, numFrames: LongWord;
cameraFilePath, soundFilePath: shortstring;
+ thumbnailSaved : Boolean;
function BeginVideoRecording: Boolean;
var filename, desc: shortstring;
@@ -98,7 +99,7 @@
{$IOCHECKS OFF}
// open file with prerecorded camera positions
- cameraFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.txtin';
+ cameraFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtin';
Assign(cameraFile, cameraFilePath);
Reset(cameraFile);
if IOResult <> 0 then
@@ -117,10 +118,11 @@
desc+= 'Map: ' + cMapName + #10;
if Theme <> '' then
desc+= 'Theme: ' + Theme + #10;
+ desc+= 'prefix[' + RecPrefix + ']prefix';
desc+= #0;
- filename:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + #0;
- soundFilePath:= UserPathPrefix + '/VideoTemp/' + cRecPrefix + '.sw' + #0;
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + #0;
+ soundFilePath:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw' + #0;
cAVFormat+= #0;
cAudioCodec+= #0;
cVideoCodec+= #0;
@@ -222,9 +224,25 @@
{$IOCHECKS ON}
end;
+procedure SaveThumbnail;
+var thumbpath: shortstring;
+ k: LongInt;
+begin
+ thumbpath:= '/VideoTemp/' + RecPrefix;
+ AddFileLog('Saving thumbnail ' + thumbpath);
+ if cScreenWidth > cScreenHeight then
+ k:= cScreenWidth div 400 // here 400 is minimum size of thumbnail
+ else
+ k:= cScreenHeight div 400;
+ if k = 0 then
+ k:= 1;
+ MakeScreenshot(thumbpath, k);
+ thumbnailSaved:= true;
+end;
+
procedure BeginPreRecording;
var format: word;
- filePrefix, filename: shortstring;
+ filename: shortstring;
frequency, channels: LongInt;
begin
AddFileLog('BeginPreRecording');
@@ -232,7 +250,11 @@
numFrames:= 0;
startTime:= SDL_GetTicks();
- filePrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
+ RecPrefix:= FormatDateTime('YYYY-MM-DD_HH-mm-ss', Now());
+
+ thumbnailSaved:= false;
+ if (not (gameState in [gsLandGen, gsStart])) and (ScreenFade = sfNone) then
+ SaveThumbnail();
Mix_QuerySpec(@frequency, @format, @channels);
AddFileLog('sound: frequency = ' + IntToStr(frequency) + ', format = ' + IntToStr(format) + ', channels = ' + IntToStr(channels));
@@ -245,7 +267,7 @@
{$IOCHECKS OFF}
// create sound file
- filename:= UserPathPrefix + '/VideoTemp/' + filePrefix + '.sw';
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.sw';
Assign(audioFile, filename);
Rewrite(audioFile, 1);
if IOResult <> 0 then
@@ -255,7 +277,7 @@
end;
// create file with camera positions
- filename:= UserPathPrefix + '/VideoTemp/' + filePrefix + '.txtout';
+ filename:= UserPathPrefix + '/VideoTemp/' + RecPrefix + '.txtout';
Assign(cameraFile, filename);
Rewrite(cameraFile);
if IOResult <> 0 then
@@ -285,12 +307,18 @@
Close(cameraFile);
Mix_SetPostMix(nil, nil);
SDL_UnlockAudio();
+
+ if (not thumbnailSaved) then
+ SaveThumbnail();
end;
procedure SaveCameraPosition;
var curTime: LongInt;
frame: TFrame;
begin
+ if (not thumbnailSaved) and (ScreenFade = sfNone) then
+ SaveThumbnail();
+
curTime:= SDL_GetTicks();
while Int64(curTime - startTime)*cVideoFramerateNum > Int64(numFrames)*cVideoFramerateDen*1000 do
begin