private bool startVideo() { endOfVideo = false; if (!readPsmfHeader()) { return(false); } videoCodec = CodecFactory.VideoCodec; videoCodec.init(null); startTime = DateTimeHelper.CurrentUnixTimeMillis(); frame = 0; 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.TraceEnabled) { log.trace(string.Format("sceVideocodecDecode mp4Data:{0}", Utilities.getMemoryDump(mp4Data, mp4Size))); } if (videoCodec == null) { videoCodec = CodecFactory.VideoCodec; 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.DebugEnabled) { Console.WriteLine(string.Format("sceVideocodecDecode videoCodec returned 0x{0:X} from 0x{1:X} data bytes", result, mp4Size)); } releaseIntBuffer(mp4Buffer); buffer.setValue32(8, 0); int frameWidth = videoCodec.ImageWidth; int frameHeight = videoCodec.ImageHeight; if (log.TraceEnabled) { log.trace(string.Format("sceVideocodecDecode codec image size {0:D}x{1:D}, frame size {2:D}x{3:D}", videoCodec.ImageWidth, videoCodec.ImageHeight, frameWidth, frameHeight)); } int frameBufferWidthY = videoCodec.ImageWidth; int frameBufferWidthCr = frameBufferWidthY / 2; int frameBufferWidthCb = frameBufferWidthY / 2; Memory mem = buffer.Memory; TPointer buffer2 = buffer.getPointer(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, (sbyte)0, 36); bufferUnknown2 = @base + 36; mem.memset(bufferUnknown2, (sbyte)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.ImageWidth; int height = videoCodec.ImageHeight; 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) { Array.Copy(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) { Array.Copy(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) { Array.Copy(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) { Array.Copy(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 = buffer.getPointer(44); for (int i = 0; i < 8; i++) { mpegAvcYuvStruct.setValue32(i * 4, buffers[buffersIndex][i]); if (log.TraceEnabled) { log.trace(string.Format("sceVideocodecDecode YUV buffer[{0:D}]=0x{1:X8}", i, buffers[buffersIndex][i])); } } mpegAvcYuvStruct.setValue32(32, videoCodec.hasImage()); // 0 or 1 mpegAvcYuvStruct.setValue32(36, bufferUnknown1); mem.write8(bufferUnknown1 + 0, (sbyte)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, (sbyte)0x00); // 0x00 or 0x01 or 0x02 mem.write8(bufferUnknown1 + 33, (sbyte)0x01); mpegAvcYuvStruct.setValue32(40, bufferUnknown2); mem.write8(bufferUnknown2 + 0, (sbyte)0x00); // 0x00 or 0x04 mem.write32(bufferUnknown2 + 24, 0); mem.write32(bufferUnknown2 + 28, 0); TPointer buffer3 = buffer.getPointer(48); buffer3.setValue8(0, (sbyte)0x01); buffer3.setValue8(1, unchecked ((sbyte)0xFF)); buffer3.setValue32(4, 3); buffer3.setValue32(8, 4); buffer3.setValue32(12, 1); buffer3.setValue8(16, (sbyte)0); buffer3.setValue32(20, 0x10000); buffer3.setValue32(32, 4004); // 4004 or 5005 buffer3.setValue32(36, 240000); TPointer decodeSEI = buffer.getPointer(80); decodeSEI.setValue8(0, (sbyte)0x02); decodeSEI.setValue32(8, sceMpeg.mpegTimestampPerSecond); decodeSEI.setValue32(16, sceMpeg.mpegTimestampPerSecond); decodeSEI.setValue32(24, frameCount * 2); decodeSEI.setValue32(28, 2); decodeSEI.setValue8(32, (sbyte)0x00); decodeSEI.setValue8(33, (sbyte)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; } } bool 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, unchecked ((sbyte)0x80), frameBufferWidthY * frameHeight); mem.memset(bufferCr | MemoryMap.START_RAM, (sbyte)(buffer1 ? 0x50 : 0x80), frameBufferWidthCr * (frameHeight / 2)); mem.memset(bufferCb | MemoryMap.START_RAM, unchecked ((sbyte)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: Console.WriteLine(string.Format("sceVideocodecDecode unknown type=0x{0:X}", type)); break; } if (videoCodec.hasImage()) { frameCount++; } IAction action; long delayMicros = threadWakeupMicroTime - Emulator.Clock.microTime(); if (delayMicros > 0L) { //if (log.DebugEnabled) { Console.WriteLine(string.Format("Further delaying thread=0x{0:X} by {1:D} microseconds", threadUid, delayMicros)); } action = new DelayThreadAction(threadUid, (int)delayMicros, false, true); } else { //if (log.DebugEnabled) { Console.WriteLine(string.Format("Unblocking thread=0x{0: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.Scheduler.addAction(action); }