Merge pull request #2143 from unknownbrackets/mpeg-fix

Improvements to sceMpeg: fix end cutoff, early audio end, and minor
This commit is contained in:
Henrik Rydgård committed 2013-06-08 04:07:48 -07:00
commit 30536fa7cf
7 files changed
+131 -76

No files matched your search

+5
View File
@@ -493,6 +493,11 @@ public:
bool GetIDType(SceUID handle, int *type) const
{
if (handle < handleOffset || handle >= handleOffset+maxCount || !occupied[handle-handleOffset])
{
ERROR_LOG(HLE, "Kernel: Bad object handle %i (%08x)", handle, handle);
return false;
}
KernelObject *t = pool[handle - handleOffset];
*type = t->GetIDType();
return true;
+42 -17
View File
@@ -182,6 +182,7 @@ struct MpegContext {
MediaEngine *mediaengine;
};
static bool isMpegInit;
static u32 streamIdGen;
static bool isCurrentMpegAnalyzed;
static int actionPostPut;
@@ -299,6 +300,7 @@ void __MpegInit(bool useMediaEngine_) {
lastMpegHandle = 0;
streamIdGen = 1;
isCurrentMpegAnalyzed = false;
isMpegInit = false;
actionPostPut = __KernelRegisterActionType(PostPutAction::Create);
}
@@ -306,6 +308,7 @@ void __MpegDoState(PointerWrap &p) {
p.Do(lastMpegHandle);
p.Do(streamIdGen);
p.Do(isCurrentMpegAnalyzed);
p.Do(isMpegInit);
p.Do(actionPostPut);
__KernelRestoreActionType(actionPostPut, PostPutAction::Create);
@@ -322,15 +325,20 @@ void __MpegShutdown() {
mpegMap.clear();
}
u32 sceMpegInit()
{
if (!g_Config.bUseMediaEngine){
u32 sceMpegInit() {
if (!g_Config.bUseMediaEngine) {
WARN_LOG(HLE, "Media Engine disabled");
return -1;
}
WARN_LOG(HLE, "sceMpegInit()");
return 0;
if (isMpegInit) {
WARN_LOG(HLE, "sceMpegInit(): already initialized");
return ERROR_MPEG_ALREADY_INIT;
}
INFO_LOG(HLE, "sceMpegInit()");
isMpegInit = true;
return hleDelayResult(0, "mpeg init", 750);
}
u32 sceMpegRingbufferQueryMemSize(int packets)
@@ -351,7 +359,7 @@ u32 sceMpegRingbufferConstruct(u32 ringbufferAddr, u32 numPackets, u32 data, u32
u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 frameWidth, u32 mode, u32 ddrTop)
{
if (!g_Config.bUseMediaEngine){
if (!g_Config.bUseMediaEngine) {
WARN_LOG(HLE, "Media Engine disabled");
return -1;
}
@@ -410,7 +418,7 @@ u32 sceMpegCreate(u32 mpegAddr, u32 dataPtr, u32 size, u32 ringbufferAddr, u32 f
INFO_LOG(HLE, "%08x=sceMpegCreate(%08x, %08x, %i, %08x, %i, %i, %i)",
mpegHandle, mpegAddr, dataPtr, size, ringbufferAddr, frameWidth, mode, ddrTop);
return 0;
return hleDelayResult(0, "mpeg create", 29000);
}
int sceMpegDelete(u32 mpeg)
@@ -621,8 +629,8 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
SceMpegRingBuffer ringbuffer;
Memory::ReadStruct(ctx->mpegRingbufferAddr, &ringbuffer);
if (ringbuffer.packetsRead == 0) {
// empty!
if (ringbuffer.packetsRead == 0 || ctx->mediaengine->IsVideoEnd()) {
WARN_LOG(HLE, "sceMpegAvcDecode(%08x, %08x, %d, %08x, %08x): mpeg buffer empty", mpeg, auAddr, frameWidth, bufferAddr, initAddr);
return hleDelayResult(MPEG_AVC_DECODE_ERROR_FATAL, "mpeg buffer empty", avcEmptyDelayMs);
}
@@ -632,14 +640,15 @@ u32 sceMpegAvcDecode(u32 mpeg, u32 auAddr, u32 frameWidth, u32 bufferAddr, u32 i
if (ctx->mediaengine->stepVideo(ctx->videoPixelMode)) {
ctx->mediaengine->writeVideoImage(Memory::GetPointer(buffer), frameWidth, ctx->videoPixelMode);
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
} else {
ctx->avc.avcFrameStatus = 0;
}
ringbuffer.packetsFree = std::max(0, ringbuffer.packets - ctx->mediaengine->getBufferedSize() / 2048);
avcAu.pts = ctx->mediaengine->getVideoTimeStamp();
ctx->avc.avcFrameStatus = 1;
ctx->videoFrameCount++;
ctx->avc.avcDecodeResult = MPEG_AVC_DECODE_SUCCESS;
// Flush structs back to memory
@@ -920,9 +929,13 @@ u32 sceMpegRingbufferPut(u32 ringbufferAddr, u32 numPackets, u32 available)
// Execute callback function as a direct MipsCall, no blocking here so no messing around with wait states etc
if (ringbuffer.callback_addr) {
PostPutAction *action = (PostPutAction *) __KernelCreateAction(actionPostPut);
PostPutAction *action = (PostPutAction *)__KernelCreateAction(actionPostPut);
action->setRingAddr(ringbufferAddr);
u32 args[3] = {(u32)ringbuffer.data, numPackets, (u32)ringbuffer.callback_args};
// TODO: Should call this multiple times until we get numPackets.
// Normally this would be if it did not read enough, but also if available > packets.
// Should ultimately return the TOTAL number of returned packets.
u32 packetsThisRound = std::min(numPackets, (u32)ringbuffer.packets);
u32 args[3] = {(u32)ringbuffer.data, packetsThisRound, (u32)ringbuffer.callback_args};
__KernelDirectMipsCall(ringbuffer.callback_addr, action, args, 3, false);
} else {
ERROR_LOG(HLE, "sceMpegRingbufferPut: callback_addr zero");
@@ -946,6 +959,9 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
if (mpegRingbuffer.packetsRead == 0 || mpegRingbuffer.packetsFree == mpegRingbuffer.packets) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAvcAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
sceAu.pts = -1;
sceAu.dts = -1;
sceAu.write(auAddr);
// TODO: Does this really reschedule?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get avc", mpegDecodeErrorDelayMs);
}
@@ -974,6 +990,7 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
int result = 0;
sceAu.pts = ctx->mediaengine->getVideoTimeStamp();
sceAu.dts = sceAu.pts - videoTimestampStep;
if (ctx->mediaengine->IsVideoEnd()) {
INFO_LOG(HLE, "video end reach. pts: %i dts: %i", (int)sceAu.pts, (int)ctx->mediaengine->getLastTimeStamp());
mpegRingbuffer.packetsFree = mpegRingbuffer.packets;
@@ -997,9 +1014,16 @@ int sceMpegGetAvcAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
u32 sceMpegFinish()
{
ERROR_LOG(HLE, "sceMpegFinish(...)");
if (!isMpegInit)
{
WARN_LOG(HLE, "sceMpegFinish(...): not initialized");
return ERROR_MPEG_NOT_YET_INIT;
}
INFO_LOG(HLE, "sceMpegFinish(...)");
isMpegInit = false;
//__MpegFinish();
return 0;
return hleDelayResult(0, "mpeg finish", 250);
}
u32 sceMpegQueryMemSize()
@@ -1029,7 +1053,8 @@ int sceMpegGetAtracAu(u32 mpeg, u32 streamId, u32 auAddr, u32 attrAddr)
streamInfo->second.needsReset = false;
}
if (mpegRingbuffer.packetsFree == mpegRingbuffer.packets) {
// The audio can end earlier than the video does.
if (mpegRingbuffer.packetsFree == mpegRingbuffer.packets || (ctx->mediaengine->IsAudioEnd() && !ctx->mediaengine->IsVideoEnd())) {
DEBUG_LOG(HLE, "PSP_ERROR_MPEG_NO_DATA=sceMpegGetAtracAu(%08x, %08x, %08x, %08x)", mpeg, streamId, auAddr, attrAddr);
// TODO: Does this really delay?
return hleDelayResult(PSP_ERROR_MPEG_NO_DATA, "mpeg get atrac", mpegDecodeErrorDelayMs);
+2
View File
@@ -40,6 +40,8 @@ enum {
ERROR_PSMFPLAYER_NO_MORE_DATA = 0x8061600c,
ERROR_MPEG_NO_DATA = 0x80618001,
ERROR_MPEG_ALREADY_INIT = 0x80618005,
ERROR_MPEG_NOT_YET_INIT = 0x80618009,
};
// MPEG statics.
+1 -2
View File
@@ -555,8 +555,7 @@ u32 scePsmfQueryStreamOffset(u32 bufferAddr, u32 offsetAddr)
if (Memory::IsValidAddress(offsetAddr)) {
Memory::Write_U32(bswap32(Memory::Read_U32(bufferAddr + PSMF_STREAM_OFFSET_OFFSET)), offsetAddr);
}
// return 0 breaks history mode in Saint Seiya Omega
return 1;
return 0;
}
u32 scePsmfQueryStreamSize(u32 bufferAddr, u32 sizeAddr)
+67 -50
View File
@@ -81,7 +81,7 @@ static int getPixelFormatBytes(int pspFormat)
}
}
MediaEngine::MediaEngine(): m_streamSize(0), m_readSize(0), m_pdata(0) {
MediaEngine::MediaEngine(): m_streamSize(0), m_readSize(0), m_decodedPos(0), m_pdata(0) {
m_pFormatCtx = 0;
m_pCodecCtx = 0;
m_pFrame = 0;
@@ -91,10 +91,8 @@ MediaEngine::MediaEngine(): m_streamSize(0), m_readSize(0), m_pdata(0) {
m_buffer = 0;
m_demux = 0;
m_audioContext = 0;
m_pdata = 0;
m_streamSize = 0;
m_readSize = 0;
m_isVideoEnd = false;
m_isAudioEnd = false;
}
MediaEngine::~MediaEngine() {
@@ -109,14 +107,14 @@ void MediaEngine::closeMedia() {
av_free(m_pFrameRGB);
if (m_pFrame)
av_free(m_pFrame);
if (m_pIOContext && ((AVIOContext*)m_pIOContext)->buffer)
av_free(((AVIOContext*)m_pIOContext)->buffer);
if (m_pIOContext && m_pIOContext->buffer)
av_free(m_pIOContext->buffer);
if (m_pIOContext)
av_free(m_pIOContext);
if (m_pCodecCtx)
avcodec_close((AVCodecContext*)m_pCodecCtx);
avcodec_close(m_pCodecCtx);
if (m_pFormatCtx)
avformat_close_input((AVFormatContext**)&m_pFormatCtx);
avformat_close_input(&m_pFormatCtx);
#endif // USE_FFMPEG
if (m_pdata)
delete [] m_pdata;
@@ -133,16 +131,17 @@ void MediaEngine::closeMedia() {
m_demux = 0;
Atrac3plus_Decoder::CloseContext(&m_audioContext);
m_isVideoEnd = false;
m_isAudioEnd = false;
}
int _MpegReadbuffer(void *opaque, uint8_t *buf, int buf_size)
{
MediaEngine *mpeg = (MediaEngine*)opaque;
int size = std::min(mpeg->m_bufSize, buf_size);
size = std::max(std::min((mpeg->m_readSize - mpeg->m_decodePos), size), 0);
size = std::max(std::min((mpeg->m_readSize - mpeg->m_decodeNextPos), size), 0);
if (size > 0)
memcpy(buf, mpeg->m_pdata + mpeg->m_decodePos, size);
mpeg->m_decodePos += size;
memcpy(buf, mpeg->m_pdata + mpeg->m_decodeNextPos, size);
mpeg->m_decodeNextPos += size;
return size;
}
@@ -151,13 +150,13 @@ int64_t _MpegSeekbuffer(void *opaque, int64_t offset, int whence)
MediaEngine *mpeg = (MediaEngine*)opaque;
switch (whence) {
case SEEK_SET:
mpeg->m_decodePos = offset;
mpeg->m_decodeNextPos = offset;
break;
case SEEK_CUR:
mpeg->m_decodePos += offset;
mpeg->m_decodeNextPos += offset;
break;
case SEEK_END:
mpeg->m_decodePos = mpeg->m_streamSize - (u32)offset;
mpeg->m_decodeNextPos = mpeg->m_streamSize - (u32)offset;
break;
}
return offset;
@@ -181,21 +180,20 @@ bool MediaEngine::openContext() {
u8* tempbuf = (u8*)av_malloc(m_bufSize);
AVFormatContext *pFormatCtx = avformat_alloc_context();
m_pFormatCtx = (void*)pFormatCtx;
m_pIOContext = (void*)avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, _MpegSeekbuffer);
pFormatCtx->pb = (AVIOContext*)m_pIOContext;
m_pFormatCtx = avformat_alloc_context();
m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, _MpegSeekbuffer);
m_pFormatCtx->pb = m_pIOContext;
// Open video file
if(avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, NULL) != 0)
return false;
if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
if(avformat_find_stream_info(m_pFormatCtx, NULL) < 0)
return false;
// Find the first video stream
for(int i = 0; i < (int)pFormatCtx->nb_streams; i++) {
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
for(int i = 0; i < (int)m_pFormatCtx->nb_streams; i++) {
if(m_pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
m_videoStream = i;
break;
}
@@ -204,17 +202,16 @@ bool MediaEngine::openContext() {
return false;
// Get a pointer to the codec context for the video stream
m_pCodecCtx = (void*)pFormatCtx->streams[m_videoStream]->codec;
AVCodecContext *pCodecCtx = (AVCodecContext*)m_pCodecCtx;
m_pCodecCtx = m_pFormatCtx->streams[m_videoStream]->codec;
// Find the decoder for the video stream
AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
AVCodec *pCodec = avcodec_find_decoder(m_pCodecCtx->codec_id);
if(pCodec == NULL)
return false;
// Open codec
AVDictionary *optionsDict = 0;
if(avcodec_open2(pCodecCtx, pCodec, &optionsDict)<0)
if(avcodec_open2(m_pCodecCtx, pCodec, &optionsDict)<0)
return false; // Could not open codec
setVideoDim();
@@ -225,6 +222,8 @@ bool MediaEngine::openContext() {
m_audioPos = 0;
m_audioContext = Atrac3plus_Decoder::OpenContext();
m_isVideoEnd = false;
m_isAudioEnd = false;
m_decodedPos = mpegoffset;
#endif // USE_FFMPEG
return true;
}
@@ -238,7 +237,7 @@ bool MediaEngine::loadStream(u8* buffer, int readSize, int StreamSize)
m_videopts = 0;
m_audiopts = 0;
m_bufSize = 0x2000;
m_decodePos = 0;
m_decodeNextPos = 0;
m_readSize = readSize;
m_streamSize = StreamSize;
m_pdata = new u8[StreamSize];
@@ -269,7 +268,7 @@ bool MediaEngine::loadFile(const char* filename)
m_videopts = 0;
m_audiopts = 0;
m_bufSize = 0x2000;
m_decodePos = 0;
m_decodeNextPos = 0;
m_readSize = infosize;
m_streamSize = infosize;
m_pdata = buf;
@@ -300,12 +299,11 @@ bool MediaEngine::setVideoDim(int width, int height)
if (!m_pCodecCtx)
return false;
#ifdef USE_FFMPEG
AVCodecContext *pCodecCtx = (AVCodecContext*)m_pCodecCtx;
if (width == 0 && height == 0)
{
// use the orignal video size
m_desWidth = pCodecCtx->width;
m_desHeight = pCodecCtx->height;
m_desWidth = m_pCodecCtx->width;
m_desHeight = m_pCodecCtx->height;
}
else
{
@@ -333,16 +331,15 @@ bool MediaEngine::setVideoDim(int width, int height)
void MediaEngine::updateSwsFormat(int videoPixelMode) {
#ifdef USE_FFMPEG
AVCodecContext *pCodecCtx = (AVCodecContext*)m_pCodecCtx;
AVPixelFormat swsDesired = getSwsFormat(videoPixelMode);
if (swsDesired != m_sws_fmt) {
m_sws_fmt = swsDesired;
m_sws_ctx = sws_getCachedContext
(
m_sws_ctx,
pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
m_pCodecCtx->width,
m_pCodecCtx->height,
m_pCodecCtx->pix_fmt,
m_desWidth,
m_desHeight,
(AVPixelFormat)m_sws_fmt,
@@ -365,33 +362,47 @@ bool MediaEngine::stepVideo(int videoPixelMode) {
// Update the linesize for the new format too. We started with the largest size, so it should fit.
m_pFrameRGB->linesize[0] = getPixelFormatBytes(videoPixelMode) * m_desWidth;
AVFormatContext *pFormatCtx = (AVFormatContext*)m_pFormatCtx;
AVCodecContext *pCodecCtx = (AVCodecContext*)m_pCodecCtx;
AVFrame *pFrame = (AVFrame*)m_pFrame;
AVFrame *pFrameRGB = (AVFrame*)m_pFrameRGB;
if ((!m_pFrame)||(!m_pFrameRGB))
return false;
AVPacket packet;
int frameFinished;
bool bGetFrame = false;
while(av_read_frame(pFormatCtx, &packet)>=0) {
if(packet.stream_index == m_videoStream) {
// Decode video frame
avcodec_decode_video2(pCodecCtx, m_pFrame, &frameFinished, &packet);
sws_scale(m_sws_ctx, m_pFrame->data, m_pFrame->linesize, 0,
pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
if(frameFinished) {
while (!bGetFrame) {
bool dataEnd = av_read_frame(m_pFormatCtx, &packet) < 0;
if (!dataEnd) {
if (packet.pos != -1) {
m_decodedPos = packet.pos;
} else {
// Packet doesn't know where it is in the file, let's try to approximate.
m_decodedPos += packet.size;
}
}
// Even if we've read all frames, some may have been re-ordered frames at the end.
// Still need to decode those, so keep calling avcodec_decode_video2().
if (dataEnd || packet.stream_index == m_videoStream) {
// avcodec_decode_video2() gives us the re-ordered frames with a NULL packet.
if (dataEnd)
av_free_packet(&packet);
int result = avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished, &packet);
if (frameFinished) {
sws_scale(m_sws_ctx, m_pFrame->data, m_pFrame->linesize, 0,
m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize);
int firstTimeStamp = bswap32(*(int*)(m_pdata + 86));
m_videopts = pFrame->pkt_dts + pFrame->pkt_duration - firstTimeStamp;
m_videopts = m_pFrame->pkt_dts + av_frame_get_pkt_duration(m_pFrame) - firstTimeStamp;
bGetFrame = true;
}
if (result <= 0 && dataEnd) {
m_isVideoEnd = !bGetFrame && m_readSize >= m_streamSize;
if (m_isVideoEnd)
m_decodedPos = m_readSize;
break;
}
}
av_free_packet(&packet);
if (bGetFrame) break;
}
if (!bGetFrame && m_readSize >= m_streamSize)
m_isVideoEnd = true;
return bGetFrame;
#else
return true;
@@ -541,6 +552,10 @@ static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int fra
return -1;
}
int MediaEngine::getBufferedSize() {
return std::max(0, m_readSize - (int)m_decodedPos);
}
int MediaEngine::getAudioSamples(u8* buffer) {
if (!m_demux) {
return 0;
@@ -549,6 +564,7 @@ int MediaEngine::getAudioSamples(u8* buffer) {
int audioSize = m_demux->getaudioStream(&audioStream);
if (m_audioPos >= audioSize || !isHeader(audioStream, m_audioPos))
{
m_isAudioEnd = m_demux->getFilePosition() >= m_streamSize;
return 0;
}
u8 headerCode1 = audioStream[2];
@@ -576,6 +592,7 @@ int MediaEngine::getAudioSamples(u8* buffer) {
} else
m_audioPos = audioSize;
m_audiopts += 4180;
m_decodedPos += frameSize;
return outbytes;
}
+12 -6
View File
@@ -32,6 +32,9 @@
struct SwsContext;
struct AVFrame;
struct AVIOContext;
struct AVFormatContext;
struct AVCodecContext;
class MediaEngine
{
@@ -45,7 +48,7 @@ public:
// Returns number of packets actually added.
int addStreamData(u8* buffer, int addSize);
int getRemainSize() { return m_streamSize - m_readSize;}
int getBufferedSize() { return m_readSize - m_decodePos; }
int getBufferedSize();
bool stepVideo(int videoPixelMode);
bool writeVideoImage(u8* buffer, int frameWidth = 512, int videoPixelMode = 3);
@@ -58,7 +61,8 @@ public:
s64 getAudioTimeStamp();
s64 getLastTimeStamp();
bool IsVideoEnd() { return m_isVideoEnd;}
bool IsVideoEnd() { return m_isVideoEnd; }
bool IsAudioEnd() { return m_isAudioEnd; }
void DoState(PointerWrap &p) {
p.Do(m_streamSize);
@@ -72,11 +76,11 @@ private:
public:
void *m_pFormatCtx;
void *m_pCodecCtx;
AVFormatContext *m_pFormatCtx;
AVCodecContext *m_pCodecCtx;
AVFrame *m_pFrame;
AVFrame *m_pFrameRGB;
void *m_pIOContext;
AVIOContext *m_pIOContext;
int m_videoStream;
SwsContext *m_sws_ctx;
int m_sws_fmt;
@@ -86,7 +90,8 @@ public:
int m_desHeight;
int m_streamSize;
int m_readSize;
int m_decodePos;
int m_decodeNextPos;
s64 m_decodedPos;
int m_bufSize;
s64 m_videopts;
u8* m_pdata;
@@ -97,4 +102,5 @@ public:
s64 m_audiopts;
bool m_isVideoEnd;
bool m_isAudioEnd;
};
+2 -1
View File
@@ -15,8 +15,9 @@ public:
void demux();
// return it's size
// return its size
int getaudioStream(u8 **audioStream);
int getFilePosition() { return m_index; }
private:
struct PesHeader {
long pts;