Skip to content

Instantly share code, notes, and snippets.

@unknownbrackets
Last active December 10, 2015 17:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unknownbrackets/4469815 to your computer and use it in GitHub Desktop.
Save unknownbrackets/4469815 to your computer and use it in GitHub Desktop.
basic mpeg investigation
#define sceMpegFinish sceMpegFinish_IGNORE
#define sceMpegRingbufferDestruct sceMpegRingbufferDestruct_IGNORE
#define sceMpegDelete sceMpegDelete_IGNORE
#include <common.h>
#include <pspkernel.h>
#include <pspsdk.h>
#include <psptypes.h>
#include <psppower.h>
#include <psputilsforkernel.h>
#include <pspdisplay.h>
#include <pspge.h>
#include <pspgu.h>
#include <pspctrl.h>
#include <pspaudio.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <pspmpeg.h>
#include <psputility.h>
#include "sysmem-imports.h"
#undef sceMpegFinish
#undef sceMpegRingbufferDestruct
#undef sceMpegDelete
extern int sceMpegFinish();
extern int sceMpegRingbufferDestruct(SceMpegRingbuffer* Ringbuffer);
extern int sceMpegDelete(SceMpeg *mpeg);
#pragma pack(1)
typedef struct SceMpegRingbuffer2
{
s32 packetsTotal;
s32 packetsRead;
s32 packetsWritten;
s32 packetsAvail;
s32 packetSize;
void *data;
sceMpegRingbufferCB callback;
void *callbackArg;
void *dataEnd;
int unknownValue;
SceMpeg mpeg;
u32 gp;
} SceMpegRingbuffer2;
typedef struct SceMpegBufferHeader {
// "LIBMPEG"
char magic[8];
// "001"
char version[4];
// Always -1?
u32 pad1;
SceMpegRingbuffer2 *ringbuffer;
// Same as ringbuffer->dataEnd?
void *dataEnd;
// Points to start of struct + 0x0740.
void *unknown1;
// Points to start of struct + 0x0740 + 0x0740.
void *unknown2;
// Points to start of struct + 0x0740 + 0x0740 + 0x1E00.
void *unknown3;
// Always 0?
u32 unknown4;
// Always 99?
int unknownListLength;
} SceMpegBufferHeader;
struct SceMpegBufferUnknownList1;
typedef struct SceMpegBufferUnknownList1 {
u32 unknown1;
u32 unknown2;
struct SceMpegBufferUnknownList1 *next;
u32 unknown3;
} SceMpegBufferUnknownList1;
typedef struct SceMpegBuffer {
SceMpegBufferHeader header;
SceMpegBufferUnknownList1 unknownList[99];
u32 unknown4[8];
SceUID unknownSema1;
u32 unknown5[7];
SceUID unknownSema2;
} SceMpegBuffer;
#define ARRAY_SIZE(a) (sizeof((a)) / (sizeof((a)[0])))
static char schedulingLog[65536];
static volatile int schedulingLogPos = 0;
inline void schedf(const char *format, ...) {
va_list args;
va_start(args, format);
schedulingLogPos += vsprintf(schedulingLog + schedulingLogPos, format, args);
// This is easier to debug in the emulator, but printf() reschedules on the real PSP.
//vprintf(format, args);
va_end(args);
}
inline void flushschedf() {
printf("%s", schedulingLog);
schedulingLogPos = 0;
}
int loadModules() {
int status = 0;
status |= sceUtilityLoadModule(PSP_MODULE_AV_AVCODEC);
status |= sceUtilityLoadModule(PSP_MODULE_AV_ATRAC3PLUS);
status |= sceUtilityLoadModule(PSP_MODULE_AV_MP3);
status |= sceUtilityLoadModule(PSP_MODULE_AV_MPEGBASE);
status |= sceUtilityLoadModule(PSP_MODULE_AV_VAUDIO);
if (status != 0) {
printf("ERROR: Could not load modules.\n");
return 0;
}
return 1;
}
void cleanupModules() {
sceMpegFinish();
int status = 0;
status |= sceUtilityUnloadModule(PSP_MODULE_AV_AVCODEC);
status |= sceUtilityUnloadModule(PSP_MODULE_AV_ATRAC3PLUS);
status |= sceUtilityUnloadModule(PSP_MODULE_AV_MP3);
status |= sceUtilityUnloadModule(PSP_MODULE_AV_MPEGBASE);
status |= sceUtilityUnloadModule(PSP_MODULE_AV_VAUDIO);
if (status != 0) {
printf("ERROR: Could not unload modules.\n");
}
}
void testInit() {
int result;
result = sceMpegInit();
printf("sceMpegInit: %08x\n", result);
result = sceMpegInit();
printf("sceMpegInit: %08x\n", result);
asm("lui $v0, 0xDEAD");
result = sceMpegFinish();
printf("sceMpegFinish: %08x\n", result);
result = sceMpegFinish();
printf("sceMpegFinish: %08x\n", result);
result = sceMpegInit();
printf("sceMpegInit: %08x\n", result);
}
void testQueryMemSize() {
unsigned int result;
printf("\nsceMpegRingbufferQueryMemSize:\n");
unsigned int packets[] = {-1, 0, 1, 2, 5, 10, 0x179, 1024, 65536};
int i;
for (i = 0; i < ARRAY_SIZE(packets); ++i) {
result = sceMpegRingbufferQueryMemSize(packets[i]);
printf(" %d: %d\n", packets[i], result);
}
printf("\nsceMpegQueryMemSize:\n");
printf("%08x\n", (unsigned int) sceMpegQueryMemSize(0));
printf("%08x\n", (unsigned int) sceMpegQueryMemSize(0));
}
SceInt32 mpeg_callback(ScePVoid data, SceInt32 numPackets, ScePVoid arg) {
schedf("mpeg_callback called: %08x, %d, %08x\n", (char *) data, numPackets, (unsigned int) arg);
return 0;
}
void schedfRingbuffer(int result, SceMpegRingbuffer2 *ringbuffer, u32 *data, SceMpeg *mpeg) {
u32 gp;
asm("sw $gp, %0" : "=m"(gp));
if (result == 0) {
// TODO: cleanup mpeg, sema/unknown?
schedf("Ringbuffer: OK (packets=%d,r=%d/w=%d/f=%d,data=%d,cb=%d,arg=%08x,end=%d,sema?=%08x,mpeg=%d,gp=%d)\n", ringbuffer->packetsTotal, ringbuffer->packetsRead, ringbuffer->packetsWritten, ringbuffer->packetsAvail, ringbuffer->data == data, ringbuffer->callback != NULL, (unsigned int) ringbuffer->callbackArg, ringbuffer->dataEnd - ringbuffer->data, (unsigned int) ringbuffer->unknownValue, mpeg != NULL && ringbuffer->mpeg == mpeg, ringbuffer->gp == gp);
if (data != NULL && ringbuffer->dataEnd > ringbuffer->data) {
u32 first = *(u32 *) ringbuffer->data;
u32 *p;
for (p = (u32 *) ringbuffer->data; p < (u32 *) ringbuffer->dataEnd; p++) {
if (*p != first) {
schedf(" Diff @data+%08x: %08x\n", (p - (u32 *) ringbuffer->data) * sizeof(u32), (unsigned int) *p);
}
}
}
} else {
schedf("Ringbuffer: Failed (%08x)\n", result);
}
}
void runCreateRingbufferStruct(const char *title, int numPackets, int bufSize) {
SceMpegRingbuffer2 ringbuffer;
u32 *buf = bufSize > 0 ? (u32 *) malloc(bufSize) : NULL;
memset(&ringbuffer, 0xCC, sizeof(ringbuffer));
if (buf != NULL) {
memset(buf, 0xCC, bufSize);
}
schedf("%s: ", title);
int result = sceMpegRingbufferConstruct((SceMpegRingbuffer *) &ringbuffer, numPackets, buf, bufSize, &mpeg_callback, (void *) 0x1234);
schedfRingbuffer(result, &ringbuffer, buf, NULL);
if (result == 0) {
sceMpegRingbufferDestruct((SceMpegRingbuffer *) &ringbuffer);
}
if (buf != NULL) {
free(buf);
}
}
void testRingCreate() {
schedf("\nsceMpegRingbufferConstruct:\n");
int bufSize = sceMpegRingbufferQueryMemSize(0x179);
runCreateRingbufferStruct("Normal", 0x179, bufSize);
runCreateRingbufferStruct("Buffer too small", 0x179, 10);
runCreateRingbufferStruct("No buffer", 0x179, 0);
runCreateRingbufferStruct("No packets", 0, bufSize);
runCreateRingbufferStruct("No packets or buffer", 0, 0);
runCreateRingbufferStruct("Negative packets", -1, 0);
// TODO: Test passing other invalid args, NULL callback etc.?
flushschedf();
}
void schedfMpeg(int result, SceMpeg mpeg, u32 *data, int dataSize, SceMpegRingbuffer2 *ringbuffer) {
u32 *dataAligned = data == NULL ? NULL : (u32 *) (((u32) data + 63) & ~63);
if (result == 0) {
schedf("Mpeg: OK (%d), buf0=%08x", mpeg == dataAligned, dataAligned == NULL ? 0xFFFFFFFF : *(unsigned int *) dataAligned);
if (dataAligned != NULL && dataSize > 0) {
SceMpegBuffer *mpegData = (SceMpegBuffer *) dataAligned;
schedf(", header: %.*s %.*s %08x ringbuffer:%d end:%d, ", sizeof(mpegData->header.magic), mpegData->header.magic, sizeof(mpegData->header.version), mpegData->header.version, mpegData->header.pad1, ringbuffer == mpegData->header.ringbuffer, ringbuffer->dataEnd == mpegData->header.dataEnd);
// TODO: What? These are now very wrong?
/*if (mpegData->header.unknown1 == dataAligned + 0x740 && mpegData->header.unknown2 == dataAligned + 0x740 + 0x740 && mpegData->header.unknown3 == dataAligned + 0x740 + 0x740 + 0x1E00 && mpegData->header.unknown4 == 0 && mpegData->header.unknownListLength == 99) {
schedf(", unknown fields: OK");
} else {
schedf(", unknown fields: Failed (%08x == %08x, %08x == %08x, %08x == %08x, %08x == %08x, %08x == %08x)", mpegData->header.unknown1, dataAligned + 0x740, mpegData->header.unknown2, dataAligned + 0x740 + 0x740, mpegData->header.unknown3, dataAligned + 0x740 + 0x740 + 0x1E00, mpegData->header.unknown4, 0, mpegData->header.unknownListLength, 99);
}*/
// Don't really care.
/*int i;
int ok = 1;
for (i = 0; i < mpegData->header.unknownListLength - 1; ++i) {
if (mpegData->unknownList[i].next != &mpegData->unknownList[i + 1]) {
ok = 0;
}
}
schedf(", unknown linked list: %s", ok ? "OK" : "Failed");*/
if (sceKernelGetThreadmanIdType(mpegData->unknownSema1) == 0x0D && sceKernelGetThreadmanIdType(mpegData->unknownSema2) == 0x0D) {
schedf(", unknown semas: OK");
} else {
schedf(", unknown semas: Failed (%08x, %08x)", mpegData->unknownSema1, mpegData->unknownSema2);
}
schedf("\n");
/*u32 *p, *e;
for (p = dataAligned + sizeof(SceMpegBuffer) / 4, e = dataAligned + dataSize / sizeof(u32); p < e; p++) {
if (*p != 0) {
schedf(" Diff @data+%08x: %08x\n", (p - dataAligned) * sizeof(u32), (unsigned int) *p);
}
}*/
}
} else {
schedf("Mpeg: Failed (%08x), buf0=%08x\n", result, dataAligned == NULL ? 0xFFFFFFFF : *(unsigned int *) dataAligned);
}
}
void runMpegCreate(const char *title, int bufSize, int frameWidth) {
SceMpegRingbuffer2 ringbuffer;
int ringbufSize = sceMpegRingbufferQueryMemSize(0x179);
u32 *ringbuf = (u32 *) malloc(ringbufSize);
memset(&ringbuffer, 0xCC, sizeof(ringbuffer));
memset(ringbuf, 0xCC, ringbufSize);
schedf("%s: ", title);
int result = sceMpegRingbufferConstruct((SceMpegRingbuffer *) &ringbuffer, 0x179, ringbuf, ringbufSize, &mpeg_callback, (void *) 0x1234);
if (result == 0) {
SceMpeg mpeg = (SceMpeg) 0xDEADBEEF;
u32 *mpegbuf = bufSize > 0 ? (u32 *) malloc(bufSize) : NULL;
memset(mpegbuf, 0xCC, bufSize);
result = sceMpegCreate(&mpeg, mpegbuf, bufSize, (SceMpegRingbuffer *) &ringbuffer, frameWidth, 0, 0);
schedfMpeg(result, mpeg, mpegbuf, bufSize, &ringbuffer);
schedfRingbuffer(0, &ringbuffer, ringbuf, &mpeg);
if (result == 0) {
sceMpegDelete(&mpeg);
}
sceMpegRingbufferDestruct((SceMpegRingbuffer *) &ringbuffer);
if (mpegbuf != NULL) {
free(mpegbuf);
}
} else {
schedfRingbuffer(result, &ringbuffer, ringbuf, NULL);
}
if (ringbuf != NULL) {
free(ringbuf);
}
}
void testMpegCreate() {
schedf("\nsceMpegCreate:\n");
runMpegCreate("Normal", sceMpegQueryMemSize(0), 512);
// TODO: Test more? All seem to crash?
schedf("\n");
flushschedf();
}
SceMpeg g_mpeg;
SceMpegRingbuffer2 g_ringbuffer;
void *g_mpegData = NULL;
void *g_ringbufferData = NULL;
void *g_atracData = NULL;
SceMpegAu g_avc_au;
SceMpegAu g_atrac_au;
SceUID g_mpegFile;
SceInt32 g_streamOffset;
SceMpegStream *g_avc_stream, *g_atrac_stream;
int test_mpeg_callback_ret = 0;
SceInt32 test_mpeg_callback(ScePVoid data, SceInt32 numPackets, ScePVoid arg) {
schedf("mpeg_callback called: %08x, %d, %08x\n", (char *) data - (char *) g_ringbufferData, numPackets, (unsigned int) arg);
return test_mpeg_callback_ret;
}
int createTestMpeg() {
int ringbufSize = sceMpegRingbufferQueryMemSize(0x179);
int bufSize = sceMpegQueryMemSize(0);
g_ringbufferData = malloc(ringbufSize);
g_mpegData = malloc(bufSize);
if (g_mpegData == NULL || g_ringbufferData == NULL) {
printf("TEST FAILURE: Could not allocate buffers?\n");
return 0;
}
int result = sceMpegRingbufferConstruct((SceMpegRingbuffer *) &g_ringbuffer, 0x179, g_ringbufferData, ringbufSize, &test_mpeg_callback, (void *) 0x1234);
if (result != 0) {
printf("TEST FAILURE: Unable to create ringbuffer: %08x\n", result);
return 0;
}
result = sceMpegCreate(&g_mpeg, g_mpegData, bufSize, (SceMpegRingbuffer *) &g_ringbuffer, 512, 0, 0);
if (result != 0) {
printf("TEST FAILURE: Unable to create mpeg: %08x\n", result);
return 0;
}
SceMpegBuffer *mpeg = (SceMpegBuffer *) g_mpeg;
printf("%.*s\n", 8, mpeg->header.magic);
return 1;
}
void deleteTestMpeg() {
if (g_mpegData != NULL) {
sceMpegDelete(&g_mpeg);
free(g_mpegData);
}
if (g_ringbufferData != NULL) {
sceMpegRingbufferDestruct((SceMpegRingbuffer *) &g_ringbuffer);
free(g_ringbufferData);
}
if (g_atracData != NULL) {
free(g_atracData);
}
sceIoClose(g_mpegFile);
}
void testDecodeMode() {
SceMpegAvcMode mode = {-1, 3};
int result = sceMpegAvcDecodeMode(&g_mpeg, &mode);
printf("sceMpegAvcDecodeMode: %08x (%08x, %08x)\n", result, (unsigned int) mode.iUnk0, (unsigned int) mode.iPixelFormat);
}
void testRegistStream() {
g_avc_stream = sceMpegRegistStream(&g_mpeg, 0, 0);
u32 *data = (u32 *) g_avc_stream;
printf("sceMpegRegistStream: %08x\n", (char *) g_avc_stream - (char *) g_mpeg);
void *avc_buf = sceMpegMallocAvcEsBuf(&g_mpeg);
printf("sceMpegMallocAvcEsBuf: %08x\n", (unsigned int) avc_buf);
int result = sceMpegInitAu(&g_mpeg, avc_buf, &g_avc_au);
printf("sceMpegInitAu AVC: %08x\n", result);
g_atrac_stream = sceMpegRegistStream(&g_mpeg, 1, 0);
data = (u32 *) g_atrac_stream;
printf("sceMpegRegistStream: %08x\n", (char *) g_atrac_stream - (char *) g_mpeg);
SceInt32 atrac_esSize, atrac_outSize;
result = sceMpegQueryAtracEsSize(&g_mpeg, &atrac_esSize, &atrac_outSize);
printf("sceMpegQueryAtracEsSize: %08x (%08x, %08x)\n", result, (unsigned int) atrac_esSize, (unsigned int) atrac_outSize);
// Not sure?
g_atracData = memalign(64, atrac_outSize);
result = sceMpegInitAu(&g_mpeg, g_atracData, &g_atrac_au);
printf("sceMpegInitAu ATRAC: %08x\n", result);
}
void testStreamInfo() {
g_mpegFile = sceIoOpen("test.pmf", PSP_O_RDONLY, 0777);
printf("sceIoOpen: %s\n", g_mpegFile > 0 ? "OK" : "Failed");
char header[2048];
sceIoRead(g_mpegFile, header, 2048);
int result = sceMpegQueryStreamOffset(&g_mpeg, header, &g_streamOffset);
printf("sceMpegQueryStreamOffset: %08x (%08x)\n", result, (unsigned int) g_streamOffset);
SceInt32 streamSize;
result = sceMpegQueryStreamSize(header, &streamSize);
printf("sceMpegQueryStreamSize: %08x (%08x)\n", result, (unsigned int) streamSize);
sceIoLseek(g_mpegFile, g_streamOffset, SEEK_SET);
flushschedf();
}
void testDecodeVideo() {
void *vbuffer = memalign(64, 512 * 272 * 4);
int freePackets = sceMpegRingbufferAvailableSize((SceMpegRingbuffer *) &g_ringbuffer);
schedf("sceMpegRingbufferAvailableSize: %d\n", freePackets);
test_mpeg_callback_ret = 32;
int result = sceMpegRingbufferPut((SceMpegRingbuffer *) &g_ringbuffer, 32, freePackets);
schedf("sceMpegRingbufferPut: %08x\n", result);
int something1 = 1;
result = sceMpegGetAtracAu(&g_mpeg, g_atrac_stream, &g_atrac_au, &something1);
schedf("sceMpegGetAtracAu: %08x (%08x)\n", result, something1);
SceInt32 something2 = 0;
result = sceMpegAtracDecode(&g_mpeg, &g_atrac_au, g_atracData, (SceInt32) &something2);
schedf("sceMpegAtracDecode: %08x\n", result);
SceInt32 somethingElse = 6;
result = sceMpegGetAvcAu(&g_mpeg, g_avc_stream, &g_avc_au, &somethingElse);
schedf("sceMpegGetAvcAu: %08x (%08x)\n", result, somethingElse);
somethingElse = 0x179;
SceInt32 *somethingElsep = &somethingElse;
result = sceMpegAvcDecode(&g_mpeg, &g_avc_au, 512, &vbuffer, (SceInt32 *) &somethingElsep);
schedf("sceMpegAvcDecode: %08x (%08x)\n", result, somethingElse);
flushschedf();
free(vbuffer);
}
int main(int argc, char *argv[]) {
sceKernelSetCompiledSdkVersion606(0x6060010);
if (!loadModules())
return 1;
testInit();
/*testQueryMemSize();
testRingCreate();
testMpegCreate();*/
if (createTestMpeg()) {
testDecodeMode();
testRegistStream();
testStreamInfo();
testDecodeVideo();
}
deleteTestMpeg();
cleanupModules();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment