Last active
December 10, 2015 17:48
-
-
Save unknownbrackets/4469815 to your computer and use it in GitHub Desktop.
basic mpeg investigation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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