/*
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.kernel.types.SceKernelErrors.ERROR_PSMFPLAYER_NOT_INITIALIZED;
import static jpcsp.HLE.kernel.types.SceMpegRingbuffer.ringbufferPacketSize;
import static jpcsp.HLE.modules.SysMemUserForUser.KERNEL_PARTITION_ID;
import static jpcsp.HLE.modules.SysMemUserForUser.PSP_SMEM_Low;
import static jpcsp.HLE.modules.sceMpeg.MPEG_MEMSIZE;
import static jpcsp.HLE.modules.sceMpeg.PSMF_MAGIC;
import static jpcsp.HLE.modules.sceMpeg.PSMF_MAGIC_OFFSET;
import static jpcsp.HLE.modules.sceMpeg.PSMF_STREAM_OFFSET_OFFSET;
import static jpcsp.HLE.modules.sceMpeg.PSMF_STREAM_SIZE_OFFSET;
import static jpcsp.HLE.modules.sceMpeg.read32;
import static jpcsp.graphics.GeCommands.TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444;
import static jpcsp.graphics.GeCommands.TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551;
import static jpcsp.graphics.GeCommands.TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650;
import static jpcsp.graphics.GeCommands.TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888;
import static jpcsp.util.Utilities.endianSwap32;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.PspString;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import jpcsp.HLE.TPointer32;
import java.io.IOException;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceMpegRingbuffer;
import jpcsp.HLE.Modules;
import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo;
import jpcsp.filesystems.SeekableDataInput;
import jpcsp.filesystems.umdiso.ISectorDevice;
import jpcsp.graphics.VideoEngine;
import jpcsp.hardware.Screen;
import jpcsp.media.codec.atrac3plus.Atrac3plusDecoder;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
public class scePsmfPlayer extends HLEModule {
public static Logger log = Modules.getLogger("scePsmfPlayer");
// PSMF Player timing management.
protected static final int psmfPlayerVideoTimestampStep = sceMpeg.videoTimestampStep;
protected static final int psmfPlayerAudioTimestampStep = sceMpeg.audioTimestampStep;
protected static final int psmfTimestampPerSecond = sceMpeg.mpegTimestampPerSecond;
// PSMF Player status.
protected static final int PSMF_PLAYER_STATUS_NONE = 0x0;
protected static final int PSMF_PLAYER_STATUS_INIT = 0x1;
protected static final int PSMF_PLAYER_STATUS_STANDBY = 0x2;
protected static final int PSMF_PLAYER_STATUS_PLAYING = 0x4;
protected static final int PSMF_PLAYER_STATUS_ERROR = 0x100;
protected static final int PSMF_PLAYER_STATUS_PLAYING_FINISHED = 0x200;
// PSMF Player status vars.
protected int psmfPlayerStatus;
// PSMF Player mode.
protected static final int PSMF_PLAYER_MODE_PLAY = 0;
protected static final int PSMF_PLAYER_MODE_SLOWMOTION = 1;
protected static final int PSMF_PLAYER_MODE_STEPFRAME = 2;
protected static final int PSMF_PLAYER_MODE_PAUSE = 3;
protected static final int PSMF_PLAYER_MODE_FORWARD = 4;
protected static final int PSMF_PLAYER_MODE_REWIND = 5;
// PSMF Player stream type.
protected static final int PSMF_PLAYER_STREAM_VIDEO = 14;
// PSMF Player playback speed.
protected static final int PSMF_PLAYER_SPEED_SLOW = 1;
protected static final int PSMF_PLAYER_SPEED_NORMAL = 2;
protected static final int PSMF_PLAYER_SPEED_FAST = 3;
// PSMF Player config mode.
protected static final int PSMF_PLAYER_CONFIG_MODE_LOOP = 0;
protected static final int PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE = 1;
// PSMF Player config loop.
protected static final int PSMF_PLAYER_CONFIG_LOOP = 0;
protected static final int PSMF_PLAYER_CONFIG_NO_LOOP = 1;
// PSMF Player config pixel type.
protected static final int PSMF_PLAYER_PIXEL_TYPE_NONE = -1;
// PSMF Player version.
protected static final int PSMF_PLAYER_VERSION_FULL = 0;
protected static final int PSMF_PLAYER_VERSION_BASIC = 1;
protected static final int PSMF_PLAYER_VERSION_NET = 2;
// PMF file vars.
protected String pmfFilePath;
protected byte[] pmfFileData;
// PMSF info.
protected int psmfAvcStreamNum = 1;
protected int psmfAtracStreamNum = 1;
protected int psmfPcmStreamNum = 0;
protected int psmfPlayerVersion = PSMF_PLAYER_VERSION_FULL;
// PSMF Player playback params.
protected int displayBuffer;
protected int displayBufferSize;
protected int playbackThreadPriority;
// PSMF Player playback info.
protected int videoCodec;
protected int videoStreamNum;
protected int audioCodec;
protected int audioStreamNum;
protected int playMode;
protected int playSpeed;
protected int initPts;
// PSMF Player video data.
protected int videoDataFrameWidth = 512; // Default.
protected int videoDataDisplayBuffer;
protected int videoDataDisplayPts;
// PSMF Player config.
protected int videoPixelMode = TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888; // Default.
protected int videoLoopStatus = PSMF_PLAYER_CONFIG_NO_LOOP; // Default.
// PSMF Player audio size
protected final int audioSamples = Atrac3plusDecoder.ATRAC3P_FRAME_SAMPLES;
protected final int audioSamplesBytes = audioSamples * 4;
// Internal vars.
protected SysMemInfo mpegMem;
protected SysMemInfo ringbufferMem;
protected int pmfFileDataRingbufferPosition;
private static final int MAX_TIMESTAMP_DIFFERENCE = sceMpeg.audioTimestampStep * 2;
protected int lastMpegGetAtracAuResult;
public int checkPlayerInitialized(int psmfPlayer) {
if (psmfPlayerStatus == PSMF_PLAYER_STATUS_NONE) {
if (log.isDebugEnabled()) {
log.debug(String.format("checkPlayerInitialized player not initialized (status=0x%X)", psmfPlayerStatus));
}
throw new SceKernelErrorException(ERROR_PSMFPLAYER_NOT_INITIALIZED);
}
return psmfPlayer;
}
public int checkPlayerPlaying(int psmfPlayer) {
psmfPlayer = checkPlayerInitialized(psmfPlayer);
if (psmfPlayerStatus != PSMF_PLAYER_STATUS_PLAYING && psmfPlayerStatus != PSMF_PLAYER_STATUS_PLAYING_FINISHED && psmfPlayerStatus != PSMF_PLAYER_STATUS_ERROR) {
if (log.isDebugEnabled()) {
log.debug(String.format("checkPlayerInitialized player not playing (status=0x%X)", psmfPlayerStatus));
}
throw new SceKernelErrorException(ERROR_PSMFPLAYER_NOT_INITIALIZED);
}
return psmfPlayer;
}
public long getCurrentVideoTimestamp() {
return Modules.sceMpegModule.getCurrentVideoTimestamp();
}
public long getCurrentAudioTimestamp() {
return Modules.sceMpegModule.getCurrentAudioTimestamp();
}
protected int getMaxTimestampDifference() {
int maxTimestampDifference = MAX_TIMESTAMP_DIFFERENCE;
// At video startup, allow for a longer timestamp difference to avoid audio stuttering.
long firstTimestamp = Modules.sceMpegModule.getPsmfHeader().mpegFirstTimestamp;
if (getCurrentVideoTimestamp() < firstTimestamp + sceMpeg.videoTimestampStep * 10) {
maxTimestampDifference *= 2;
}
return maxTimestampDifference;
}
protected int hlePsmfPlayerSetPsmf(int psmfPlayer, PspString fileAddr, int offset, boolean doCallbacks, boolean useSizeFromPsmfHeader) {
if (psmfPlayerStatus != PSMF_PLAYER_STATUS_INIT) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
if (offset != 0) {
log.warn(String.format("hlePsmfPlayerSetPsmf unimplemented offset=0x%X", offset));
}
pmfFilePath = fileAddr.getString();
// Get the file and read it to a buffer.
try {
if (log.isInfoEnabled()) {
log.info(String.format("Loading PSMF file '%s'", pmfFilePath));
}
SeekableDataInput psmfFile = Modules.IoFileMgrForUserModule.getFile(pmfFilePath, 0);
psmfFile.seek(offset);
int length = (int) psmfFile.length() - offset;
// Some PSMF files have an incorrect size stored into their header.
// It seems that the PSP is ignoring this size when using scePsmfPlayerSetPsmf().
// However, the size is probably not ignored when using scePsmfPlayerSetPsmfOffset().
if (useSizeFromPsmfHeader) {
// Try to find the length of the PSMF file by reading the PSMF header
byte[] header = new byte[ISectorDevice.sectorLength];
psmfFile.readFully(header);
int psmfMagic = read32(null, 0, header, PSMF_MAGIC_OFFSET);
if (psmfMagic == PSMF_MAGIC) {
// Found the PSMF header, extract the file size from the stream size and offset.
length = endianSwap32(read32(null, 0, header, PSMF_STREAM_SIZE_OFFSET));
length += endianSwap32(read32(null, 0, header, PSMF_STREAM_OFFSET_OFFSET));
if (log.isDebugEnabled()) {
log.debug(String.format("PSMF length=0x%X, header: %s", length, Utilities.getMemoryDump(header, 0, header.length)));
}
}
}
psmfFile.seek(offset);
pmfFileData = new byte[length];
psmfFile.readFully(pmfFileData);
psmfFile.close();
Modules.sceMpegModule.analyseMpeg(0, pmfFileData);
pmfFileDataRingbufferPosition = Modules.sceMpegModule.getPsmfHeader().mpegOffset;
} catch (OutOfMemoryError e) {
log.error("hlePsmfPlayerSetPsmf", e);
} catch (IOException e) {
log.error("hlePsmfPlayerSetPsmf", e);
}
// Switch to STANDBY.
psmfPlayerStatus = PSMF_PLAYER_STATUS_STANDBY;
// Delay the thread for 100ms
Modules.ThreadManForUserModule.hleKernelDelayThread(100000, doCallbacks);
return 0;
}
protected int getRemainingFileData() {
SceMpegRingbuffer ringbuffer = Modules.sceMpegModule.getMpegRingbuffer();
int packetSize = ringbuffer.getPacketSize();
int packetsInRingbuffer = ringbuffer.getPacketsInRingbuffer();
int bytesInRingbuffer = packetsInRingbuffer * packetSize;
int bytesRemainingInFileData = pmfFileData.length - pmfFileDataRingbufferPosition;
int bytesRemaining = bytesRemainingInFileData + bytesInRingbuffer;
if (log.isTraceEnabled()) {
log.trace(String.format("getRemainingFileData packetsInRingbuffer=0x%X, bytesRemainingInFileData=0x%X, bytesRemaining=0x%X", packetsInRingbuffer, bytesRemainingInFileData, bytesRemaining));
}
return bytesRemaining;
}
protected void hlePsmfFillRingbuffer(Memory mem) {
SceMpegRingbuffer ringbuffer = Modules.sceMpegModule.getMpegRingbuffer();
ringbuffer.notifyConsumed();
if (ringbuffer.getPutSequentialPackets() > 0) {
int packetSize = ringbuffer.getPacketSize();
int addr = ringbuffer.getPutDataAddr();
int size = ringbuffer.getPutSequentialPackets() * packetSize;
size = Math.min(size, pmfFileData.length - pmfFileDataRingbufferPosition);
if (log.isTraceEnabled()) {
log.trace(String.format("Filling ringbuffer at 0x%08X, size=0x%X with file data from offset 0x%X", addr, size, pmfFileDataRingbufferPosition));
log.trace(String.format("Ringbuffer putSequentialPackets=%d, file data length=0x%X, position=0x%X", ringbuffer.getPutSequentialPackets(), pmfFileData.length, pmfFileDataRingbufferPosition));
}
for (int i = 0; i < size; i++) {
mem.write8(addr + i, pmfFileData[pmfFileDataRingbufferPosition + i]);
}
ringbuffer.addPackets((size + packetSize - 1) / packetSize);
pmfFileDataRingbufferPosition += size;
Modules.sceMpegModule.hleMpegNotifyVideoDecoderThread();
}
}
@HLEFunction(nid = 0x235D8787, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerCreate(int psmfPlayer, TPointer32 psmfPlayerDataAddr) {
// The psmfDataAddr contains three fields that are manually set before
// scePsmfPlayerCreate is called.
displayBuffer = psmfPlayerDataAddr.getValue(0) & Memory.addressMask; // The buffer allocated for scePsmf, which is ported into scePsmfPlayer.
displayBufferSize = psmfPlayerDataAddr.getValue(4); // The buffer's size.
playbackThreadPriority = psmfPlayerDataAddr.getValue(8); // Priority of the "START" thread.
if (log.isInfoEnabled()) {
log.info(String.format("PSMF Player Data: displayBuffer=0x%08X, displayBufferSize=0x%X, playbackThreadPriority=%d", displayBuffer, displayBufferSize, playbackThreadPriority));
}
// Allocate memory for the MPEG structure
Memory mem = Memory.getInstance();
mpegMem = Modules.SysMemUserForUserModule.malloc(KERNEL_PARTITION_ID, getName() + "-Mpeg", PSP_SMEM_Low, MPEG_MEMSIZE, 0);
int result = Modules.sceMpegModule.hleMpegCreate(TPointer.NULL, new TPointer(mem, mpegMem.addr), MPEG_MEMSIZE, null, Screen.width, 0, 0);
if (result < 0) {
log.error(String.format("scePsmfPlayerCreate: error 0x%08X while calling hleMpegCreate", result));
}
// Allocate memory for the ringbuffer, scePsmfPlayer creates a ringbuffer with 581 packets
final int packets = 581;
ringbufferMem = Modules.SysMemUserForUserModule.malloc(KERNEL_PARTITION_ID, getName() + "-Ringbuffer", PSP_SMEM_Low, packets * ringbufferPacketSize, 0);
Modules.sceMpegModule.hleCreateRingbuffer(packets, ringbufferMem.addr, ringbufferMem.size);
SceMpegRingbuffer ringbuffer = Modules.sceMpegModule.getMpegRingbuffer();
// This ringbuffer is used both for audio and video
ringbuffer.setHasAudio(true);
ringbuffer.setHasVideo(true);
// Start with INIT.
psmfPlayerStatus = PSMF_PLAYER_STATUS_INIT;
return 0;
}
@HLEFunction(nid = 0x9B71A274, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerDelete(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
VideoEngine.getInstance().resetVideoTextures();
if (ringbufferMem != null) {
Modules.SysMemUserForUserModule.free(ringbufferMem);
ringbufferMem = null;
}
if (mpegMem != null) {
Modules.SysMemUserForUserModule.free(mpegMem);
mpegMem = null;
}
// Set to NONE.
psmfPlayerStatus = PSMF_PLAYER_STATUS_NONE;
return 0;
}
@HLEFunction(nid = 0x3D6D25A9, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSetPsmf(@CheckArgument("checkPlayerInitialized") int psmfPlayer, PspString fileAddr) {
return hlePsmfPlayerSetPsmf(psmfPlayer, fileAddr, 0, false, false);
}
@HLEFunction(nid = 0x58B83577, version = 150)
public int scePsmfPlayerSetPsmfCB(@CheckArgument("checkPlayerInitialized") int psmfPlayer, PspString fileAddr) {
return hlePsmfPlayerSetPsmf(psmfPlayer, fileAddr, 0, true, false);
}
@HLEFunction(nid = 0xE792CD94, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerReleasePsmf(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
if (psmfPlayerStatus != PSMF_PLAYER_STATUS_STANDBY) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
Modules.sceMpegModule.finishMpeg();
VideoEngine.getInstance().resetVideoTextures();
// Go back to INIT, because some applications recognize that another file can be
// loaded after scePsmfPlayerReleasePsmf has been called.
psmfPlayerStatus = PSMF_PLAYER_STATUS_INIT;
Modules.ThreadManForUserModule.hleKernelDelayThread(10000, false);
return 0;
}
@HLEFunction(nid = 0x95A84EE5, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerStart(@CheckArgument("checkPlayerInitialized") int psmfPlayer, @CanBeNull TPointer32 initPlayInfoAddr, int initPts) {
// Read the playback parameters.
if (initPlayInfoAddr.isNotNull()) {
videoCodec = initPlayInfoAddr.getValue(0);
videoStreamNum = initPlayInfoAddr.getValue(4);
audioCodec = initPlayInfoAddr.getValue(8);
audioStreamNum = initPlayInfoAddr.getValue(12);
playMode = initPlayInfoAddr.getValue(16);
playSpeed = initPlayInfoAddr.getValue(20);
Modules.sceMpegModule.setRegisteredVideoChannel(videoStreamNum);
Modules.sceMpegModule.setRegisteredAudioChannel(audioStreamNum);
if (log.isInfoEnabled()) {
log.info(String.format("Found play info data: videoCodec=0x%X, videoStreamNum=%d, audioCodec=0x%X, audioStreamNum=%d, playMode=%d, playSpeed=%d", videoCodec, videoStreamNum, audioCodec, audioStreamNum, playMode, playSpeed));
}
}
this.initPts = initPts;
// Switch to PLAYING.
psmfPlayerStatus = PSMF_PLAYER_STATUS_PLAYING;
lastMpegGetAtracAuResult = 0;
return 0;
}
@HLEFunction(nid = 0x3EA82A4B, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetAudioOutSize(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
return audioSamplesBytes;
}
@HLEFunction(nid = 0x1078C008, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerStop(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
VideoEngine.getInstance().resetVideoTextures();
// Always switch to STANDBY, because this PSMF can still be resumed.
psmfPlayerStatus = PSMF_PLAYER_STATUS_STANDBY;
// scePsmfPlayerStop does not reschedule threads
return 0;
}
@HLEFunction(nid = 0xA0B8CA55, version = 150)
public int scePsmfPlayerUpdate(@CheckArgument("checkPlayerPlaying") int psmfPlayer) {
// Can be called from interrupt.
// Check playback status.
int remainingFileData = getRemainingFileData();
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerUpdate remainingFileData=0x%X", remainingFileData));
}
if (remainingFileData <= 0) {
// If we've reached the end of the file data, change the status to PLAYING_FINISHED.
// Remark: do not use the PSMF header last timestamp as it may contain an incorrect
// value which seems to be ignored by the PSP.
psmfPlayerStatus = PSMF_PLAYER_STATUS_PLAYING_FINISHED;
}
return 0;
}
@HLEFunction(nid = 0x46F61F8B, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetVideoData(@CheckArgument("checkPlayerPlaying") int psmfPlayer, @CanBeNull TPointer32 videoDataAddr) {
int result = 0;
if (psmfPlayerStatus != PSMF_PLAYER_STATUS_PLAYING && psmfPlayerStatus != PSMF_PLAYER_STATUS_PLAYING_FINISHED) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
if (playMode == PSMF_PLAYER_MODE_PAUSE) {
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetVideoData in pause mode, returning 0x%08X", result));
}
return result;
}
if (videoDataAddr.isNotNull()) {
videoDataFrameWidth = videoDataAddr.getValue(0);
videoDataDisplayBuffer = videoDataAddr.getValue(4) & Memory.addressMask;
videoDataDisplayPts = videoDataAddr.getValue(8);
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetVideoData videoDataFrameWidth=%d, videoDataDisplayBuffer=0x%08X, videoDataDisplayPts=%d", videoDataFrameWidth, videoDataDisplayBuffer, videoDataDisplayPts));
}
}
// Check if there's already a valid pointer at videoDataAddr.
// If not, use the displayBuffer from scePsmfPlayerCreate.
if (Memory.isAddressGood(videoDataDisplayBuffer)) {
displayBuffer = videoDataDisplayBuffer;
} else if (videoDataAddr.isNotNull()) {
videoDataAddr.setValue(4, displayBuffer);
// Valid frame width?
if (videoDataFrameWidth <= 0 || videoDataFrameWidth > 512) {
videoDataFrameWidth = 512;
videoDataAddr.setValue(0, videoDataFrameWidth);
}
}
if (getCurrentAudioTimestamp() > 0 && getCurrentVideoTimestamp() > 0 && getCurrentVideoTimestamp() > getCurrentAudioTimestamp() + getMaxTimestampDifference() && lastMpegGetAtracAuResult == 0) {
//result = SceKernelErrors.ERROR_PSMFPLAYER_AUDIO_VIDEO_OUT_OF_SYNC;
Modules.sceMpegModule.writeLastFrameABGR(displayBuffer, videoDataFrameWidth, videoPixelMode);
} else {
// Check if the ringbuffer needs additional data
hlePsmfFillRingbuffer(Emulator.getMemory());
// Retrieve the video Au
result = Modules.sceMpegModule.hleMpegGetAvcAu(null);
if (result < 0) {
// We have reached the end of the file...
if (pmfFileDataRingbufferPosition >= pmfFileData.length) {
SceMpegRingbuffer ringbuffer = Modules.sceMpegModule.getMpegRingbuffer();
ringbuffer.consumeAllPackets();
}
} else {
// Write the video data
result = Modules.sceMpegModule.hleMpegAvcDecode(displayBuffer, videoDataFrameWidth, videoPixelMode, null, true, TPointer.NULL);
}
}
// Do not cache the video image as a texture in the VideoEngine to allow fluid rendering
VideoEngine.getInstance().addVideoTexture(displayBuffer, displayBuffer + 272 * videoDataFrameWidth * sceDisplay.getPixelFormatBytes(videoPixelMode));
// Return updated timestamp
videoDataAddr.setValue(8, (int) getCurrentVideoTimestamp());
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetVideoData currentVideoTimestamp=%d, returning 0x%08X", getCurrentVideoTimestamp(), result));
}
return result;
}
@HLEFunction(nid = 0xB9848A74, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetAudioData(@CheckArgument("checkPlayerPlaying") int psmfPlayer, TPointer audioDataAddr) {
int result = 0;
if (playMode == PSMF_PLAYER_MODE_PAUSE) {
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetAudioData in pause mode, returning 0x%08X", result));
}
// Clear the audio buffer (silent audio returned)
audioDataAddr.clear(audioSamplesBytes);
return result;
}
if (getCurrentAudioTimestamp() > 0 && getCurrentVideoTimestamp() > 0 && getCurrentAudioTimestamp() > getCurrentVideoTimestamp() + getMaxTimestampDifference() && lastMpegGetAtracAuResult == 0) {
result = SceKernelErrors.ERROR_PSMFPLAYER_AUDIO_VIDEO_OUT_OF_SYNC;
} else {
// Check if the ringbuffer needs additional data
hlePsmfFillRingbuffer(audioDataAddr.getMemory());
// Retrieve the audio Au
result = Modules.sceMpegModule.hleMpegGetAtracAu(null);
lastMpegGetAtracAuResult = result;
// Write the audio data
result = Modules.sceMpegModule.hleMpegAtracDecode(null, audioDataAddr, audioSamplesBytes);
}
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetAudioData currentAudioTimestamp=%d, returning 0x%08X", getCurrentAudioTimestamp(), result));
}
return result;
}
@HLEFunction(nid = 0xF8EF08A6, version = 150)
public int scePsmfPlayerGetCurrentStatus(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
// scePsmfPlayerGetCurrentStatus can be called from an interrupt
if (log.isDebugEnabled()) {
log.debug(String.format("scePsmfPlayerGetCurrentStatus returning status 0x%X", psmfPlayerStatus));
}
return psmfPlayerStatus;
}
@HLEFunction(nid = 0xDF089680, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetPsmfInfo(@CheckArgument("checkPlayerInitialized") int psmfPlayer, TPointer32 psmfInfoAddr) {
if (psmfPlayerStatus < PSMF_PLAYER_STATUS_STANDBY) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
psmfInfoAddr.setValue(0, (int) Modules.sceMpegModule.psmfHeader.mpegLastTimestamp);
psmfInfoAddr.setValue(4, psmfAvcStreamNum);
psmfInfoAddr.setValue(8, psmfAtracStreamNum);
psmfInfoAddr.setValue(12, psmfPcmStreamNum);
psmfInfoAddr.setValue(16, psmfPlayerVersion);
return 0;
}
@HLEFunction(nid = 0x1E57A8E7, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerConfigPlayer(@CheckArgument("checkPlayerInitialized") int psmfPlayer, int configMode, int configAttr) {
if (psmfPlayerStatus == PSMF_PLAYER_STATUS_NONE) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
if (configMode == PSMF_PLAYER_CONFIG_MODE_LOOP) { // Sets if the video is looped or not.
if (configAttr < 0 || configAttr > 1) {
return SceKernelErrors.ERROR_PSMFPLAYER_INVALID_CONFIG_VALUE;
}
videoLoopStatus = configAttr;
} else if (configMode == PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE) { // Sets the display's pixel type.
switch (configAttr) {
case PSMF_PLAYER_PIXEL_TYPE_NONE:
// -1 means nothing to change
break;
case TPSM_PIXEL_STORAGE_MODE_16BIT_BGR5650:
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR5551:
case TPSM_PIXEL_STORAGE_MODE_16BIT_ABGR4444:
case TPSM_PIXEL_STORAGE_MODE_32BIT_ABGR8888:
videoPixelMode = configAttr;
break;
case 4:
// This value is accepted, but its function is unknown
log.warn(String.format("scePsmfPlayerConfigPlayer unknown pixelMode=%d", configAttr));
break;
default:
return SceKernelErrors.ERROR_PSMFPLAYER_INVALID_CONFIG_VALUE;
}
} else {
log.warn(String.format("scePsmfPlayerConfigPlayer invalid configMode=%d, configAttr=%d", configMode, configAttr));
return SceKernelErrors.ERROR_PSMFPLAYER_INVALID_CONFIG_MODE;
}
return 0;
}
@HLEFunction(nid = 0xA3D81169, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerChangePlayMode(@CheckArgument("checkPlayerInitialized") int psmfPlayer, int playMode, int playSpeed) {
this.playMode = playMode;
this.playSpeed = playSpeed;
return 0;
}
@HLEFunction(nid = 0x68F07175, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetCurrentAudioStream(@CheckArgument("checkPlayerInitialized") int psmfPlayer, @CanBeNull TPointer32 audioCodecAddr, @CanBeNull TPointer32 audioStreamNumAddr) {
if (psmfPlayerStatus < PSMF_PLAYER_STATUS_STANDBY) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
audioCodecAddr.setValue(audioCodec);
audioStreamNumAddr.setValue(audioStreamNum);
return 0;
}
@HLEFunction(nid = 0xF3EFAA91, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetCurrentPlayMode(@CheckArgument("checkPlayerInitialized") int psmfPlayer, @CanBeNull TPointer32 playModeAddr, @CanBeNull TPointer32 playSpeedAddr) {
playModeAddr.setValue(playMode);
playSpeedAddr.setValue(playSpeed);
return 0;
}
@HLEFunction(nid = 0x3ED62233, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetCurrentPts(@CheckArgument("checkPlayerInitialized") int psmfPlayer, TPointer32 currentPtsAddr) {
if (psmfPlayerStatus < PSMF_PLAYER_STATUS_STANDBY) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
// Write our current video presentation timestamp.
currentPtsAddr.setValue((int) getCurrentVideoTimestamp());
return 0;
}
@HLEFunction(nid = 0x9FF2B2E7, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerGetCurrentVideoStream(@CheckArgument("checkPlayerInitialized") int psmfPlayer, @CanBeNull TPointer32 videoCodecAddr, @CanBeNull TPointer32 videoStreamNumAddr) {
if (psmfPlayerStatus < PSMF_PLAYER_STATUS_STANDBY) {
return ERROR_PSMFPLAYER_NOT_INITIALIZED;
}
videoCodecAddr.setValue(videoCodec);
videoStreamNumAddr.setValue(videoStreamNum);
return 0;
}
@HLEUnimplemented
@HLEFunction(nid = 0x2BEB1569, version = 150)
public int scePsmfPlayerBreak(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
// Can be called from interrupt.
return 0;
}
@HLEFunction(nid = 0x76C0F4AE, version = 150)
public int scePsmfPlayerSetPsmfOffset(@CheckArgument("checkPlayerInitialized") int psmfPlayer, PspString fileAddr, int offset) {
return hlePsmfPlayerSetPsmf(psmfPlayer, fileAddr, offset, false, true);
}
@HLEFunction(nid = 0xA72DB4F9, version = 150)
public int scePsmfPlayerSetPsmfOffsetCB(@CheckArgument("checkPlayerInitialized") int psmfPlayer, PspString fileAddr, int offset) {
return hlePsmfPlayerSetPsmf(psmfPlayer, fileAddr, offset, true, true);
}
@HLEUnimplemented
@HLEFunction(nid = 0x2D0E4E0A, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSetTempBuf(int psmfPlayer, TPointer tempBufAddr, int tempBufSize) {
return 0;
}
@HLEFunction(nid = 0x75F03FA2, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSelectSpecificVideo(@CheckArgument("checkPlayerInitialized") int psmfPlayer, int videoCodec, int videoStreamNum) {
this.videoCodec = videoCodec;
this.videoStreamNum = videoStreamNum;
return 0;
}
@HLEFunction(nid = 0x85461EFF, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSelectSpecificAudio(@CheckArgument("checkPlayerInitialized") int psmfPlayer, int audioCodec, int audioStreamNum) {
this.audioCodec = audioCodec;
this.audioStreamNum = audioStreamNum;
return 0;
}
@HLEFunction(nid = 0x8A9EBDCD, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSelectVideo(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
// Advances to the next video stream number.
videoStreamNum++;
return 0;
}
@HLEFunction(nid = 0xB8D10C56, version = 150, checkInsideInterrupt = true)
public int scePsmfPlayerSelectAudio(@CheckArgument("checkPlayerInitialized") int psmfPlayer) {
// Advances to the next audio stream number.
audioStreamNum++;
return 0;
}
}