/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import static jpcsp.HLE.modules.sceMpeg.getIntBuffer;
import static jpcsp.HLE.modules.sceMpeg.releaseIntBuffer;
import static jpcsp.util.Utilities.alignUp;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import org.apache.log4j.Logger;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.Allegrex.compiler.RuntimeContext;
import jpcsp.HLE.BufferInfo;
import jpcsp.HLE.BufferInfo.LengthInfo;
import jpcsp.HLE.BufferInfo.Usage;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.Modules;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.kernel.types.IAction;
import jpcsp.HLE.kernel.types.SceKernelThreadInfo;
import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo;
import jpcsp.media.codec.CodecFactory;
import jpcsp.media.codec.IVideoCodec;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.IMemoryWriter;
import jpcsp.memory.MemoryReader;
import jpcsp.memory.MemoryWriter;
import jpcsp.scheduler.DelayThreadAction;
import jpcsp.scheduler.UnblockThreadAction;
import jpcsp.util.Utilities;
public class sceVideocodec extends HLEModule {
public static Logger log = Modules.getLogger("sceVideocodec");
private static final int videocodecDecodeDelay = 4000;
// Based on JpcspTrace tests, sceVideocodecDelete delays for 40ms
public static final int videocodecDeleteDelay = 40000;
public static final int EDRAM_MEMORY_MASK = 0x03FFFFFF;
protected SysMemInfo memoryInfo;
protected SysMemInfo edramInfo;
protected int frameCount;
protected int bufferY1;
protected int bufferY2;
protected int bufferCr1;
protected int bufferCr2;
protected int bufferCb1;
protected int bufferCb2;
protected final int buffers[][] = new int[4][8];
protected int bufferUnknown1;
protected int bufferUnknown2;
protected IVideoCodec videoCodec;
private VideocodecDecoderThread videocodecDecoderThread;
private class VideocodecDecoderThread extends Thread {
private volatile boolean exit = false;
private volatile boolean done = false;
private Semaphore sema = new Semaphore(1);
private TPointer buffer;
private int type;
private int threadUid;
private long threadWakeupMicroTime;
@Override
public void run() {
while (!exit) {
if (waitForTrigger(100) && !exit) {
hleVideocodecDecoderStep(buffer, type, threadUid, threadWakeupMicroTime);
}
}
if (log.isDebugEnabled()) {
log.debug("Exiting the VideocodecDecoderThread");
}
done = true;
}
public void exit() {
exit = true;
while (!done) {
Utilities.sleep(1);
}
}
public void trigger(TPointer buffer, int type, int threadUid, long threadWakeupMicroTime) {
this.buffer = buffer;
this.type = type;
this.threadUid = threadUid;
this.threadWakeupMicroTime = threadWakeupMicroTime;
trigger();
}
private void trigger() {
if (sema != null) {
sema.release();
}
}
private boolean waitForTrigger(int millis) {
while (true) {
try {
int availablePermits = sema.drainPermits();
if (availablePermits > 0) {
break;
}
if (sema.tryAcquire(millis, TimeUnit.MILLISECONDS)) {
break;
}
return false;
} catch (InterruptedException e) {
// Ignore exception and retry
}
}
return true;
}
}
private void hleVideocodecDecoderStep(TPointer buffer, int type, int threadUid, long threadWakeupMicroTime) {
if (buffer == null) {
return;
}
int mp4Data = buffer.getValue32(36) | MemoryMap.START_RAM;
int mp4Size = buffer.getValue32(40);
if (log.isTraceEnabled()) {
log.trace(String.format("sceVideocodecDecode mp4Data:%s", Utilities.getMemoryDump(mp4Data, mp4Size)));
}
if (videoCodec == null) {
videoCodec = CodecFactory.getVideoCodec();
videoCodec.init(null);
}
int[] mp4Buffer = getIntBuffer(mp4Size);
IMemoryReader memoryReader = MemoryReader.getMemoryReader(mp4Data, mp4Size, 1);
for (int i = 0; i < mp4Size; i++) {
mp4Buffer[i] = memoryReader.readNext();
}
int result = videoCodec.decode(mp4Buffer, 0, mp4Size);
if (log.isDebugEnabled()) {
log.debug(String.format("sceVideocodecDecode videoCodec returned 0x%X from 0x%X data bytes", result, mp4Size));
}
releaseIntBuffer(mp4Buffer);
buffer.setValue32(8, 0);
int frameWidth = videoCodec.getImageWidth();
int frameHeight = videoCodec.getImageHeight();
if (log.isTraceEnabled()) {
log.trace(String.format("sceVideocodecDecode codec image size %dx%d, frame size %dx%d", videoCodec.getImageWidth(), videoCodec.getImageHeight(), frameWidth, frameHeight));
}
int frameBufferWidthY = videoCodec.getImageWidth();
int frameBufferWidthCr = frameBufferWidthY / 2;
int frameBufferWidthCb = frameBufferWidthY / 2;
Memory mem = buffer.getMemory();
TPointer buffer2 = new TPointer(mem, buffer.getValue32(16));
switch (type) {
case 0:
buffer2.setValue32(8, frameWidth);
buffer2.setValue32(12, frameHeight);
buffer2.setValue32(28, 1);
buffer2.setValue32(32, videoCodec.hasImage());
buffer2.setValue32(36, !videoCodec.hasImage());
if (videoCodec.hasImage()) {
if (memoryInfo == null) {
int sizeY1 = alignUp(((frameWidth + 16) >> 5) * (frameHeight >> 1) * 16, 0x1FF);
int sizeY2 = alignUp((frameWidth >> 5) * (frameHeight >> 1) * 16, 0x1FF);
int sizeCr1 = alignUp(((frameWidth + 16) >> 5) * (frameHeight >> 1) * 8, 0x1FF);
int sizeCr2 = alignUp((frameWidth >> 5) * (frameHeight >> 1) * 8, 0x1FF);
int size = 256 + (sizeY1 + sizeY2 + sizeCr1 + sizeCr2) * 2 * buffers.length;
memoryInfo = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceVideocodecDecode", SysMemUserForUser.PSP_SMEM_Low, size, 0);
int base = memoryInfo.addr;
bufferUnknown1 = base;
mem.memset(bufferUnknown1, (byte) 0, 36);
bufferUnknown2 = base + 36;
mem.memset(bufferUnknown2, (byte) 0, 32);
int yuvBuffersBase = base + 256; // Add 256 to keep aligned
int base1 = yuvBuffersBase & EDRAM_MEMORY_MASK;
int base2 = base1 + (sizeY1 + sizeY2) * buffers.length;
int step = (sizeY1 + sizeY2 + sizeCr1 + sizeCr2) * buffers.length;
for (int i = 0; i < buffers.length; i++) {
buffers[i][0] = base1;
buffers[i][1] = buffers[i][0] + step;
buffers[i][2] = base1 + sizeY1;
buffers[i][3] = buffers[i][2] + step;
buffers[i][4] = base2;
buffers[i][5] = buffers[i][4] + step;
buffers[i][6] = base2 + sizeCr1;
buffers[i][7] = buffers[i][6] + step;
base1 += sizeY1 + sizeY2;
base2 += sizeCr1 + sizeCr2;
}
}
int buffersIndex = frameCount % 3;
int width = videoCodec.getImageWidth();
int height = videoCodec.getImageHeight();
int[] luma = getIntBuffer(width * height);
int[] cb = getIntBuffer(width * height / 4);
int[] cr = getIntBuffer(width * height / 4);
if (videoCodec.getImage(luma, cb, cr) == 0) {
// The PSP is storing the YCbCr information in a non-linear format.
// By analyzing the output of sceMpegBaseYCrCbCopy on a real PSP,
// the following format for the YCbCr was found:
// the image is divided vertically into bands of 32 pixels.
// Each band is stored vertically into different buffers.
// The Y information is stored as 1 byte per pixel.
// The Cb information is stored as 1 byte for a square of four pixels (2x2).
// The Cr information is stored as 1 byte for a square of four pixels (2x2).
// For a square of four pixels, the one Cb byte is stored first,
// followed by the one Cr byte.
//
// - buffer0:
// storing the Y information of the first block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=0,y=0),
// 16 horizontal pixels are stored sequentially in the buffer,
// followed by 16 pixels of the next next image row (i.e. every 2nd row).
// The rows are stored from the image top to the image bottom.
// [x=0-15,y=0], [x=0-15,y=2], [x=0-15,y=4]...
// [x=32-47,y=0], [x=32-47,y=2], [x=32-47,y=4]...
// [x=64-79,y=0], [x=64-79,y=2], [x=64-79,y=4]...
// - buffer1:
// storing the Y information of the second block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=16,y=0),
// 16 horizontal pixels are stored sequentially in the buffer,
// followed by 16 pixels of the next next image row (i.e. every 2nd row).
// The rows are stored from the image top to the image bottom.
// [x=16-31,y=0], [x=16-31,y=2], [x=16-31,y=4]...
// [x=48-63,y=0], [x=48-63,y=2], [x=48-63,y=4]...
// [x=80-95,y=0], [x=80-95,y=2], [x=80-95,y=4]...
// - buffer2:
// storing the Y information of the first block of 16 pixels
// of a 32 pixels wide vertical band.
// Starting at the image pixel (x=0,y=1),
// 16 horizontal pixels are stored sequentially in the buffer,
// followed by 16 pixels of the next next image row (i.e. every 2nd row).
// The rows are stored from the image top to the image bottom.
// [x=0-15,y=1], [x=0-15,y=3], [x=0-15,y=5]...
// [x=32-47,y=1], [x=32-47,y=3], [x=32-47,y=5]...
// [x=64-79,y=1], [x=64-79,y=3], [x=64-79,y=5]...
// - buffer3:
// storing the Y information of the second block of 16 pixels
// of a 32 pixels wide vertical band.
// Starting at the image pixel (x=16,y=1),
// 16 horizontal pixels are stored sequentially in the buffer,
// followed by 16 pixels of the next next image row (i.e. every 2nd row).
// The rows are stored from the image top to the image bottom.
// [x=16-31,y=1], [x=16-31,y=3], [x=16-31,y=5]...
// [x=48-63,y=1], [x=48-63,y=3], [x=48-63,y=5]...
// [x=80-95,y=1], [x=80-95,y=3], [x=80-95,y=5]...
// - buffer4:
// storing the Cb and Cr information of the first block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=0,y=0),
// 8 byte pairs of (Cb,Cr) are stored sequentially in the buffer
// (representing 16 horizontal pixels),
// then the next 3 rows are being skipped,
// and then followed by 8 byte pairs of the next image row (i.e. every 4th row).
// The rows are stored from the image top to the image bottom.
// CbCr[x=0,y=0], CbCr[x=2,y=0], CbCr[x=4,y=0], CbCr[x=6,y=0], CbCr[x=8,y=0], CbCr[x=10,y=0], CbCr[x=12,y=0], CbCr[x=14,y=0]
// CbCr[x=32,y=0], CbCr[x=34,y=0], CbCr[x=36,y=0], CbCr[x=38,y=0], CbCr[x=40,y=0], CbCr[x=42,y=0], CbCr[x=44,y=0], CbCr[x=46,y=0]
// ...
// CbCr[x=0,y=4], CbCr[x=2,y=4], CbCr[x=4,y=4], CbCr[x=6,y=4], CbCr[x=8,y=4], CbCr[x=10,y=4], CbCr[x=12,y=4], CbCr[x=14,y=4]
// CbCr[x=32,y=4], CbCr[x=34,y=4], CbCr[x=36,y=4], CbCr[x=38,y=4], CbCr[x=40,y=4], CbCr[x=42,y=4], CbCr[x=44,y=4], CbCr[x=46,y=4]
// ...
// - buffer5:
// storing the Cb and Cr information of the first block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=0,y=2),
// 8 byte pairs of (Cb,Cr) are stored sequentially in the buffer
// (representing 16 horizontal pixels),
// then the next 3 rows are being skipped,
// and then followed by 8 byte pairs of the next image row (i.e. every 4th row).
// The rows are stored from the image top to the image bottom.
// CbCr[x=0,y=2], CbCr[x=2,y=2], CbCr[x=4,y=2], CbCr[x=6,y=2], CbCr[x=8,y=2], CbCr[x=10,y=2], CbCr[x=12,y=2], CbCr[x=14,y=2]
// CbCr[x=32,y=2], CbCr[x=34,y=2], CbCr[x=36,y=2], CbCr[x=38,y=2], CbCr[x=40,y=2], CbCr[x=42,y=2], CbCr[x=44,y=2], CbCr[x=46,y=2]
// ...
// CbCr[x=0,y=6], CbCr[x=2,y=6], CbCr[x=4,y=6], CbCr[x=6,y=6], CbCr[x=8,y=6], CbCr[x=10,y=6], CbCr[x=12,y=6], CbCr[x=14,y=6]
// CbCr[x=32,y=6], CbCr[x=34,y=6], CbCr[x=36,y=6], CbCr[x=38,y=6], CbCr[x=40,y=6], CbCr[x=42,y=6], CbCr[x=44,y=6], CbCr[x=46,y=6]
// ...
// - buffer6:
// storing the Cb and Cr information of the second block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=16,y=0),
// 8 byte pairs of (Cb,Cr) are stored sequentially in the buffer
// (representing 16 horizontal pixels),
// then the next 3 rows are being skipped,
// and then followed by 8 byte pairs of the next image row (i.e. every 4th row).
// The rows are stored from the image top to the image bottom.
// CbCr[x=16,y=0], CbCr[x=18,y=0], CbCr[x=20,y=0], CbCr[x=22,y=0], CbCr[x=24,y=0], CbCr[x=26,y=0], CbCr[x=28,y=0], CbCr[x=30,y=0]
// CbCr[x=48,y=0], CbCr[x=50,y=0], CbCr[x=52,y=0], CbCr[x=54,y=0], CbCr[x=56,y=0], CbCr[x=58,y=0], CbCr[x=60,y=0], CbCr[x=62,y=0]
// ...
// CbCr[x=16,y=4], CbCr[x=18,y=4], CbCr[x=20,y=4], CbCr[x=22,y=4], CbCr[x=24,y=4], CbCr[x=26,y=4], CbCr[x=28,y=4], CbCr[x=30,y=4]
// CbCr[x=48,y=4], CbCr[x=50,y=4], CbCr[x=52,y=4], CbCr[x=54,y=4], CbCr[x=56,y=4], CbCr[x=58,y=4], CbCr[x=60,y=4], CbCr[x=62,y=4]
// ...
// - buffer7:
// storing the Cb and Cr information of the second block
// of 16 pixels of a 32 pixels wide vertical band.
// Starting at the image pixel (x=16,y=2),
// 8 byte pairs of (Cb,Cr) are stored sequentially in the buffer
// (representing 16 horizontal pixels),
// then the next 3 rows are being skipped,
// and then followed by 8 byte pairs of the next image row (i.e. every 4th row).
// The rows are stored from the image top to the image bottom.
// CbCr[x=16,y=2], CbCr[x=18,y=2], CbCr[x=20,y=2], CbCr[x=22,y=2], CbCr[x=24,y=2], CbCr[x=26,y=2], CbCr[x=28,y=2], CbCr[x=30,y=2]
// CbCr[x=48,y=2], CbCr[x=50,y=2], CbCr[x=52,y=2], CbCr[x=54,y=2], CbCr[x=56,y=2], CbCr[x=58,y=2], CbCr[x=60,y=2], CbCr[x=62,y=2]
// ...
// CbCr[x=16,y=6], CbCr[x=18,y=6], CbCr[x=20,y=6], CbCr[x=22,y=6], CbCr[x=24,y=6], CbCr[x=26,y=6], CbCr[x=28,y=6], CbCr[x=30,y=6]
// CbCr[x=48,y=6], CbCr[x=50,y=6], CbCr[x=52,y=6], CbCr[x=54,y=6], CbCr[x=56,y=6], CbCr[x=58,y=6], CbCr[x=60,y=6], CbCr[x=62,y=6]
// ...
int width2 = width / 2;
int height2 = height / 2;
int sizeY1 = ((width + 16) >> 5) * (height >> 1) * 16;
int sizeY2 = (width >> 5) * (height >> 1) * 16;
int sizeCrCb1 = sizeY1 >> 1;
int sizeCrCb2 = sizeY1 >> 1;
int[] bufferY1 = getIntBuffer(sizeY1);
for (int x = 0, j = 0; x < width; x += 32) {
for (int y = 0, i = x; y < height; y += 2, j += 16, i += 2 * width) {
System.arraycopy(luma, i, bufferY1, j, 16);
}
}
write(buffers[buffersIndex][0] | MemoryMap.START_RAM, sizeY1, bufferY1, 0);
int[] bufferY2 = getIntBuffer(sizeY2);
for (int x = 16, j = 0; x < width; x += 32) {
for (int y = 0, i = x; y < height; y += 2, j += 16, i += 2 * width) {
System.arraycopy(luma, i, bufferY2, j, 16);
}
}
write(buffers[buffersIndex][1] | MemoryMap.START_RAM, sizeY2, bufferY2, 0);
int[] bufferCrCb1 = getIntBuffer(sizeCrCb1);
for (int x = 0, j = 0; x < width2; x += 16) {
for (int y = 0; y < height2; y += 2) {
for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) {
bufferCrCb1[j++] = cb[i];
bufferCrCb1[j++] = cr[i];
}
}
}
write(buffers[buffersIndex][4] | MemoryMap.START_RAM, sizeCrCb1, bufferCrCb1, 0);
int[] bufferCrCb2 = getIntBuffer(sizeCrCb2);
for (int x = 0, j = 0; x < width2; x += 16) {
for (int y = 1; y < height2; y += 2) {
for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) {
bufferCrCb2[j++] = cb[i];
bufferCrCb2[j++] = cr[i];
}
}
}
write(buffers[buffersIndex][5] | MemoryMap.START_RAM, sizeCrCb2, bufferCrCb2, 0);
for (int x = 0, j = 0; x < width; x += 32) {
for (int y = 1, i = x + width; y < height; y += 2, j += 16, i += 2 * width) {
System.arraycopy(luma, i, bufferY1, j, 16);
}
}
write(buffers[buffersIndex][2] | MemoryMap.START_RAM, sizeY1, bufferY1, 0);
releaseIntBuffer(bufferY1);
for (int x = 16, j = 0; x < width; x += 32) {
for (int y = 1, i = x + width; y < height; y += 2, j += 16, i += 2 * width) {
System.arraycopy(luma, i, bufferY2, j, 16);
}
}
write(buffers[buffersIndex][3] | MemoryMap.START_RAM, sizeY2, bufferY2, 0);
releaseIntBuffer(bufferY2);
for (int x = 8, j = 0; x < width2; x += 16) {
for (int y = 0; y < height2; y += 2) {
for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) {
bufferCrCb1[j++] = cb[i];
bufferCrCb1[j++] = cr[i];
}
}
}
write(buffers[buffersIndex][6] | MemoryMap.START_RAM, sizeCrCb1, bufferCrCb1, 0);
releaseIntBuffer(bufferCrCb1);
for (int x = 8, j = 0; x < width2; x += 16) {
for (int y = 1; y < height2; y += 2) {
for (int xx = 0, i = y * width2 + x; xx < 8; xx++, i++) {
bufferCrCb2[j++] = cb[i];
bufferCrCb2[j++] = cr[i];
}
}
}
write(buffers[buffersIndex][7] | MemoryMap.START_RAM, sizeCrCb2, bufferCrCb2, 0);
releaseIntBuffer(bufferCrCb2);
}
releaseIntBuffer(luma);
releaseIntBuffer(cb);
releaseIntBuffer(cr);
TPointer mpegAvcYuvStruct = new TPointer(buffer.getMemory(), buffer.getValue32(44));
for (int i = 0; i < 8; i++) {
mpegAvcYuvStruct.setValue32(i * 4, buffers[buffersIndex][i]);
if (log.isTraceEnabled()) {
log.trace(String.format("sceVideocodecDecode YUV buffer[%d]=0x%08X", i, buffers[buffersIndex][i]));
}
}
mpegAvcYuvStruct.setValue32(32, videoCodec.hasImage()); // 0 or 1
mpegAvcYuvStruct.setValue32(36, bufferUnknown1);
mem.write8(bufferUnknown1 + 0, (byte) 0x02); // 0x00 or 0x04
mem.write32(bufferUnknown1 + 8, sceMpeg.mpegTimestampPerSecond);
mem.write32(bufferUnknown1 + 16, sceMpeg.mpegTimestampPerSecond);
mem.write32(bufferUnknown1 + 24, frameCount * 2);
mem.write32(bufferUnknown1 + 28, 2);
mem.write8(bufferUnknown1 + 32, (byte) 0x00); // 0x00 or 0x01 or 0x02
mem.write8(bufferUnknown1 + 33, (byte) 0x01);
mpegAvcYuvStruct.setValue32(40, bufferUnknown2);
mem.write8(bufferUnknown2 + 0, (byte) 0x00); // 0x00 or 0x04
mem.write32(bufferUnknown2 + 24, 0);
mem.write32(bufferUnknown2 + 28, 0);
TPointer buffer3 = new TPointer(buffer.getMemory(), buffer.getValue32(48));
buffer3.setValue8(0, (byte) 0x01);
buffer3.setValue8(1, (byte) 0xFF);
buffer3.setValue32(4, 3);
buffer3.setValue32(8, 4);
buffer3.setValue32(12, 1);
buffer3.setValue8(16, (byte) 0);
buffer3.setValue32(20, 0x10000);
buffer3.setValue32(32, 4004); // 4004 or 5005
buffer3.setValue32(36, 240000);
TPointer decodeSEI = new TPointer(buffer.getMemory(), buffer.getValue32(80));
decodeSEI.setValue8(0, (byte) 0x02);
decodeSEI.setValue32(8, sceMpeg.mpegTimestampPerSecond);
decodeSEI.setValue32(16, sceMpeg.mpegTimestampPerSecond);
decodeSEI.setValue32(24, frameCount * 2);
decodeSEI.setValue32(28, 2);
decodeSEI.setValue8(32, (byte) 0x00);
decodeSEI.setValue8(33, (byte) 0x01);
}
break;
case 1:
if (videoCodec.hasImage()) {
if (memoryInfo == null) {
int sizeY = frameBufferWidthY * frameHeight;
int sizeCr = frameBufferWidthCr * (frameHeight / 2);
int sizeCb = frameBufferWidthCr * (frameHeight / 2);
int size = (sizeY + sizeCr + sizeCb) * 2;
memoryInfo = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceVideocodecDecode", SysMemUserForUser.PSP_SMEM_Low, size, 0);
bufferY1 = memoryInfo.addr & EDRAM_MEMORY_MASK;
bufferY2 = bufferY1 + sizeY;
bufferCr1 = bufferY1 + sizeY;
bufferCb1 = bufferCr1 + sizeCr;
bufferCr2 = bufferY2 + sizeY;
bufferCb2 = bufferCr2 + sizeCr;
}
}
boolean buffer1 = (frameCount & 1) == 0;
int bufferY = buffer1 ? bufferY1 : bufferY2;
int bufferCr = buffer1 ? bufferCr1 : bufferCr2;
int bufferCb = buffer1 ? bufferCb1 : bufferCb2;
if (videoCodec.hasImage()) {
mem.memset(bufferY | MemoryMap.START_RAM, (byte) 0x80, frameBufferWidthY * frameHeight);
mem.memset(bufferCr | MemoryMap.START_RAM, (byte) (buffer1 ? 0x50 : 0x80), frameBufferWidthCr * (frameHeight / 2));
mem.memset(bufferCb | MemoryMap.START_RAM, (byte) 0x80, frameBufferWidthCb * (frameHeight / 2));
}
buffer2.setValue32(0, mp4Data);
buffer2.setValue32(4, mp4Size);
buffer2.setValue32(8, buffer.getValue32(56));
buffer2.setValue32(12, 0x40);
buffer2.setValue32(16, 0);
buffer2.setValue32(44, mp4Size);
buffer2.setValue32(48, frameWidth);
buffer2.setValue32(52, frameHeight);
buffer2.setValue32(60, videoCodec.hasImage() ? 2 : 1);
buffer2.setValue32(64, 1);
buffer2.setValue32(72, -1);
buffer2.setValue32(76, frameCount * 0x64);
buffer2.setValue32(80, 2997);
buffer2.setValue32(84, bufferY);
buffer2.setValue32(88, bufferCr);
buffer2.setValue32(92, bufferCb);
buffer2.setValue32(96, frameBufferWidthY);
buffer2.setValue32(100, frameBufferWidthCr);
buffer2.setValue32(104, frameBufferWidthCb);
break;
default:
log.warn(String.format("sceVideocodecDecode unknown type=0x%X", type));
break;
}
if (videoCodec.hasImage()) {
frameCount++;
}
IAction action;
long delayMicros = threadWakeupMicroTime - Emulator.getClock().microTime();
if (delayMicros > 0L) {
if (log.isDebugEnabled()) {
log.debug(String.format("Further delaying thread=0x%X by %d microseconds", threadUid, delayMicros));
}
action = new DelayThreadAction(threadUid, (int) delayMicros, false, true);
} else {
if (log.isDebugEnabled()) {
log.debug(String.format("Unblocking thread=0x%X", threadUid));
}
action = new UnblockThreadAction(threadUid);
}
// The action cannot be executed immediately as we are running
// in a non-PSP thread. The action has to be executed by the scheduler
// as soon as possible.
Emulator.getScheduler().addAction(action);
}
public static void write(int addr, int length, int[] buffer, int offset) {
length = Math.min(length, buffer.length - offset);
if (log.isTraceEnabled()) {
log.trace(String.format("write addr=0x%08X, length=0x%X", addr, length));
}
// Optimize the most common case
if (RuntimeContext.hasMemoryInt()) {
int length4 = length >> 2;
int addrOffset = addr >> 2;
int[] memoryInt = RuntimeContext.getMemoryInt();
for (int i = 0, j = offset; i < length4; i++) {
int value = buffer[j++] & 0xFF;
value += (buffer[j++] & 0xFF) << 8;
value += (buffer[j++] & 0xFF) << 16;
value += buffer[j++] << 24;
memoryInt[addrOffset++] = value;
}
} else {
IMemoryWriter memoryWriter = MemoryWriter.getMemoryWriter(addr, length, 1);
for (int i = 0, j = offset; i < length; i++) {
memoryWriter.writeNext(buffer[j++] & 0xFF);
}
memoryWriter.flush();
}
}
@HLEFunction(nid = 0xC01EC829, version = 150)
public int sceVideocodecOpen(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
TPointer buffer2 = new TPointer(buffer.getMemory(), buffer.getValue32(16));
buffer.setValue32(0, 0x05100601);
switch (type) {
case 0:
buffer.setValue32(8, 1);
buffer.setValue32(24, 0x3C2C);
buffer.setValue32(32, 0x15C00);
buffer2.setValue32(0, 0x1F6400);
buffer2.setValue32(4, 0x15C00);
break;
case 1:
buffer.setValue32(8, 0);
buffer.setValue32(24, 0x264C);
buffer.setValue32(32, 0xB69E3);
break;
default:
log.warn(String.format("sceVideocodecOpen unknown type %d", type));
return -1;
}
if (videocodecDecoderThread == null) {
videocodecDecoderThread = new VideocodecDecoderThread();
videocodecDecoderThread.setDaemon(true);
videocodecDecoderThread.setName("Videocodec Decoder Thread");
videocodecDecoderThread.start();
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xA2F0564E, version = 150)
public int sceVideocodecStop(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
return 0;
}
@HLEFunction(nid = 0x17099F0A, version = 150)
public int sceVideocodecInit(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
buffer.setValue32(12, buffer.getValue32(20) + 8);
return 0;
}
@HLEFunction(nid = 0x2D31F5B1, version = 150)
public int sceVideocodecGetEDRAM(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
int size = (buffer.getValue32(24) + 63) | 0x3F;
edramInfo = Modules.SysMemUserForUserModule.malloc(SysMemUserForUser.KERNEL_PARTITION_ID, "sceVideocodecEDRAM", SysMemUserForUser.PSP_SMEM_Low, size, 0);
if (edramInfo == null) {
return -1;
}
int addrEDRAM = edramInfo.addr & EDRAM_MEMORY_MASK;
buffer.setValue32(20, alignUp(addrEDRAM, 63));
buffer.setValue32(92, addrEDRAM);
return 0;
}
@HLEFunction(nid = 0x4F160BF4, version = 150)
public int sceVideocodecReleaseEDRAM(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer) {
buffer.setValue32(20, 0);
buffer.setValue32(92, 0);
if (edramInfo != null) {
Modules.SysMemUserForUserModule.free(edramInfo);
edramInfo = null;
}
return 0;
}
@HLEFunction(nid = 0xDBA273FA, version = 150)
public int sceVideocodecDecode(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
if (type != 0 && type != 1) {
log.warn(String.format("sceVideocodecDecode unknown type=0x%X", type));
return -1;
}
int threadUid = Modules.ThreadManForUserModule.getCurrentThreadID();
Modules.ThreadManForUserModule.hleBlockCurrentThread(SceKernelThreadInfo.JPCSP_WAIT_VIDEO_DECODER);
videocodecDecoderThread.trigger(buffer, type, threadUid, Emulator.getClock().microTime() + videocodecDecodeDelay);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x17CF7D2C, version = 150)
public int sceVideocodecGetFrameCrop() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x26927D19, version = 150)
public int sceVideocodecGetVersion(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
// This is the value returned on my PSP according to JpcspTrace.
buffer.setValue32(4, 0x78);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x2F385E7F, version = 150)
public int sceVideocodecScanHeader() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x307E6E1C, version = 150)
public int sceVideocodecDelete() {
if (videocodecDecoderThread != null) {
videocodecDecoderThread.exit();
videocodecDecoderThread = null;
}
if (videoCodec != null) {
videoCodec = null;
}
if (memoryInfo != null) {
Modules.SysMemUserForUserModule.free(memoryInfo);
memoryInfo = null;
}
if (edramInfo != null) {
Modules.SysMemUserForUserModule.free(edramInfo);
edramInfo = null;
}
Modules.ThreadManForUserModule.hleKernelDelayThread(videocodecDeleteDelay, false);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x627B7D42, version = 150)
public int sceVideocodecGetSEI(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
TPointer decodeSEI = new TPointer(buffer.getMemory(), buffer.getValue32(80));
if (log.isDebugEnabled()) {
log.debug(String.format("sceVideocodecGetSEI storing decodeSEI to %s", decodeSEI));
}
decodeSEI.setValue32(28, 0);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x745A7B7A, version = 150)
public int sceVideocodecSetMemory(@BufferInfo(lengthInfo=LengthInfo.fixedLength, length=96, usage=Usage.inout) TPointer buffer, int type) {
int unknown1 = buffer.getValue32(64);
int unknown2 = buffer.getValue32(68);
int unknown3 = buffer.getValue32(72);
int unknown4 = buffer.getValue32(76);
if (log.isDebugEnabled()) {
log.debug(String.format("sceVideocodecSetMemory unknown1=0x%08X, unknown2=0x%08X, unknown3=0x%08X, unknown4=0x%08X", unknown1, unknown2, unknown3, unknown4));
}
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x893B32B1, version = 150)
public int sceVideocodec_893B32B1() {
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0xD95C24D5, version = 150)
public int sceVideocodec_D95C24D5() {
return 0;
}
}