# HG changeset patch # User Stepan777 # Date 1340738981 -14400 # Node ID 3cff5c7695090b9336ceccf616f57c6d91dda2b9 # Parent 998128081b8652be790c40ff8fe4a0f08bdfdaa9 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. diff -r 998128081b86 -r 3cff5c769509 QTfrontend/ui/page/pagevideos.cpp --- 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 #include #include +#include #include #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); } diff -r 998128081b86 -r 3cff5c769509 hedgewars/ArgParsers.inc --- 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)); diff -r 998128081b86 -r 3cff5c769509 hedgewars/avwrapper.c --- 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 diff -r 998128081b86 -r 3cff5c769509 hedgewars/hwengine.pas --- 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 diff -r 998128081b86 -r 3cff5c769509 hedgewars/uMisc.pas --- 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; diff -r 998128081b86 -r 3cff5c769509 hedgewars/uVariables.pas --- 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; diff -r 998128081b86 -r 3cff5c769509 hedgewars/uVideoRec.pas --- 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