/*
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.kernel.types;
import jpcsp.HLE.Modules;
import jpcsp.HLE.TPointer;
public class SceMpegRingbuffer extends pspAbstractMemoryMappedStructure {
public static final int ringbufferPacketSize = 2048;
// PSP info
private int packets;
private int packetsRead;
private int packetsWritten;
private int packetsInRingbuffer;
private int packetSize; // 2048
private int data; // address, ring buffer
private int callbackAddr; // see sceMpegRingbufferPut
private int callbackArgs;
private int dataUpperBound;
private int semaID; // unused?
private int mpeg; // pointer to mpeg struct, fixed up in sceMpegCreate
// Internal info
private pspFileBuffer videoBuffer;
private pspFileBuffer audioBuffer;
private pspFileBuffer userDataBuffer;
private boolean hasAudio = false;
private boolean hasVideo = true;
private boolean hasUserData = false;
private int writePacketOffset;
private int internalPacketsInRingbuffer;
public SceMpegRingbuffer(int packets, int data, int size, int callbackAddr, int callbackArgs) {
this.packets = packets;
packetSize = ringbufferPacketSize;
this.data = data;
this.callbackAddr = callbackAddr;
this.callbackArgs = callbackArgs;
dataUpperBound = data + packets * ringbufferPacketSize;
semaID = -1;
mpeg = 0;
initBuffer();
if (dataUpperBound > data + size) {
dataUpperBound = data + size;
Modules.log.warn("SceMpegRingbuffer clamping dataUpperBound to " + dataUpperBound);
}
reset();
}
private SceMpegRingbuffer() {
}
private void initBuffer() {
videoBuffer = new pspFileBuffer(data, dataUpperBound - data);
audioBuffer = new pspFileBuffer(data, dataUpperBound - data);
userDataBuffer = new pspFileBuffer(data, dataUpperBound - data);
// No check on file size for MPEG.
videoBuffer.setFileMaxSize(Integer.MAX_VALUE);
audioBuffer.setFileMaxSize(Integer.MAX_VALUE);
userDataBuffer.setFileMaxSize(Integer.MAX_VALUE);
writePacketOffset = 0;
}
public static SceMpegRingbuffer fromMem(TPointer address) {
SceMpegRingbuffer ringbuffer = new SceMpegRingbuffer();
ringbuffer.read(address);
ringbuffer.initBuffer();
return ringbuffer;
}
public void reset() {
packetsRead = 0;
packetsWritten = 0;
packetsInRingbuffer = 0;
internalPacketsInRingbuffer = 0;
writePacketOffset = 0;
videoBuffer.reset(0, 0);
audioBuffer.reset(0, 0);
userDataBuffer.reset(0, 0);
}
@Override
protected void read() {
packets = read32();
packetsRead = read32();
packetsWritten = read32();
packetsInRingbuffer = read32();
packetSize = read32();
data = read32();
callbackAddr = read32();
callbackArgs = read32();
dataUpperBound = read32();
semaID = read32();
mpeg = read32();
}
@Override
protected void write() {
write32(packets);
write32(packetsRead);
write32(packetsWritten);
write32(packetsInRingbuffer);
write32(packetSize);
write32(data);
write32(callbackAddr);
write32(callbackArgs);
write32(dataUpperBound);
write32(semaID);
write32(mpeg);
}
public int getFreePackets() {
return packets - getPacketsInRingbuffer();
}
public void addPackets(int packetsAdded) {
videoBuffer.notifyWrite(packetsAdded * packetSize);
audioBuffer.notifyWrite(packetsAdded * packetSize);
userDataBuffer.notifyWrite(packetsAdded * packetSize);
packetsRead += packetsAdded;
packetsWritten += packetsAdded;
packetsInRingbuffer += packetsAdded;
internalPacketsInRingbuffer += packetsAdded;
writePacketOffset += packetsAdded;
if (writePacketOffset >= packets) {
// Wrap around
writePacketOffset -= packets;
}
}
public void consumeAllPackets() {
videoBuffer.notifyReadAll();
audioBuffer.notifyReadAll();
userDataBuffer.notifyReadAll();
packetsInRingbuffer = 0;
internalPacketsInRingbuffer = 0;
}
public int getPacketsInRingbuffer() {
return packetsInRingbuffer;
}
public boolean isEmpty() {
return getPacketsInRingbuffer() == 0;
}
public int getReadPackets() {
return packetsRead;
}
public boolean hasReadPackets() {
return packetsRead != 0;
}
public void setReadPackets(int packetsRead) {
this.packetsRead = packetsRead;
}
public int getTotalPackets() {
return packets;
}
public int getPacketSize() {
return packetSize;
}
public int getPutDataAddr() {
return data + writePacketOffset * packetSize;
}
public int getPutSequentialPackets() {
return Math.min(getFreePackets(), packets - writePacketOffset);
}
public int getTmpAddress(int length) {
return dataUpperBound - length;
}
public void setMpeg(int mpeg) {
this.mpeg = mpeg;
}
public int getUpperDataAddr() {
return dataUpperBound;
}
public int getCallbackAddr() {
return callbackAddr;
}
public int getCallbackArgs() {
return callbackArgs;
}
public pspFileBuffer getAudioBuffer() {
return audioBuffer;
}
public pspFileBuffer getVideoBuffer() {
return videoBuffer;
}
public pspFileBuffer getUserDataBuffer() {
return userDataBuffer;
}
public void notifyRead(int pendingImages) {
if (packetSize == 0) {
return;
}
int remainingLength = 0;
if (hasAudio()) {
remainingLength = Math.max(remainingLength, audioBuffer.getCurrentSize());
}
if (hasVideo()) {
int videoBufferLength = videoBuffer.getCurrentSize();
// Do not empty completely the ringbuffer when we still have pending images
if (pendingImages > 1) {
videoBufferLength = Math.max(videoBufferLength, 1);
}
remainingLength = Math.max(remainingLength, videoBufferLength);
}
if (hasUserData()) {
remainingLength = Math.max(remainingLength, userDataBuffer.getCurrentSize());
}
int remainingPackets = (remainingLength + packetSize - 1) / packetSize;
if (internalPacketsInRingbuffer > remainingPackets) {
internalPacketsInRingbuffer = remainingPackets;
}
}
public void notifyConsumed() {
packetsInRingbuffer = internalPacketsInRingbuffer;
}
public boolean hasAudio() {
return hasAudio;
}
public void setHasAudio(boolean hasAudio) {
this.hasAudio = hasAudio;
}
public boolean hasVideo() {
return hasVideo;
}
public void setHasVideo(boolean hasVideo) {
this.hasVideo = hasVideo;
}
public boolean hasUserData() {
return hasUserData;
}
public void setHasUserData(boolean hasUserData) {
this.hasUserData = hasUserData;
}
@Override
public int sizeof() {
return 44;
}
@Override
public String toString() {
return String.format("SceMpegRingbuffer(packets=0x%X, packetsRead=0x%X, packetsWritten=0x%X, packetsFree=0x%X, packetSize=0x%X, hasVideo=%b, videoBuffer=%s, hasAudio=%b, audioBuffer=%s)", packets, packetsRead, packetsWritten, getFreePackets(), packetSize, hasVideo, videoBuffer, hasAudio, audioBuffer);
}
}