Add strplay example

This commit is contained in:
ABelliqueux 2021-09-13 18:56:10 +02:00
parent bfb9edb3e1
commit c1023a1150
8 changed files with 680 additions and 1 deletions

View File

@ -48,6 +48,8 @@ hello_bs:
$(MAKE) -C hello_bs all
hello_str:
$(MAKE) -C hello_str all
hello_strplay:
$(MAKE) -C hello_strplay all
clean:
$(MAKE) -C hello_2pads clean
@ -76,6 +78,7 @@ clean:
$(MAKE) -C hello_xa cleansub
$(MAKE) -C hello_bs cleansub
$(MAKE) -C hello_str cleansub
$(MAKE) -C hello_strplay cleansub
all:
$(MAKE) -C hello_2pads
@ -102,11 +105,12 @@ all:
$(MAKE) -C hello_xa all
$(MAKE) -C hello_bs all
$(MAKE) -C hello_str all
$(MAKE) -C hello_strplay all
# declare phony rules
.PHONY: hello_2pads hello_cube hello_cubetex hello_poly_fun hello_gte_opti \
hello_light hello_multivag hello_pad hello_poly hello_poly_ft hello_poly_gt \
hello_poly_gt_tw hello_poly_inline hello_sio hello_sprt hello_tile \
hello_vag hello_world hello_cdda hello_cd hello_xa hello_bs hello_str \
hello_vag hello_world hello_cdda hello_cd hello_xa hello_bs hello_str hello_strplay \
hello_poly_stp hello_cubetex_stp \
clean all

12
hello_strplay/Makefile Normal file
View File

@ -0,0 +1,12 @@
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_strplay.cue hello_strplay.bin
TARGET = hello_strplay
SRCS = hello_strplay.c \
include ../common.mk

36
hello_strplay/README.md Normal file
View File

@ -0,0 +1,36 @@
This example show how to use Lameguy64's [STR playback library](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str#str-playback-library).
For a barebone example, see the [hello_str](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str) example.
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory.
## STR playback library
@Lameguy64 has spent some time making a STR playback library that's easily included in a project :
> One thing that I find somewhat missing here is a decent piece of code for playing STR video files easily. So, what I did was take the old and messy PsyQ STR player example, clean it up entirely, and finally make it into a cute little c library for easy implementation.
Original post : http://www.psxdev.net/forum/viewtopic.php?t=507
Original download link : https://www.mediafire.com/download/s61u86sxd1djncy/strplay.7z
Mirror : http://psx.arthus.net/code/strplay.7z
## Video encoding and more informations
See the [wiki](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR).
## Video credits
The video and song used in this example are by Nina Paley : https://archive.org/details/CopyingIsNotTheft-ScratchTrack1280X720Hdv

View File

@ -0,0 +1,115 @@
// Using the strplay library.
// Schnappy 07-2021
// based on Lameguy64 strplay library : http://www.psxdev.net/forum/viewtopic.php?t=507
// Original PsyQ sample code : /psyq/addons/cd/MOVIE
// Video to STR conversion : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR
#include <sys/types.h>
#include <stdio.h>
#include <libgte.h>
#include <libetc.h>
#include <libgpu.h>
// CD library
#include <libcd.h>
// CODEC library
#include <libpress.h>
// include Lameguy64's library
#include "strplay.c"
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define TRUECOL 1 // 0 : 16bpp, 1: 24bpp
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 8 // margins for text display
#define MARGINY 16
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
STRFILE StrFile[] = {
// File name Resolution Frame count
"\\COPY.STR;1", 320, 240, 893
};
// When using RGB24, a special routine has to be setup as a callback function to avoid MDEC/CDROM conflict
// See http://psx.arthus.net/sdk/Psy-Q/DOCS/LibRef47.pdf , p.713
static void strCheckRGB24();
void init(void)
{
ResetCallback();
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
#if VMODE
SetVideoMode(MODE_PAL);
disp[0].disp.y = 8;
disp[1].disp.y = 8;
#endif
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 155, 0, 150); // set color for first draw area
setRGB0(&draw[1], 155, 0, 150); // set color for second draw area
draw[0].isbg = 0; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 0;
#if TRUECOL
disp[0].isrgb24 = 1;
disp[1].isrgb24 = 1;
#endif
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
void main() {
// Reset and initialize stuff
ResetCallback();
CdInit();
PadInit(0);
ResetGraph(0);
SetGraphDebug(0);
// Play the video in loop
while (1) {
if (PlayStr(320, 240, 0, 0, &StrFile[0]) == 0) // If player presses Start
break; // Exit the loop
}
}
static void strCheckRGB24() {
/* From http://psx.arthus.net/sdk/Psy-Q/DOCS/, p.713
* When playing a movie in 24-bit mode, there is a potential hardware conflict between the CD subsystem
* and the MDEC image decompression system which can result in corrupted data. To avoid this,
* StCdInterrupt() may defer transferring a sector and instead set a flag variable called StCdInterFlag to
* indicate that a CD sector is ready to be transferred. Once the MDEC is finished transferring data, your
* application should check StCdIntrFlag and call StCdInterrupt() directly if it is set.
*/
#if TRUECOL
extern u_long StCdIntrFlag;
// If flag was set
if ( StCdIntrFlag ) {
// Trigger data transfer
StCdInterrupt();
// Reset flag
StCdIntrFlag = 0;
}
#endif
}

101
hello_strplay/isoconfig.xml Normal file
View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- MKPSXISO example XML script -->
<!-- <iso_project>
Starts an ISO image project to build. Multiple <iso_project> elements may be
specified within the same xml script which useful for multi-disc projects.
<iso_project> elements must contain at least one <track> element.
Attributes:
image_name - File name of the ISO image file to generate.
cue_sheet - Optional, file name of the cue sheet for the image file
(required if more than one track is specified).
-->
<iso_project image_name="hello_strplay.bin" cue_sheet="hello_strplay.cue">
<!-- <track>
Specifies a track to the ISO project. This example element creates a data
track for storing data files and CD-XA/STR streams.
Only one data track is allowed and data tracks must only be specified as the
first track in the ISO image and cannot be specified after an audio track.
Attributes:
type - Track type (either data or audio).
source - For audio tracks only, specifies the file name of a wav audio
file to use for the audio track.
-->
<track type="data">
<!-- <identifiers>
Optional, Specifies the identifier strings to use for the data track.
Attributes:
system - Optional, specifies the system identifier (PLAYSTATION if unspecified).
application - Optional, specifies the application identifier (PLAYSTATION if unspecified).
volume - Optional, specifies the volume identifier.
volume_set - Optional, specifies the volume set identifier.
publisher - Optional, specifies the publisher identifier.
data_preparer - Optional, specifies the data preparer identifier. If unspecified, MKPSXISO
will fill it with lengthy text telling that the image file was generated
using MKPSXISO.
-->
<identifiers
system ="PLAYSTATION"
application ="PLAYSTATION"
volume ="HELOCD"
volume_set ="HELOCD"
publisher ="SCHNAPPY"
data_preparer ="MKPSXISO"
/>
<!-- <license>
Optional, specifies the license file to use, the format of the license file must be in
raw 2336 byte sector format, like the ones included with the PsyQ SDK in psyq\cdgen\LCNSFILE.
License data is not included within the MKPSXISO program to avoid possible legal problems
in the open source environment... Better be safe than sorry.
Attributes:
file - Specifies the license file to inject into the ISO image.
-->
<!--
<license file="LICENSEA.DAT"/>
-->
<!-- <directory_tree>
Specifies and contains the directory structure for the data track.
Attributes:
None.
-->
<directory_tree>
<!-- <file>
Specifies a file in the directory tree.
Attributes:
name - File name to use in the directory tree (can be used for renaming).
type - Optional, type of file (data for regular files and is the default, xa for
XA audio and str for MDEC video).
source - File name of the source file.
-->
<!-- Stores system.txt as system.cnf -->
<file name="system.cnf" type="data" source="system.cnf"/>
<file name="SCES_313.37" type="data" source="hello_strplay.ps-exe"/>
<file name="COPY.STR" type="str" source="str/copyings.str"/>
<dummy sectors="1024"/>
<!-- <dir>
Specifies a directory in the directory tree. <file> and <dir> elements inside the element
will be inside the specified directory.
-->
</directory_tree>
</track>
</iso_project>

Binary file not shown.

407
hello_strplay/strplay.c Normal file
View File

@ -0,0 +1,407 @@
/*
Simple STR Player Library by Lameguy64
(?) 2014 Meido-Tek Productions/Lame Studios
Original PsyQ sample programmed by:
Yutaka
Suzu
Masa
Ume
Code heavily refined by:
Lameguy64
What Lameguy did to the original code:
- Removed all of the icky yucky UTF-16 junk
- Fixed all crap-English comments
- Greatly improved code formatting
- Renamed variables with better names
- Buffer arrays are now initialized only when the playback routine is called
Libraries Required:
libetc
libgte
libgpu
libcd
Function list:
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str)
Parameters:
xres, yres - Video resolution.
xpos, ypos - Framebuffer offset on where to draw the video.
STRFILE *str - STRFILE entry to play.
Notes:
Just make sure that you have at least 192KB of free memory before calling
the PlayStr function otherwise, the console will crash. As for the video
resolution, it must be equal or less than 256 as the second buffer
is located directly below the first buffer.
Note:
If compiling the sample fails, open your psyq.ini file located in \psyq\bin and
append the following into the stdlib line:
libds.lib libpress.lib
*/
#define IS_RGB24 1 // 0:16-bit playback, 1:24-bit playback (recommended for quality)
#define RING_SIZE 32 // Ring Buffer size (32 sectors seems good enough)
#if IS_RGB24==1
#define PPW 3/2 // pixels per short word
#define DCT_MODE 3 // Decode mode for DecDCTin routine
#else
#define PPW 1
#define DCT_MODE 2
#endif
// A simple struct to make STR handling a bit easier
typedef struct {
char FileName[32];
int Xres;
int Yres;
int NumFrames;
} STRFILE;
// Decode environment
typedef struct {
u_long *VlcBuff_ptr[2]; // Pointers to the VLC buffers
u_short *ImgBuff_ptr[2]; // Pointers to the frame slice buffers
RECT rect[2]; // VRAM parameters on where to draw the frame data to
RECT slice; // Frame slice parameters for loading into VRAM
int VlcID; // Current VLC buffer ID
int ImgID; // Current slice buffer ID
int RectID; // Current video buffer ID
int FrameDone; // Frame decode completion flag
} STRENV;
// A bunch of internal variables
static STRENV strEnv;
static int strScreenWidth=0,strScreenHeight=0;
static int strFrameX=0,strFrameY=0;
static int strNumFrames=0;
static int strFrameWidth=0,strFrameHeight=0; // Frame size of STR file
static int strPlayDone=0; // Playback completion flag
// Main function prototypes
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str);
// Internal function prototypes
static void strDoPlayback(STRFILE *str);
static void strCallback();
static void strNextVlc(STRENV *strEnv);
static void strSync(STRENV *strEnv, int mode);
static u_long *strNext(STRENV *strEnv);
static void strKickCD(CdlLOC *loc);
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str) {
/*
Main STR playback routine.
Returns:
0 - Playback failed or was skipped.
1 - Playback was finished.
*/
strNumFrames=str->NumFrames;
strScreenWidth=xres;
strScreenHeight=yres;
strFrameX=xpos;
strFrameY=ypos;
strPlayDone=0;
strDoPlayback(str);
if (strPlayDone == 0)
return(0);
else
return(1);
}
static void strDoPlayback(STRFILE *str) {
/*
Does the actual STR playback.
*/
int id; // Display buffer ID
DISPENV disp; // Display environment
CdlFILE file; // File info of video file
// Buffers initialized here so we won't waste too much memory for playing FMVs
// (just make sure you have at least 192KB of free memory before calling this routine)
u_long RingBuff[RING_SIZE*SECTOR_SIZE]; // Ring buffer
u_long VlcBuff[2][str->Xres/2*str->Yres]; // VLC buffers
u_short ImgBuff[2][16*PPW*str->Yres]; // Frame 'slice' buffers
// Set display mask so we won't see garbage while the stream is being prepared
SetDispMask(0);
// Get the CD location of the STR file to play
if (CdSearchFile(&file, str->FileName) == 0) {
#ifdef DEBUG
printf("ERROR: I cannot find video file %s\n", str->FileName);
#endif
SetDispMask(1);
return;
}
// Setup the buffer pointers
strEnv.VlcBuff_ptr[0] = &VlcBuff[0][0];
strEnv.VlcBuff_ptr[1] = &VlcBuff[1][0];
strEnv.VlcID = 0;
strEnv.ImgBuff_ptr[0] = &ImgBuff[0][0];
strEnv.ImgBuff_ptr[1] = &ImgBuff[1][0];
strEnv.ImgID = 0;
// Setup the display buffers on VRAM
strEnv.rect[0].x = strFrameX; // First page
strEnv.rect[0].y = strFrameY;
strEnv.rect[1].x = strFrameX; // Second page
strEnv.rect[1].y = strFrameY+strScreenHeight;
strEnv.RectID = 0;
// Set the parameters for uploading frame slices
strEnv.slice.x = strFrameX;
strEnv.slice.y = strFrameY;
strEnv.slice.w = 16*PPW;
strEnv.FrameDone = 0;
// Reset the MDEC
DecDCTReset(0);
// Set callback routine
DecDCToutCallback(strCallback);
// Set ring buffer
StSetRing(RingBuff, RING_SIZE);
// Set streaming parameters
StSetStream(IS_RGB24, 1, 0xffffffff, 0, 0);
// Begin streaming!
strKickCD(&file.pos);
// Load the first frame of video before entering main loop
strNextVlc(&strEnv);
while (1) {
// Decode the compressed frame data
DecDCTin(strEnv.VlcBuff_ptr[strEnv.VlcID], DCT_MODE);
// Prepare to receive the decoded image data from the MDEC
DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2);
// Get the next frame
strNextVlc(&strEnv);
// Wait for the frame to finish decoding
strSync(&strEnv, 0);
// Switch between the display buffers per frame
id = strEnv.RectID? 0: 1;
SetDefDispEnv(&disp, 0, strScreenHeight*id, strScreenWidth*PPW, strScreenHeight);
// Set parameters for 24-bit color mode
#if IS_RGB24 == 1
disp.isrgb24 = IS_RGB24;
disp.disp.w = disp.disp.w*2/3;
#endif
VSync(0); // VSync to avoid screen tearing
PutDispEnv(&disp); // Apply the video parameters
SetDispMask(1); // Remove the display mask
if(strPlayDone == 1) {
break;
}
if(PadRead(1) & PADstart) { // stop button pressed exit animation routine
break;
}
}
// Shutdown streaming
DecDCToutCallback(0);
StUnSetRing();
CdControlB(CdlPause, 0, 0);
}
static void strCallback() {
/*
Callback routine which is called whenever a slice has finished decoding.
All it does is transfer the decoded slice into VRAM.
*/
RECT TransferRect;
int id;
// In 24-bit color, StCdInterrupt must be called in every callback
#if IS_RGB24==1
extern u_long StCdIntrFlag;
if (StCdIntrFlag) {
StCdInterrupt();
StCdIntrFlag = 0;
}
#endif
id = strEnv.ImgID;
TransferRect = strEnv.slice;
// Switch slice buffers
strEnv.ImgID = strEnv.ImgID? 0:1;
// Step to next slice
strEnv.slice.x += strEnv.slice.w;
// Frame not yet decoded completely?
if (strEnv.slice.x < strEnv.rect[strEnv.RectID].x + strEnv.rect[strEnv.RectID].w) {
// Prepare for next slice
DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2);
} else { // Frame has been decoded completely
// Set the FrameDone flag
strEnv.FrameDone = 1;
// Switch display buffers
strEnv.RectID = strEnv.RectID? 0: 1;
strEnv.slice.x = strEnv.rect[strEnv.RectID].x;
strEnv.slice.y = strEnv.rect[strEnv.RectID].y;
}
// Transfer the slice into VRAM
LoadImage(&TransferRect, (u_long *)strEnv.ImgBuff_ptr[id]);
}
static void strNextVlc(STRENV *strEnv) {
/*
Performs VLC decoding and grabs a frame from the stream.
*/
int cnt=WAIT_TIME;
u_long *next;
u_long *strNext();
// Grab a frame from the stream
while ((next = strNext(strEnv)) == 0) {
if (--cnt == 0) // Timeout handler
return;
}
// Switch VLC buffers
strEnv->VlcID = strEnv->VlcID? 0: 1;
// Decode the VLC
DecDCTvlc(next, strEnv->VlcBuff_ptr[strEnv->VlcID]);
// Free the ring buffer
StFreeRing(next);
}
static u_long *strNext(STRENV *strEnv) {
/*
Grabs a frame of video from the stream.
*/
u_long *addr;
StHEADER *sector;
int cnt = WAIT_TIME;
// Grab a frame
while (StGetNext((u_long **)&addr,(u_long **)&sector)) {
if (--cnt == 0) // Timeout handler
return(0);
}
// If the frame's number has reached number of frames the video has,
// set the strPlayDone flag.
if (sector->frameCount >= strNumFrames)
strPlayDone = 1;
// if the resolution is differ to previous frame, clear frame buffer
if (strFrameWidth != sector->width || strFrameHeight != sector->height) {
RECT rect;
setRECT(&rect, 0, 0, strScreenWidth * PPW, strScreenHeight*2);
ClearImage(&rect, 0, 0, 0);
strFrameWidth = sector->width;
strFrameHeight = sector->height;
}
// set STRENV according to the data on the STR format
strEnv->rect[0].w = strEnv->rect[1].w = strFrameWidth*PPW;
strEnv->rect[0].h = strEnv->rect[1].h = strFrameHeight;
strEnv->slice.h = strFrameHeight;
return(addr);
}
static void strSync(STRENV *strEnv, int mode) {
/*
Waits for the frame to finish decoding.
*/
u_long cnt = WAIT_TIME;
// Wait for the frame to finish decoding
while (strEnv->FrameDone == 0) {
if (--cnt == 0) { // Timeout handler
// If a timeout occurs, force switching buffers
#ifdef DEBUG
printf("ERROR: A frame cannot be played!\n");
#endif
strEnv->FrameDone = 1;
strEnv->RectID = strEnv->RectID? 0: 1;
strEnv->slice.x = strEnv->rect[strEnv->RectID].x;
strEnv->slice.y = strEnv->rect[strEnv->RectID].y;
}
}
strEnv->FrameDone = 0;
}
static void strKickCD(CdlLOC *loc) {
/*
Begins CD streaming.
*/
u_char param=CdlModeSpeed;
loop:
// Seek to the STR file to play
while (CdControl(CdlSetloc, (u_char *)loc, 0) == 0);
while (CdControl(CdlSetmode, &param, 0) == 0);
VSync(3); // Wait for 3 screen cycles before changing drive speed
// Start streaming
if(CdRead2(CdlModeStream|CdlModeSpeed|CdlModeRT) == 0)
goto loop; // If it fails, try again
}

4
hello_strplay/system.cnf Normal file
View File

@ -0,0 +1,4 @@
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0