JD2022-TU1/main/extern/Camcam/LIBS/Input/AVI.cpp

1619 lines
No EOL
38 KiB
C++
Raw Blame History

#include "TRACK.h"
#include "MEDIA.h"
#include "SCENE.h"
//#define FRAME_BY_FRAME
//#define SHOWFRAMENUM
char EncoderBuffer[512*512*4];
char EncoderBufferRes[512*512*4];
extern "C" {
#include "JPEG\INTERFCE.H"
};
BOOL SaveJPEG(MAP *src, char *filename)
{
JPEGDATA jData;
char Filenamecorrected[2048];
unsigned char *ptr;
unsigned char *basePtr = (unsigned char *)src->GetBase();
memset(&jData, 0, sizeof(jData));
if (GetFileNameExtend(filename) != '.JPG')
{
sprintf(Filenamecorrected,"%s.JPG",filename);
filename = Filenamecorrected;
}
ptr = jData.ptr = (unsigned char *)EncoderBuffer;
for (int counter=0; counter < src->PITCH*src->SY; counter++)
{
*(ptr + 0) = *(basePtr + 2);
*(ptr + 1) = *(basePtr + 1);
*(ptr + 2) = *(basePtr + 0);
basePtr +=4;
ptr +=3;
}
// Invert Image
for (u32 Y = 0 ; Y < src->SY >> 1 ; Y++ )
{
char Swap[8192*4];
memcpy(Swap, jData.ptr + Y*src->SX*3, src->SX*3);
memcpy(jData.ptr + Y*src->SX*3, jData.ptr + (src->SY-Y-1)*src->SX*3, src->SX*3);
memcpy(jData.ptr + (src->SY-Y-1)*src->SX*3, Swap, src->SX*3);
}
jData.width = src->SX;
jData.height = src->SY;
jData.quality = 200;
jData.components = 3;
lJADE_JPEG_INPOUT_FILE_LENGHT = 0;
ucJADE_JPEG_INPOUT_FILE = (unsigned char *)EncoderBufferRes;
jData.output_file = ucJADE_JPEG_INPOUT_FILE;
JpegWrite(&jData);
FILE *jpgFile;
jpgFile = CC_fopen(filename, "wb");
if (jpgFile)
{
if (fwrite(ucJADE_JPEG_INPOUT_FILE, 1, lJADE_JPEG_INPOUT_FILE_LENGHT, jpgFile) == lJADE_JPEG_INPOUT_FILE_LENGHT)
{
CC_fclose(jpgFile);
return true;
}
CC_fclose(jpgFile);
DeleteFile(filename);
}
return false;
}
int LoadJPEG(MAP *dst, char *filename)
{
u32 Fl;
u8 *Fi;
Fl = GetFileLenght(filename);
if (Fl)
{
FILE *F;
JPEGDATA stJPD;
Fi = (u8*)CC_malloc(Fl);
F = GetSceneFileHanler(filename);
if (F)
{
fread( Fi, sizeof( char ), Fl, F );
CC_fclose(F);
}
lJADE_JPEG_INPOUT_FILE_LENGHT = Fl;
ucJADE_JPEG_INPOUT_FILE = Fi;
JpegInfo(&stJPD);
dst->SetSize(stJPD.width, stJPD.height);
stJPD.ptr = (u8*)dst->GetBase();
lJADE_JPEG_INPOUT_FILE_LENGHT = Fl;
ucJADE_JPEG_INPOUT_FILE = Fi;
JpegRead(&stJPD);
CC_free(Fi);
/* 24 -> 32 */
u8 *SRC,*DST;
u32 Counter;
Counter = dst->SX * dst->SY;
SRC = ((u8*)dst->GetBase()) + dst->SX * dst->SY * 3 - 3;
DST = ((u8*)dst->GetBase()) + dst->SX * dst->SY * 4 - 4;
while (Counter--)
{
*(DST + 3) = 0xff000000;
*(DST + 2) = *(SRC + 0);
*(DST + 1) = *(SRC + 1);
*(DST + 0) = *(SRC + 2);
SRC-=3;
DST-=4;
}
/***** Flip V *****/
s32 *pBASE = dst->GetBase();
for (u32 X = 0 ; X < dst->SX ; X++ )
for (u32 Y = 0 ; Y < dst->SY >> 1 ; Y++ )
{
u32 Swap;
Swap = *(pBASE + X + Y * dst->SX);
*(pBASE + X + Y * dst->SX) = *(pBASE + X + (dst->SY - Y - 1) * dst->SX);
*(pBASE + X + (dst->SY - Y - 1) * dst->SX) = Swap;
}
/***** Flip V *****/
return 1;
}
return 0;
}
#include <windows.h>
#include <atlcomcli.h>
#include <dshow.h>
#include <qedit.h>
#include "CODEC.h"
#include "process.h"
HRESULT AddToGraphEdit(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
IMoniker *pMoniker;
IRunningObjectTable *pROT;
if (FAILED(GetRunningObjectTable(0, &pROT))) {
return E_FAIL;
}
WCHAR wsz[256];
wsprintfW(wsz, L"FilterGraph %08x pid %08x", (DWORD_PTR)pUnkGraph, GetCurrentProcessId());
HRESULT hr = CreateItemMoniker(L"!", wsz, &pMoniker);
if (SUCCEEDED(hr)) {
hr = pROT->Register(0, pUnkGraph, pMoniker, pdwRegister);
pMoniker->Release();
}
pROT->Release();
return hr;
}
void RemoveFromGraphEdit(DWORD pdwRegister)
{
IRunningObjectTable *pROT;
if (SUCCEEDED(GetRunningObjectTable(0, &pROT))) {
pROT->Revoke(pdwRegister);
pROT->Release();
}
}
#if 1
class CSampleGrabberCB : public ISampleGrabberCB
{
public:
IMediaControl *mediaControl;
u32 NewFrame;
volatile u32 copying;
BYTE *imgBuffer;
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown )
{
*ppv = (void *) static_cast<ISampleGrabberCB*> ( this );
return NOERROR;
}
return E_NOINTERFACE;
}
STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample )
{
return 0;
}
STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
{
NewFrame = 1;
if (!imgBuffer)
imgBuffer = (BYTE *)CC_malloc(lBufferSize);
while (copying)
Sleep(2);
copying = 1;
memcpy(imgBuffer, pBuffer, lBufferSize);
copying = 0;
/*
if (FrameByFrame && !Locked)
{
if (mediaControl)
{
mediaControl->Pause();
}
}//*/
return 0;
}
};
class VideoWrapper {
public :
CComPtr<ICaptureGraphBuilder2> graphBuilder;
CComPtr<IFilterGraph2> filterGraph;
CComPtr<ISampleGrabber> sampleGrabber;
CComPtr<IBaseFilter> sourceFilter;
CComPtr<IBaseFilter> grabberFilter;
CComPtr<IBaseFilter> nullRenderer;
CComPtr<IBasicAudio> audioConfig;
CComPtr<IMediaSeeking> mediaSeeking;
CComPtr<IMediaPosition> mediaPosition;
CComPtr<IMediaControl> mediaControl;
CComPtr<IMediaEvent> mediaEvent;
CSampleGrabberCB CB;
bool videoInitialized;
double dAvgFramePerSecond;
int videoWidth;
int videoHeight;
long *frameBuffer;
long bufferSize;
int StatePausedLocker;
DWORD grabberNum;
INPUT_IO *pDad;
/** G3D Texture object */
VideoWrapper() {
// This must be callled before
// interfaces can be accessed
CoInitialize(NULL);
videoInitialized = false;
frameBuffer = NULL;
grabberNum = 0;
}
~VideoWrapper() {
// Technically, uninit ever init
CoUninitialize();
}
bool loadVideoFile(char *TheFile, bool loadWithSound);
int grabFrameTexture();
void uninitVideo();
int ConnectPins(IBaseFilter* outputFilter,
unsigned int outputNum,
IBaseFilter* inputFilter,
unsigned int inputNum);
bool ConnectToFreePin(IBaseFilter* inputFilter,
unsigned int inputNum);
void FindPin(IBaseFilter* baseFilter,
PIN_DIRECTION direction,
int pinNumber,
IPin** destPin);
int FindFreePin(PIN_DIRECTION direction, int number,
IPin** destPin);
};
class AVI_Redirect
{
char redirPath[1024];
char redirBase[1024];
public:
BOOL isRedirect;
int curFrame;
int nbFrames;
public:
AVI_Redirect() {memset(this, 0, sizeof(AVI_Redirect));};
int GenRedirFiles(char *theFile, VideoWrapper *vw, MAP *dstImg, TrackPlayer* tracks);
BOOL GetImage(int frame, MAP *dstImg);
};
class AVI_Grabber:public INPUT_IO
{
VideoWrapper *vw;
WORLD * pScn;
HRESULT hrinit;
DWORD sTimer;
double lastTime;
float volume;
float rate;
AVI_Redirect aviRedirect;
public:
u32 UseAlphaMode;
int INIT (WORLD *pScene);
int FRAME ();
int END ();
IOTYPE GetType() {return IOtype_AVI;};
void SetData(int dataIdx, void *data);
void * GetData(int dataIdx);
};
bool VideoWrapper::loadVideoFile(char *TheFile, bool loadWithSound) {
if (videoInitialized) {
uninitVideo();
}
StatePausedLocker = 0;
// Create the main object that runs the graph
graphBuilder.CoCreateInstance(CLSID_CaptureGraphBuilder2);
filterGraph.CoCreateInstance(CLSID_FilterGraph);
CB.NewFrame = 0;
CB.imgBuffer = NULL;
CB.copying = 0;
graphBuilder->SetFiltergraph(filterGraph);
// This takes the absolute filename path and
// Loads the appropriate file reader and splitter
// Depending in the file type.
WCHAR filename[ 1024 ];
MultiByteToWideChar(CP_ACP,0,TheFile,-1,filename,1024);
filterGraph->AddSourceFilter(filename,//(LPCWSTR )filename,
L"Video Source",
&sourceFilter);
// Create the Sample Grabber which we will use
// To take each frame for texture generation
grabberFilter.CoCreateInstance(CLSID_SampleGrabber);
grabberFilter->QueryInterface(IID_ISampleGrabber, reinterpret_cast<void**>(&sampleGrabber));
// We have to set the 24-bit RGB desire here
// So that the proper conversion filters
// Are added automatically.
AM_MEDIA_TYPE desiredType;
memset(&desiredType, 0, sizeof(desiredType));
desiredType.majortype = MEDIATYPE_Video;
desiredType.subtype = MEDIASUBTYPE_RGB32;
desiredType.formattype = FORMAT_VideoInfo;
sampleGrabber->SetMediaType(&desiredType);
sampleGrabber->SetBufferSamples(TRUE);
filterGraph->AddFilter(grabberFilter, L"Sample Grabber");
//sampleGrabber->SetOneShot(TRUE);
// Use pin connection methods instead of
// ICaptureGraphBuilder::RenderStream because of
// the SampleGrabber setting we're using.
int pinNum = 0;
while (true)
{
int res = ConnectPins(sourceFilter, pinNum++, grabberFilter, 0);
if (res > 0)
break;
if (res < 0)
{
uninitVideo();
return false;
}
}
// A Null Renderer does not display the video
// But it allows the Sample Grabber to run
// And it will keep proper playback timing
// Unless specified otherwise.
nullRenderer.CoCreateInstance(CLSID_NullRenderer);
filterGraph->AddFilter(nullRenderer, L"Null Renderer");
if (ConnectPins(grabberFilter, 0, nullRenderer, 0) <= 0) {
uninitVideo();
return false;
}//*/
if (loadWithSound)
{
CComPtr<IPin> outputPin;
if (FindFreePin(PINDIR_OUTPUT, 0, &outputPin))
{
filterGraph->Render(outputPin);
}
outputPin.Release();
}
// test function to get generated direct show graph
AddToGraphEdit(filterGraph, &grabberNum);
// Just a little trick so that we don't have to know
// The video resolution when calling this method.
bool mediaConnected = false;
AM_MEDIA_TYPE connectedType;
if (SUCCEEDED(sampleGrabber->GetConnectedMediaType(&connectedType))) {
if (connectedType.formattype == FORMAT_VideoInfo) {
VIDEOINFOHEADER* infoHeader = (VIDEOINFOHEADER*)connectedType.pbFormat;
videoWidth = infoHeader->bmiHeader.biWidth;
videoHeight = infoHeader->bmiHeader.biHeight;
//if (infoHeader->AvgTimePerFrame)
// dAvgFramePerSecond = (double) 10000000 / infoHeader->AvgTimePerFrame;
//else
dAvgFramePerSecond = 30.0;
mediaConnected = true;
}
CoTaskMemFree(connectedType.pbFormat);
}
if (!mediaConnected) {
uninitVideo();
return false;
}
sampleGrabber->SetCallback(&CB,1);
filterGraph->QueryInterface(IID_IBasicAudio, (void **)&audioConfig);
filterGraph->QueryInterface(IID_IMediaSeeking, (void **)&mediaSeeking);
filterGraph->QueryInterface(IID_IMediaPosition, (void **)&mediaPosition);
//if (CB.FrameByFrame)
//mediaSeeking->SetRate(5.0);
if (S_OK == mediaSeeking->IsFormatSupported(&TIME_FORMAT_MEDIA_TIME ))
mediaSeeking->SetTimeFormat(&TIME_FORMAT_MEDIA_TIME );
filterGraph->QueryInterface(IID_IMediaControl, (void **)&mediaControl);
filterGraph->QueryInterface(IID_IMediaEvent, (void **)&mediaEvent);
CB.mediaControl = mediaControl;
if (SUCCEEDED(mediaControl->Run())) {
videoInitialized = true;
return true;
} else {
uninitVideo();
return false;
}
}
/** For a later time but probably faster displays. */
int VideoWrapper::grabFrameTexture() {
if (videoInitialized) {
// Only need to do this once
if (!frameBuffer) {
// The Sample Grabber requires an arbitrary buffer
// That we only know at runtime.
// (width * height * 3) bytes will not work.
bufferSize =4*videoWidth*videoHeight;
sampleGrabber->GetCurrentBuffer(&bufferSize, NULL);
frameBuffer = new long[bufferSize];
}
sampleGrabber->GetCurrentBuffer(&bufferSize, (long*)frameBuffer);
}
return 0;
}
void VideoWrapper::uninitVideo() {
if (videoInitialized) {
if (mediaControl) mediaControl->Stop();
if (grabberNum)
RemoveFromGraphEdit(grabberNum);
grabberNum = 0;
mediaControl.Release();
mediaSeeking.Release();
mediaPosition.Release();
audioConfig.Release();
grabberFilter.Release();
sourceFilter.Release();
nullRenderer.Release();
sampleGrabber.Release();
filterGraph.Release();
graphBuilder.Release();
CB.mediaControl = NULL;
}
videoInitialized = false;
if (frameBuffer) delete[] frameBuffer;
frameBuffer = NULL;
}
int VideoWrapper::ConnectPins(IBaseFilter* outputFilter,
unsigned int outputNum,
IBaseFilter* inputFilter,
unsigned int inputNum) {
CComPtr<IPin> inputPin;
CComPtr<IPin> outputPin;
if (!outputFilter || !inputFilter) {
return -1;
}
FindPin(outputFilter, PINDIR_OUTPUT, outputNum, &outputPin);
FindPin(inputFilter, PINDIR_INPUT, inputNum, &inputPin);
if (inputPin && outputPin) {
return SUCCEEDED(filterGraph->Connect(outputPin, inputPin));
} else {
return -1;
}
}
bool VideoWrapper::ConnectToFreePin(IBaseFilter* inputFilter,
unsigned int inputNum) {
CComPtr<IPin> inputPin;
CComPtr<IPin> outputPin;
if (!inputFilter) {
return false;
}
int number = 0;
bool funcReturn = false;
FindPin(inputFilter, PINDIR_INPUT, inputNum, &inputPin);
while (true)
{
funcReturn = FindFreePin(PINDIR_OUTPUT, number++, &outputPin);
if (!funcReturn)
return false;
if (inputPin && outputPin) {
funcReturn = SUCCEEDED(filterGraph->Connect(outputPin, inputPin));
if (funcReturn)
return funcReturn;
} else {
return false;
}
outputPin.Release();
}
}
void VideoWrapper::FindPin(IBaseFilter* baseFilter,
PIN_DIRECTION direction,
int pinNumber,
IPin** destPin) {
CComPtr<IEnumPins> enumPins;
*destPin = NULL;
if (SUCCEEDED(baseFilter->EnumPins(&enumPins))) {
ULONG numFound;
IPin* tmpPin;
while (SUCCEEDED(enumPins->Next(1, &tmpPin, &numFound)) && numFound > 0) {
PIN_DIRECTION pinDirection;
tmpPin->QueryDirection(&pinDirection);
if (pinDirection == direction) {
if (pinNumber == 0) {
// Return the pin's interface
*destPin = tmpPin;
break;
}
pinNumber--;
}
tmpPin->Release();
}
enumPins.Release();
}
}
int VideoWrapper::FindFreePin(PIN_DIRECTION direction, int number,
IPin** destPin)
{
CComPtr<IEnumFilters> enumFilters;
CComPtr<IEnumPins> enumPins;
CComPtr<IBaseFilter> currentFilter;
IPin *filterPin;
IPin *connectedPin;
ULONG numFilterFound;
ULONG numPinFound;
filterGraph->EnumFilters(&enumFilters);
while (SUCCEEDED(enumFilters->Next(1,&currentFilter,&numFilterFound)) && numFilterFound > 0)
{
//FILTER_INFO infoFilter;
//currentFilter->QueryFilterInfo(&infoFilter);
if (SUCCEEDED(currentFilter->EnumPins(&enumPins)))
{
ULONG numFound2;
while (SUCCEEDED(enumPins->Next(1, &filterPin, &numFound2)) && numFound2 > 0)
{
PIN_DIRECTION pinDirection;
filterPin->QueryDirection(&pinDirection);
if (pinDirection == direction)
{
if (S_OK != filterPin->ConnectedTo(&connectedPin))
{
if (number == 0)
{
// ajouter test du type
*destPin = filterPin;
return 1;
}
number--;
} else {
connectedPin->Release();
}
}
filterPin->Release();
}
enumPins.Release();
}
currentFilter.Release();
numFilterFound = numFilterFound;
}
enumFilters.Release();
return 0;
}
int AVI_Redirect::GenRedirFiles(char *theFile, VideoWrapper *vw, MAP *dstImg, TrackPlayer* tracks)
{
char * lastChar;
char spChar;
char pathFile[1024];
char pathFileDst[1024];
INPUT_IO *Ret;
FILE *fileBuffer;
if (isRedirect) return nbFrames;
for (lastChar=theFile; strchr(lastChar, '.') != NULL; lastChar=strchr(lastChar, '.')+1);
strncpy(redirPath, theFile, lastChar-theFile-1);
redirPath[lastChar-theFile] = 0;
if (strchr(redirPath, '/') != NULL)
spChar = '/';
else
spChar = '\\';
for (lastChar=redirPath; strchr(lastChar, spChar) != NULL; lastChar=strchr(lastChar, spChar)+1);
strcpy(redirBase, lastChar);
CreateDirectory(redirPath, 0);
sprintf(pathFile, "%s\\%s_%06i.jpg", redirPath, redirBase, 1);
if (IsExist(pathFile)) {
if (!vw)
{
u32 Fl;
u8 *Fi;
FILE *F;
JPEGDATA stJPD;
Fl = GetFileLenght((char*)pathFile);
Fi = (u8*)CC_malloc(Fl);
F = GetSceneFileHanler((char*)pathFile);
if (F)
{
fread( Fi, sizeof( char ), Fl, F );
CC_fclose(F);
}
lJADE_JPEG_INPOUT_FILE_LENGHT = Fl;
ucJADE_JPEG_INPOUT_FILE = Fi;
JpegInfo(&stJPD);
if (stJPD.status)
{
CC_free(Fi);
return 0;
}
dstImg->PITCH =
dstImg->SX = stJPD.width;
dstImg->SY = stJPD.height;
CC_free(Fi);
}
sprintf(pathFile,"%s\\%s_*.jpg", redirPath, redirBase);
WIN32_FIND_DATA FD;
HANDLE HHH = FindFirstFile(pathFile,&FD);
nbFrames = 1;
if (HHH != INVALID_HANDLE_VALUE)
{
do
{
strcpy(pathFile, FD.cFileName + (strlen(FD.cFileName) - 10));
pathFile[6] = 0;
int frm = atoi(pathFile);
if (nbFrames < frm)
nbFrames = frm;
} while (FindNextFile(HHH,&FD));
FindClose(HHH);
}
curFrame = -1;
isRedirect = true;
return nbFrames;
}
if (!vw)
return 0;
int lastTrkFrame = -1;
int currTrkFrame = -1;
double lastTrkTime = -1.0;
double currTrkTime;
vw->mediaSeeking->SetRate(0.5);
vw->mediaControl->Stop();
vw->mediaPosition->put_CurrentPosition(0);
vw->mediaControl->Run();
nbFrames = 1;
BOOL bEnd = false;
while (!bEnd)
{
long lEventCode, lParam1, lParam2;
while (E_ABORT != vw->mediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0))
{
if (lEventCode == EC_COMPLETE)
bEnd = true;
vw->mediaEvent->FreeEventParams(lEventCode, lParam1, lParam2);
}
vw->mediaPosition->get_CurrentPosition(&currTrkTime);
currTrkFrame = TIME_TO_FRAME(currTrkTime, vw->dAvgFramePerSecond);
if (lastTrkTime >= currTrkTime)
{
Sleep(3); continue;
}
lastTrkTime = currTrkTime;
if (currTrkFrame == lastTrkFrame)
{
Sleep(3); continue;
}
for (int currTrkIn = lastTrkFrame + 1; currTrkIn < currTrkFrame; currTrkIn++)
{
sprintf(pathFile, "%s\\%s_%06i.jpg", redirPath, redirBase, lastTrkFrame);
sprintf(pathFileDst,"%s\\%s_%06i.jpg", redirPath, redirBase, currTrkIn);
CopyFile(pathFile, pathFileDst, 0);
}
lastTrkFrame = currTrkFrame;
vw->grabFrameTexture();
memcpy(dstImg->GetBase() , (s32*)vw->frameBuffer , vw->videoWidth * vw->videoHeight * 4);
if (vw->videoWidth > dstImg->SX)
{
int i,j,IncX,AddX,IncY,AddY;
unsigned long *SRC_512,*SRC_640,*SRC_LAST;
SRC_512 = SRC_640 = (unsigned long*)dstImg->GetBase();
IncY = 0;
AddY = (dstImg->SY<<16) / vw->videoHeight;
AddX = (dstImg->SX<<16) / vw->videoWidth;
for (i = 0,j = 0 ; i < vw->videoHeight; i++)
{
SRC_LAST = SRC_640 + vw->videoWidth;
IncX = 0;
while (SRC_640 < SRC_LAST)
{
*(SRC_512+(IncX>>16)) = *(SRC_640++);
IncX += AddX;
}
IncY += AddY;
SRC_512 = (unsigned long*)dstImg->GetBase() + ((IncY>>16) * dstImg->SX);
j++;
}
}
sprintf(pathFile, "%s\\%s_%06i.jpg", redirPath, redirBase, currTrkFrame);
SaveJPEG(dstImg, pathFile);
tracks->GetDescriptor(currTrkFrame)->ImageMarker = GET_MARKER(dstImg);
nbFrames = currTrkFrame;
}
if (!nbFrames)
nbFrames = 1;
curFrame = 0;
isRedirect = true;
return nbFrames;
}
BOOL AVI_Redirect::GetImage(int frame, MAP *dstImg)
{
char pathFile[1024];
FILE *fileBuffer;
if (!isRedirect || redirPath[0] == 0) return false;
if (frame == curFrame) return true;
//frame = frame % nbFrames;
if (frame > nbFrames) frame = nbFrames;
if (frame < 0) frame = nbFrames - 1;
curFrame = frame;
sprintf(pathFile, "%s\\%s_%06i.jpg", redirPath, redirBase, curFrame);
if (!LoadJPEG(dstImg, pathFile)) return false;
return true;
}
int AVI_Grabber::INIT(WORLD *pScene)
{
dCurrentTime = 0.0f;
dTotalTime = 0.0f;
ulCurrentFrame = 0;
lTimeState = 0;
ulMediaParams = 0x1;
volume = 1.0f;
rate = 1.0f;
vw = new VideoWrapper();
vw->pDad = NULL;
if (!vw->loadVideoFile((char*)MediaName, !pScene->AVIMegaRedirect))
{
delete vw;
vw = NULL;
if (!pScene->AVIMegaRedirect)
return false;
int nbFrame = aviRedirect.GenRedirFiles((char *)MediaName, NULL, &Image, Tracks);
if (!nbFrame)
return false;
dTotalTime = (double)nbFrame / 30;
dAvgFramePerSecond = 30;
} else
{
LONGLONG time;
vw->mediaSeeking->GetDuration(&time);
dTotalTime = (double)time / 10000000;
dAvgFramePerSecond = vw->dAvgFramePerSecond;
Image.PITCH =
Image.SX = vw->videoWidth;
Image.SY = vw->videoHeight;
vw->pDad = this;
vw->grabFrameTexture();
vw->mediaControl->Pause();
if (pScene->AVIMegaRedirect)
aviRedirect.GenRedirFiles((char *)MediaName, vw, &Image, Tracks);
}
ulCurrentFrame = 0;
pScn = pScene;
Image.SetSize(Image.SX, Image.SY, 1);
if (Image.SX > 512)
{
Image.SX = 512;
Image.SY = ((Image.SX) * ((vw->videoHeight << 16) / vw->videoWidth)>>16);
}
if (!UseAlphaMode)
if (Image.SY > 512)
{
Image.SX = ((Image.SY) * ((vw->videoWidth << 16) / vw->videoHeight)>>16);
Image.SY = 512;
}
//CoUninitialize();
if (UseAlphaMode)
Image.SY >>= 1;
return 1;
}
int AVI_Grabber::FRAME()
{
double prevTime;
int imgChanged = 0;
if (pScn->bManualBoot & 2) return 0;
if (UseAlphaMode) Image.SY <<= 1;
if (vw || pScn->AVIMegaRedirect)
{
START_RASTER(Avi);
if (pScn->AVIMegaRedirect)
{
if (Tracks == NULL)
{
Tracks = (TrackPlayer*)CC_malloc(sizeof(TrackPlayer));
memset(Tracks,0,sizeof(TrackPlayer));
};
if (lTimeState < 0)
aviRedirect.GetImage(ulCurrentFrame-1, &Image);
else if (!lTimeState || lTimeState == 255)
aviRedirect.GetImage(ulCurrentFrame, &Image);
else
aviRedirect.GetImage(ulCurrentFrame+1, &Image);
ulCurrentFrame = aviRedirect.curFrame;
} else
{
BOOL needState = false;
if (abs((float)dCurrentTime - (float)lastTime) > 0.001f)
{
vw->mediaPosition->put_CurrentPosition(dCurrentTime);
needState = true;
}
switch (lTimeState)
{
case 0:
vw->mediaControl->Pause();
break;
case 255:
vw->mediaPosition->put_CurrentPosition(0);
vw->mediaControl->StopWhenReady();
needState = true;
break;
case 1:
vw->mediaControl->Run();
break;
}
if (needState)
{
OAFilterState state;
vw->mediaControl->GetState(500, &state);
}
// if (lTimeState || ulCurrentFrame != lastTime)
if (vw->CB.NewFrame)
{
vw->CB.NewFrame = 0;
imgChanged = 1;
}
if (lTimeState != 1)
lTimeState = 0;
prevTime = dCurrentTime;
vw->mediaPosition->get_CurrentPosition(&dCurrentTime);
BOOL bEnd = false;
long lEventCode, lParam1, lParam2;
while (E_ABORT != vw->mediaEvent->GetEvent(&lEventCode, &lParam1, &lParam2, 0))
{
if (lEventCode == EC_COMPLETE)
bEnd = true;
vw->mediaEvent->FreeEventParams(lEventCode, lParam1, lParam2);
}
if (bEnd)
{
if (ulMediaParams & 1)
{
dCurrentTime = 0.0f;
vw->mediaPosition->put_CurrentPosition(dCurrentTime);
vw->mediaControl->Stop();
if (lTimeState == 1)
vw->mediaControl->Run();
} else
{
lTimeState = 0;
}
}
ulCurrentFrame = TIME_TO_FRAME(dCurrentTime, dAvgFramePerSecond);
lastTime = dCurrentTime;
if (imgChanged)
{
while (vw->CB.copying)
Sleep(2);
vw->CB.copying = 1;
memcpy(Image.GetBase() , (s32*)vw->CB.imgBuffer, vw->videoWidth * vw->videoHeight * 4);
vw->CB.copying = 0;
if (vw->videoWidth > Image.SX)
{
int i,j,IncX,AddX,IncY,AddY;
unsigned long *SRC_512,*SRC_640,*SRC_LAST;
SRC_512 = SRC_640 = (unsigned long*)Image.GetBase();
IncY = 0;
AddY = (Image.SY<<16) / vw->videoHeight;
AddX = (Image.SX<<16) / vw->videoWidth;
for (i = 0,j = 0 ; i < vw->videoHeight; i++)
{
SRC_LAST = SRC_640 + vw->videoWidth;
IncX = 0;
while (SRC_640 < SRC_LAST)
{
*(SRC_512+(IncX>>16)) = *(SRC_640++);
IncX += AddX;
}
IncY += AddY;
SRC_512 = (unsigned long*)Image.GetBase() + ((IncY>>16) * Image.SX);
j++;
}
}
if (UseAlphaMode)
{
s32 *pAlpha,*pRGB,*pLast;
pAlpha = Image.GetBase();
pRGB = Image.GetBase() + (Image.SY>>1) * Image.PITCH;
pLast = Image.GetBase() + Image.SY * Image.PITCH;
while (pRGB < pLast)
{
*(u32*)pAlpha <<= 24;
*pAlpha |= *pRGB & 0xffffff;
pAlpha++;
pRGB++;
}//*/
}
}
}
if (vw && vw->audioConfig)
{
if (ulMediaParams & 2)
vw->audioConfig->put_Volume(-10000);
else
vw->audioConfig->put_Volume(-10000*(1-volume));
}
//if (pScn->WSPC->INTERP_MatchingPercent < 10.0f) I_Frame = 20;
#ifdef SHOWFRAMENUM
if (!lTimeState)
{
char TEXT[2048];
sprintf(TEXT,"%d TF:%d SPL:%d",vw->FrameNum,ulCurrentFrame,vw->StatePausedLocker);
DRawTEXT_Soft_B((u8*)TEXT,(u32*)Image.BASE,0, 0, Image.SX, Image.SY , 0xffff00);
}
#endif
STOP_RASTER(Avi);
}
if (UseAlphaMode) Image.SY >>= 1;
return imgChanged;
}
void AVI_Grabber::SetData(int dataIdx, void *data)
{
switch (dataIdx)
{
case 3:
volume = *(float *)data;
volume = log(volume+1.0f)/log(2.0f);
if (vw && vw->audioConfig)
vw->audioConfig->put_Volume(-10000*(1-volume));
break;
case 4:
rate = *(float *)data;
if (vw && vw->mediaSeeking)
vw->mediaSeeking->SetRate(rate);
break;
}
}
void *AVI_Grabber::GetData(int dataIdx)
{
switch (dataIdx)
{
case 3:
return &volume;
case 4:
return &rate;
case 5:
vw->mediaPosition->get_CurrentPosition(&dCurrentTime);
return &dCurrentTime;
}
return NULL;
}
int AVI_Grabber::END()
{
if (vw)
{
vw->uninitVideo();
delete vw;
}
Image.Delete();
return 0;
}
#else
//**************************************************************
//**************************************************************
//**************************************************************
extern "C" {
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavcodec/avcodec.h"
};
typedef struct AVI_Video_Data_
{
int streamIndex;
uint8_t *pFrameBuffer;
AVFrame *pFrame;
AVFrame *pFrameMAP;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
} AVI_Video_Data;
#define NB_WHDR 2
#define WAVE_BUFFER_SIZE 1024*16
#define WAVE_AVI_BUFFER_SIZE 16*AVCODEC_MAX_AUDIO_FRAME_SIZE
typedef struct AVI_Audio_Data_
{
int streamIndex;
HWAVEOUT waveOut;
WAVEHDR waveHDR[NB_WHDR];
uint8_t *pAVBuffer;
char *pWaveBuffer;
int waveAVIBegin;
int waveAVIEnd;
int waveAVISize;
char *pWaveAVIBuffer;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
} AVI_Audio_Data;
unsigned __stdcall PlayThread(void *params);
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
class AVI_Grabber:public INPUT_IO
{
WORLD *pScn;
HRESULT hrinit;
DWORD sTimer;
double lastTime;
float volume;
//AVI_Redirect aviRedirect;
public:
AVFormatContext *pFormatCtx;
AVI_Video_Data vd;
AVI_Audio_Data sd;
int frameNew;
int processing;
HANDLE aviMutex;
public:
u32 UseAlphaMode;
int INIT (WORLD *pScene);
int FRAME ();
int END ();
IOTYPE GetType() {return IOtype_AVI;};
};
int AVI_Grabber::INIT(WORLD *pScene)
{
int allocSize;
int error;
av_register_all();
memset(&vd, 0, sizeof(vd));
memset(&sd, 0, sizeof(sd));
error = av_open_input_file(&pFormatCtx, (char *)MediaName, NULL, 0, NULL);
if (error < 0) return 0;
error = av_find_stream_info(pFormatCtx);
if (error < 0) return 0;
vd.streamIndex = -1;
for(int i=0; i<pFormatCtx->nb_streams && vd.streamIndex == -1; i++)
if(pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO)
vd.streamIndex=i;
if (vd.streamIndex < 0)
return 0;
sd.streamIndex = -1;
for(int i=0; i<pFormatCtx->nb_streams && sd.streamIndex == -1; i++)
if(pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
sd.streamIndex=i;
vd.pCodecCtx = pFormatCtx->streams[vd.streamIndex]->codec;
vd.pCodec = avcodec_find_decoder(vd.pCodecCtx->codec_id);
if (!vd.pCodec)
return 0;
if(vd.pCodec->capabilities & CODEC_CAP_TRUNCATED)
vd.pCodecCtx->flags |= CODEC_FLAG_TRUNCATED;
if (avcodec_open(vd.pCodecCtx, vd.pCodec) < 0)
return 0;
vd.pFrame = avcodec_alloc_frame();
allocSize = avpicture_get_size(vd.pCodecCtx->pix_fmt, vd.pCodecCtx->width, vd.pCodecCtx->height);
vd.pFrameBuffer = (uint8_t *)CC_malloc(allocSize);
avpicture_fill((AVPicture *)vd.pFrame,
vd.pFrameBuffer,
vd.pCodecCtx->pix_fmt,
vd.pCodecCtx->width, vd.pCodecCtx->height);
vd.pFrameMAP = avcodec_alloc_frame();
if (!vd.pFrame || !vd.pFrameMAP)
return 0;
Image.PITCH =
Image.SX = vd.pCodecCtx->width;
Image.SY = vd.pCodecCtx->height;
ulCurrentFrame = 0;
lTimeState = 0;
pScn = pScene;
allocSize = avpicture_get_size(PIX_FMT_RGB32, Image.SX , Image.SY);
Image.BASE = (s32 *)CC_malloc(allocSize);
avpicture_fill((AVPicture *)vd.pFrameMAP,
(uint8_t*)Image.BASE,
PIX_FMT_RGB32,
Image.SX, Image.SY);
if (Image.SX > 512)
{
Image.SX = 512;
Image.SY = ((Image.SX) * ((vd.pCodecCtx->height << 16) / vd.pCodecCtx->width)>>16);
}
if (!UseAlphaMode)
if (Image.SY > 512)
{
Image.SX = ((Image.SY) * ((vd.pCodecCtx->width << 16) / vd.pCodecCtx->height)>>16);
Image.SY = 512;
}
//CoUninitialize();
if (UseAlphaMode)
Image.SY >>= 1;
if (sd.streamIndex >= 0)
{
WAVEFORMATEX waveFormat;
memset(&waveFormat, 0, sizeof(waveFormat));
sd.pCodecCtx = pFormatCtx->streams[sd.streamIndex]->codec;
sd.pCodec = avcodec_find_decoder(sd.pCodecCtx->codec_id);
if (avcodec_open(sd.pCodecCtx, sd.pCodec) >= 0)
{
waveFormat.nSamplesPerSec = sd.pCodecCtx->sample_rate;
waveFormat.wBitsPerSample = 16; //sd.pCodecCtx->;
waveFormat.nChannels = sd.pCodecCtx->channels;
waveFormat.cbSize = 0; /* size of _extra_ info */
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample >> 3) * waveFormat.nChannels;
waveFormat.nAvgBytesPerSec = waveFormat.nBlockAlign * waveFormat.nSamplesPerSec;
memset(sd.waveHDR, 0, sizeof(sd.waveHDR));
sd.pAVBuffer = (uint8_t *)CC_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE);
sd.pWaveAVIBuffer = (char *)CC_malloc(WAVE_AVI_BUFFER_SIZE);
for (int uCurrentWHDR=0; uCurrentWHDR<NB_WHDR; uCurrentWHDR++)
{
sd.waveHDR[uCurrentWHDR].lpData = (char *)CC_malloc(WAVE_BUFFER_SIZE);
sd.waveHDR[uCurrentWHDR].dwBufferLength = WAVE_BUFFER_SIZE;
}
MMRESULT result = waveOutOpen(&sd.waveOut, WAVE_MAPPER, &waveFormat, (DWORD_PTR)waveOutProc, (DWORD_PTR)this, CALLBACK_FUNCTION);
for (int uCurrentWHDR=0; uCurrentWHDR<NB_WHDR; uCurrentWHDR++)
{
waveOutPrepareHeader( sd.waveOut, sd.waveHDR + uCurrentWHDR,sizeof(WAVEHDR) );
waveOutWrite( sd.waveOut, sd.waveHDR + uCurrentWHDR,sizeof(WAVEHDR) );
}
} else
{
sd.streamIndex = -1;
}
}
u32 threadID;
frameNew = 0;
processing = 1;
aviMutex = CreateMutex(NULL, FALSE, "AVIMutex");
_beginthreadex(NULL, 256, &PlayThread, (void *)this, 0, &threadID);
return 1;
}
int AVI_Grabber::END()
{
if (sd.waveOut)
{
waveOutPause( sd.waveOut );
waveOutClose( sd.waveOut );
}
processing = 0;
while (!processing)
Sleep(5);
if (vd.pFrameBuffer)
CC_free(vd.pFrameBuffer);
av_free(vd.pFrameMAP);
av_free(vd.pFrame);
// Close the codec
avcodec_close(vd.pCodecCtx);
// Close the video file
av_close_input_file(pFormatCtx);
return 1;
}
unsigned __stdcall PlayThread(void *params)
{
AVI_Grabber *pAviGrabber = (AVI_Grabber *)params;
AVPacket packet;
int bytesRemaining = 0;
int bytesDecoded = 0;
int got_picture = 0;
uint8_t *rawData = NULL;
u32 time = timeGetTime();
u32 currPacketTime = 0;
u32 frameTime = 40;
int sleepTime = 0;
int frameSize = 0;
packet.data = NULL;
while (pAviGrabber->processing)
{
switch (pAviGrabber->lTimeState)
{
case 0:
Sleep(10);
time = timeGetTime() - currPacketTime;
continue;
case 255:
av_seek_frame(pAviGrabber->pFormatCtx, -1, 0, AVSEEK_FLAG_ANY);
pAviGrabber->lTimeState = 0;
currPacketTime = 0;
time = timeGetTime();
continue;
}
if ((rawData || av_read_frame(pAviGrabber->pFormatCtx, &packet) >= 0) &&
(WAIT_OBJECT_0 == WaitForSingleObject(pAviGrabber->aviMutex, INFINITE)))
{
if (packet.stream_index == pAviGrabber->vd.streamIndex)
{
if (!rawData)
{
rawData = packet.data;
bytesRemaining = packet.size;
}
got_picture = 0;
bytesDecoded = avcodec_decode_video( pAviGrabber->vd.pCodecCtx, pAviGrabber->vd.pFrame,
&got_picture, rawData, bytesRemaining);
//if (bytesDecoded > bytesRemaining)
//{
// bytesDecoded = avcodec_decode_video( pAviGrabber->vd.pCodecCtx, pAviGrabber->vd.pFrame,
// &got_picture, rawData, bytesRemaining);
//}
bytesRemaining -= bytesDecoded;
if (bytesRemaining > 0)
rawData += bytesDecoded;
else
rawData = NULL;
if (got_picture)
{
currPacketTime = 1000 * /*packet.pts*/packet.dts * pAviGrabber->pFormatCtx->streams[pAviGrabber->vd.streamIndex]->time_base.num / pAviGrabber->pFormatCtx->streams[pAviGrabber->vd.streamIndex]->time_base.den;
sleepTime = currPacketTime - (timeGetTime() - time);
}
if (!rawData)
av_free_packet(&packet);
} else if (packet.stream_index == pAviGrabber->sd.streamIndex)
{
frameSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
avcodec_decode_audio2( pAviGrabber->sd.pCodecCtx, (int16_t *)pAviGrabber->sd.pAVBuffer,
&frameSize, packet.data, packet.size);
currPacketTime = 1000 * /*packet.pts*/packet.dts * pAviGrabber->pFormatCtx->streams[pAviGrabber->sd.streamIndex]->time_base.num / pAviGrabber->pFormatCtx->streams[pAviGrabber->sd.streamIndex]->time_base.den;
sleepTime = currPacketTime - (timeGetTime() - time);
int copySize = min(frameSize, WAVE_BUFFER_SIZE);
if (pAviGrabber->sd.waveAVIEnd + frameSize > WAVE_AVI_BUFFER_SIZE)
{
int copySize1 = WAVE_AVI_BUFFER_SIZE - pAviGrabber->sd.waveAVIEnd;
memcpy( pAviGrabber->sd.pWaveAVIBuffer + pAviGrabber->sd.waveAVIEnd,
pAviGrabber->sd.pAVBuffer,
copySize1);
memcpy( pAviGrabber->sd.pWaveAVIBuffer,
pAviGrabber->sd.pAVBuffer + copySize1,
frameSize - copySize1);
pAviGrabber->sd.waveAVIEnd = frameSize - copySize1;
} else
{
memcpy( pAviGrabber->sd.pWaveAVIBuffer + pAviGrabber->sd.waveAVIEnd,
pAviGrabber->sd.pAVBuffer,
frameSize);
pAviGrabber->sd.waveAVIEnd += frameSize;
}
pAviGrabber->sd.waveAVISize += frameSize;
av_free_packet(&packet);
} else
av_free_packet(&packet);
ReleaseMutex(pAviGrabber->aviMutex);
}
if (sleepTime > 0)
{
Sleep(sleepTime);
sleepTime = 0;
}
if (got_picture)
{
pAviGrabber->frameNew = 1;
got_picture = 0;
}
}
pAviGrabber->processing = -1;
return 0;
}
void CALLBACK waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
AVI_Grabber *pAviGrabber = (AVI_Grabber *)dwInstance;
if(uMsg != WOM_DONE)
return;
for ( int uCurrentWHDR = 0; uCurrentWHDR< NB_WHDR; uCurrentWHDR++)
{
if (pAviGrabber->sd.waveHDR[uCurrentWHDR].dwFlags & WHDR_DONE )
{
int copySize = min(pAviGrabber->sd.waveAVISize, WAVE_BUFFER_SIZE);
if (pAviGrabber->sd.waveAVIBegin + copySize > WAVE_AVI_BUFFER_SIZE)
{
int copySize1 = WAVE_AVI_BUFFER_SIZE - pAviGrabber->sd.waveAVIBegin;
memcpy( pAviGrabber->sd.waveHDR[uCurrentWHDR].lpData,
pAviGrabber->sd.pWaveAVIBuffer + pAviGrabber->sd.waveAVIBegin,
copySize1);
memcpy( pAviGrabber->sd.waveHDR[uCurrentWHDR].lpData + copySize1,
pAviGrabber->sd.pWaveAVIBuffer,
copySize - copySize1);
pAviGrabber->sd.waveAVIBegin = copySize - copySize1;
} else
{
memcpy( pAviGrabber->sd.waveHDR[uCurrentWHDR].lpData,
pAviGrabber->sd.pWaveAVIBuffer + pAviGrabber->sd.waveAVIBegin,
copySize );
pAviGrabber->sd.waveAVIBegin += copySize;
}
pAviGrabber->sd.waveAVISize -= copySize;
pAviGrabber->sd.waveHDR[uCurrentWHDR].dwBufferLength = copySize;
// Cas g<>n<EFBFBD>rique
waveOutUnprepareHeader ( hWaveOut, pAviGrabber->sd.waveHDR + uCurrentWHDR,sizeof(WAVEHDR) );
pAviGrabber->sd.waveHDR[uCurrentWHDR].dwFlags = 0;
waveOutPrepareHeader ( hWaveOut, pAviGrabber->sd.waveHDR + uCurrentWHDR,sizeof(WAVEHDR) );
waveOutWrite ( hWaveOut, pAviGrabber->sd.waveHDR + uCurrentWHDR,sizeof(WAVEHDR) );
}
}
}
int AVI_Grabber::FRAME()
{
if (!frameNew)
return 0;
if (WAIT_OBJECT_0 == WaitForSingleObject(aviMutex, INFINITE))
{
img_convert((AVPicture *)vd.pFrameMAP, PIX_FMT_RGB32,
(AVPicture *)vd.pFrame, vd.pCodecCtx->pix_fmt, vd.pCodecCtx->width, vd.pCodecCtx->height);
if (vd.pCodecCtx->width > Image.SX)
{
int i,j,IncX,AddX,IncY,AddY;
unsigned long *SRC_512,*SRC_640,*SRC_LAST;
SRC_512 = SRC_640 = (unsigned long*)Image.BASE;
IncY = 0;
AddY = (Image.SY<<16) / vd.pCodecCtx->height;
AddX = (Image.SX<<16) / vd.pCodecCtx->width;
for (i = 0,j = 0 ; i < vd.pCodecCtx->height; i++)
{
SRC_LAST = SRC_640 + vd.pCodecCtx->width;
IncX = 0;
while (SRC_640 < SRC_LAST)
{
*(SRC_512+(IncX>>16)) = *(SRC_640++);
IncX += AddX;
}
IncY += AddY;
SRC_512 = (unsigned long*)Image.BASE + ((IncY>>16) * Image.SX);
j++;
}
}
FLIP_V(&Image);
if (UseAlphaMode)
{
s32 *pAlpha,*pRGB,*pLast;
pAlpha = Image.BASE;
pRGB = Image.BASE + (Image.SY>>1) * Image.PITCH;
pLast = Image.BASE + Image.SY * Image.PITCH;
while (pRGB < pLast)
{
*(u32*)pAlpha <<= 24;
*pAlpha |= *pRGB & 0xffffff;
pAlpha++;
pRGB++;
}//*/
}
frameNew = 0;
ReleaseMutex(aviMutex);
}
return 1;
}
//*/
#endif
//*********************************************
//*********************************************
INPUT_IO *AVI_CREATE(WORLD *pScene,char *Filename)
{
INPUT_IO *Ret;
u32 Alpha;
Alpha = 0;
if (GetFileNameExtend((char *)Filename) == 'AVIA' || GetFileNameExtend((char *)Filename) == 'WMVA')
{
Filename[strlen((char*)Filename)-1] = 0;
Alpha = 1;
}
if (!IsExist(Filename) && strnicmp(Filename, "http://", 7))
{
Ret = ERROR_CREATE("Avi not\nexist");
return Ret;
}
Ret = (INPUT_IO *)new(AVI_Grabber);
strcpy((char*)Ret->MediaName,Filename);
((AVI_Grabber*)Ret)->UseAlphaMode = Alpha;
if (!Ret->INIT(pScene))
{
delete Ret;
return NULL;
}
//Ret->FRAME();
return Ret;
}