- AVI (MJPEG) video player prototype for FMV

This commit is contained in:
Ilya Shurumov 2020-09-21 00:21:36 +06:00
parent 4e1af5f216
commit 20d1a3738f
7 changed files with 1234 additions and 10 deletions

View File

@ -380,6 +380,11 @@ void SpuInit(void)
_SpuInit(0);
}
void SpuQuit(void)
{
// do nothing!
}
void UpdateVoiceSample(SPUVoice& voice)
{
//if (!voice.sampledirty)

View File

@ -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();
}

View File

@ -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

View 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"

View 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);
}

View 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_ */

View 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;
}