2020-09-20 20:21:36 +02:00
|
|
|
|
/*
|
|
|
|
|
* player.h
|
|
|
|
|
*
|
|
|
|
|
* Created on: 8 мая 2018 г.
|
|
|
|
|
* Author: olegvedi@gmail.com
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <SDL.h>
|
|
|
|
|
#include <AL/al.h>
|
|
|
|
|
|
|
|
|
|
extern "C"
|
|
|
|
|
{
|
|
|
|
|
#include <jpeglib.h>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "EMULATOR_PRIVATE.H"
|
|
|
|
|
|
|
|
|
|
#include "ReadAVI.h"
|
|
|
|
|
|
|
|
|
|
#include "DRIVER2.H"
|
|
|
|
|
#include "C\PAD.H"
|
2020-09-20 21:51:46 +02:00
|
|
|
|
#include "C\SYSTEM.H"
|
|
|
|
|
#include "C\E3STUFF.H"
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
|
|
|
|
int UnpackJPEG(unsigned char* src_buf, unsigned src_length, unsigned bpp, unsigned char* dst_buf)
|
|
|
|
|
{
|
|
|
|
|
// it's rough but it works...
|
|
|
|
|
jpeg_decompress_struct cinfo;
|
|
|
|
|
jpeg_error_mgr jerr;
|
|
|
|
|
|
|
|
|
|
cinfo.err = jpeg_std_error(&jerr);
|
|
|
|
|
jpeg_create_decompress(&cinfo);
|
|
|
|
|
|
|
|
|
|
jpeg_mem_src(&cinfo, src_buf, src_length);
|
|
|
|
|
jpeg_read_header(&cinfo, TRUE);
|
|
|
|
|
jpeg_start_decompress(&cinfo);
|
|
|
|
|
|
|
|
|
|
for (u_char* curr_scanline = dst_buf; cinfo.output_scanline < cinfo.output_height; curr_scanline += cinfo.output_width * cinfo.num_components)
|
|
|
|
|
{
|
|
|
|
|
jpeg_read_scanlines(&cinfo, &curr_scanline, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jpeg_finish_decompress(&cinfo);
|
|
|
|
|
jpeg_destroy_decompress(&cinfo);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// emulator window TODO: interface
|
|
|
|
|
extern SDL_Window* g_window;
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
void SetupMovieRectangle(ReadAVI::stream_format_t& strFmt)
|
2020-09-20 20:21:36 +02:00
|
|
|
|
{
|
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
|
Emulator_GetScreenSize(windowWidth, windowHeight);
|
|
|
|
|
|
|
|
|
|
float psxScreenW = 320.0f;
|
|
|
|
|
float psxScreenH = 240.0f;
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
RECT16 rect;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
rect.x = 0;
|
|
|
|
|
rect.y = (psxScreenH - strFmt.image_height) / 2;
|
|
|
|
|
rect.w = strFmt.image_width;
|
|
|
|
|
rect.h = strFmt.image_height;
|
|
|
|
|
|
|
|
|
|
const float video_aspect = float(strFmt.image_width) / float(strFmt.image_height + 48);
|
|
|
|
|
float emuScreenAspect = float(windowHeight) / float(windowWidth);
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
// first map to 0..1
|
|
|
|
|
float clipRectX = (float)(rect.x - activeDispEnv.disp.x) / psxScreenW;
|
|
|
|
|
float clipRectY = (float)(rect.y - activeDispEnv.disp.y) / psxScreenH;
|
|
|
|
|
float clipRectW = (float)(rect.w) / psxScreenW;
|
|
|
|
|
float clipRectH = (float)(rect.h) / psxScreenH;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
// then map to screen
|
|
|
|
|
clipRectY -= 1.0f;
|
|
|
|
|
clipRectX -= 1.0f;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
clipRectY /= video_aspect * emuScreenAspect;
|
|
|
|
|
clipRectH /= emuScreenAspect * video_aspect;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
clipRectW *= 2.0f;
|
|
|
|
|
clipRectH *= 2.0f;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
clipRectY += 0.10f;
|
|
|
|
|
|
|
|
|
|
u_char l = 0;
|
|
|
|
|
u_char t = 0;
|
|
|
|
|
u_char r = 1;
|
|
|
|
|
u_char b = 1;
|
|
|
|
|
|
|
|
|
|
Vertex blit_vertices[] =
|
|
|
|
|
{
|
|
|
|
|
{ clipRectX+ clipRectW, clipRectY + clipRectH, 0, 0, r, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
{ clipRectX, clipRectY, 0, 0, l, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
{ clipRectX, clipRectY + clipRectH, 0, 0, l, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
|
|
|
|
|
{ clipRectX + clipRectW, clipRectY, 0, 0, r, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
{ clipRectX, clipRectY, 0, 0, l, b, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
{ clipRectX + clipRectW, clipRectY + clipRectH, 0, 0, r, t, 0, 0, 0, 0, 0, 0 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Emulator_UpdateVertexBuffer(blit_vertices, 6);
|
2020-09-20 20:21:36 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// send audio buffer
|
|
|
|
|
void QueueAudioBuffer(ALuint buffer, ALuint source, ReadAVI::frame_entry_t& frame_entry, ReadAVI::stream_format_auds_t& audio_format, int frame_offset, int frame_size)
|
|
|
|
|
{
|
|
|
|
|
ALenum alFormat;
|
|
|
|
|
|
|
|
|
|
if (audio_format.block_size_of_data == 8)
|
|
|
|
|
alFormat = audio_format.channels == 2 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;
|
|
|
|
|
else if (audio_format.block_size_of_data == 16)
|
|
|
|
|
alFormat = audio_format.channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
|
|
|
|
|
else
|
|
|
|
|
alFormat = AL_FORMAT_MONO16;
|
|
|
|
|
|
|
|
|
|
// upload to specific buffer
|
|
|
|
|
alBufferData(buffer, alFormat, frame_entry.buf + frame_offset, frame_size, audio_format.samples_per_second);
|
|
|
|
|
|
|
|
|
|
// queue after uploading
|
|
|
|
|
alSourceQueueBuffers(source, 1, &buffer);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
const char* fmv_shader =
|
|
|
|
|
"varying vec4 v_texcoord;\n"
|
|
|
|
|
"#ifdef VERTEX\n"
|
|
|
|
|
" attribute vec4 a_position;\n"
|
|
|
|
|
" attribute vec4 a_texcoord;\n"
|
|
|
|
|
" void main() {\n"
|
|
|
|
|
" v_texcoord = a_texcoord;\n"
|
|
|
|
|
" gl_Position = vec4(a_position.xy, 0.0, 1.0);\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"#else\n"
|
|
|
|
|
" uniform sampler2D s_texture;\n"
|
|
|
|
|
" void main() {\n"
|
|
|
|
|
" fragColor = texture2D(s_texture, v_texcoord.xy);\n"
|
|
|
|
|
" }\n"
|
|
|
|
|
"#endif\n";
|
|
|
|
|
|
|
|
|
|
TextureID g_FMVTexture = 0;
|
|
|
|
|
ShaderID g_FMVShader = 0;
|
|
|
|
|
|
|
|
|
|
extern void Shader_CheckShaderStatus(GLuint shader);
|
|
|
|
|
extern void Shader_CheckProgramStatus(GLuint program);
|
|
|
|
|
extern ShaderID Shader_Compile(const char* source);
|
|
|
|
|
extern void Emulator_SetShader(const ShaderID& shader);
|
|
|
|
|
|
|
|
|
|
void FMVPlayerInitGL()
|
|
|
|
|
{
|
|
|
|
|
#if defined(OGL) || defined(OGLES)
|
|
|
|
|
glGenTextures(1, &g_FMVTexture);
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, g_FMVTexture);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 320, 240, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
|
|
|
|
|
if(!g_FMVShader)
|
|
|
|
|
g_FMVShader = Shader_Compile(fmv_shader);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FMVPlayerShutdownGL()
|
|
|
|
|
{
|
|
|
|
|
Emulator_DestroyTexture(g_FMVTexture);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrawFrame(ReadAVI::stream_format_t& stream_format)
|
|
|
|
|
{
|
|
|
|
|
int windowWidth, windowHeight;
|
|
|
|
|
Emulator_GetScreenSize(windowWidth, windowHeight);
|
|
|
|
|
|
|
|
|
|
Emulator_BeginScene();
|
|
|
|
|
|
|
|
|
|
Emulator_Clear(0, 0, windowWidth, windowHeight, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, g_FMVTexture);
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, stream_format.image_width, stream_format.image_height, 0, GL_RGB, GL_UNSIGNED_BYTE, _frontend_buffer);
|
|
|
|
|
|
|
|
|
|
Emulator_SetViewPort(0, 0, windowWidth, windowHeight);
|
|
|
|
|
Emulator_SetTexture(g_FMVTexture, (TexFormat)-1);
|
|
|
|
|
Emulator_SetShader(g_FMVShader);
|
|
|
|
|
|
|
|
|
|
SetupMovieRectangle(stream_format);
|
|
|
|
|
|
|
|
|
|
Emulator_SetBlendMode(BM_NONE);
|
|
|
|
|
Emulator_DrawTriangles(0, 2);
|
|
|
|
|
|
|
|
|
|
Emulator_EndScene();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 20:21:36 +02:00
|
|
|
|
void DoPlayFMV(RENDER_ARG* arg, int subtitles)
|
|
|
|
|
{
|
|
|
|
|
int fd = arg->render - (arg->render % 10);
|
|
|
|
|
if (fd > 0)
|
|
|
|
|
fd /= 10;
|
|
|
|
|
|
|
|
|
|
char filename[250];
|
|
|
|
|
sprintf(filename, "DRIVER2\\FMV\\%d\\RENDER%d.STR[0].AVI", fd, arg->render);
|
|
|
|
|
|
|
|
|
|
ReadAVI readAVI(filename);
|
|
|
|
|
|
|
|
|
|
int indexes = readAVI.GetIndexCnt();
|
|
|
|
|
printf("indexes:%d\n", indexes);
|
|
|
|
|
|
|
|
|
|
ReadAVI::avi_header_t avi_header = readAVI.GetAviHeader();
|
|
|
|
|
|
|
|
|
|
ReadAVI::stream_format_t stream_format = readAVI.GetVideoFormat();
|
|
|
|
|
ReadAVI::stream_format_auds_t audio_format = readAVI.GetAudioFormat();
|
|
|
|
|
|
|
|
|
|
printf("video stream compression: %s\n", stream_format.compression_type);
|
|
|
|
|
printf("audio stream format: %d\n", audio_format.format);
|
|
|
|
|
|
|
|
|
|
if (strcmp(stream_format.compression_type, "MJPG")) {
|
|
|
|
|
printf("Only MJPG supported\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Surface* BufSurface = SDL_CreateRGBSurface(0, stream_format.image_width, stream_format.image_height, stream_format.bits_per_pixel, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
|
|
ReadAVI::frame_entry_t frame_entry;
|
|
|
|
|
frame_entry.type = (ReadAVI::chunk_type_t)(ReadAVI::ctype_video_data | ReadAVI::ctype_audio_data);
|
|
|
|
|
frame_entry.pointer = 0;
|
|
|
|
|
|
|
|
|
|
ALuint audioStreamSource;
|
|
|
|
|
ALuint audioStreamBuffers[4];
|
|
|
|
|
|
|
|
|
|
alGenSources(1, &audioStreamSource);
|
|
|
|
|
alGenBuffers(4, audioStreamBuffers);
|
|
|
|
|
alSourcei(audioStreamSource, AL_LOOPING, AL_FALSE);
|
|
|
|
|
|
|
|
|
|
int nextTime = SDL_GetTicks();
|
|
|
|
|
|
|
|
|
|
int frame_size;
|
|
|
|
|
int queue_counter = 0;
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
if (SDL_GetTicks() < nextTime) // wait for frame
|
|
|
|
|
{
|
|
|
|
|
Emulator_EndScene();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
frame_entry.type = (ReadAVI::chunk_type_t)(ReadAVI::ctype_video_data | ReadAVI::ctype_audio_data);
|
|
|
|
|
frame_size = readAVI.GetFrameFromIndex(&frame_entry);
|
|
|
|
|
|
|
|
|
|
// done, no frames
|
|
|
|
|
if (frame_size < 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (frame_size > 0)
|
|
|
|
|
{
|
|
|
|
|
if (frame_entry.type == ReadAVI::ctype_compressed_video_frame)
|
|
|
|
|
{
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
int ret = UnpackJPEG(frame_entry.buf, frame_size, stream_format.bits_per_pixel, (unsigned char*)_frontend_buffer);
|
2020-09-20 20:21:36 +02:00
|
|
|
|
if (ret == 0)
|
2020-09-20 21:51:46 +02:00
|
|
|
|
DrawFrame(stream_format);
|
2020-09-20 20:21:36 +02:00
|
|
|
|
|
|
|
|
|
// set next step time
|
2020-09-20 21:51:46 +02:00
|
|
|
|
nextTime = SDL_GetTicks() + (avi_header.TimeBetweenFrames-10000) / 1000;
|
2020-09-20 20:21:36 +02:00
|
|
|
|
}
|
|
|
|
|
else if (frame_entry.type == ReadAVI::ctype_audio_data)
|
|
|
|
|
{
|
|
|
|
|
// Update audio buffer
|
|
|
|
|
ALint state;
|
|
|
|
|
alGetSourcei(audioStreamSource, AL_SOURCE_STATE, &state);
|
|
|
|
|
|
|
|
|
|
int numProcessed = 0;
|
|
|
|
|
alGetSourcei(audioStreamSource, AL_BUFFERS_PROCESSED, &numProcessed);
|
|
|
|
|
|
|
|
|
|
if (state == AL_STOPPED || numProcessed > 0)
|
|
|
|
|
{
|
|
|
|
|
ALuint qbuffer;
|
|
|
|
|
|
|
|
|
|
// stop queued
|
|
|
|
|
if (state == AL_STOPPED)
|
|
|
|
|
{
|
|
|
|
|
alGetSourcei(audioStreamSource, AL_BUFFERS_QUEUED, &numProcessed);
|
|
|
|
|
|
|
|
|
|
// dequeue all buffers for restarting
|
|
|
|
|
while (numProcessed--)
|
|
|
|
|
alSourceUnqueueBuffers(audioStreamSource, 1, &qbuffer);
|
|
|
|
|
|
|
|
|
|
// restart
|
|
|
|
|
queue_counter = 0;
|
|
|
|
|
}
|
|
|
|
|
else if(numProcessed && queue_counter >= 4)
|
|
|
|
|
{
|
|
|
|
|
// dequeue one buffer
|
|
|
|
|
alSourceUnqueueBuffers(audioStreamSource, 1, &qbuffer);
|
|
|
|
|
QueueAudioBuffer(qbuffer, audioStreamSource, frame_entry, audio_format, 0, frame_size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// for starting only
|
|
|
|
|
if (queue_counter < 4)
|
|
|
|
|
QueueAudioBuffer(audioStreamBuffers[queue_counter++], audioStreamSource, frame_entry, audio_format, 0, frame_size);
|
|
|
|
|
|
|
|
|
|
if(queue_counter > 0 && state != AL_PLAYING)
|
|
|
|
|
alSourcePlay(audioStreamSource);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReadControllers();
|
|
|
|
|
|
|
|
|
|
if (Pads[0].mapnew > 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
alDeleteSources(1, &audioStreamSource);
|
|
|
|
|
alDeleteBuffers(4, audioStreamBuffers);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FMV main function
|
|
|
|
|
int FMV_main(RENDER_ARGS* args)
|
|
|
|
|
{
|
2020-09-20 21:51:46 +02:00
|
|
|
|
FMVPlayerInitGL();
|
|
|
|
|
|
2020-09-20 20:21:36 +02:00
|
|
|
|
for (int i = 0; i < args->nRenders; i++)
|
|
|
|
|
{
|
|
|
|
|
DoPlayFMV(&args->Args[i], args->subtitle);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-20 21:51:46 +02:00
|
|
|
|
FMVPlayerShutdownGL();
|
|
|
|
|
|
2020-09-20 20:21:36 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|