From 17907b204b78bbf8427b4fc86c1d5f6d0b2927d5 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 6 Jun 2013 23:18:12 -0700 Subject: [PATCH 01/13] Return a more accurate mpeg ringbuffer decode pos. --- Core/HW/MediaEngine.cpp | 14 ++++++++++---- Core/HW/MediaEngine.h | 5 +++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 244feb3f234d..5475b0e7a964 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -109,8 +109,8 @@ 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) @@ -183,8 +183,8 @@ bool MediaEngine::openContext() { 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_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, _MpegSeekbuffer); + pFormatCtx->pb = m_pIOContext; // Open video file if(avformat_open_input((AVFormatContext**)&m_pFormatCtx, NULL, NULL, NULL) != 0) @@ -541,6 +541,12 @@ static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int fra return -1; } +int MediaEngine::getBufferedSize() { + // m_decodePos is technically "decoderNextReadPos", we want what has actually been decoded. + int buffer_left = m_pIOContext->buffer_size - (m_pIOContext->buf_ptr - m_pIOContext->buffer); + return m_readSize - m_decodePos + buffer_left; +} + int MediaEngine::getAudioSamples(u8* buffer) { if (!m_demux) { return 0; diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index 7af600096789..b608d0c2faaa 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -32,6 +32,7 @@ struct SwsContext; struct AVFrame; +struct AVIOContext; class MediaEngine { @@ -45,7 +46,7 @@ class MediaEngine // 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); @@ -76,7 +77,7 @@ class MediaEngine void *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; From 8b25545849adc799b77aff834fffbfcbd020ab93 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 7 Jun 2013 01:10:25 -0700 Subject: [PATCH 02/13] Don't allow sceMpegInit() twice. --- Core/HLE/sceMpeg.cpp | 29 ++++++++++++++++++++++------- Core/HLE/sceMpeg.h | 2 ++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 916614cae241..79d497fd8720 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -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) @@ -997,9 +1005,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() diff --git a/Core/HLE/sceMpeg.h b/Core/HLE/sceMpeg.h index 63d7b35b2917..2f474a0b32c0 100644 --- a/Core/HLE/sceMpeg.h +++ b/Core/HLE/sceMpeg.h @@ -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. From 8544cf23593c2093b5634dae0dd6b0049e3c3e71 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 7 Jun 2013 01:13:09 -0700 Subject: [PATCH 03/13] Delay sceMpegCreate() a bit to match PSP firmware. --- Core/HLE/sceMpeg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 79d497fd8720..947a406174ac 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -359,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; } @@ -418,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) From 51d0f73008a0515c54988f22e633261ae0820bf6 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 7 Jun 2013 01:27:15 -0700 Subject: [PATCH 04/13] Apparently, return 1 is not needed now. Hurray. --- Core/HLE/scePsmf.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index 8a2ac145152f..14f7cbf07d7f 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -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) From 6844c7cab64b08b6ee860a503601d259170fce97 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Fri, 7 Jun 2013 01:38:06 -0700 Subject: [PATCH 05/13] Don't read more than total packets per mpeg put. And add notes on how it's supposed to really work. --- Core/HLE/sceMpeg.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 947a406174ac..bd57065aefa4 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -928,9 +928,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"); From b04b6019583834387923ba8436813211344c7b44 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 00:15:53 -0700 Subject: [PATCH 06/13] Make sure to decode reordered mpeg frames. --- Core/HW/MediaEngine.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 5475b0e7a964..0e7e53fcaf49 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -374,21 +374,30 @@ bool MediaEngine::stepVideo(int videoPixelMode) { 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, + while (!bGetFrame) { + bool dataEnd = av_read_frame(pFormatCtx, &packet) < 0; + + // 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(pCodecCtx, m_pFrame, &frameFinished, &packet); + if (frameFinished) { + sws_scale(m_sws_ctx, m_pFrame->data, m_pFrame->linesize, 0, pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); - - if(frameFinished) { + int firstTimeStamp = bswap32(*(int*)(m_pdata + 86)); m_videopts = pFrame->pkt_dts + pFrame->pkt_duration - firstTimeStamp; bGetFrame = true; } + if (result <= 0 && dataEnd) { + break; + } } av_free_packet(&packet); - if (bGetFrame) break; } if (!bGetFrame && m_readSize >= m_streamSize) m_isVideoEnd = true; From 6f9a879aad505548a741a458a03435e3914a6d2a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 00:16:32 -0700 Subject: [PATCH 07/13] Set video end only when we run out of frames. --- Core/HW/MediaEngine.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 0e7e53fcaf49..2d927d6a5322 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -394,13 +394,12 @@ bool MediaEngine::stepVideo(int videoPixelMode) { bGetFrame = true; } if (result <= 0 && dataEnd) { + m_isVideoEnd = !bGetFrame && m_readSize >= m_streamSize; break; } } av_free_packet(&packet); } - if (!bGetFrame && m_readSize >= m_streamSize) - m_isVideoEnd = true; return bGetFrame; #else return true; From 36eb90293ef17166b429ba3d64acba1b0f5bde5b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 00:17:55 -0700 Subject: [PATCH 08/13] Don't use m_pFrame->pkt_duration directly. Docs say you're supposed to call this func instead. --- Core/HW/MediaEngine.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 2d927d6a5322..80bea0d39a45 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -367,8 +367,6 @@ bool MediaEngine::stepVideo(int videoPixelMode) { 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; @@ -390,7 +388,7 @@ bool MediaEngine::stepVideo(int videoPixelMode) { 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) { From 073282217c6c82c99c8e1b554dbeb472503767dd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 00:22:59 -0700 Subject: [PATCH 09/13] Cut down on void *'s for easier debugging. Nice to be able to see inside structs with the debugger. --- Core/HW/MediaEngine.cpp | 42 ++++++++++++++++++----------------------- Core/HW/MediaEngine.h | 6 ++++-- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 80bea0d39a45..d45e5e9d2157 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -114,9 +114,9 @@ void MediaEngine::closeMedia() { 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; @@ -181,21 +181,20 @@ bool MediaEngine::openContext() { u8* tempbuf = (u8*)av_malloc(m_bufSize); - AVFormatContext *pFormatCtx = avformat_alloc_context(); - m_pFormatCtx = (void*)pFormatCtx; + m_pFormatCtx = avformat_alloc_context(); m_pIOContext = avio_alloc_context(tempbuf, m_bufSize, 0, (void*)this, _MpegReadbuffer, NULL, _MpegSeekbuffer); - pFormatCtx->pb = m_pIOContext; + 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 +203,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(); @@ -300,12 +298,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 +330,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,15 +361,13 @@ 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; if ((!m_pFrame)||(!m_pFrameRGB)) return false; AVPacket packet; int frameFinished; bool bGetFrame = false; while (!bGetFrame) { - bool dataEnd = av_read_frame(pFormatCtx, &packet) < 0; + bool dataEnd = av_read_frame(m_pFormatCtx, &packet) < 0; // 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(). @@ -382,10 +376,10 @@ bool MediaEngine::stepVideo(int videoPixelMode) { if (dataEnd) av_free_packet(&packet); - int result = avcodec_decode_video2(pCodecCtx, m_pFrame, &frameFinished, &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, - pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); + m_pCodecCtx->height, m_pFrameRGB->data, m_pFrameRGB->linesize); int firstTimeStamp = bswap32(*(int*)(m_pdata + 86)); m_videopts = m_pFrame->pkt_dts + av_frame_get_pkt_duration(m_pFrame) - firstTimeStamp; diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index b608d0c2faaa..8f586bdb602b 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -33,6 +33,8 @@ struct SwsContext; struct AVFrame; struct AVIOContext; +struct AVFormatContext; +struct AVCodecContext; class MediaEngine { @@ -73,8 +75,8 @@ class MediaEngine public: - void *m_pFormatCtx; - void *m_pCodecCtx; + AVFormatContext *m_pFormatCtx; + AVCodecContext *m_pCodecCtx; AVFrame *m_pFrame; AVFrame *m_pFrameRGB; AVIOContext *m_pIOContext; From 0852d8734aff52d737ca6d45fa9b176ba49ea17a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 00:25:27 -0700 Subject: [PATCH 10/13] Try to handle video end more correctly. --- Core/HLE/sceMpeg.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index bd57065aefa4..1c2bfcef566a 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -629,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); } @@ -640,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 @@ -958,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); } @@ -986,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; From be7fd47fc2a031edb7b50ca4342766d2d8c29262 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 01:17:14 -0700 Subject: [PATCH 11/13] Calculate packets left based on decode, not read. This seems to be more correct. Fixes #2039. --- Core/HW/MediaEngine.cpp | 36 +++++++++++++++++++++--------------- Core/HW/MediaEngine.h | 3 ++- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index d45e5e9d2157..81664d74b608 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -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,9 +91,6 @@ 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; } @@ -139,10 +136,10 @@ 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 +148,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; @@ -223,6 +220,7 @@ bool MediaEngine::openContext() { m_audioPos = 0; m_audioContext = Atrac3plus_Decoder::OpenContext(); m_isVideoEnd = false; + m_decodedPos = mpegoffset; #endif // USE_FFMPEG return true; } @@ -236,7 +234,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]; @@ -267,7 +265,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; @@ -368,6 +366,14 @@ bool MediaEngine::stepVideo(int videoPixelMode) { bool bGetFrame = false; 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(). @@ -387,6 +393,8 @@ bool MediaEngine::stepVideo(int videoPixelMode) { } if (result <= 0 && dataEnd) { m_isVideoEnd = !bGetFrame && m_readSize >= m_streamSize; + if (m_isVideoEnd) + m_decodedPos = m_readSize; break; } } @@ -542,9 +550,7 @@ static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int fra } int MediaEngine::getBufferedSize() { - // m_decodePos is technically "decoderNextReadPos", we want what has actually been decoded. - int buffer_left = m_pIOContext->buffer_size - (m_pIOContext->buf_ptr - m_pIOContext->buffer); - return m_readSize - m_decodePos + buffer_left; + return std::max(0, m_readSize - (int)m_decodedPos); } int MediaEngine::getAudioSamples(u8* buffer) { diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index 8f586bdb602b..f9ac3304ff4f 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -89,7 +89,8 @@ class MediaEngine 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; From 1a987dda26f4cae403cffd2176c08d415555c9f8 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 02:21:21 -0700 Subject: [PATCH 12/13] Fix crash in sceKernelGetThreadmanIdType(). --- Core/HLE/sceKernel.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 53dde682864e..3a6f31ca25a2 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -493,6 +493,11 @@ class KernelObjectPool { 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; From 4de27773d2aaef505639453b0634b13fe93673b0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 8 Jun 2013 02:45:58 -0700 Subject: [PATCH 13/13] Detect audio end separately from video end. Fixes Crisis Core, broken by Wipeout Pure fix. --- Core/HLE/sceMpeg.cpp | 3 ++- Core/HW/MediaEngine.cpp | 5 +++++ Core/HW/MediaEngine.h | 4 +++- Core/HW/MpegDemux.h | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 1c2bfcef566a..0a27c082b66f 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -1053,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); diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 81664d74b608..88e24b2f7ed9 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -92,6 +92,7 @@ MediaEngine::MediaEngine(): m_streamSize(0), m_readSize(0), m_decodedPos(0), m_p m_demux = 0; m_audioContext = 0; m_isVideoEnd = false; + m_isAudioEnd = false; } MediaEngine::~MediaEngine() { @@ -130,6 +131,7 @@ 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) @@ -220,6 +222,7 @@ 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; @@ -561,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]; @@ -588,6 +592,7 @@ int MediaEngine::getAudioSamples(u8* buffer) { } else m_audioPos = audioSize; m_audiopts += 4180; + m_decodedPos += frameSize; return outbytes; } diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index f9ac3304ff4f..d060ca9a40b5 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -61,7 +61,8 @@ class MediaEngine 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); @@ -101,4 +102,5 @@ class MediaEngine s64 m_audiopts; bool m_isVideoEnd; + bool m_isAudioEnd; }; diff --git a/Core/HW/MpegDemux.h b/Core/HW/MpegDemux.h index 5d760a6ae656..7eb6374d161b 100644 --- a/Core/HW/MpegDemux.h +++ b/Core/HW/MpegDemux.h @@ -15,8 +15,9 @@ class MpegDemux void demux(); - // return it's size + // return its size int getaudioStream(u8 **audioStream); + int getFilePosition() { return m_index; } private: struct PesHeader { long pts;