WiiFlow_Lite/source/gui/WiiMovie.cpp
2012-07-05 14:27:05 +00:00

352 lines
8.0 KiB
C++

/***************************************************************************
* Copyright (C) 2010
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* WiiMovie.cpp
*
* for WiiXplorer 2010
***************************************************************************/
#include <unistd.h>
#include <asndlib.h>
#include "wiiuse/wpad.h"
#include "WiiMovie.hpp"
#include "gecko.h"
#include "mem2.hpp"
#define SND_BUFFERS 8
#define FRAME_BUFFERS 8
static BufferCircle * soundBuffer = NULL;
WiiMovie::WiiMovie(const char * filepath)
{
VideoFrameCount = 0;
fps = 0.0f;
ExitRequested = false;
fullScreen = false;
Playing = false;
volume = 128;
ThreadStack = NULL;
PlayThread = LWP_THREAD_NULL;
gprintf("Opening video '%s'\n", filepath);
string file(filepath);
Video = openVideo(file);
if(!Video)
{
gprintf("Open video failed\n");
ExitRequested = true;
return;
}
SndChannels = (Video->getNumChannels() == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT;
SndFrequence = Video->getFrequency();
fps = Video->getFps();
maxSoundSize = Video->getMaxAudioSamples()*Video->getNumChannels()*2;
gprintf("Open video succeeded: sound channels: %d, Frequency: %d, FPS: %4.3f\n", SndChannels, SndFrequence, fps);
if (Video->hasSound())
{
gprintf("Video has sound\n");
soundBuffer = &SoundBuffer;
soundBuffer->Resize(SND_BUFFERS);
soundBuffer->SetBufferBlockSize(maxSoundSize * FRAME_BUFFERS);
}
PlayThreadStack = NULL;
ThreadStack = (u8 *)MEM2_alloc(32768);
if (!ThreadStack)
return;
LWP_MutexInit(&mutex, true);
LWP_CreateThread (&ReadThread, UpdateThread, this, ThreadStack, 32768, LWP_PRIO_HIGHEST);
gprintf("Reading frames thread started\n");
}
WiiMovie::~WiiMovie()
{
gprintf("Destructing WiiMovie object\n");
Playing = true;
ExitRequested = true;
Stop();
LWP_ResumeThread(ReadThread);
LWP_JoinThread(ReadThread, NULL);
LWP_MutexDestroy(mutex);
ASND_StopVoice(10);
if (ReadThread != LWP_THREAD_NULL)
{
LWP_ResumeThread(ReadThread);
LWP_JoinThread(ReadThread, NULL);
}
if (mutex != LWP_MUTEX_NULL)
{
LWP_MutexUnlock(mutex);
LWP_MutexDestroy(mutex);
}
if (ThreadStack != NULL)
{
MEM2_free(ThreadStack);
ThreadStack = NULL;
}
soundBuffer = NULL;
Frames.clear();
if(Video)
closeVideo(Video);
}
bool WiiMovie::Play(bool loop)
{
if(!Video) return false;
gprintf("Start playing video\n");
PlayThreadStack = (u8 *)MEM2_alloc(32768);
if (PlayThreadStack == NULL)
return false;
Playing = true;
PlayTime.reset();
Video->loop = loop;
gprintf("Start playing thread\n");
LWP_ResumeThread(ReadThread);
LWP_CreateThread(&PlayThread, PlayingThread, this, PlayThreadStack, 32768, 70);
return true;
}
void WiiMovie::Stop()
{
gprintf("Stopping WiiMovie video\n");
ExitRequested = true;
if (PlayThread != LWP_THREAD_NULL)
LWP_JoinThread(PlayThread, NULL);
PlayThread = LWP_THREAD_NULL;
gprintf("Playing thread stopped\n");
if(PlayThreadStack != NULL)
MEM2_free(PlayThreadStack);
}
void WiiMovie::SetVolume(int vol)
{
volume = 255 * vol/100;
ASND_ChangeVolumeVoice(10, volume, volume);
}
void WiiMovie::SetScreenSize(int width, int height, int top, int left)
{
screenwidth = width;
screenheight = height;
screenleft = left;
screentop = top;
}
void WiiMovie::SetFullscreen()
{
if(!Video) return;
float newscale = 1000.0f;
float vidwidth = (float) width * 1.0f;
float vidheight = (float) height * 1.0f;
int retries = 100;
fullScreen = true;
while(vidheight * newscale > screenheight || vidwidth * newscale > screenwidth)
{
if(vidheight * newscale > screenheight)
newscale = screenheight/vidheight;
if(vidwidth * newscale > screenwidth)
newscale = screenwidth/vidwidth;
retries--;
if(retries == 0)
{
newscale = 1.0f;
break;
}
}
scaleX = scaleY = newscale;
}
void WiiMovie::SetFrameSize(int w, int h)
{
if(!Video) return;
scaleX = (float) w /(float) width;
scaleY = (float) h /(float) height;
}
void WiiMovie::SetAspectRatio(float Aspect)
{
if(!Video) return;
float vidwidth = (float) height*scaleY*Aspect;
scaleX = (float) width/vidwidth;
}
extern "C" void THPSoundCallback(int voice)
{
if (!soundBuffer || !soundBuffer->IsBufferReady()) return;
if(ASND_AddVoice(voice, soundBuffer->GetBuffer(), soundBuffer->GetBufferSize()) != SND_OK)
return;
soundBuffer->LoadNext();
}
void WiiMovie::FrameLoadLoop()
{
while (!ExitRequested)
{
LoadNextFrame();
while (Frames.size() > FRAME_BUFFERS && !ExitRequested)
usleep(100);
}
}
void * WiiMovie::UpdateThread(void *arg)
{
WiiMovie *movie = static_cast<WiiMovie *>(arg);
while (!movie->ExitRequested)
{
movie->ReadNextFrame();
usleep(100);
}
return NULL;
}
void * WiiMovie::PlayingThread(void *arg)
{
WiiMovie *movie = static_cast<WiiMovie *>(arg);
movie->FrameLoadLoop();
return NULL;
}
void WiiMovie::ReadNextFrame()
{
if(!Playing) LWP_SuspendThread(ReadThread);
u32 FramesNeeded = (u32) (PlayTime.elapsed()*fps);
gprintf("Reading needed frames: %d\n", FramesNeeded);
while(VideoFrameCount < FramesNeeded)
{
LWP_MutexLock(mutex);
Video->loadNextFrame();
LWP_MutexUnlock(mutex);
++VideoFrameCount;
gprintf("Loaded video frame: %d\n", VideoFrameCount);
if(Video->hasSound())
{
u32 newWhich = SoundBuffer.Which();
int i = 0;
for (i = 0; i < SoundBuffer.Size()-2; ++i)
{
if (!SoundBuffer.IsBufferReady(newWhich)) break;
newWhich = (newWhich + 1) % SoundBuffer.Size();
}
if (i == SoundBuffer.Size() - 2) return;
int currentSize = SoundBuffer.GetBufferSize(newWhich);
currentSize += Video->getCurrentBuffer((s16 *) (&SoundBuffer.GetBuffer(newWhich)[currentSize]))*SndChannels*2;
SoundBuffer.SetBufferSize(newWhich, currentSize);
if(currentSize >= (FRAME_BUFFERS-1)*maxSoundSize)
SoundBuffer.SetBufferReady(newWhich, true);
if(ASND_StatusVoice(10) == SND_UNUSED && SoundBuffer.IsBufferReady())
{
ASND_StopVoice(10);
ASND_SetVoice(10, SndChannels == 2 ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT, SndFrequence, 0, SoundBuffer.GetBuffer(), SoundBuffer.GetBufferSize(), volume, volume, THPSoundCallback);
SoundBuffer.LoadNext();
}
}
}
// usleep(100);
}
void WiiMovie::LoadNextFrame()
{
if(!Video || !Playing)
{
return;
}
VideoFrame VideoF;
LWP_MutexLock(mutex);
Video->getCurrentFrame(VideoF);
LWP_MutexUnlock(mutex);
if(!VideoF.getData()) return;
if(width != VideoF.getWidth())
{
width = VideoF.getWidth();
height = VideoF.getHeight();
if (fullScreen)
SetFullscreen();
else
{
// Calculate new top and left
screenleft = (screenwidth - width) / 2;
screentop = (screenheight - height) / 2;
}
}
STexture frame;
if (frame.fromRAW(VideoF.getData(), VideoF.getWidth(), VideoF.getHeight()) == STexture::TE_OK)
Frames.push_back(frame);
}
bool WiiMovie::GetNextFrame(STexture *tex)
{
if (!Video || Frames.size() == 0) return false;
*tex = Frames.at(0);
Frames.erase(Frames.begin());
return true;
}