Thank you so much for this! It will be fantastic to have this immediately available to all KOS users.


Thank you so much for this! It will be fantastic to have this immediately available to all KOS users.
I think it's possible.Ian Robinson wrote: ↑Wed Sep 20, 2023 8:46 am Do you think with this it would be possible to add function for pause/resume a movie ? ..
That's great news!GyroVorbis wrote: ↑Wed Sep 20, 2023 2:57 pm Oh yeah, this is totally expected. After all, you just barely got the thing up and working! I figured there would be some polishing that would need to be done. I've added a task to the KOS backlog for helping to get this ready to be added as a KOS-Port!![]()
Code: Select all
#include <kos.h>
#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg.h"
#include "mpeg1.h"
static plm_t *plm;
/* textures */
static pvr_ptr_t texture;
static int width, height;
// Output texture width and height initial values
// You can choose from 32, 64, 128, 256, 512, 1024
#define MPEG1_TEXTURE_WIDTH 512
#define MPEG1_TEXTURE_HEIGHT 256
snd_stream_hnd_t snd_hnd;
__attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4];
void display_draw(void)
{
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
pvr_vertex_t vert;
float u = (float)width / (float)MPEG1_TEXTURE_WIDTH;
float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT;
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR);
pvr_poly_compile(&hdr, &cxt);
// hdr.mode3 |= 0x02000000; /* stride */
pvr_prim(&hdr, sizeof(hdr));
vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert.oargb = 0;
vert.flags = PVR_CMD_VERTEX;
vert.x = 1;
vert.y = 1;
vert.z = 1;
vert.u = 0.0f;
vert.v = 0.0f;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 1;
vert.z = 1;
vert.u = u;
vert.v = 0.0;
pvr_prim(&vert, sizeof(vert));
vert.x = 1;
vert.y = 480;
vert.z = 1;
vert.u = 0.0f;
vert.v = v;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 480;
vert.z = 1;
vert.u = u;
vert.v = v;
vert.flags = PVR_CMD_VERTEX_EOL;
pvr_prim(&vert, sizeof(vert));
}
void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user)
{
unsigned int *dest = (unsigned int *)texture;
unsigned int *src = (unsigned int *)frame->display;
volatile unsigned int *d = (volatile unsigned int *)0xa05f8148;
volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c;
volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4;
int stride_value;
int stride = 0;
int x, y, w, h, i;
if (!frame)
return;
/* set frame size. */
w = frame->width >> 4;
h = frame->height >> 4;
stride_value = (w >> 1); /* 16 pixel / 2 */
/* Set Stride value. */
*stride_reg &= 0xffffffe0;
*stride_reg |= stride_value & 0x01f;
/* Set SQ to YUV converter. */
*d = ((unsigned int)dest) & 0xffffff;
*cfg = 0x00000f1f;
x = *cfg; /* read on once */
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++, src += 96)
{
sq_cpy((void *)0x10800000, (void *)src, 384);
}
if (!stride)
{
/* Send dummy mb */
for (i = 0; i < 32 - w; i++)
{
sq_set((void *)0x10800000, 0, 384);
}
}
}
for (i = 0; i < 16 - h; i++)
{
if (!stride)
sq_set((void *)0x10800000, 0, 384 * 32);
else
sq_set((void *)0x10800000, 0, 384 * w);
}
}
// Define a buffer for audio samples
#define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed
// Static variables for sound callback
static int mod_start = 0;
static int mod_size = 0;
static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE];
// Function to reset sound callback's static variables
void reset_sound_callback()
{
mod_start = 0;
mod_size = 0;
}
// Sound callback function
void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out)
{
unsigned int *dest = audio_sample_buffer;
unsigned int *src;
int out = 0;
src = audio_sample_buffer + mod_start / 4;
for (int i = 0; i < mod_size / 4; i++)
*dest++ = *src++;
out += mod_size;
while (size > out)
{
plm_samples_t *sample = plm_decode_audio(plm);
if (sample == NULL)
{
reset_sound_callback(); // Reset static variables when audio ends
break;
}
src = (unsigned int *)sample->pcm;
for (int i = 0; i < 1152 / 2; i++)
*dest++ = *src++;
out += 1152 * 2;
}
mod_start = size;
mod_size = out - size;
*size_out = size;
return (void *)audio_sample_buffer;
}
int Mpeg1Play(const char *filename, unsigned int buttons)
{
int cancel = 0;
plm = plm_create_with_filename(filename);
if (!plm)
return -1;
texture = pvr_mem_malloc(512 * 256 * 2);
width = plm_get_width(plm);
height = plm_get_height(plm);
/* Set SQ to YUV converter. */
PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff));
// Divide texture width and texture height by 16 and subtract 1.
// The actual values to set are 1, 3, 7, 15, 31, 63.
PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1));
PVR_GET(PVR_YUV_CFG_1);
/* First frame */
plm_frame_t *frame = plm_decode_video(plm);
float start_time = (float)timer_ms_gettime64() / 1000.0;
float playing_time = 0.0f;
float frame_time = 1.0f / (float)plm_get_framerate(plm);
int decoded = 1;
/* Init sound stream. */
int samplerate = plm_get_samplerate(plm);
snd_hnd = snd_stream_alloc(sound_callback, 0x10000);
snd_stream_volume(snd_hnd, 0xff);
snd_stream_queue_enable(snd_hnd);
snd_stream_start(snd_hnd, samplerate, 0);
snd_stream_queue_go(snd_hnd);
while (!cancel)
{
/* Check cancel buttons. */
if (buttons)
{
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if ((st->buttons & buttons) == buttons)
cancel = 1;
MAPLE_FOREACH_END()
}
/* Decode */
playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time;
if ((frame->time - playing_time) < frame_time)
{
frame = plm_decode_video(plm);
if (!frame)
break;
decoded = 1;
}
snd_stream_poll(snd_hnd);
/* Render */
pvr_wait_ready();
pvr_scene_begin();
if (decoded)
{
app_on_video(plm, frame, 0);
decoded = 0;
}
pvr_list_begin(PVR_LIST_OP_POLY);
display_draw();
pvr_list_finish();
pvr_scene_finish();
}
plm_destroy(plm);
pvr_mem_free(texture);
snd_stream_destroy(snd_hnd);
return 0;
}
Code: Select all
ffmpeg -i input.mp4 -vf "scale=320:240" -b:v 800k -ac 1 -ar 44100 -b:a 80k -f mpeg output.mpeg
Thank you for the necessary correction.Ian Robinson wrote: ↑Thu Sep 21, 2023 9:37 am Ok i worked on tighter frame control and resetting the sound call back and adding a buffer to keep it more in sync on longer videos
Here are the changes look it over for me ..
seems to be working correct. always good to have other eyes. I notice running from a cdr not /rd/ the audio getting out of sync and out of no where it gets super slow. So far this fixes this problem.Code: Select all
#include <kos.h> #define PL_MPEG_IMPLEMENTATION #include "pl_mpeg.h" #include "mpeg1.h" static plm_t *plm; /* textures */ static pvr_ptr_t texture; static int width, height; // Output texture width and height initial values // You can choose from 32, 64, 128, 256, 512, 1024 #define MPEG1_TEXTURE_WIDTH 512 #define MPEG1_TEXTURE_HEIGHT 256 snd_stream_hnd_t snd_hnd; __attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4]; void display_draw(void) { pvr_poly_cxt_t cxt; pvr_poly_hdr_t hdr; pvr_vertex_t vert; float u = (float)width / (float)MPEG1_TEXTURE_WIDTH; float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT; pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR); pvr_poly_compile(&hdr, &cxt); // hdr.mode3 |= 0x02000000; /* stride */ pvr_prim(&hdr, sizeof(hdr)); vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); vert.oargb = 0; vert.flags = PVR_CMD_VERTEX; vert.x = 1; vert.y = 1; vert.z = 1; vert.u = 0.0f; vert.v = 0.0f; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 1; vert.z = 1; vert.u = u; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = 1; vert.y = 480; vert.z = 1; vert.u = 0.0f; vert.v = v; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 480; vert.z = 1; vert.u = u; vert.v = v; vert.flags = PVR_CMD_VERTEX_EOL; pvr_prim(&vert, sizeof(vert)); } void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { unsigned int *dest = (unsigned int *)texture; unsigned int *src = (unsigned int *)frame->display; volatile unsigned int *d = (volatile unsigned int *)0xa05f8148; volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c; volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4; int stride_value; int stride = 0; int x, y, w, h, i; if (!frame) return; /* set frame size. */ w = frame->width >> 4; h = frame->height >> 4; stride_value = (w >> 1); /* 16 pixel / 2 */ /* Set Stride value. */ *stride_reg &= 0xffffffe0; *stride_reg |= stride_value & 0x01f; /* Set SQ to YUV converter. */ *d = ((unsigned int)dest) & 0xffffff; *cfg = 0x00000f1f; x = *cfg; /* read on once */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++, src += 96) { sq_cpy((void *)0x10800000, (void *)src, 384); } if (!stride) { /* Send dummy mb */ for (i = 0; i < 32 - w; i++) { sq_set((void *)0x10800000, 0, 384); } } } for (i = 0; i < 16 - h; i++) { if (!stride) sq_set((void *)0x10800000, 0, 384 * 32); else sq_set((void *)0x10800000, 0, 384 * w); } } // Define a buffer for audio samples #define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed // Static variables for sound callback static int mod_start = 0; static int mod_size = 0; static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE]; // Function to reset sound callback's static variables void reset_sound_callback() { mod_start = 0; mod_size = 0; } // Sound callback function void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out) { unsigned int *dest = audio_sample_buffer; unsigned int *src; int out = 0; src = audio_sample_buffer + mod_start / 4; for (int i = 0; i < mod_size / 4; i++) *dest++ = *src++; out += mod_size; while (size > out) { plm_samples_t *sample = plm_decode_audio(plm); if (sample == NULL) { reset_sound_callback(); // Reset static variables when audio ends break; } src = (unsigned int *)sample->pcm; for (int i = 0; i < 1152 / 2; i++) *dest++ = *src++; out += 1152 * 2; } mod_start = size; mod_size = out - size; *size_out = size; return (void *)audio_sample_buffer; } int Mpeg1Play(const char *filename, unsigned int buttons) { int cancel = 0; plm = plm_create_with_filename(filename); if (!plm) return -1; texture = pvr_mem_malloc(512 * 256 * 2); width = plm_get_width(plm); height = plm_get_height(plm); /* Set SQ to YUV converter. */ PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff)); // Divide texture width and texture height by 16 and subtract 1. // The actual values to set are 1, 3, 7, 15, 31, 63. PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1)); PVR_GET(PVR_YUV_CFG_1); /* First frame */ plm_frame_t *frame = plm_decode_video(plm); float start_time = (float)timer_ms_gettime64() / 1000.0; float playing_time = 0.0f; float frame_time = 1.0f / (float)plm_get_framerate(plm); int decoded = 1; /* Init sound stream. */ int samplerate = plm_get_samplerate(plm); snd_hnd = snd_stream_alloc(sound_callback, 0x10000); snd_stream_volume(snd_hnd, 0xff); snd_stream_queue_enable(snd_hnd); snd_stream_start(snd_hnd, samplerate, 0); snd_stream_queue_go(snd_hnd); while (!cancel) { /* Check cancel buttons. */ if (buttons) { MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) if ((st->buttons & buttons) == buttons) cancel = 1; MAPLE_FOREACH_END() } /* Decode */ playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time; if ((frame->time - playing_time) < frame_time) { frame = plm_decode_video(plm); if (!frame) break; decoded = 1; } snd_stream_poll(snd_hnd); /* Render */ pvr_wait_ready(); pvr_scene_begin(); if (decoded) { app_on_video(plm, frame, 0); decoded = 0; } pvr_list_begin(PVR_LIST_OP_POLY); display_draw(); pvr_list_finish(); pvr_scene_finish(); } plm_destroy(plm); pvr_mem_free(texture); snd_stream_destroy(snd_hnd); return 0; }
also for a cdrCode: Select all
ffmpeg -i input.mp4 -vf "scale=320:240" -b:v 800k -ac 1 -ar 44100 -b:a 80k -f mpeg output.mpeg
Code: Select all
#include <kos.h>
#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg.h"
#include "mpeg1.h"
static plm_t *plm;
/* textures */
static pvr_ptr_t texture;
static int width, height;
// Output texture width and height initial values
// You can choose from 32, 64, 128, 256, 512, 1024
#define MPEG1_TEXTURE_WIDTH 512
#define MPEG1_TEXTURE_HEIGHT 256
snd_stream_hnd_t snd_hnd;
__attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4];
void display_draw(void)
{
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
pvr_vertex_t vert;
float u = (float)width / (float)MPEG1_TEXTURE_WIDTH;
float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT;
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR);
pvr_poly_compile(&hdr, &cxt);
// hdr.mode3 |= 0x02000000; /* stride */
pvr_prim(&hdr, sizeof(hdr));
vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert.oargb = 0;
vert.flags = PVR_CMD_VERTEX;
vert.x = 1;
vert.y = 1;
vert.z = 1;
vert.u = 0.0f;
vert.v = 0.0f;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 1;
vert.z = 1;
vert.u = u;
vert.v = 0.0;
pvr_prim(&vert, sizeof(vert));
vert.x = 1;
vert.y = 480;
vert.z = 1;
vert.u = 0.0f;
vert.v = v;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 480;
vert.z = 1;
vert.u = u;
vert.v = v;
vert.flags = PVR_CMD_VERTEX_EOL;
pvr_prim(&vert, sizeof(vert));
}
void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user)
{
unsigned int *dest = (unsigned int *)texture;
unsigned int *src = (unsigned int *)frame->display;
volatile unsigned int *d = (volatile unsigned int *)0xa05f8148;
volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c;
volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4;
int stride_value;
int stride = 0;
int x, y, w, h, i;
if (!frame)
return;
/* set frame size. */
w = frame->width >> 4;
h = frame->height >> 4;
stride_value = (w >> 1); /* 16 pixel / 2 */
/* Set Stride value. */
*stride_reg &= 0xffffffe0;
*stride_reg |= stride_value & 0x01f;
/* Set SQ to YUV converter. */
*d = ((unsigned int)dest) & 0xffffff;
*cfg = 0x00000f1f;
x = *cfg; /* read on once */
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++, src += 96)
{
sq_cpy((void *)0x10800000, (void *)src, 384);
}
if (!stride)
{
/* Send dummy mb */
for (i = 0; i < 32 - w; i++)
{
sq_set((void *)0x10800000, 0, 384);
}
}
}
for (i = 0; i < 16 - h; i++)
{
if (!stride)
sq_set((void *)0x10800000, 0, 384 * 32);
else
sq_set((void *)0x10800000, 0, 384 * w);
}
}
// Define a buffer for audio samples
#define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed
// Static variables for sound callback
static int mod_start = 0;
static int mod_size = 0;
static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE];
// Function to reset sound callback's static variables
void reset_sound_callback()
{
mod_start = 0;
mod_size = 0;
}
// Sound callback function
void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out)
{
unsigned int *dest = audio_sample_buffer;
unsigned int *src;
int out = 0;
src = audio_sample_buffer + mod_start / 4;
for (int i = 0; i < mod_size / 4; i++)
*dest++ = *src++;
out += mod_size;
while (size > out)
{
plm_samples_t *sample = plm_decode_audio(plm);
if (sample == NULL)
{
reset_sound_callback(); // Reset static variables when audio ends
break;
}
src = (unsigned int *)sample->pcm;
for (int i = 0; i < 1152 / 2; i++)
*dest++ = *src++;
out += 1152 * 2;
}
mod_start = size;
mod_size = out - size;
*size_out = size;
return (void *)audio_sample_buffer;
}
int Mpeg1Play(const char *filename, unsigned int buttons)
{
int cancel = 0;
plm = plm_create_with_filename(filename);
if (!plm)
return -1;
texture = pvr_mem_malloc(512 * 256 * 2);
width = plm_get_width(plm);
height = plm_get_height(plm);
/* Set SQ to YUV converter. */
PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff));
// Divide texture width and texture height by 16 and subtract 1.
// The actual values to set are 1, 3, 7, 15, 31, 63.
PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1));
PVR_GET(PVR_YUV_CFG_1);
/* First frame */
plm_frame_t *frame = plm_decode_video(plm);
float start_time = (float)timer_ms_gettime64() / 1000.0;
float playing_time = 0.0f;
float frame_time = 1.0f / (float)plm_get_framerate(plm);
int decoded = 1;
reset_sound_callback();
/* Init sound stream. */
int samplerate = plm_get_samplerate(plm);
snd_hnd = snd_stream_alloc(sound_callback, 0x10000);
snd_stream_volume(snd_hnd, 0xff);
snd_stream_queue_enable(snd_hnd);
snd_stream_start(snd_hnd, samplerate, 0);
snd_stream_queue_go(snd_hnd);
while (!cancel)
{
/* Check cancel buttons. */
if (buttons)
{
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if ((st->buttons & buttons) == buttons)
cancel = 1;
MAPLE_FOREACH_END()
}
/* Decode */
playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time;
if ((frame->time - playing_time) < frame_time)
{
frame = plm_decode_video(plm);
if (!frame)
break;
decoded = 1;
}
snd_stream_poll(snd_hnd);
/* Render */
pvr_wait_ready();
pvr_scene_begin();
if (decoded)
{
app_on_video(plm, frame, 0);
decoded = 0;
}
pvr_list_begin(PVR_LIST_OP_POLY);
display_draw();
pvr_list_finish();
pvr_scene_finish();
}
reset_sound_callback();
plm_destroy(plm);
pvr_mem_free(texture);
snd_stream_destroy(snd_hnd);
return 0;
}
Code: Select all
#include <kos.h>
#include <string.h>
#include <stdio.h>
#include <arch/arch.h>
#include "mpeg1.h"
/* romdisk */
extern uint8 romdisk_boot[];
KOS_INIT_ROMDISK(romdisk_boot);
int main(void)
{
pvr_init_defaults();
snd_stream_init();
Mpeg1Play("/cd/zample.mpg", CONT_START);
//Ian micheal now exits at the end of video
snd_stream_shutdown();
spu_disable(); // Disable the Sound Processing Unit (SPU).
arch_exit(); // Perform system-specific exit operations.
return 0;
}
Ian Robinson wrote: ↑Thu Sep 21, 2023 8:47 pm Ok this played my test video and stayed on sync
correct me where i went wrong right now it's the first time it played
the matrix trailer and did not slow downexample.cCode: Select all
#include <kos.h> #define PL_MPEG_IMPLEMENTATION #include "pl_mpeg.h" #include "mpeg1.h" static plm_t *plm; /* textures */ static pvr_ptr_t texture; static int width, height; // Output texture width and height initial values // You can choose from 32, 64, 128, 256, 512, 1024 #define MPEG1_TEXTURE_WIDTH 512 #define MPEG1_TEXTURE_HEIGHT 256 snd_stream_hnd_t snd_hnd; __attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4]; void display_draw(void) { pvr_poly_cxt_t cxt; pvr_poly_hdr_t hdr; pvr_vertex_t vert; float u = (float)width / (float)MPEG1_TEXTURE_WIDTH; float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT; pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR); pvr_poly_compile(&hdr, &cxt); // hdr.mode3 |= 0x02000000; /* stride */ pvr_prim(&hdr, sizeof(hdr)); vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); vert.oargb = 0; vert.flags = PVR_CMD_VERTEX; vert.x = 1; vert.y = 1; vert.z = 1; vert.u = 0.0f; vert.v = 0.0f; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 1; vert.z = 1; vert.u = u; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = 1; vert.y = 480; vert.z = 1; vert.u = 0.0f; vert.v = v; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 480; vert.z = 1; vert.u = u; vert.v = v; vert.flags = PVR_CMD_VERTEX_EOL; pvr_prim(&vert, sizeof(vert)); } void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { unsigned int *dest = (unsigned int *)texture; unsigned int *src = (unsigned int *)frame->display; volatile unsigned int *d = (volatile unsigned int *)0xa05f8148; volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c; volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4; int stride_value; int stride = 0; int x, y, w, h, i; if (!frame) return; /* set frame size. */ w = frame->width >> 4; h = frame->height >> 4; stride_value = (w >> 1); /* 16 pixel / 2 */ /* Set Stride value. */ *stride_reg &= 0xffffffe0; *stride_reg |= stride_value & 0x01f; /* Set SQ to YUV converter. */ *d = ((unsigned int)dest) & 0xffffff; *cfg = 0x00000f1f; x = *cfg; /* read on once */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++, src += 96) { sq_cpy((void *)0x10800000, (void *)src, 384); } if (!stride) { /* Send dummy mb */ for (i = 0; i < 32 - w; i++) { sq_set((void *)0x10800000, 0, 384); } } } for (i = 0; i < 16 - h; i++) { if (!stride) sq_set((void *)0x10800000, 0, 384 * 32); else sq_set((void *)0x10800000, 0, 384 * w); } } // Define a buffer for audio samples #define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed // Static variables for sound callback static int mod_start = 0; static int mod_size = 0; static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE]; // Function to reset sound callback's static variables void reset_sound_callback() { mod_start = 0; mod_size = 0; } // Sound callback function void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out) { unsigned int *dest = audio_sample_buffer; unsigned int *src; int out = 0; src = audio_sample_buffer + mod_start / 4; for (int i = 0; i < mod_size / 4; i++) *dest++ = *src++; out += mod_size; while (size > out) { plm_samples_t *sample = plm_decode_audio(plm); if (sample == NULL) { reset_sound_callback(); // Reset static variables when audio ends break; } src = (unsigned int *)sample->pcm; for (int i = 0; i < 1152 / 2; i++) *dest++ = *src++; out += 1152 * 2; } mod_start = size; mod_size = out - size; *size_out = size; return (void *)audio_sample_buffer; } int Mpeg1Play(const char *filename, unsigned int buttons) { int cancel = 0; plm = plm_create_with_filename(filename); if (!plm) return -1; texture = pvr_mem_malloc(512 * 256 * 2); width = plm_get_width(plm); height = plm_get_height(plm); /* Set SQ to YUV converter. */ PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff)); // Divide texture width and texture height by 16 and subtract 1. // The actual values to set are 1, 3, 7, 15, 31, 63. PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1)); PVR_GET(PVR_YUV_CFG_1); /* First frame */ plm_frame_t *frame = plm_decode_video(plm); float start_time = (float)timer_ms_gettime64() / 1000.0; float playing_time = 0.0f; float frame_time = 1.0f / (float)plm_get_framerate(plm); int decoded = 1; reset_sound_callback(); /* Init sound stream. */ int samplerate = plm_get_samplerate(plm); snd_hnd = snd_stream_alloc(sound_callback, 0x10000); snd_stream_volume(snd_hnd, 0xff); snd_stream_queue_enable(snd_hnd); snd_stream_start(snd_hnd, samplerate, 0); snd_stream_queue_go(snd_hnd); while (!cancel) { /* Check cancel buttons. */ if (buttons) { MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) if ((st->buttons & buttons) == buttons) cancel = 1; MAPLE_FOREACH_END() } /* Decode */ playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time; if ((frame->time - playing_time) < frame_time) { frame = plm_decode_video(plm); if (!frame) break; decoded = 1; } snd_stream_poll(snd_hnd); /* Render */ pvr_wait_ready(); pvr_scene_begin(); if (decoded) { app_on_video(plm, frame, 0); decoded = 0; } pvr_list_begin(PVR_LIST_OP_POLY); display_draw(); pvr_list_finish(); pvr_scene_finish(); } reset_sound_callback(); plm_destroy(plm); pvr_mem_free(texture); snd_stream_destroy(snd_hnd); return 0; }
Names starting with Z are the fastest when dummied.Code: Select all
#include <kos.h> #include <string.h> #include <stdio.h> #include <arch/arch.h> #include "mpeg1.h" /* romdisk */ extern uint8 romdisk_boot[]; KOS_INIT_ROMDISK(romdisk_boot); int main(void) { pvr_init_defaults(); snd_stream_init(); Mpeg1Play("/cd/zample.mpg", CONT_START); //Ian micheal now exits at the end of video snd_stream_shutdown(); spu_disable(); // Disable the Sound Processing Unit (SPU). arch_exit(); // Perform system-specific exit operations. return 0; }
Before these changes the music would always complete first. Ok still a few places it goes out of sync but much better https://streamable.com/qtn6s4 .. I have done the ring buffer thing before i will see what i can do with that.. Threading did not make it better i tried and it made it crash all the time.. 5 hours wasted!
I will leave your version and not update it till you say so i made a folder with the work today now exits to dcload serial when the video ends it did not before.
https://github.com/ianmicheal/MPEG1-Dec ... updates%5D marked where the changes are in this folder.
Developing a cdrom/gdrom benchmark tool which will show you all speeds of all files on the disc almost done..It benchmarks the disc captures the log to the vmu and can read back with the pvr log viewer with scrolling..
Toolmenu https://streamable.com/5ub9iz
Pvr vmu log viewer https://streamable.com/eb2sp9
Benchmark menu https://streamable.com/lrn7av
It will load the texture of the game benchmark then save to the vmu..
I did this to see speed on a cdr where the best place and names are for media files..There is a very big change depending on how you create and order the files.
Code: Select all
/* Check cancel buttons. */
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if (buttons && ((st->buttons & buttons) == buttons))
cancel = 1; /* Push cancel buttons */
if (st->buttons == 0x60e)
cancel = 2; /* ABXY + START (Software reset) */
MAPLE_FOREACH_END()
...
...
return cancel;
Code: Select all
#include <kos.h>
#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg.h"
#include "mpeg1.h"
static plm_t *plm;
/* textures */
static pvr_ptr_t texture;
static int width, height;
// Output texture width and height initial values
// You can choose from 32, 64, 128, 256, 512, 1024
#define MPEG1_TEXTURE_WIDTH 512
#define MPEG1_TEXTURE_HEIGHT 256
snd_stream_hnd_t snd_hnd;
__attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4];
// Structure to represent a video frame
typedef struct {
plm_frame_t *frame;
} VideoFrame;
//Ian micheal Implemented a ringbuffer version
// Ring buffer to hold video frames
#define RING_BUFFER_SIZE 10
static VideoFrame ringBuffer[RING_BUFFER_SIZE];
static int ringBufferHead = 0;
static int ringBufferTail = 0;
// Function to initialize the ring buffer
void initRingBuffer() {
ringBufferHead = 0;
ringBufferTail = 0;
memset(ringBuffer, 0, sizeof(ringBuffer));
}
// Function to enqueue a video frame into the ring buffer
void enqueueFrame(plm_frame_t *frame) {
if ((ringBufferHead + 1) % RING_BUFFER_SIZE != ringBufferTail) {
ringBuffer[ringBufferHead].frame = frame;
ringBufferHead = (ringBufferHead + 1) % RING_BUFFER_SIZE;
}
}
// Function to dequeue a video frame from the ring buffer
plm_frame_t *dequeueFrame() {
plm_frame_t *frame = NULL;
if (ringBufferHead != ringBufferTail) {
frame = ringBuffer[ringBufferTail].frame;
ringBufferTail = (ringBufferTail + 1) % RING_BUFFER_SIZE;
}
return frame;
}
void display_draw(void)
{
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
pvr_vertex_t vert;
float u = (float)width / (float)MPEG1_TEXTURE_WIDTH;
float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT;
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR);
pvr_poly_compile(&hdr, &cxt);
// hdr.mode3 |= 0x02000000; /* stride */
pvr_prim(&hdr, sizeof(hdr));
vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert.oargb = 0;
vert.flags = PVR_CMD_VERTEX;
vert.x = 1;
vert.y = 1;
vert.z = 1;
vert.u = 0.0f;
vert.v = 0.0f;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 1;
vert.z = 1;
vert.u = u;
vert.v = 0.0;
pvr_prim(&vert, sizeof(vert));
vert.x = 1;
vert.y = 480;
vert.z = 1;
vert.u = 0.0f;
vert.v = v;
pvr_prim(&vert, sizeof(vert));
vert.x = 640;
vert.y = 480;
vert.z = 1;
vert.u = u;
vert.v = v;
vert.flags = PVR_CMD_VERTEX_EOL;
pvr_prim(&vert, sizeof(vert));
}
void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user)
{
unsigned int *dest = (unsigned int *)texture;
unsigned int *src = (unsigned int *)frame->display;
volatile unsigned int *d = (volatile unsigned int *)0xa05f8148;
volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c;
volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4;
int stride_value;
int stride = 0;
int x, y, w, h, i;
if (!frame)
return;
/* set frame size. */
w = frame->width >> 4;
h = frame->height >> 4;
stride_value = (w >> 1); /* 16 pixel / 2 */
/* Set Stride value. */
*stride_reg &= 0xffffffe0;
*stride_reg |= stride_value & 0x01f;
/* Set SQ to YUV converter. */
*d = ((unsigned int)dest) & 0xffffff;
*cfg = 0x00000f1f;
x = *cfg; /* read on once */
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++, src += 96)
{
sq_cpy((void *)0x10800000, (void *)src, 384);
}
if (!stride)
{
/* Send dummy mb */
for (i = 0; i < 32 - w; i++)
{
sq_set((void *)0x10800000, 0, 384);
}
}
}
for (i = 0; i < 16 - h; i++)
{
if (!stride)
sq_set((void *)0x10800000, 0, 384 * 32);
else
sq_set((void *)0x10800000, 0, 384 * w);
}
}
// Define a buffer for audio samples
#define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed
// Static variables for sound callback
static int mod_start = 0;
static int mod_size = 0;
static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE];
//Ian micheal added call back sound reset and buffer
// Function to reset sound callback's static variables
void reset_sound_callback()
{
mod_start = 0;
mod_size = 0;
}
// Sound callback function
void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out)
{
unsigned int *dest = audio_sample_buffer;
unsigned int *src;
int out = 0;
src = audio_sample_buffer + mod_start / 4;
for (int i = 0; i < mod_size / 4; i++)
*dest++ = *src++;
out += mod_size;
while (size > out)
{
plm_samples_t *sample = plm_decode_audio(plm);
if (sample == NULL)
{
reset_sound_callback(); // Reset static variables when audio ends
break;
}
src = (unsigned int *)sample->pcm;
for (int i = 0; i < 1152 / 2; i++)
*dest++ = *src++;
out += 1152 * 2;
}
mod_start = size;
mod_size = out - size;
*size_out = size;
return (void *)audio_sample_buffer;
}
int Mpeg1Play(const char *filename, unsigned int buttons)
{
int cancel = 0;
plm = plm_create_with_filename(filename);
if (!plm)
return -1;
texture = pvr_mem_malloc(512 * 256 * 2);
width = plm_get_width(plm);
height = plm_get_height(plm);
// Initialize the ring buffer
initRingBuffer();
/* Set SQ to YUV converter. */
PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff));
// Divide texture width and texture height by 16 and subtract 1.
// The actual values to set are 1, 3, 7, 15, 31, 63.
PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1));
PVR_GET(PVR_YUV_CFG_1);
/* First frame */
plm_frame_t *frame = plm_decode_video(plm);
float start_time = (float)timer_ms_gettime64() / 1000.0;
float playing_time = 0.0f;
float frame_time = 1.0f / (float)plm_get_framerate(plm);
int decoded = 1;
//Ian micheal added call back sound reset and buffer
reset_sound_callback();
/* Init sound stream. */
int samplerate = plm_get_samplerate(plm);
snd_hnd = snd_stream_alloc(sound_callback, 0x10000);
snd_stream_volume(snd_hnd, 0xff);
snd_stream_queue_enable(snd_hnd);
snd_stream_start(snd_hnd, samplerate, 0);
snd_stream_queue_go(snd_hnd);
while (!cancel)
{
/* Check cancel buttons. */
if (buttons)
{
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if ((st->buttons & buttons) == buttons)
cancel = 1;
MAPLE_FOREACH_END()
}
/* Decode */
playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time;
if ((frame->time - playing_time) < frame_time)
{
frame = plm_decode_video(plm);
if (!frame)
break;
decoded = 1;
// Enqueue the frame into the ring buffer
enqueueFrame(frame);
}
snd_stream_poll(snd_hnd);
/* Render */
pvr_wait_ready();
pvr_scene_begin();
if (decoded)
{
frame = dequeueFrame();
if (frame) {
app_on_video(plm, frame, 0);
}
decoded = 0;
}
pvr_list_begin(PVR_LIST_OP_POLY);
display_draw();
pvr_list_finish();
pvr_scene_finish();
}
//Ian micheal added call back sound reset and buffer
reset_sound_callback();
plm_destroy(plm);
pvr_mem_free(texture);
snd_stream_destroy(snd_hnd);
return 0;
}
Oh, cool ring buffer!Ian Robinson wrote: ↑Fri Sep 22, 2023 5:10 pm Implemented a ringbuffer versionworking about the same but slightly betterCode: Select all
#include <kos.h> #define PL_MPEG_IMPLEMENTATION #include "pl_mpeg.h" #include "mpeg1.h" static plm_t *plm; /* textures */ static pvr_ptr_t texture; static int width, height; // Output texture width and height initial values // You can choose from 32, 64, 128, 256, 512, 1024 #define MPEG1_TEXTURE_WIDTH 512 #define MPEG1_TEXTURE_HEIGHT 256 snd_stream_hnd_t snd_hnd; __attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4]; // Structure to represent a video frame typedef struct { plm_frame_t *frame; } VideoFrame; //Ian micheal Implemented a ringbuffer version // Ring buffer to hold video frames #define RING_BUFFER_SIZE 10 static VideoFrame ringBuffer[RING_BUFFER_SIZE]; static int ringBufferHead = 0; static int ringBufferTail = 0; // Function to initialize the ring buffer void initRingBuffer() { ringBufferHead = 0; ringBufferTail = 0; memset(ringBuffer, 0, sizeof(ringBuffer)); } // Function to enqueue a video frame into the ring buffer void enqueueFrame(plm_frame_t *frame) { if ((ringBufferHead + 1) % RING_BUFFER_SIZE != ringBufferTail) { ringBuffer[ringBufferHead].frame = frame; ringBufferHead = (ringBufferHead + 1) % RING_BUFFER_SIZE; } } // Function to dequeue a video frame from the ring buffer plm_frame_t *dequeueFrame() { plm_frame_t *frame = NULL; if (ringBufferHead != ringBufferTail) { frame = ringBuffer[ringBufferTail].frame; ringBufferTail = (ringBufferTail + 1) % RING_BUFFER_SIZE; } return frame; } void display_draw(void) { pvr_poly_cxt_t cxt; pvr_poly_hdr_t hdr; pvr_vertex_t vert; float u = (float)width / (float)MPEG1_TEXTURE_WIDTH; float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT; pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR); pvr_poly_compile(&hdr, &cxt); // hdr.mode3 |= 0x02000000; /* stride */ pvr_prim(&hdr, sizeof(hdr)); vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); vert.oargb = 0; vert.flags = PVR_CMD_VERTEX; vert.x = 1; vert.y = 1; vert.z = 1; vert.u = 0.0f; vert.v = 0.0f; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 1; vert.z = 1; vert.u = u; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = 1; vert.y = 480; vert.z = 1; vert.u = 0.0f; vert.v = v; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 480; vert.z = 1; vert.u = u; vert.v = v; vert.flags = PVR_CMD_VERTEX_EOL; pvr_prim(&vert, sizeof(vert)); } void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { unsigned int *dest = (unsigned int *)texture; unsigned int *src = (unsigned int *)frame->display; volatile unsigned int *d = (volatile unsigned int *)0xa05f8148; volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c; volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4; int stride_value; int stride = 0; int x, y, w, h, i; if (!frame) return; /* set frame size. */ w = frame->width >> 4; h = frame->height >> 4; stride_value = (w >> 1); /* 16 pixel / 2 */ /* Set Stride value. */ *stride_reg &= 0xffffffe0; *stride_reg |= stride_value & 0x01f; /* Set SQ to YUV converter. */ *d = ((unsigned int)dest) & 0xffffff; *cfg = 0x00000f1f; x = *cfg; /* read on once */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++, src += 96) { sq_cpy((void *)0x10800000, (void *)src, 384); } if (!stride) { /* Send dummy mb */ for (i = 0; i < 32 - w; i++) { sq_set((void *)0x10800000, 0, 384); } } } for (i = 0; i < 16 - h; i++) { if (!stride) sq_set((void *)0x10800000, 0, 384 * 32); else sq_set((void *)0x10800000, 0, 384 * w); } } // Define a buffer for audio samples #define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed // Static variables for sound callback static int mod_start = 0; static int mod_size = 0; static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE]; //Ian micheal added call back sound reset and buffer // Function to reset sound callback's static variables void reset_sound_callback() { mod_start = 0; mod_size = 0; } // Sound callback function void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out) { unsigned int *dest = audio_sample_buffer; unsigned int *src; int out = 0; src = audio_sample_buffer + mod_start / 4; for (int i = 0; i < mod_size / 4; i++) *dest++ = *src++; out += mod_size; while (size > out) { plm_samples_t *sample = plm_decode_audio(plm); if (sample == NULL) { reset_sound_callback(); // Reset static variables when audio ends break; } src = (unsigned int *)sample->pcm; for (int i = 0; i < 1152 / 2; i++) *dest++ = *src++; out += 1152 * 2; } mod_start = size; mod_size = out - size; *size_out = size; return (void *)audio_sample_buffer; } int Mpeg1Play(const char *filename, unsigned int buttons) { int cancel = 0; plm = plm_create_with_filename(filename); if (!plm) return -1; texture = pvr_mem_malloc(512 * 256 * 2); width = plm_get_width(plm); height = plm_get_height(plm); // Initialize the ring buffer initRingBuffer(); /* Set SQ to YUV converter. */ PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff)); // Divide texture width and texture height by 16 and subtract 1. // The actual values to set are 1, 3, 7, 15, 31, 63. PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1)); PVR_GET(PVR_YUV_CFG_1); /* First frame */ plm_frame_t *frame = plm_decode_video(plm); float start_time = (float)timer_ms_gettime64() / 1000.0; float playing_time = 0.0f; float frame_time = 1.0f / (float)plm_get_framerate(plm); int decoded = 1; //Ian micheal added call back sound reset and buffer reset_sound_callback(); /* Init sound stream. */ int samplerate = plm_get_samplerate(plm); snd_hnd = snd_stream_alloc(sound_callback, 0x10000); snd_stream_volume(snd_hnd, 0xff); snd_stream_queue_enable(snd_hnd); snd_stream_start(snd_hnd, samplerate, 0); snd_stream_queue_go(snd_hnd); while (!cancel) { /* Check cancel buttons. */ if (buttons) { MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) if ((st->buttons & buttons) == buttons) cancel = 1; MAPLE_FOREACH_END() } /* Decode */ playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time; if ((frame->time - playing_time) < frame_time) { frame = plm_decode_video(plm); if (!frame) break; decoded = 1; // Enqueue the frame into the ring buffer enqueueFrame(frame); } snd_stream_poll(snd_hnd); /* Render */ pvr_wait_ready(); pvr_scene_begin(); if (decoded) { frame = dequeueFrame(); if (frame) { app_on_video(plm, frame, 0); } decoded = 0; } pvr_list_begin(PVR_LIST_OP_POLY); display_draw(); pvr_list_finish(); pvr_scene_finish(); } //Ian micheal added call back sound reset and buffer reset_sound_callback(); plm_destroy(plm); pvr_mem_free(texture); snd_stream_destroy(snd_hnd); return 0; }
https://github.com/ianmicheal/MPEG1-Dec ... ngbuffer.c
Right i was just trying it outTwada wrote: ↑Fri Sep 22, 2023 6:04 pmOh, cool ring buffer!Ian Robinson wrote: ↑Fri Sep 22, 2023 5:10 pm Implemented a ringbuffer versionworking about the same but slightly betterCode: Select all
#include <kos.h> #define PL_MPEG_IMPLEMENTATION #include "pl_mpeg.h" #include "mpeg1.h" static plm_t *plm; /* textures */ static pvr_ptr_t texture; static int width, height; // Output texture width and height initial values // You can choose from 32, 64, 128, 256, 512, 1024 #define MPEG1_TEXTURE_WIDTH 512 #define MPEG1_TEXTURE_HEIGHT 256 snd_stream_hnd_t snd_hnd; __attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4]; // Structure to represent a video frame typedef struct { plm_frame_t *frame; } VideoFrame; //Ian micheal Implemented a ringbuffer version // Ring buffer to hold video frames #define RING_BUFFER_SIZE 10 static VideoFrame ringBuffer[RING_BUFFER_SIZE]; static int ringBufferHead = 0; static int ringBufferTail = 0; // Function to initialize the ring buffer void initRingBuffer() { ringBufferHead = 0; ringBufferTail = 0; memset(ringBuffer, 0, sizeof(ringBuffer)); } // Function to enqueue a video frame into the ring buffer void enqueueFrame(plm_frame_t *frame) { if ((ringBufferHead + 1) % RING_BUFFER_SIZE != ringBufferTail) { ringBuffer[ringBufferHead].frame = frame; ringBufferHead = (ringBufferHead + 1) % RING_BUFFER_SIZE; } } // Function to dequeue a video frame from the ring buffer plm_frame_t *dequeueFrame() { plm_frame_t *frame = NULL; if (ringBufferHead != ringBufferTail) { frame = ringBuffer[ringBufferTail].frame; ringBufferTail = (ringBufferTail + 1) % RING_BUFFER_SIZE; } return frame; } void display_draw(void) { pvr_poly_cxt_t cxt; pvr_poly_hdr_t hdr; pvr_vertex_t vert; float u = (float)width / (float)MPEG1_TEXTURE_WIDTH; float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT; pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR); pvr_poly_compile(&hdr, &cxt); // hdr.mode3 |= 0x02000000; /* stride */ pvr_prim(&hdr, sizeof(hdr)); vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); vert.oargb = 0; vert.flags = PVR_CMD_VERTEX; vert.x = 1; vert.y = 1; vert.z = 1; vert.u = 0.0f; vert.v = 0.0f; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 1; vert.z = 1; vert.u = u; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = 1; vert.y = 480; vert.z = 1; vert.u = 0.0f; vert.v = v; pvr_prim(&vert, sizeof(vert)); vert.x = 640; vert.y = 480; vert.z = 1; vert.u = u; vert.v = v; vert.flags = PVR_CMD_VERTEX_EOL; pvr_prim(&vert, sizeof(vert)); } void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user) { unsigned int *dest = (unsigned int *)texture; unsigned int *src = (unsigned int *)frame->display; volatile unsigned int *d = (volatile unsigned int *)0xa05f8148; volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c; volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4; int stride_value; int stride = 0; int x, y, w, h, i; if (!frame) return; /* set frame size. */ w = frame->width >> 4; h = frame->height >> 4; stride_value = (w >> 1); /* 16 pixel / 2 */ /* Set Stride value. */ *stride_reg &= 0xffffffe0; *stride_reg |= stride_value & 0x01f; /* Set SQ to YUV converter. */ *d = ((unsigned int)dest) & 0xffffff; *cfg = 0x00000f1f; x = *cfg; /* read on once */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++, src += 96) { sq_cpy((void *)0x10800000, (void *)src, 384); } if (!stride) { /* Send dummy mb */ for (i = 0; i < 32 - w; i++) { sq_set((void *)0x10800000, 0, 384); } } } for (i = 0; i < 16 - h; i++) { if (!stride) sq_set((void *)0x10800000, 0, 384 * 32); else sq_set((void *)0x10800000, 0, 384 * w); } } // Define a buffer for audio samples #define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed // Static variables for sound callback static int mod_start = 0; static int mod_size = 0; static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE]; //Ian micheal added call back sound reset and buffer // Function to reset sound callback's static variables void reset_sound_callback() { mod_start = 0; mod_size = 0; } // Sound callback function void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out) { unsigned int *dest = audio_sample_buffer; unsigned int *src; int out = 0; src = audio_sample_buffer + mod_start / 4; for (int i = 0; i < mod_size / 4; i++) *dest++ = *src++; out += mod_size; while (size > out) { plm_samples_t *sample = plm_decode_audio(plm); if (sample == NULL) { reset_sound_callback(); // Reset static variables when audio ends break; } src = (unsigned int *)sample->pcm; for (int i = 0; i < 1152 / 2; i++) *dest++ = *src++; out += 1152 * 2; } mod_start = size; mod_size = out - size; *size_out = size; return (void *)audio_sample_buffer; } int Mpeg1Play(const char *filename, unsigned int buttons) { int cancel = 0; plm = plm_create_with_filename(filename); if (!plm) return -1; texture = pvr_mem_malloc(512 * 256 * 2); width = plm_get_width(plm); height = plm_get_height(plm); // Initialize the ring buffer initRingBuffer(); /* Set SQ to YUV converter. */ PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff)); // Divide texture width and texture height by 16 and subtract 1. // The actual values to set are 1, 3, 7, 15, 31, 63. PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1)); PVR_GET(PVR_YUV_CFG_1); /* First frame */ plm_frame_t *frame = plm_decode_video(plm); float start_time = (float)timer_ms_gettime64() / 1000.0; float playing_time = 0.0f; float frame_time = 1.0f / (float)plm_get_framerate(plm); int decoded = 1; //Ian micheal added call back sound reset and buffer reset_sound_callback(); /* Init sound stream. */ int samplerate = plm_get_samplerate(plm); snd_hnd = snd_stream_alloc(sound_callback, 0x10000); snd_stream_volume(snd_hnd, 0xff); snd_stream_queue_enable(snd_hnd); snd_stream_start(snd_hnd, samplerate, 0); snd_stream_queue_go(snd_hnd); while (!cancel) { /* Check cancel buttons. */ if (buttons) { MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st) if ((st->buttons & buttons) == buttons) cancel = 1; MAPLE_FOREACH_END() } /* Decode */ playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time; if ((frame->time - playing_time) < frame_time) { frame = plm_decode_video(plm); if (!frame) break; decoded = 1; // Enqueue the frame into the ring buffer enqueueFrame(frame); } snd_stream_poll(snd_hnd); /* Render */ pvr_wait_ready(); pvr_scene_begin(); if (decoded) { frame = dequeueFrame(); if (frame) { app_on_video(plm, frame, 0); } decoded = 0; } pvr_list_begin(PVR_LIST_OP_POLY); display_draw(); pvr_list_finish(); pvr_scene_finish(); } //Ian micheal added call back sound reset and buffer reset_sound_callback(); plm_destroy(plm); pvr_mem_free(texture); snd_stream_destroy(snd_hnd); return 0; }
https://github.com/ianmicheal/MPEG1-Dec ... ngbuffer.c
However, pl_mpeg only has a three frame buffer internally.
forward, backward, and current. pl_mpeg uses these three buffers interchangeably.
This order is the reference order, not the playback order. B frames are not referenced, so they never go forward and backward.
Therefore, if you hold a pointer in a ring buffer, the contents will be overwritten.
When implementing a ring buffer, I think you have to create a new buffer and copy its contents.
Code: Select all
#define UV_EPSILON 0.100f
#define screenWidth 640
#define screenHeight 480
void display_draw(void)
{
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
pvr_vertex_t vert;
float u = (float)width / (float)MPEG1_TEXTURE_WIDTH;
float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT;
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_BILINEAR);
pvr_poly_compile(&hdr, &cxt);
// hdr.mode3 |= 0x02000000; /* stride */
pvr_prim(&hdr, sizeof(hdr));
vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert.oargb = 0;
vert.flags = PVR_CMD_VERTEX;
vert.x = 1;
vert.y = 1;
vert.z = 1;
vert.u = UV_EPSILON;
vert.v = UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = screenWidth;
vert.y = 1;
vert.z = 1;
vert.u = u - UV_EPSILON;
vert.v = UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = 1;
vert.y = screenHeight;
vert.z = 1;
vert.u = UV_EPSILON;
vert.v = v - UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = screenWidth;
vert.y = screenHeight;
vert.z = 1;
vert.u = u - UV_EPSILON;
vert.v = v - UV_EPSILON;
vert.flags = PVR_CMD_VERTEX_EOL;
pvr_prim(&vert, sizeof(vert));
}
Code: Select all
#include <kos.h>
#define PL_MPEG_IMPLEMENTATION
#include "pl_mpeg.h"
#include "mpeg1.h"
static plm_t *plm;
/* textures */
static pvr_ptr_t texture;
static int width, height;
// Output texture width and height initial values
// You can choose from 32, 64, 128, 256, 512, 1024
#define MPEG1_TEXTURE_WIDTH 512
#define MPEG1_TEXTURE_HEIGHT 256
snd_stream_hnd_t snd_hnd;
__attribute__((aligned(32))) unsigned int snd_buf[0x10000 / 4];
// Structure to represent a video frame
typedef struct {
plm_frame_t *frame;
} VideoFrame;
//Ian micheal Implemented a ringbuffer version
// Ring buffer to hold video frames
#define RING_BUFFER_SIZE 20
static VideoFrame ringBuffer[RING_BUFFER_SIZE];
static int ringBufferHead = 0;
static int ringBufferTail = 0;
// Function to initialize the ring buffer
void initRingBuffer() {
ringBufferHead = 0;
ringBufferTail = 0;
memset(ringBuffer, 0, sizeof(ringBuffer));
}
// Function to enqueue a video frame into the ring buffer
void enqueueFrame(plm_frame_t *frame) {
if ((ringBufferHead + 1) % RING_BUFFER_SIZE != ringBufferTail) {
ringBuffer[ringBufferHead].frame = frame;
ringBufferHead = (ringBufferHead + 1) % RING_BUFFER_SIZE;
}
}
// Function to dequeue a video frame from the ring buffer
plm_frame_t *dequeueFrame() {
plm_frame_t *frame = NULL;
if (ringBufferHead != ringBufferTail) {
frame = ringBuffer[ringBufferTail].frame;
ringBufferTail = (ringBufferTail + 1) % RING_BUFFER_SIZE;
}
return frame;
}
#define UV_EPSILON 0.045f // Adjust the value as needed
#define screenWidth 640
#define screenHeight 480
void display_draw(void)
{
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
pvr_vertex_t vert;
float u = (float)width / (float)MPEG1_TEXTURE_WIDTH;
float v = (float)height / (float)MPEG1_TEXTURE_HEIGHT;
pvr_poly_cxt_txr(&cxt, PVR_LIST_OP_POLY, PVR_TXRFMT_YUV422 | PVR_TXRFMT_NONTWIDDLED, MPEG1_TEXTURE_WIDTH, MPEG1_TEXTURE_HEIGHT, texture, PVR_FILTER_TRILINEAR2);
pvr_poly_compile(&hdr, &cxt);
// hdr.mode3 |= 0x02000000; /* stride */
pvr_prim(&hdr, sizeof(hdr));
vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
vert.oargb = 0;
vert.flags = PVR_CMD_VERTEX;
vert.x = 1;
vert.y = 1;
vert.z = 1;
vert.u = UV_EPSILON;
vert.v = UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = screenWidth;
vert.y = 1;
vert.z = 1;
vert.u = u - UV_EPSILON;
vert.v = UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = 1;
vert.y = screenHeight;
vert.z = 1;
vert.u = UV_EPSILON;
vert.v = v - UV_EPSILON;
pvr_prim(&vert, sizeof(vert));
vert.x = screenWidth;
vert.y = screenHeight;
vert.z = 1;
vert.u = u - UV_EPSILON;
vert.v = v - UV_EPSILON;
vert.flags = PVR_CMD_VERTEX_EOL;
pvr_prim(&vert, sizeof(vert));
}
/* n must be multiple of 64 */
void dc_sq_cpy(void *dest, void *src, int n)
{
uint32 *sq;
uint32 *d, *s;
d = (uint32 *)(0xe0000000 | (((uint32)dest) & 0x03ffffe0));
s = (uint32 *)(src);
*((volatile unsigned int*)0xFF000038) = ((((uint32)dest)>>26)<<2)&0x1c;
*((volatile unsigned int*)0xFF00003C) = ((((uint32)dest)>>26)<<2)&0x1c;
n >>= 6;
while (n--)
{
// sq0
sq = d;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
__asm__("pref @%0" : : "r" (d));
__asm__("ocbi @%0" : : "r" (d));
d += 8;
// sq1
sq = d;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
*sq++ = *s++; *sq++ = *s++;
__asm__("pref @%0" : : "r" (d));
__asm__("ocbi @%0" : : "r" (d));
d += 8;
}
*((uint32 *)(0xe0000000)) = 0;
*((uint32 *)(0xe0000020)) = 0;
}
void app_on_video(plm_t *mpeg, plm_frame_t *frame, void *user)
{
unsigned int *dest = (unsigned int *)texture;
unsigned int *src = (unsigned int *)frame->display;
volatile unsigned int *d = (volatile unsigned int *)0xa05f8148;
volatile unsigned int *cfg = (volatile unsigned int *)0xa05f814c;
volatile unsigned int *stride_reg = (volatile unsigned int *)0xa05f80e4;
int stride_value;
int stride = 0;
int x, y, w, h, i;
if (!frame)
return;
/* set frame size. */
w = frame->width >> 4;
h = frame->height >> 4;
stride_value = (w >> 1); /* 16 pixel / 2 */
/* Set Stride value. */
*stride_reg &= 0xffffffe0;
*stride_reg |= stride_value & 0x01f;
/* Set SQ to YUV converter. */
*d = ((unsigned int)dest) & 0xffffff;
*cfg = 0x00000f1f;
x = *cfg; /* read on once */
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++, src += 96)
{
dc_sq_cpy((void *)0x10800000, (void *)src, 384);
}
if (!stride)
{
/* Send dummy mb */
for (i = 0; i < 32 - w; i++)
{
sq_set((void *)0x10800000, 0, 384);
}
}
}
for (i = 0; i < 16 - h; i++)
{
if (!stride)
sq_set((void *)0x10800000, 0, 384 * 32);
else
sq_set((void *)0x10800000, 0, 384 * w);
}
}
// Define a buffer for audio samples
#define AUDIO_SAMPLE_BUFFER_SIZE (1152 * 2 * 10) // Adjust the size as needed
// Static variables for sound callback
static int mod_start = 0;
static int mod_size = 0;
static unsigned int audio_sample_buffer[AUDIO_SAMPLE_BUFFER_SIZE];
//Ian micheal added call back sound reset and buffer
// Function to reset sound callback's static variables
void reset_sound_callback()
{
mod_start = 0;
mod_size = 0;
}
// Sound callback function
// Sound callback function
void *sound_callback(snd_stream_hnd_t hnd, int size, int *size_out)
{
unsigned int *dest = audio_sample_buffer;
unsigned int *src = NULL; // Initialize src pointer
int out = 0;
// Prefetch the initial 'src' address
__asm__("pref @%0" : : "r" (src));
src = audio_sample_buffer + mod_start / 4;
for (int i = 0; i < mod_size / 4; i++) {
*dest++ = *src++;
// Prefetch the next memory location (adjust the distance as needed)
__asm__("pref @%0" : : "r" (src));
}
out += mod_size;
while (size > out)
{
plm_samples_t *sample = plm_decode_audio(plm);
if (sample == NULL)
{
reset_sound_callback(); // Reset static variables when audio ends
break;
}
// Prefetch the initial 'src' address for the audio samples
__asm__("pref @%0" : : "r" (src));
src = (unsigned int *)sample->pcm;
for (int i = 0; i < 1152 / 2; i++) {
*dest++ = *src++;
// Prefetch the next memory location (adjust the distance as needed)
__asm__("pref @%0" : : "r" (src));
}
out += 1152 * 2;
}
mod_start = size;
mod_size = out - size;
*size_out = size;
return (void *)audio_sample_buffer;
}
int Mpeg1Play(const char *filename, unsigned int buttons)
{
int cancel = 0;
plm = plm_create_with_filename(filename);
if (!plm)
return -1;
texture = pvr_mem_malloc(512 * 256 * 2);
width = plm_get_width(plm);
height = plm_get_height(plm);
// Initialize the ring buffer
initRingBuffer();
/* Set SQ to YUV converter. */
PVR_SET(PVR_YUV_ADDR, (((unsigned int)texture) & 0xffffff));
// Divide texture width and texture height by 16 and subtract 1.
// The actual values to set are 1, 3, 7, 15, 31, 63.
PVR_SET(PVR_YUV_CFG_1, (((MPEG1_TEXTURE_HEIGHT / 16) - 1) << 8) | ((MPEG1_TEXTURE_WIDTH / 16) - 1));
PVR_GET(PVR_YUV_CFG_1);
/* First frame */
plm_frame_t *frame = plm_decode_video(plm);
float start_time = (float)timer_ms_gettime64() / 1000.0;
float playing_time = 0.0f;
float frame_time = 1.0f / (float)plm_get_framerate(plm);
int decoded = 1;
//Ian micheal added call back sound reset and buffer
reset_sound_callback();
/* Init sound stream. */
int samplerate = plm_get_samplerate(plm);
snd_hnd = snd_stream_alloc(sound_callback, 0x10000);
snd_stream_volume(snd_hnd, 0xff);
snd_stream_queue_enable(snd_hnd);
snd_stream_start(snd_hnd, samplerate, 0);
snd_stream_queue_go(snd_hnd);
while (!cancel)
{
/* Check cancel buttons. */
if (buttons)
{
/* Check cancel buttons. */
MAPLE_FOREACH_BEGIN(MAPLE_FUNC_CONTROLLER, cont_state_t, st)
if (buttons && ((st->buttons & buttons) == buttons))
cancel = 1; /* Push cancel buttons */
if (st->buttons == 0x60e)
cancel = 2; /* ABXY + START (Software reset) */
MAPLE_FOREACH_END()
}
/* Decode */
playing_time = ((float)timer_ms_gettime64() / 1000.0) - start_time;
if ((frame->time - playing_time) < frame_time)
{
frame = plm_decode_video(plm);
if (!frame)
break;
decoded = 1;
// Enqueue the frame into the ring buffer
enqueueFrame(frame);
}
snd_stream_poll(snd_hnd);
/* Render */
pvr_wait_ready();
pvr_scene_begin();
if (decoded)
{
frame = dequeueFrame();
if (frame) {
app_on_video(plm, frame, 0);
}
decoded = 0;
}
pvr_list_begin(PVR_LIST_OP_POLY);
display_draw();
pvr_list_finish();
pvr_scene_finish();
}
//Ian micheal added call back sound reset and buffer
reset_sound_callback();
plm_destroy(plm);
pvr_mem_free(texture);
snd_stream_destroy(snd_hnd);
return cancel;
}
Code: Select all
ffmpeg -i input.mp4 -vf "scale=320:240" -b:v 642k -ac 1 -ar 44100 -c:a mp2 -b:a 64k -f mpg output.mpeg
Code: Select all
ffmpeg -i input.mp4 -vf "scale=320:240" -b:v 642k -minrate 642k -maxrate 642k -bufsize 642k -ac 1 -ar 44100 -c:a mp2 -b:a 64k -f mpeg output.mpg
Ok after many cdrs burned today i found best
video sample
https://streamable.com/leic1n
[code]ffmpeg -i input.mp4 -vf "scale=320:240" -b:v 742k -minrate 742k -maxrate 742k -bufsize 742k -ac 1 -ar 32000 -c:a mp2 -b:a 64k -f mpeg output.mpg
I did notice that can see it even on the sample video you did but very much not notice-able..Twada wrote: ↑Wed Sep 27, 2023 6:36 pm Thank you for your detailed verification.
The 64-byte original store queue is also cool. Please merge anytime.
I realized something important thanks to your sample video.
There seems to be a bug in video decoding. U and V are flickering in YUV. This is especially evident in the Universal logo on the Jurassic Park trailer.
Please wait while we identify the issue...
Code: Select all
sh-elf-gprof.exe YOUR.elf gmon.out > gprof.out
Code: Select all
cat gprof.out | gprof2dot | dot -Tpng -o output.png
I see, this is how profiling can be used! It is very easy to see where the bottleneck is.Ian Robinson wrote: ↑Wed Sep 27, 2023 8:50 pm Ok i did the profiling to see the bottlenecks whats taking the most time..
when it first starts
output.png
When it plays longer
output.png
As it plays longer you can see it gets more heavy
seems strange but i do see it we have to look at this.
That fixed itTwada wrote: ↑Fri Sep 29, 2023 2:48 amI see, this is how profiling can be used! It is very easy to see where the bottleneck is.Ian Robinson wrote: ↑Wed Sep 27, 2023 8:50 pm Ok i did the profiling to see the bottlenecks whats taking the most time..
when it first starts
output.png
When it plays longer
output.png
As it plays longer you can see it gets more heavy
seems strange but i do see it we have to look at this.
We need to fix the problem that slows down when playing videos for a long time. Is there a memory leak occurring somewhere?
Your profiling can help. Please give me some time.
Fixed UV flickering. It was my mistake.
Please replace pl_mpeg.h.
pl_mpeg.h