/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: TNE Video Clip Implementation
*
*/
#include "HXTNEVideoClipInfoImp.h"
#include <e32base.h>
#include <fbs.h>
#include <coemain.h>
#include <ecom/ecom.h>
#include "yuv2rgb12.h"
#include "yuv2rgb16.h"
#include "yuv2rgb24.h"
#include <flogger.h>
// PRINT macro
#ifdef _DEBUG
#include <e32svr.h>
#define PRINT(x) RDebug::Print x
#else
#define PRINT(x)
#endif
#ifdef _DEBUG
#define ENABLE_S60_TNE_LOGGING 1
#endif
//implementation
#ifdef ENABLE_S60_TNE_LOGGING
#define FLOG FileLog
inline static void FileLog(TRefByValue<const TDesC16> aFmt,...)
{
VA_LIST args;
VA_START (args, aFmt);
RFileLogger::WriteFormat(_L("tne"), _L("S60tne.log"),
EFileLoggingModeAppend, aFmt, args);
}
#else // logging is disabled
#define FLOG LogFNull
inline static void LogFNull(TRefByValue<const TDesC16> aFmt,...){}
#endif // enable logging
//server details
const TUint kDefaultMessageSlots=4;
// -----------------------------------------------------------------------------
// CTNEVideoClipInfo::NewL
// API exposed by TNE to
// TNEVideoClipInfo using RFile handle
// -----------------------------------------------------------------------------
EXPORT_C CTNEVideoClipInfo* CTNEVideoClipInfo::NewL(const RFile& aFileHandle,
MTNEVideoClipInfoObserver& aObserver)
{
FLOG(_L("CTNEVideoClipInfo::NewL"));
CHXTNEVideoClipInfoImp* self = ( CHXTNEVideoClipInfoImp*)NewLC(aFileHandle, aObserver);
FLOG(_L("CTNEVideoClipInfo::NewL out"));
return self;
}
// -----------------------------------------------------------------------------
// CTNEVideoClipInfo::NewL
// API exposed by TNE to create TNEVideoClipInfo using file name
// -----------------------------------------------------------------------------
EXPORT_C CTNEVideoClipInfo* CTNEVideoClipInfo::NewL(const TDesC& aFileName,
MTNEVideoClipInfoObserver& aObserver)
{
FLOG(_L("CTNEVideoClipInfo::NewL"));
CHXTNEVideoClipInfoImp* self = ( CHXTNEVideoClipInfoImp*)NewLC(aFileName, aObserver);
FLOG(_L("CTNEVideoClipInfo::NewL out"));
return self;
}
EXPORT_C CTNEVideoClipInfo* CTNEVideoClipInfo::NewLC(const RFile& aFileHandle,
MTNEVideoClipInfoObserver& aObserver)
{
CHXTNEVideoClipInfoImp* self = new (ELeave) CHXTNEVideoClipInfoImp();
CleanupStack::PushL(self);
self->ConstructL(aFileHandle, aObserver);
CleanupStack::Pop();
return self;
}
EXPORT_C CTNEVideoClipInfo* CTNEVideoClipInfo::NewLC(const TDesC& aFileName,
MTNEVideoClipInfoObserver& aObserver)
{
CHXTNEVideoClipInfoImp* self = new (ELeave) CHXTNEVideoClipInfoImp();
CleanupStack::PushL(self);
self->ConstructL(aFileName, aObserver);
CleanupStack::Pop();
return self;
}
CTNEVideoClipInfo* CHXTNEVideoClipInfoImp::NewL(const RFile& aFileHandle,
MTNEVideoClipInfoObserver& aObserver)
{
CHXTNEVideoClipInfoImp* self = new (ELeave) CHXTNEVideoClipInfoImp();
CleanupStack::PushL(self);
self->ConstructL(aFileHandle, aObserver);
CleanupStack::Pop(self);
return self;
}
//constructor
CHXTNEVideoClipInfoImp::CHXTNEVideoClipInfoImp()
{
//empty constructor
}
//destructor
CHXTNEVideoClipInfoImp::~CHXTNEVideoClipInfoImp()
{
FLOG(_L("CTNEVideoClipInfo::~ CHXTNEVideoClipInfoImp in"));
RSessionBase::Close();
iServerThread.Close();
if (iServerName)
{
delete iServerName;
iServerName = NULL;
}
if (iObserver)
{
delete iObserver;
iObserver = NULL;
}
if (iCloseHandle)
{
iCloseHandle = EFalse;
iFileHandle.Close();
}
if (iOutBitmap)
{
delete iOutBitmap;
iOutBitmap = 0;
}
if(iFs)
{
iFs->Close();
delete iFs;
iFs = NULL;
}
if (iEnhancer)
{
delete iEnhancer;
iEnhancer = NULL;
}
FLOG(_L("CTNEVideoClipInfo::~ CHXTNEVideoClipInfoImp out"));
}
void CHXTNEVideoClipInfoImp::ConstructL(const RFile& aFileHandle,
MTNEVideoClipInfoObserver& aObserver)
{
FLOG(_L("CTNEVideoClipInfo::ConstructL in this=%x"), this);
iObserver = CTNEClientObserver::NewL(this);
iClipInfoObserver = &aObserver;
User::LeaveIfError(CreateServer());
iFileHandle.Duplicate(aFileHandle);
iCloseHandle = ETrue;
User::LeaveIfError(OpenFile(iFileHandle, 0));
FLOG(_L("CTNEVideoClipInfo::ConstructL out this=%x"), this);
}
void CHXTNEVideoClipInfoImp::ConstructL(const TDesC& aFileName,
MTNEVideoClipInfoObserver& aObserver)
{
FLOG(_L("CTNEVideoClipInfo::ConstructL in this=%x"), this);
iObserver = CTNEClientObserver::NewL(this);
iClipInfoObserver = &aObserver;
iFileName.Copy(aFileName);
User::LeaveIfError(CreateServer());
User::LeaveIfError(OpenFile(0));
FLOG(_L("CTNEVideoClipInfo::ConstructL out this=%x"), this);
}
TInt CHXTNEVideoClipInfoImp::CreateServer()
{
TInt err = KErrNoMemory;
FLOG(_L("CHXTNEVideoClipInfoImp::CreateServer in"));
TRAP(err,iServerName = HBufC::NewL(KMaxFileName));
if (iServerName && err == KErrNone)
{
TPtr ptr = iServerName->Des();
ptr.Copy(KTneServerName);
ptr.AppendNum(Math::Random(), EHex);
err = StartThread(iServerThread, iServerName);
}
if ( err == KErrNone)
{
//server name FIXME
err = CreateSession(*iServerName, Version(), kDefaultMessageSlots);
}
FLOG(_L("CHXTNEVideoClipInfoImp::CreateServer out err=%d"), err);
return(err);
}
TVersion CHXTNEVideoClipInfoImp::Version(void) const
{
return(TVersion(KTNEServMajorVersionNumber,KTNEServMinorVersionNumber,KTNEServBuildVersionNumber));
}
//SendRecieve() commands to server
TInt CHXTNEVideoClipInfoImp::OpenFile(const RFile& aFileHandle, TInt aPosition)
{
FLOG(_L("CHXTNEVideoClipInfoImp::OpenFile in"));
c_State = EStartGettingMetadata;
iObserver->Activate();
SendReceive(EOpenFileRFmsg,TIpcArgs(&aFileHandle,aPosition), iObserver->iStatus);
FLOG(_L("CHXTNEVideoClipInfoImp::OpenFile out"));
return KErrNone;
}
TInt CHXTNEVideoClipInfoImp::OpenFile(TInt aPosition)
{
FLOG(_L("CHXTNEVideoClipInfoImp::OpenFile in"));
c_State = EStartGettingMetadata;
iObserver->Activate();
SendReceive(EOpenFIleNamemsg,TIpcArgs(&iFileName,aPosition), iObserver->iStatus);
FLOG(_L("CHXTNEVideoClipInfoImp::OpenFile out"));
return KErrNone;
}
void CHXTNEVideoClipInfoImp::GetThumbL(MTNEVideoClipThumbObserver& aObserver,
TInt aIndex,
TSize* const aResolution,
TDisplayMode aDisplayMode,
TBool aEnhance,
TInt aPriority)
{
FLOG(_L("CHXTNEVideoClipInfoImp::GetThumbL in"));
iThumbObserver = &aObserver;
iThumbIndex = aIndex;
iThumbEnhance = aEnhance;
iThumbDisplayMode = aDisplayMode;
iPriority = aPriority;
iThumbRequestData.iIndex = aIndex;
if (aResolution)
{
iOutputThumbResolution = *aResolution;
}
else
{
iOutputThumbResolution.iHeight = iMetaData.iHeight;
iOutputThumbResolution.iWidth = iMetaData.iWidth;
}
iThumbRequestData.iYUVBuffer = NULL;
c_State = EStartGettingThumbNail;
iObserver->Activate();
SendReceive(EGetThumbmsg, TIpcArgs(&iThumbRequestData), iObserver->iStatus);
FLOG(_L("CHXTNEVideoClipInfoImp::GetThumbL out"));
}
void CHXTNEVideoClipInfoImp::CancelThumb()
{
FLOG(_L("CHXTNEVideoClipInfoImp::CancelThumb in"));
c_State = ECancelling;
SendReceive(ECancelThumbmsg);
FLOG(_L("CHXTNEVideoClipInfoImp::CancelThumb out"));
}
TPtrC CHXTNEVideoClipInfoImp::FileName() const
{
TFileName fileName;
if (iCloseHandle)
{
iFileHandle.FullName(fileName);
}
return fileName;
}
TInt CHXTNEVideoClipInfoImp::VideoFrameCount() const
{
return (TInt)iMetaData.iFrameCount ;
}
// Called from Observer after a Complete() from Server
void CHXTNEVideoClipInfoImp::ServiceCompleted(TInt aError)
{
FLOG(_L("CHXTNEVideoClipInfoImp::ServiceCompleted in aError=%d c_State=%d"),
aError, c_State);
switch(c_State)
{
case EStartGettingMetadata:
FetchAndSendMetaData(aError);
break;
case EStartGettingThumbNail:
if (aError == KErrNone)
{
GenerateThumbNail();
}
else
{
if (iOutBitmap)
{
delete iOutBitmap;
iOutBitmap = NULL;
}
m_LastError = aError;
}
NotifyIfGetThumbPending(m_LastError, iOutBitmap);
break;
case ECancelling:
default:
break;
}
FLOG(_L("CHXTNEVideoClipInfoImp::ServiceCompleted out"));
}
void CHXTNEVideoClipInfoImp::FetchAndSendMetaData(TInt aError)
{
FLOG(_L("CHXTNEVideoClipInfoImp::FetchAndSendMetaData in"));
if (aError == KErrNone)
{
aError = SendReceive(EGetMetaDatamsg, TIpcArgs(&iMetaData));
}
if(c_State== EStartGettingMetadata)
{
CTNEVideoClipInfo *pInfo = (CTNEVideoClipInfo *) this;
iClipInfoObserver->NotifyVideoClipInfoReady(*pInfo, aError);
}
FLOG(_L("CHXTNEVideoClipInfoImp::FetchAndSendMetaData out and state = %d"), c_State);
}
// ownership of pBitMap will be passed to Observer
void CHXTNEVideoClipInfoImp::NotifyIfGetThumbPending(TInt aError, CFbsBitmap *&pBitMap)
{
FLOG(_L("CHXTNEVideoClipInfoImp::NotifyIfGetThumbPending in"));
if (iThumbObserver)
{
CFbsBitmap* pTmpBitmap = pBitMap;
pBitMap = NULL;
iThumbObserver->NotifyVideoClipThumbCompleted(*this, aError, pTmpBitmap);
}
FLOG(_L("CHXTNEVideoClipInfoImp::NotifyIfGetThumbPending out"));
}
void CHXTNEVideoClipInfoImp::GenerateThumbNail()
{
FLOG(_L("CHXTNEVideoClipInfoImp::GenerateThumbNail in"));
TInt bytesPerPixel = 0;
TInt error = KErrNone;
if ( !iRgbBuf )
{
TSize inputFrameResolution(iMetaData.iWidth,iMetaData.iHeight);
// rgb specs
TUint thumbLength = iMetaData.iWidth * iMetaData.iHeight;
TUint thumbUVLength = thumbLength>>2;
// VPASSERT(iYuvBuf);
// assign yuv pointers
TUint8* yBuf = iThumbRequestData.iYUVBuffer;
TUint8* uBuf = yBuf + thumbLength;
TUint8* vBuf = uBuf + thumbUVLength;
// convert to no enhabnce if 64K is selected as thumb display mode
if(iThumbDisplayMode == EColor64K)
iThumbEnhance = EFalse;
// check validity of thumbnail and associated operation
if(iThumbEnhance) // for saving to file
{
iThumbDisplayMode = EColor16M; // 24-bit color image for enhancement
// no enhancements if clip is too small.
if (iOutputThumbResolution.iWidth < 16 || iOutputThumbResolution.iHeight < 16)
{
iThumbEnhance = EFalse;
}
}
else // for screen display
{
if(iThumbDisplayMode == ENone) // if no preference
iThumbDisplayMode = EColor64K; // 16-bit image
}
// determine proper bit depth for the bitmap
if(iThumbDisplayMode == EColor16M)
bytesPerPixel = 3; // 24-bit rgb takes 3 bytes, stored as bbbbbbbb gggggggg rrrrrrrr
else if(iThumbDisplayMode == EColor64K || iThumbDisplayMode == EColor4K)
bytesPerPixel = 2; // 12-bit rgb takes 2 bytes, stored as ggggbbbb xxxxrrrr
else
{
HandleThumbnailError(KErrNotSupported);
return; // support for 12-, 16- and 24-bit color images only
}
// create output rgb buffer
TRAP(error, iRgbBuf = (TUint8*) User::AllocL(thumbLength * bytesPerPixel));
if (HandleThumbnailError(error))
return;
TInt scanLineLength;
// convert yuv to rgb
switch (iThumbDisplayMode)
{
case EColor4K:
{
TInt error;
CYuv2Rgb12* yuvConverter = NULL;
TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb12);
if (HandleThumbnailError(error))
return;
scanLineLength = inputFrameResolution.iWidth * bytesPerPixel;
TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight));
if (HandleThumbnailError(error))
return;
yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength);
delete yuvConverter;
yuvConverter=0;
}
break;
default:
case EColor64K:
{
TInt error;
CYuv2Rgb16* yuvConverter = NULL;
TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb16);
if (HandleThumbnailError(error))
return;
scanLineLength = inputFrameResolution.iWidth * bytesPerPixel;
TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight);)
if (HandleThumbnailError(error))
return;
yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength);
delete yuvConverter;
yuvConverter=0;
}
break;
case EColor16M:
{
TInt error;
CYuv2Rgb24* yuvConverter = NULL;
TRAP(error, yuvConverter = new(ELeave) CYuv2Rgb24);
if (HandleThumbnailError(error))
return;
scanLineLength = inputFrameResolution.iWidth * bytesPerPixel;
TRAP(error, yuvConverter->ConstructL(inputFrameResolution.iWidth, inputFrameResolution.iHeight, inputFrameResolution.iWidth, inputFrameResolution.iHeight))
if (HandleThumbnailError(error))
return;
yuvConverter->Convert(yBuf, uBuf, vBuf, inputFrameResolution.iWidth, inputFrameResolution.iHeight, iRgbBuf, scanLineLength);
delete yuvConverter;
yuvConverter=0;
}
break;
}
}
if(!iThumbEnhance)
{
TSize inputFrameResolution(iMetaData.iWidth,iMetaData.iHeight);
/* Pre-calculate pixel indices for horizontal scaling. */
// inputFrameResolution is the resolution of the image read from video clip.
// iOutputThumbResolution is the final resolution desired by the caller.
TInt OutW = (iOutputThumbResolution.iWidth + 3 )/4 * 4;
TInt OutH = (iOutputThumbResolution.iHeight + 3 )/4 * 4;
TSize roundedsize = TSize (OutW,OutH);
TInt xIncrement = inputFrameResolution.iWidth * OutW;
TInt xBoundary = OutW * OutW;
TInt* xIndices = 0;
TRAPD(xIndicesErr, xIndices = new (ELeave) TInt[OutW]);
if (xIndicesErr == KErrNone)
{
TInt xDecision = xIncrement / bytesPerPixel; // looks like they changed here - orig was /2
TInt sourceIndex = 0;
for (TInt x = 0; x < OutW; x++)
{
while (xDecision > xBoundary)
{
xDecision -= xBoundary;
sourceIndex += bytesPerPixel;
}
xIndices[x] = sourceIndex;
xDecision += xIncrement;
}
}
else
{
HandleThumbnailError(xIndicesErr);
return;
}
/* Initialize bitmap. */
TRAPD(bitmapErr, iOutBitmap = new (ELeave) CFbsBitmap);
if ((xIndicesErr == KErrNone) && (bitmapErr == KErrNone))
{
bitmapErr = iOutBitmap->Create(roundedsize, iThumbDisplayMode/*EColor64K*/);
if (bitmapErr == KErrNone)
{
// Lock the heap to prevent the FBS server from invalidating the address
iOutBitmap->LockHeap();
/* Scale to desired iOutputThumbResolution and copy to bitmap. */
TUint8* dataAddress = (TUint8*)iOutBitmap->DataAddress(); // fix
TInt yIncrement = inputFrameResolution.iHeight * OutH;
TInt yBoundary = OutH * OutH;
TInt targetIndex = 0;
TInt sourceRowIndex = 0;
TInt yDecision = yIncrement / 2;
for (TInt y = 0; y < OutH; y++)
{
while (yDecision > yBoundary)
{
yDecision -= yBoundary;
sourceRowIndex += (inputFrameResolution.iWidth * bytesPerPixel);
}
yDecision += yIncrement;
for (TInt x = 0; x < OutW; x++)
{
for (TInt i = 0; i < bytesPerPixel; ++i)
{
const TInt firstPixelSourceIndex = sourceRowIndex + xIndices[x] + i;
dataAddress[targetIndex] = iRgbBuf[firstPixelSourceIndex];
targetIndex++;
}
}
}
iOutBitmap->UnlockHeap();
// resing to original if odd values were input
if(roundedsize != iOutputThumbResolution)
{
TInt err ;
err = iOutBitmap->Resize(iOutputThumbResolution);
if(err!= KErrNone)
HandleThumbnailError(err);
}
}
else
{
delete iOutBitmap; iOutBitmap = 0;
HandleThumbnailError(bitmapErr);
return;
}
}
else
{
HandleThumbnailError(bitmapErr);
delete[] xIndices; xIndices = 0;
return;
}
delete[] xIndices;
xIndices = 0;
}
else // enhance
{
TInt i,j;
// create input bitmap and buffer
CFbsBitmap* inBitmap = 0;
TRAPD(inBitmapErr, inBitmap = new (ELeave) CFbsBitmap);
if( inBitmapErr == KErrNone )
{
// create bitmaps
TSize originalResolution(iMetaData.iWidth, iMetaData.iHeight);
inBitmapErr = inBitmap->Create(originalResolution, iThumbDisplayMode/*EColor16M*/);
if( inBitmapErr == KErrNone )
{
// fill image from rgb buffer to input bitmap buffer
TPtr8 linePtr(0,0);
TInt lineLength = inBitmap->ScanLineLength(originalResolution.iWidth, iThumbDisplayMode);
for(j=0, i=0; j<originalResolution.iHeight; j++, i+=lineLength)
{
linePtr.Set(iRgbBuf+i, lineLength, lineLength);
inBitmap->SetScanLine((TDes8&)linePtr,j);
}
// create output bitmap
TRAPD(outBitmapErr, iOutBitmap = new (ELeave) CFbsBitmap);
if( outBitmapErr == KErrNone )
{
outBitmapErr = iOutBitmap->Create(iOutputThumbResolution, iThumbDisplayMode/*EColor16M*/); // same size as input frame
if( outBitmapErr == KErrNone )
{
// post-processing enhancement
TRAP(outBitmapErr, EnhanceThumbnailL((const CFbsBitmap*)inBitmap, (CFbsBitmap*)iOutBitmap));
}
else
{
delete inBitmap; inBitmap = 0;
delete iOutBitmap; iOutBitmap = 0;
HandleThumbnailError(outBitmapErr);
return;
}
}
else
{
delete inBitmap; inBitmap = 0;
HandleThumbnailError(outBitmapErr);
return;
}
}
else
{
delete inBitmap; inBitmap = 0;
HandleThumbnailError(inBitmapErr);
return;
}
// delete input bitmap
delete inBitmap;
inBitmap = 0;
}
else
{
HandleThumbnailError(inBitmapErr);
return;
}
}
delete iRgbBuf;
iRgbBuf = 0;
FLOG(_L("CHXTNEVideoClipInfoImp::GenerateThumbNail out"));
}
TBool CHXTNEVideoClipInfoImp::HandleThumbnailError(TInt aError)
{
if (aError != KErrNone)
{
m_LastError = aError;
return ETrue;
}
return EFalse;
}
void CHXTNEVideoClipInfoImp::EnhanceThumbnailL(const CFbsBitmap* aInBitmap,
CFbsBitmap* aTargetBitmap)
{
FLOG(_L("CHXTNEVideoClipInfoImp::EnhanceThumbnailL in"));
// create enhancement object
if(!iEnhancer)
iEnhancer = (CDisplayChain*) CDisplayChain::NewL();
// enhance image
iEnhancer->ProcessL(aInBitmap, aTargetBitmap);
// clear enhancement object
delete iEnhancer;
iEnhancer=0;
FLOG(_L("CHXTNEVideoClipInfoImp::EnhanceThumbnailL out"));
}