mirror of
https://github.com/OpenDriver2/REDRIVER2.git
synced 2024-11-21 18:02:43 +01:00
- AVI (MJPEG) video player prototype for FMV
This commit is contained in:
parent
4e1af5f216
commit
20d1a3738f
@ -380,6 +380,11 @@ void SpuInit(void)
|
||||
_SpuInit(0);
|
||||
}
|
||||
|
||||
void SpuQuit(void)
|
||||
{
|
||||
// do nothing!
|
||||
}
|
||||
|
||||
void UpdateVoiceSample(SPUVoice& voice)
|
||||
{
|
||||
//if (!voice.sampledirty)
|
||||
|
@ -155,10 +155,11 @@ void PlayFMV(unsigned char render)
|
||||
/* end block 3 */
|
||||
// End Line: 223
|
||||
|
||||
extern int FMV_main(RENDER_ARGS* args);
|
||||
|
||||
// [D] [A]
|
||||
void PlayRender(RENDER_ARGS *args)
|
||||
{
|
||||
#ifdef PSX
|
||||
static unsigned long oldsp;
|
||||
|
||||
StopAllChannels();
|
||||
@ -169,7 +170,7 @@ void PlayRender(RENDER_ARGS *args)
|
||||
args->screenx = draw_mode_pal.framex;
|
||||
args->screeny = draw_mode_pal.framey;
|
||||
args->subtitle = gSubtitles;
|
||||
|
||||
#ifdef PSX
|
||||
if (Loadfile("FMV\\FMV.EXE", &DAT_800ff800) != 0)
|
||||
{
|
||||
oldsp = GetSp();
|
||||
@ -179,11 +180,11 @@ void PlayRender(RENDER_ARGS *args)
|
||||
Exec(&DAT_800ff810, 1, args);
|
||||
SetSp(oldsp);
|
||||
}
|
||||
|
||||
ReInitSystem();
|
||||
#else
|
||||
// TODO: use jpsx?
|
||||
FMV_main(args);
|
||||
#endif
|
||||
ReInitSystem();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
-- premake5.lua
|
||||
|
||||
-- you can redefine dependencies
|
||||
local SDL2_DIR = os.getenv("SDL2_DIR") or "dependencies/SDL2"
|
||||
local GLEW_DIR = os.getenv("GLEW_DIR") or "dependencies/glew"
|
||||
local OPENAL_DIR = os.getenv("OPENAL_DIR") or "dependencies/openal-soft"
|
||||
local GAME_REGION = os.getenv("GAME_REGION") or "NTSC_VERSION" -- or PAL_VERSION
|
||||
SDL2_DIR = os.getenv("SDL2_DIR") or "dependencies/SDL2"
|
||||
GLEW_DIR = os.getenv("GLEW_DIR") or "dependencies/glew"
|
||||
OPENAL_DIR = os.getenv("OPENAL_DIR") or "dependencies/openal-soft"
|
||||
JPEG_DIR = os.getenv("JPEG_DIR") or "dependencies/jpeg"
|
||||
|
||||
GAME_REGION = os.getenv("GAME_REGION") or "NTSC_VERSION" -- or PAL_VERSION
|
||||
|
||||
if not (GAME_REGION == "NTSC_VERSION" or GAME_REGION == "PAL_VERSION") then
|
||||
error("'GAME_REGION' should be 'NTSC_VERSION' or 'PAL_VERSION'")
|
||||
@ -28,6 +30,8 @@ workspace "REDRIVER2"
|
||||
defines {
|
||||
"NDEBUG",
|
||||
}
|
||||
|
||||
dofile("premake_libjpeg.lua")
|
||||
|
||||
-- EMULATOR layer
|
||||
project "PSX"
|
||||
@ -110,9 +114,10 @@ project "REDRIVER2"
|
||||
SDL2_DIR.."/include",
|
||||
GLEW_DIR.."/include",
|
||||
OPENAL_DIR.."/include",
|
||||
JPEG_DIR.."/",
|
||||
}
|
||||
|
||||
links { "PSX" } -- only need to link emulator
|
||||
links { "PSX", "jpeg" } -- only need to link emulator
|
||||
|
||||
linkoptions {
|
||||
"/SAFESEH:NO", -- Image Has Safe Exception Handers: No. Because of openal-soft
|
||||
|
58
src_rebuild/premake_libjpeg.lua
Normal file
58
src_rebuild/premake_libjpeg.lua
Normal file
@ -0,0 +1,58 @@
|
||||
project "jpeg"
|
||||
kind "StaticLib"
|
||||
language "C"
|
||||
targetdir "lib/%{cfg.buildcfg}"
|
||||
|
||||
location(JPEG_DIR)
|
||||
|
||||
files {
|
||||
JPEG_DIR.."/jaricom.c",
|
||||
JPEG_DIR.."/jcapimin.c",
|
||||
JPEG_DIR.."/jcapistd.c",
|
||||
JPEG_DIR.."/jcarith.c",
|
||||
JPEG_DIR.."/jccoefct.c",
|
||||
JPEG_DIR.."/jccolor.c",
|
||||
JPEG_DIR.."/jcdctmgr.c",
|
||||
JPEG_DIR.."/jchuff.c",
|
||||
JPEG_DIR.."/jcinit.c",
|
||||
JPEG_DIR.."/jcmainct.c",
|
||||
JPEG_DIR.."/jcmarker.c",
|
||||
JPEG_DIR.."/jcmaster.c",
|
||||
JPEG_DIR.."/jcomapi.c",
|
||||
JPEG_DIR.."/jcparam.c",
|
||||
JPEG_DIR.."/jcprepct.c",
|
||||
JPEG_DIR.."/jcsample.c",
|
||||
JPEG_DIR.."/jctrans.c",
|
||||
JPEG_DIR.."/jdapimin.c",
|
||||
JPEG_DIR.."/jdapistd.c",
|
||||
JPEG_DIR.."/jdarith.c",
|
||||
JPEG_DIR.."/jdatadst.c",
|
||||
JPEG_DIR.."/jdatasrc.c",
|
||||
JPEG_DIR.."/jdcoefct.c",
|
||||
JPEG_DIR.."/jdcolor.c",
|
||||
JPEG_DIR.."/jddctmgr.c",
|
||||
JPEG_DIR.."/jdhuff.c",
|
||||
JPEG_DIR.."/jdinput.c",
|
||||
JPEG_DIR.."/jdmainct.c",
|
||||
JPEG_DIR.."/jdmarker.c",
|
||||
JPEG_DIR.."/jdmaster.c",
|
||||
JPEG_DIR.."/jdmerge.c",
|
||||
JPEG_DIR.."/jdpostct.c",
|
||||
JPEG_DIR.."/jdsample.c",
|
||||
JPEG_DIR.."/jdtrans.c",
|
||||
JPEG_DIR.."/jerror.c",
|
||||
JPEG_DIR.."/jfdctflt.c",
|
||||
JPEG_DIR.."/jfdctfst.c",
|
||||
JPEG_DIR.."/jfdctint.c",
|
||||
JPEG_DIR.."/jidctflt.c",
|
||||
JPEG_DIR.."/jidctfst.c",
|
||||
JPEG_DIR.."/jidctint.c",
|
||||
JPEG_DIR.."/jmemmgr.c",
|
||||
JPEG_DIR.."/jmemnobs.c",
|
||||
JPEG_DIR.."/jquant1.c",
|
||||
JPEG_DIR.."/jquant2.c",
|
||||
JPEG_DIR.."/jutils.c"
|
||||
}
|
||||
|
||||
filter "configurations:Release"
|
||||
optimize "Full"
|
706
src_rebuild/utils/video_source/ReadAVI.cpp
Normal file
706
src_rebuild/utils/video_source/ReadAVI.cpp
Normal file
@ -0,0 +1,706 @@
|
||||
/*
|
||||
* ReadAVI.cpp
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2004-2013, Michael Kohn <mike@mikekohn.net> http://www.mikekohn.net/
|
||||
*
|
||||
* Copyright (c) 2018, olegvedi@gmail.com (C++ implementation and adds)
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the author nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
#include "ReadAVI.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#define ZEROIZE(x) {memset(&x, 0, sizeof(x));}
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define VERBOSE
|
||||
#define DBG_LVL_DEF 2
|
||||
|
||||
#ifdef VERBOSE
|
||||
#define MSG_DBG(dl, format, ...) {if(dl <= DBG_LVL_DEF ) fprintf(stdout, format ,##__VA_ARGS__);}
|
||||
#else
|
||||
#define MSG_DBG(dl, format, ...)
|
||||
#endif
|
||||
|
||||
ReadAVI::chunk_type_int_t ReadAVI::chunk_types[ChunkTypesCnt] = {
|
||||
{"db", ctype_uncompressed_video_frame },
|
||||
{"dc", ctype_compressed_video_frame },
|
||||
{"pc", ctype_palette_change },
|
||||
{"wb", ctype_audio_data } };
|
||||
|
||||
ReadAVI::ReadAVI(const char* filename)
|
||||
{
|
||||
data_buf = NULL;
|
||||
stream_format_vid.palette = NULL;
|
||||
data_buf_size = 0;
|
||||
inFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
inFile.open(filename, ios_base::ate | ios_base::in | ios_base::binary);
|
||||
fileSize = inFile.tellg();
|
||||
inFile.close();
|
||||
inFile.open(filename, ios_base::in | ios_base::binary);
|
||||
|
||||
try {
|
||||
parse_riff();
|
||||
}
|
||||
catch (std::system_error& e) { //file corrupted?
|
||||
std::cerr << e.code().message() << "\n";
|
||||
if (index_entries.size() == 0) {
|
||||
free();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReadAVI::~ReadAVI()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void ReadAVI::free()
|
||||
{
|
||||
if (inFile.is_open()) {
|
||||
inFile.close();
|
||||
}
|
||||
|
||||
delete[] stream_format_vid.palette;
|
||||
delete[] data_buf;
|
||||
}
|
||||
|
||||
void ReadAVI::check_data_buf(unsigned size)
|
||||
{
|
||||
if (data_buf_size < size) {
|
||||
delete[] data_buf;
|
||||
data_buf = new unsigned char[size + 1];
|
||||
data_buf_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
int ReadAVI::GetFrameFromIndex(frame_entry_t* frame_entry)
|
||||
{
|
||||
if (frame_entry->pointer >= index_entries.size()) {
|
||||
MSG_DBG(2, "Pointer out of range\n");
|
||||
return -5;
|
||||
}
|
||||
for (unsigned i = frame_entry->pointer; i < index_entries.size(); i++) {
|
||||
if (index_entries[i].type & frame_entry->type) {
|
||||
check_data_buf(index_entries[i].dwChunkLength);
|
||||
try {
|
||||
inFile.seekg(index_entries[i].dwChunkOffset, ios_base::beg);
|
||||
read_chars_bin(data_buf, index_entries[i].dwChunkLength);
|
||||
}
|
||||
catch (...) {
|
||||
return -1;
|
||||
}
|
||||
frame_entry->buf = data_buf;
|
||||
frame_entry->pointer = i + 1;
|
||||
frame_entry->stream_num = index_entries[i].stream_num;
|
||||
frame_entry->type = index_entries[i].type;
|
||||
return index_entries[i].dwChunkLength;
|
||||
}
|
||||
}
|
||||
MSG_DBG(2, "No more frames\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef VERBOSE
|
||||
int ReadAVI::hex_dump_chunk(int chunk_len)
|
||||
{
|
||||
char chars[17];
|
||||
unsigned char c;
|
||||
int ch, n;
|
||||
|
||||
chars[16] = 0;
|
||||
|
||||
for (n = 0; n < chunk_len; n++) {
|
||||
if ((n % 16) == 0) {
|
||||
if (n != 0)
|
||||
MSG_DBG(2, "%s\n", chars);
|
||||
MSG_DBG(2, " ");
|
||||
memset(chars, ' ', 16);
|
||||
}
|
||||
read_chars_bin(&c, 1);
|
||||
ch = c;
|
||||
if (ch == EOF)
|
||||
break;
|
||||
MSG_DBG(2, "%02x ", ch);
|
||||
if (ch >= ' ' && ch <= 126) {
|
||||
chars[n % 16] = ch;
|
||||
}
|
||||
else {
|
||||
chars[n % 16] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
if ((n % 16) != 0) {
|
||||
for (ch = n % 16; ch < 16; ch++) {
|
||||
MSG_DBG(2, " ");
|
||||
}
|
||||
}
|
||||
MSG_DBG(2, "%s\n", chars);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ReadAVI::decodeCkid(char* ckid, chunk_type_t* chunk_type)
|
||||
{
|
||||
int stream = (ckid[0] - '0') * 10 + (ckid[1] - '0');
|
||||
for (int i = 0; i < ChunkTypesCnt; i++) {
|
||||
if (chunk_types[i].type_id[0] == ckid[2] && chunk_types[i].type_id[1] == ckid[3]) {
|
||||
*chunk_type = chunk_types[i].type;
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ReadAVI::parse_idx1(int chunk_len)
|
||||
{
|
||||
index_entry_t index_entry;
|
||||
index_entry.type = ctype_none;
|
||||
int t;
|
||||
|
||||
MSG_DBG(2, " IDX1\n");
|
||||
MSG_DBG(2, " -------------------------------\n");
|
||||
MSG_DBG(2, " stream type dwFlags dwChunkOffset dwChunkLength\n");
|
||||
|
||||
for (t = 0; t < chunk_len / 16; t++) {
|
||||
char buf[5];
|
||||
read_chars(buf, 4);
|
||||
index_entry.stream_num = decodeCkid(buf, &index_entry.type);
|
||||
index_entry.dwFlags = read_int();
|
||||
index_entry.dwChunkOffset = read_int() + movi_offset + 0x10;
|
||||
index_entry.dwChunkLength = read_int();
|
||||
|
||||
MSG_DBG(2, " %d %d 0x%08x 0x%08x 0x%08x\n", index_entry.stream_num, index_entry.type,
|
||||
index_entry.dwFlags, index_entry.dwChunkOffset, index_entry.dwChunkLength);
|
||||
index_entries.push_back(index_entry);
|
||||
}
|
||||
|
||||
MSG_DBG(2, "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::read_avi_header()
|
||||
{
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
avi_header.TimeBetweenFrames = read_int();
|
||||
avi_header.MaximumDataRate = read_int();
|
||||
avi_header.PaddingGranularity = read_int();
|
||||
avi_header.Flags = read_int();
|
||||
avi_header.TotalNumberOfFrames = read_int();
|
||||
avi_header.NumberOfInitialFrames = read_int();
|
||||
avi_header.NumberOfStreams = read_int();
|
||||
avi_header.SuggestedBufferSize = read_int();
|
||||
avi_header.Width = read_int();
|
||||
avi_header.Height = read_int();
|
||||
avi_header.TimeScale = read_int();
|
||||
avi_header.DataRate = read_int();
|
||||
avi_header.StartTime = read_int();
|
||||
avi_header.DataLength = read_int();
|
||||
|
||||
MSG_DBG(2, " offset=0x%lx\n", offset);
|
||||
MSG_DBG(2, " TimeBetweenFrames: %d\n", avi_header.TimeBetweenFrames);
|
||||
MSG_DBG(2, " MaximumDataRate: %d\n", avi_header.MaximumDataRate);
|
||||
MSG_DBG(2, " PaddingGranularity: %d\n", avi_header.PaddingGranularity);
|
||||
MSG_DBG(2, " Flags: %d\n", avi_header.Flags);
|
||||
MSG_DBG(2, " TotalNumberOfFrames: %d\n", avi_header.TotalNumberOfFrames);
|
||||
MSG_DBG(2, " NumberOfInitialFrames: %d\n", avi_header.NumberOfInitialFrames);
|
||||
MSG_DBG(2, " NumberOfStreams: %d\n", avi_header.NumberOfStreams);
|
||||
MSG_DBG(2, " SuggestedBufferSize: %d\n", avi_header.SuggestedBufferSize);
|
||||
MSG_DBG(2, " Width: %d\n", avi_header.Width);
|
||||
MSG_DBG(2, " Height: %d\n", avi_header.Height);
|
||||
MSG_DBG(2, " TimeScale: %d\n", avi_header.TimeScale);
|
||||
MSG_DBG(2, " DataRate: %d\n", avi_header.DataRate);
|
||||
MSG_DBG(2, " StartTime: %d\n", avi_header.StartTime);
|
||||
MSG_DBG(2, " DataLength: %d\n", avi_header.DataLength);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_data_handler(unsigned char* handler)
|
||||
{
|
||||
int t;
|
||||
|
||||
for (t = 0; t < 4; t++) {
|
||||
if ((handler[t] >= 'a' && handler[t] <= 'z') || (handler[t] >= 'A' && handler[t] <= 'Z')
|
||||
|| (handler[t] >= '0' && handler[t] <= '9'))
|
||||
{
|
||||
MSG_DBG(2, "%c", handler[t]);
|
||||
}
|
||||
else {
|
||||
MSG_DBG(2, "[0x%02x]", handler[t]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ReadAVI::read_stream_header(stream_header_t* sheader)
|
||||
{
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
read_chars(sheader->DataType, 4);
|
||||
read_chars(sheader->DataHandler, 4);
|
||||
sheader->Flags = read_int();
|
||||
sheader->Priority = read_int();
|
||||
sheader->InitialFrames = read_int();
|
||||
sheader->TimeScale = read_int();
|
||||
sheader->DataRate = read_int();
|
||||
sheader->StartTime = read_int();
|
||||
sheader->DataLength = read_int();
|
||||
sheader->SuggestedBufferSize = read_int();
|
||||
sheader->Quality = read_int();
|
||||
sheader->SampleSize = read_int();
|
||||
|
||||
MSG_DBG(2, " offset=0x%lx\n", offset);
|
||||
MSG_DBG(2, " DataType: %s\n", sheader->DataType);
|
||||
MSG_DBG(2, " DataHandler: ");
|
||||
print_data_handler((unsigned char*)sheader->DataHandler);
|
||||
MSG_DBG(2, "\n");
|
||||
MSG_DBG(2, " Flags: %d\n", sheader->Flags);
|
||||
MSG_DBG(2, " Priority: %d\n", sheader->Priority);
|
||||
MSG_DBG(2, " InitialFrames: %d\n", sheader->InitialFrames);
|
||||
MSG_DBG(2, " TimeScale: %d\n", sheader->TimeScale);
|
||||
MSG_DBG(2, " DataRate: %d\n", sheader->DataRate);
|
||||
MSG_DBG(2, " StartTime: %d\n", sheader->StartTime);
|
||||
MSG_DBG(2, " DataLength: %d\n", sheader->DataLength);
|
||||
MSG_DBG(2, " SuggestedBufferSize: %d\n", sheader->SuggestedBufferSize);
|
||||
MSG_DBG(2, " Quality: %d\n", sheader->Quality);
|
||||
MSG_DBG(2, " SampleSize: %d\n", sheader->SampleSize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::read_stream_format()
|
||||
{
|
||||
int t, r, g, b;
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
stream_format_vid.header_size = read_int();
|
||||
stream_format_vid.image_width = read_int();
|
||||
stream_format_vid.image_height = read_int();
|
||||
stream_format_vid.number_of_planes = read_word();
|
||||
stream_format_vid.bits_per_pixel = read_word();
|
||||
read_chars(stream_format_vid.compression_type, 4);
|
||||
//stream_format_vid.compression_type = read_int();
|
||||
stream_format_vid.image_size_in_bytes = read_int();
|
||||
stream_format_vid.x_pels_per_meter = read_int();
|
||||
stream_format_vid.y_pels_per_meter = read_int();
|
||||
stream_format_vid.colors_used = read_int();
|
||||
stream_format_vid.colors_important = read_int();
|
||||
stream_format_vid.palette = 0;
|
||||
|
||||
if (stream_format_vid.colors_important != 0) {
|
||||
stream_format_vid.palette = new int[stream_format_vid.colors_important];
|
||||
for (t = 0; t < stream_format_vid.colors_important; t++) {
|
||||
unsigned char buf[3];
|
||||
read_chars_bin(buf, 3);
|
||||
b = buf[0];
|
||||
g = buf[1];
|
||||
r = buf[2];
|
||||
stream_format_vid.palette[t] = (r << 16) + (g << 8) + b;
|
||||
}
|
||||
}
|
||||
|
||||
MSG_DBG(2, " offset=0x%lx\n", offset);
|
||||
MSG_DBG(2, " header_size: %d\n", stream_format_vid.header_size);
|
||||
MSG_DBG(2, " image_width: %d\n", stream_format_vid.image_width);
|
||||
MSG_DBG(2, " image_height: %d\n", stream_format_vid.image_height);
|
||||
MSG_DBG(2, " number_of_planes: %d\n", stream_format_vid.number_of_planes);
|
||||
MSG_DBG(2, " bits_per_pixel: %d\n", stream_format_vid.bits_per_pixel);
|
||||
MSG_DBG(2, " compression_type: %s\n", stream_format_vid.compression_type);
|
||||
/* MSG_DBG(2, " compression_type: %04x (%c%c%c%c)\n", stream_format_vid.compression_type,
|
||||
((stream_format_vid.compression_type) & 255), ((stream_format_vid.compression_type >> 8) & 255),
|
||||
((stream_format_vid.compression_type >> 16) & 255), ((stream_format_vid.compression_type >> 24) & 255));
|
||||
*/
|
||||
MSG_DBG(2, " image_size_in_bytes: %d\n", stream_format_vid.image_size_in_bytes);
|
||||
MSG_DBG(2, " x_pels_per_meter: %d\n", stream_format_vid.x_pels_per_meter);
|
||||
MSG_DBG(2, " y_pels_per_meter: %d\n", stream_format_vid.y_pels_per_meter);
|
||||
MSG_DBG(2, " colors_used: %d\n", stream_format_vid.colors_used);
|
||||
MSG_DBG(2, " colors_important: %d\n", stream_format_vid.colors_important);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::read_stream_format_auds()
|
||||
{
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
stream_format_auds.format = read_word();
|
||||
stream_format_auds.channels = read_word();
|
||||
stream_format_auds.samples_per_second = read_int();
|
||||
stream_format_auds.bytes_per_second = read_int();
|
||||
int block_align = read_word();
|
||||
stream_format_auds.block_size_of_data = read_word();
|
||||
stream_format_auds.bits_per_sample = read_word();
|
||||
//stream_format_auds.extended_size=read_word();
|
||||
|
||||
MSG_DBG(2, " offset=0x%lx\n", offset);
|
||||
MSG_DBG(2, " format: %d\n", stream_format_auds.format);
|
||||
MSG_DBG(2, " channels: %d\n", stream_format_auds.channels);
|
||||
MSG_DBG(2, " samples_per_second: %d\n", stream_format_auds.samples_per_second);
|
||||
MSG_DBG(2, " bytes_per_second: %d\n", stream_format_auds.bytes_per_second);
|
||||
MSG_DBG(2, " block_align: %d\n", block_align);
|
||||
MSG_DBG(2, " block_size_of_data: %d\n", stream_format_auds.block_size_of_data);
|
||||
MSG_DBG(2, " bits_per_sample: %d\n", stream_format_auds.bits_per_sample);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::parse_hdrl_list()
|
||||
{
|
||||
char chunk_id[5];
|
||||
int chunk_size;
|
||||
char chunk_type[5];
|
||||
int end_of_chunk;
|
||||
int next_chunk;
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
int stream_type = 0; // 0=video 1=sound
|
||||
|
||||
read_chars(chunk_id, 4);
|
||||
chunk_size = read_int();
|
||||
read_chars(chunk_type, 4);
|
||||
|
||||
MSG_DBG(2, " AVI Header LIST (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
|
||||
MSG_DBG(2, " {\n");
|
||||
|
||||
end_of_chunk = chunk_size - 4 + inFile.tellg();
|
||||
if ((end_of_chunk % 4) != 0) {
|
||||
//MSG_DBG(2, "Adjusting end of chunk %d\n", end_of_chunk);
|
||||
//end_of_chunk=end_of_chunk+(4-(end_of_chunk%4));
|
||||
//MSG_DBG(2, "Adjusting end of chunk %d\n", end_of_chunk);
|
||||
}
|
||||
|
||||
if (strcmp(chunk_id, "JUNK") == 0) {
|
||||
inFile.seekg(end_of_chunk, ios_base::beg);
|
||||
MSG_DBG(2, " }\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (inFile.tellg() < end_of_chunk) {
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
read_chars(chunk_type, 4);
|
||||
chunk_size = read_int();
|
||||
next_chunk = chunk_size + inFile.tellg();
|
||||
if ((chunk_size % 4) != 0) {
|
||||
//MSG_DBG(2, "Chunk size not a multiple of 4?\n");
|
||||
//chunk_size=chunk_size+(4-(chunk_size%4));
|
||||
}
|
||||
|
||||
MSG_DBG(2, " %.4s (size=%d offset=0x%lx)\n", chunk_type, chunk_size, offset);
|
||||
MSG_DBG(2, " {\n");
|
||||
|
||||
if (strcmp("strh", chunk_type) == 0)
|
||||
{
|
||||
long marker = inFile.tellg();
|
||||
char buffer[5];
|
||||
read_chars(buffer, 4);
|
||||
inFile.seekg(marker, ios_base::beg);
|
||||
|
||||
if (strcmp(buffer, "vids") == 0)
|
||||
{
|
||||
stream_type = 0;
|
||||
read_stream_header(&stream_header_vid);
|
||||
}
|
||||
else if (strcmp(buffer, "auds") == 0)
|
||||
{
|
||||
stream_type = 1;
|
||||
read_stream_header(&stream_header_auds);
|
||||
}
|
||||
else
|
||||
{
|
||||
MSG_DBG(2, "Unknown stream type %s\n", buffer);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if (strcmp("strf", chunk_type) == 0)
|
||||
{
|
||||
if (stream_type == 0) {
|
||||
read_stream_format();
|
||||
}
|
||||
else {
|
||||
read_stream_format_auds();
|
||||
}
|
||||
}
|
||||
else if (strcmp("strd", chunk_type) == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
MSG_DBG(2, " Unknown chunk type: %s\n", chunk_type);
|
||||
// skip_chunk();
|
||||
}
|
||||
|
||||
MSG_DBG(2, " }\n");
|
||||
|
||||
inFile.seekg(next_chunk, ios_base::beg);
|
||||
}
|
||||
|
||||
MSG_DBG(2, " }\n");
|
||||
inFile.seekg(end_of_chunk, ios_base::beg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::parse_movi(int size)
|
||||
{
|
||||
char chunk_id[5];
|
||||
long end_of_chunk;
|
||||
index_entry_t index_entry;
|
||||
int blk_size;
|
||||
|
||||
do {
|
||||
long offset = inFile.tellg();
|
||||
/*
|
||||
if (fileSize - offset < 8)
|
||||
break;
|
||||
*/
|
||||
read_chars(chunk_id, 4);
|
||||
index_entry.stream_num = decodeCkid(chunk_id, &index_entry.type);
|
||||
|
||||
if (index_entry.stream_num < 0) {
|
||||
inFile.seekg(offset - 4, ios_base::beg);
|
||||
break;
|
||||
}
|
||||
|
||||
index_entry.dwChunkLength = read_int();
|
||||
|
||||
if (!(avi_header.Flags & AVIF_HASINDEX)) {
|
||||
index_entry.dwChunkOffset = offset + 8;
|
||||
index_entries.push_back(index_entry);
|
||||
}
|
||||
|
||||
MSG_DBG(3, " AVI Movi Chunk ( id=%s(%d-%d) chunk_size=%d offset=0x%lx)\n", chunk_id, index_entry.type,
|
||||
index_entry.stream_num, index_entry.dwChunkLength, offset);
|
||||
MSG_DBG(3, " {\n");
|
||||
|
||||
end_of_chunk = index_entry.dwChunkLength + inFile.tellg();
|
||||
end_of_chunk = (end_of_chunk + 1) & ~1;
|
||||
/* if ((end_of_chunk % 4) != 0) {
|
||||
end_of_chunk = end_of_chunk + (4 - (end_of_chunk % 4));
|
||||
}
|
||||
*/
|
||||
// MSG_DBG(2, "end_of_chunk:%d %X\n", end_of_chunk, end_of_chunk);
|
||||
inFile.seekg(end_of_chunk, ios_base::beg);
|
||||
MSG_DBG(3, " }\n");
|
||||
|
||||
blk_size = end_of_chunk - offset;
|
||||
size -= blk_size;
|
||||
MSG_DBG(3, "size:%d \n", size);
|
||||
|
||||
} while (size > 7);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::parse_hdrl(unsigned int size)
|
||||
{
|
||||
char chunk_id[5];
|
||||
int chunk_size;
|
||||
int end_of_chunk;
|
||||
long offset = inFile.tellg();
|
||||
|
||||
read_chars(chunk_id, 4);
|
||||
chunk_size = read_int();
|
||||
|
||||
MSG_DBG(2, " AVI Header Chunk (id=%s size=%d offset=0x%lx)\n", chunk_id, chunk_size, offset);
|
||||
MSG_DBG(2, " {\n");
|
||||
|
||||
end_of_chunk = chunk_size + inFile.tellg();
|
||||
if ((end_of_chunk % 4) != 0) {
|
||||
end_of_chunk = end_of_chunk + (4 - (end_of_chunk % 4));
|
||||
}
|
||||
|
||||
read_avi_header();
|
||||
MSG_DBG(2, " }\n");
|
||||
|
||||
while (inFile.tellg() < offset + size - 4) {
|
||||
//MSG_DBG(2, "Should end at 0x%lx 0x%lx\n",offset+size,inFile.tellg());
|
||||
parse_hdrl_list();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::parse_riff()
|
||||
{
|
||||
char chunk_id[5];
|
||||
int chunk_size;
|
||||
char chunk_type[5];
|
||||
int end_of_chunk, end_of_subchunk;
|
||||
|
||||
ZEROIZE(avi_header);
|
||||
ZEROIZE(stream_header_vid);
|
||||
ZEROIZE(stream_format_vid);
|
||||
ZEROIZE(stream_header_auds);
|
||||
ZEROIZE(stream_format_auds);
|
||||
#ifdef VERBOSE
|
||||
long offset = inFile.tellg();
|
||||
#endif
|
||||
read_chars(chunk_id, 4);
|
||||
chunk_size = read_int();
|
||||
read_chars(chunk_type, 4);
|
||||
|
||||
MSG_DBG(2, "RIFF Chunk (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
|
||||
MSG_DBG(2, "{\n");
|
||||
|
||||
if (strcmp("RIFF", chunk_id) != 0)
|
||||
{
|
||||
MSG_DBG(2, "Not a RIFF file.\n");
|
||||
return 1;
|
||||
}
|
||||
else if (strcmp("AVI ", chunk_type) != 0)
|
||||
{
|
||||
MSG_DBG(2, "Not an AVI file.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
end_of_chunk = chunk_size - 4 + inFile.tellg();
|
||||
|
||||
while (inFile.tellg() < end_of_chunk) {
|
||||
long offset = inFile.tellg();
|
||||
read_chars(chunk_id, 4);
|
||||
chunk_size = read_int();
|
||||
end_of_subchunk = chunk_size + inFile.tellg();
|
||||
|
||||
if (strcmp("JUNK", chunk_id) == 0 || strcmp("PAD ", chunk_id) == 0) {
|
||||
chunk_type[0] = 0;
|
||||
}
|
||||
else {
|
||||
read_chars(chunk_type, 4);
|
||||
}
|
||||
|
||||
MSG_DBG(2, " New Chunk (id=%s size=%d type=%s offset=0x%lx)\n", chunk_id, chunk_size, chunk_type, offset);
|
||||
MSG_DBG(2, " {\n");
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
if (strcmp("JUNK", chunk_id) == 0 || strcmp("PAD ", chunk_id) == 0) {
|
||||
if ((chunk_size % 4) != 0) {
|
||||
chunk_size = chunk_size + (4 - (chunk_size % 4));
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
hex_dump_chunk(chunk_size);
|
||||
#endif
|
||||
}
|
||||
else if (strcmp("INFO", chunk_type) == 0) {
|
||||
if ((chunk_size % 4) != 0) {
|
||||
chunk_size = chunk_size + (4 - (chunk_size % 4));
|
||||
}
|
||||
#ifdef VERBOSE
|
||||
hex_dump_chunk(chunk_size);
|
||||
#endif
|
||||
}
|
||||
else if (strcmp("hdrl", chunk_type) == 0) {
|
||||
parse_hdrl(chunk_size);
|
||||
/* skip_chunk(); */
|
||||
}
|
||||
else if (strcmp("movi", chunk_type) == 0) {
|
||||
movi_offset = offset;
|
||||
parse_movi(chunk_size);
|
||||
}
|
||||
else if (strcmp("idx1", chunk_id) == 0) {
|
||||
inFile.seekg(inFile.tellg() - std::streamoff(4), ios_base::beg);
|
||||
parse_idx1(chunk_size);
|
||||
}/* else if (strcmp("indx", chunk_id) == 0) {
|
||||
inFile.seekg(inFile.tellg() - 4L, ios_base::beg);
|
||||
parse_indx(chunk_size);
|
||||
}*/else {
|
||||
MSG_DBG(2, " Unknown chunk at %d (%4s)\n", (int)inFile.tellg() - 8, chunk_type);
|
||||
if (chunk_size == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
inFile.seekg(end_of_subchunk, ios_base::beg);
|
||||
MSG_DBG(2, " }\n");
|
||||
|
||||
}
|
||||
|
||||
if (stream_format_vid.palette) {
|
||||
delete[] stream_format_vid.palette;
|
||||
stream_format_vid.palette = NULL;
|
||||
}
|
||||
|
||||
MSG_DBG(2, "}\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadAVI::read_int()
|
||||
{
|
||||
int c;
|
||||
unsigned char buf[4];
|
||||
|
||||
inFile.read((char*)buf, 4);
|
||||
c = buf[0];
|
||||
c = c + (buf[1] << 8);
|
||||
c = c + (buf[2] << 16);
|
||||
c = c + (buf[3] << 24);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
int ReadAVI::read_word()
|
||||
{
|
||||
int c;
|
||||
unsigned char buf[2];
|
||||
|
||||
inFile.read((char*)buf, 2);
|
||||
c = buf[0];
|
||||
c = c + (buf[1] << 8);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void ReadAVI::read_chars(char* s, int count)
|
||||
{
|
||||
inFile.read(s, count);
|
||||
s[count] = 0;
|
||||
}
|
||||
|
||||
void ReadAVI::read_chars_bin(unsigned char* s, int count)
|
||||
{
|
||||
inFile.read((char*)s, count);
|
||||
}
|
181
src_rebuild/utils/video_source/ReadAVI.h
Normal file
181
src_rebuild/utils/video_source/ReadAVI.h
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* ReadAVI.h
|
||||
*
|
||||
* Created on: 8 мая 2018 г.
|
||||
* Author: olegvedi@gmail.com
|
||||
*/
|
||||
|
||||
#ifndef READAVI_H_
|
||||
#define READAVI_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
class ReadAVI {
|
||||
enum {
|
||||
AVIF_HASINDEX = 0x10,
|
||||
AVIF_MUSTUSEINDEX = 0x20,
|
||||
AVIF_ISINTERLEAVED = 0x100,
|
||||
AVIF_WASCAPTUREFILE = 0x10000,
|
||||
AVIF_COPYRIGHTED = 0x20000,
|
||||
};
|
||||
|
||||
public:
|
||||
typedef struct {
|
||||
int TimeBetweenFrames;
|
||||
int MaximumDataRate;
|
||||
int PaddingGranularity;
|
||||
int Flags;
|
||||
int TotalNumberOfFrames;
|
||||
int NumberOfInitialFrames;
|
||||
int NumberOfStreams;
|
||||
int SuggestedBufferSize;
|
||||
int Width;
|
||||
int Height;
|
||||
int TimeScale;
|
||||
int DataRate;
|
||||
int StartTime;
|
||||
int DataLength;
|
||||
} avi_header_t;
|
||||
|
||||
typedef struct {
|
||||
char DataType[5];
|
||||
char DataHandler[5];
|
||||
int Flags;
|
||||
int Priority;
|
||||
int InitialFrames;
|
||||
int TimeScale;
|
||||
int DataRate;
|
||||
int StartTime;
|
||||
int DataLength;
|
||||
int SuggestedBufferSize;
|
||||
int Quality;
|
||||
int SampleSize;
|
||||
} stream_header_t;
|
||||
|
||||
typedef struct {
|
||||
int header_size;
|
||||
int image_width;
|
||||
int image_height;
|
||||
int number_of_planes;
|
||||
int bits_per_pixel;
|
||||
char compression_type[5];
|
||||
int image_size_in_bytes;
|
||||
int x_pels_per_meter;
|
||||
int y_pels_per_meter;
|
||||
int colors_used;
|
||||
int colors_important;
|
||||
int* palette;
|
||||
} stream_format_t;
|
||||
|
||||
typedef struct {
|
||||
int header_size;
|
||||
int format;
|
||||
int channels;
|
||||
int samples_per_second;
|
||||
int bytes_per_second;
|
||||
int block_size_of_data;
|
||||
int bits_per_sample;
|
||||
int extended_size;
|
||||
} stream_format_auds_t;
|
||||
|
||||
#define ChunkTypesCnt 4
|
||||
typedef enum {
|
||||
ctype_none = 0,
|
||||
ctype_uncompressed_video_frame = 1,
|
||||
ctype_compressed_video_frame = 2,
|
||||
ctype_palette_change = 4,
|
||||
ctype_audio_data = 8,
|
||||
ctype_video_data = 1 + 2,
|
||||
} chunk_type_t;
|
||||
|
||||
ReadAVI(const char* filename);
|
||||
virtual ~ReadAVI();
|
||||
|
||||
avi_header_t GetAviHeader()
|
||||
{
|
||||
return avi_header;
|
||||
}
|
||||
stream_header_t GetVideoStreamHeader()
|
||||
{
|
||||
return stream_header_vid;
|
||||
}
|
||||
stream_format_t GetVideoFormat()
|
||||
{
|
||||
return stream_format_vid;
|
||||
}
|
||||
stream_header_t GetAudioStreamHeader()
|
||||
{
|
||||
return stream_header_auds;
|
||||
}
|
||||
stream_format_auds_t GetAudioFormat()
|
||||
{
|
||||
return stream_format_auds;
|
||||
}
|
||||
|
||||
unsigned GetIndexCnt()
|
||||
{
|
||||
return index_entries.size();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
chunk_type_t type;
|
||||
unsigned stream_num;
|
||||
unsigned char* buf;
|
||||
// unsigned frame_size;
|
||||
unsigned pointer;
|
||||
} frame_entry_t;
|
||||
|
||||
int GetFrameFromIndex(frame_entry_t* frame_entry);
|
||||
|
||||
private:
|
||||
typedef struct {
|
||||
chunk_type_t type;
|
||||
int stream_num;
|
||||
int dwFlags;
|
||||
int dwChunkOffset;
|
||||
int dwChunkLength;
|
||||
} index_entry_t;
|
||||
|
||||
typedef struct {
|
||||
char type_id[3];
|
||||
chunk_type_t type;
|
||||
} chunk_type_int_t;
|
||||
|
||||
static chunk_type_int_t chunk_types[ChunkTypesCnt];
|
||||
|
||||
std::vector<index_entry_t> index_entries;
|
||||
std::ifstream inFile;
|
||||
avi_header_t avi_header;
|
||||
stream_header_t stream_header_vid;
|
||||
stream_format_t stream_format_vid;
|
||||
stream_header_t stream_header_auds;
|
||||
stream_format_auds_t stream_format_auds;
|
||||
long fileSize;
|
||||
long movi_offset;
|
||||
unsigned char* data_buf;
|
||||
unsigned data_buf_size;
|
||||
|
||||
int hex_dump_chunk(int chunk_len);
|
||||
|
||||
int parse_riff();
|
||||
int parse_hdrl_list();
|
||||
int parse_idx1(int chunk_len);
|
||||
//int parse_indx(int chunk_len);
|
||||
int read_avi_header();
|
||||
int read_stream_header(stream_header_t* sheader);
|
||||
int read_stream_format();
|
||||
int read_stream_format_auds();
|
||||
int parse_hdrl(unsigned int size);
|
||||
int parse_movi(int size);
|
||||
|
||||
int decodeCkid(char* ckid, chunk_type_t* chunk_type);
|
||||
int read_int();
|
||||
int read_word();
|
||||
void read_chars(char* s, int count);
|
||||
void read_chars_bin(unsigned char* s, int count);
|
||||
void check_data_buf(unsigned size);
|
||||
void free();
|
||||
};
|
||||
|
||||
#endif /* READAVI_H_ */
|
268
src_rebuild/utils/video_source/VideoPlayer.cpp
Normal file
268
src_rebuild/utils/video_source/VideoPlayer.cpp
Normal file
@ -0,0 +1,268 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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);
|
||||
|
||||
for (int s = 0; s < cinfo.output_width * cinfo.num_components; s += cinfo.num_components)
|
||||
{
|
||||
// flip RGB
|
||||
u_char tmp = curr_scanline[s];
|
||||
curr_scanline[s] = curr_scanline[s+2];
|
||||
curr_scanline[s+2] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(&cinfo);
|
||||
jpeg_destroy_decompress(&cinfo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// emulator window TODO: interface
|
||||
extern SDL_Window* g_window;
|
||||
|
||||
void GetMovieRectangle(SDL_Rect& rect, ReadAVI::stream_format_t& strFmt)
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
Emulator_GetScreenSize(windowWidth, windowHeight);
|
||||
|
||||
float psxScreenW = 320.0f;
|
||||
float psxScreenH = 240.0f;
|
||||
|
||||
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);
|
||||
|
||||
{
|
||||
// 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;
|
||||
|
||||
// then map to screen
|
||||
clipRectY -= 0.5f;
|
||||
|
||||
clipRectY /= video_aspect * emuScreenAspect;
|
||||
clipRectH /= emuScreenAspect * video_aspect;
|
||||
|
||||
clipRectY += 0.5f;
|
||||
|
||||
rect.x = clipRectX * windowWidth;
|
||||
rect.y = clipRectY * windowHeight;
|
||||
rect.w = clipRectW * windowWidth;
|
||||
rect.h = clipRectH * windowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Update video frame
|
||||
SDL_Surface* MainSurface = SDL_GetWindowSurface(g_window);
|
||||
|
||||
SDL_LockSurface(BufSurface);
|
||||
|
||||
int ret = UnpackJPEG(frame_entry.buf, frame_size, stream_format.bits_per_pixel, (unsigned char*)BufSurface->pixels);
|
||||
SDL_UnlockSurface(BufSurface);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
GetMovieRectangle(rect, stream_format);
|
||||
|
||||
SDL_UpperBlitScaled(BufSurface, NULL, MainSurface, &rect);
|
||||
SDL_UpdateWindowSurface(g_window);
|
||||
}
|
||||
|
||||
SDL_FreeSurface(MainSurface);
|
||||
|
||||
// set next step time
|
||||
nextTime = SDL_GetTicks() + (avi_header.TimeBetweenFrames - 9000) / 1000;
|
||||
}
|
||||
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;
|
||||
|
||||
Emulator_EndScene();
|
||||
}
|
||||
|
||||
alDeleteSources(1, &audioStreamSource);
|
||||
alDeleteBuffers(4, audioStreamBuffers);
|
||||
}
|
||||
|
||||
// FMV main function
|
||||
int FMV_main(RENDER_ARGS* args)
|
||||
{
|
||||
for (int i = 0; i < args->nRenders; i++)
|
||||
{
|
||||
DoPlayFMV(&args->Args[i], args->subtitle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user