diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 1206159de..d67875e8f 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -740,7 +740,6 @@ bool InitPmp(MpegContext * ctx){ InitFFmpeg(); auto mediaengine = ctx->mediaengine; mediaengine->m_isVideoEnd = false; - mediaengine->m_noAudioData = false; mediaengine->m_firstTimeStamp = 0; mediaengine->m_lastTimeStamp = 0; ctx->mpegFirstTimestamp = 0; diff --git a/Core/HLE/scePsmf.cpp b/Core/HLE/scePsmf.cpp index ff908eec4..ea6f0c2ac 100644 --- a/Core/HLE/scePsmf.cpp +++ b/Core/HLE/scePsmf.cpp @@ -47,6 +47,8 @@ const int PSMF_PLAYER_CONFIG_NO_LOOP = 1; const int PSMF_PLAYER_CONFIG_MODE_LOOP = 0; const int PSMF_PLAYER_CONFIG_MODE_PIXEL_TYPE = 1; +const int PSMF_PLAYER_WARMUP_FRAMES = 3; + int psmfMaxAheadTimestamp = 40000; int audioSamples = 2048; int audioSamplesBytes = audioSamples * 4; @@ -112,8 +114,6 @@ struct PsmfPlayerData { s32_le audioStreamNum; s32_le playMode; s32_le playSpeed; - // TODO: Was "long", which is 32, but should this be 64? Not even used? - s32_le psmfPlayerLastTimestamp; }; struct PsmfInfo { @@ -217,7 +217,7 @@ public: int audioStreamNum; int playMode; int playSpeed; - long psmfPlayerLastTimestamp; + u64 lastTimestamp; int displayBuffer; int displayBufferSize; @@ -227,6 +227,7 @@ public: int totalAudioStreams; int playerVersion; int videoStep; + int warmUp; SceMpegAu psmfPlayerAtracAu; SceMpegAu psmfPlayerAvcAu; @@ -345,7 +346,7 @@ PsmfPlayer::PsmfPlayer(const PsmfPlayerCreateData *data) { audioStreamNum = -1; playMode = 0; playSpeed = 1; - psmfPlayerLastTimestamp = 0; + lastTimestamp = 0; status = PSMF_PLAYER_STATUS_INIT; mediaengine = new MediaEngine; filehandle = 0; @@ -353,6 +354,7 @@ PsmfPlayer::PsmfPlayer(const PsmfPlayerCreateData *data) { readSize = 0; streamSize = 0; videoStep = 0; + warmUp = 0; displayBuffer = data->buffer.ptr; displayBufferSize = data->bufferSize; @@ -410,7 +412,13 @@ void PsmfPlayer::DoState(PointerWrap &p) { p.Do(displayBufferSize); p.Do(playbackThreadPriority); p.Do(psmfMaxAheadTimestamp); - p.Do(psmfPlayerLastTimestamp); + if (s >= 4) { + p.Do(lastTimestamp); + } else { + long oldTimestamp; + p.Do(oldTimestamp); + lastTimestamp = oldTimestamp; + } if (s >= 2) { p.Do(totalVideoStreams); p.Do(totalAudioStreams); @@ -425,6 +433,11 @@ void PsmfPlayer::DoState(PointerWrap &p) { } else { videoStep = 0; } + if (s >= 4) { + p.Do(warmUp); + } else { + warmUp = 10000; + } p.DoClass(mediaengine); p.Do(filehandle); p.Do(fileoffset); @@ -432,6 +445,9 @@ void PsmfPlayer::DoState(PointerWrap &p) { p.Do(streamSize); p.Do(status); + if (s >= 4) { + p.Do(psmfPlayerAtracAu); + } p.Do(psmfPlayerAvcAu); } @@ -931,7 +947,7 @@ int scePsmfPlayerCreate(u32 psmfPlayer, u32 dataPtr) *player = 0; return SCE_KERNEL_ERROR_ILLEGAL_ADDRESS; } - if (data->bufferSize < 0x00300000) { + if (data->bufferSize < 0x00285800) { ERROR_LOG_REPORT(ME, "scePsmfPlayerCreate(%08x, %08x): buffer too small %08x", psmfPlayer, dataPtr, data->bufferSize); *player = 0; return ERROR_PSMFPLAYER_BUFFER_SIZE; @@ -1094,7 +1110,7 @@ int _PsmfPlayerSetPsmfOffset(u32 psmfPlayer, const char *filename, int offset, b psmfplayer->fileoffset = offset + mpegoffset; psmfplayer->mediaengine->loadStream(buf, 2048, std::max(2048 * 500, tempbufSize)); _PsmfPlayerFillRingbuffer(psmfplayer); - psmfplayer->psmfPlayerLastTimestamp = psmfplayer->mediaengine->getLastTimeStamp(); + psmfplayer->lastTimestamp = psmfplayer->mediaengine->getLastTimeStamp(); } psmfplayer->status = PSMF_PLAYER_STATUS_STANDBY; @@ -1191,14 +1207,6 @@ int scePsmfPlayerStart(u32 psmfPlayer, u32 psmfPlayerData, int initPts) psmfplayer->audioStreamNum = data.audioStreamNum; psmfplayer->playMode = data.playMode; psmfplayer->playSpeed = data.playSpeed; - /*data.videoCodec = psmfplayer->videoCodec; - data.videoStreamNum = psmfplayer->videoStreamNum; - data.audioCodec = psmfplayer->audioCodec; - data.audioStreamNum = psmfplayer->audioStreamNum; - data.playMode = psmfplayer->playMode; - data.playSpeed = psmfplayer->playSpeed; - data.psmfPlayerLastTimestamp = psmfplayer->psmfPlayerLastTimestamp; - Memory::WriteStruct(psmfPlayerData, &data);*/ } psmfplayer->psmfPlayerAtracAu.dts = initPts; @@ -1311,6 +1319,17 @@ int scePsmfPlayerGetVideoData(u32 psmfPlayer, u32 videoDataAddr) hleEatCycles(20000); + // On a real PSP, this takes a potentially variable amount of time. + // Normally a minimum of 3 without audio, 5 with. But if you don't delay sufficiently between, hundreds. + // It should be okay if we start videos quicker, but some games expect the first couple to fail. + if (psmfplayer->warmUp < PSMF_PLAYER_WARMUP_FRAMES) { + DEBUG_LOG(ME, "scePsmfPlayerGetVideoData(%08x, %08x): warming up", psmfPlayer, videoDataAddr); + ++psmfplayer->warmUp; + return ERROR_PSMFPLAYER_NO_MORE_DATA; + } + // In case we change warm up later, save a high value in savestates - video started. + psmfplayer->warmUp = 10000; + auto videoData = PSPPointer::Create(videoDataAddr); if (videoData.IsValid()) { if (!psmfplayer->mediaengine->IsNoAudioData()) { @@ -1318,7 +1337,9 @@ int scePsmfPlayerGetVideoData(u32 psmfPlayer, u32 videoDataAddr) if (deltapts > 0) { // Don't advance, just return the same frame again. // TODO: This also seems somewhat based on Update() calls, but audio is involved too... - psmfplayer->mediaengine->writeVideoImage(videoData->displaybuf, videoData->frameWidth, videoPixelMode); + int displaybufSize = psmfplayer->mediaengine->writeVideoImage(videoData->displaybuf, videoData->frameWidth, videoPixelMode); + // Need to invalidate, even if it didn't change, to trigger upload to framebuffer. + gpu->InvalidateCache(videoData->displaybuf, displaybufSize, GPU_INVALIDATE_SAFE); psmfplayer->psmfPlayerAvcAu.pts = psmfplayer->mediaengine->getVideoTimeStamp(); videoData->displaypts = (u32)psmfplayer->psmfPlayerAvcAu.pts; return hleDelayResult(0, "psmfPlayer behind audio", 3000); @@ -1381,6 +1402,12 @@ int scePsmfPlayerGetAudioData(u32 psmfPlayer, u32 audioDataAddr) return 0; } + // Don't return audio frames before we would return video frames. + if (psmfplayer->warmUp < PSMF_PLAYER_WARMUP_FRAMES) { + DEBUG_LOG(ME, "scePsmfPlayerGetAudioData(%08x, %08x): warming up", psmfPlayer, audioDataAddr); + return ERROR_PSMFPLAYER_NO_MORE_DATA; + } + if (Memory::IsValidAddress(audioDataAddr)) { psmfplayer->mediaengine->getAudioSamples(audioDataAddr); } @@ -1458,7 +1485,7 @@ u32 scePsmfPlayerGetPsmfInfo(u32 psmfPlayer, u32 psmfInfoAddr) } DEBUG_LOG(ME, "scePsmfPlayerGetPsmfInfo(%08x, %08x)", psmfPlayer, psmfInfoAddr); - info->lengthTS = psmfplayer->psmfPlayerLastTimestamp - 3003; + info->lengthTS = psmfplayer->lastTimestamp - 3003; info->numVideoStreams = psmfplayer->totalVideoStreams; info->numAudioStreams = psmfplayer->totalAudioStreams; // pcm stream num? diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 4592dbc9d..e0bd97467 100644 --- a/Core/HW/MediaEngine.cpp +++ b/Core/HW/MediaEngine.cpp @@ -16,8 +16,10 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "Core/Config.h" +#include "Core/Debugger/Breakpoints.h" #include "Core/HW/MediaEngine.h" #include "Core/MemMap.h" +#include "Core/MIPS/MIPS.h" #include "Core/Reporting.h" #include "GPU/GPUInterface.h" #include "Core/HW/SimpleAudioDec.h" @@ -148,7 +150,6 @@ MediaEngine::MediaEngine(): m_pdata(0) { m_firstTimeStamp = 0; m_lastTimeStamp = 0; m_isVideoEnd = false; - m_noAudioData = false; m_ringbuffersize = 0; m_mpegheaderReadPos = 0; @@ -171,7 +172,6 @@ void MediaEngine::closeMedia() { m_demux = 0; AudioClose(&m_audioContext); m_isVideoEnd = false; - m_noAudioData = false; } void MediaEngine::DoState(PointerWrap &p){ @@ -212,11 +212,11 @@ void MediaEngine::DoState(PointerWrap &p){ } p.Do(m_isVideoEnd); - p.Do(m_noAudioData); - if (s >= 3){ + bool noAudioDataRemoved; + p.Do(noAudioDataRemoved); + if (s >= 3) { p.Do(m_audioType); - } - else{ + } else { m_audioType = PSP_CODEC_AT3PLUS; } } @@ -288,7 +288,6 @@ bool MediaEngine::openContext() { setVideoDim(); m_audioContext = new SimpleAudio(m_audioType); m_isVideoEnd = false; - m_noAudioData = false; m_mpegheaderReadPos++; av_seek_frame(m_pFormatCtx, m_videoStream, 0, 0); #endif // USE_FFMPEG @@ -343,7 +342,6 @@ int MediaEngine::addStreamData(u8* buffer, int addSize) { if (!m_pdata->push(buffer, size)) size = 0; if (m_demux) { - m_noAudioData = false; m_demux->addStreamData(buffer, addSize); } #ifdef USE_FFMPEG @@ -618,6 +616,10 @@ int MediaEngine::writeVideoImage(u32 bufferPtr, int frameWidth, int videoPixelMo ERROR_LOG_REPORT(ME, "Unsupported video pixel format %d", videoPixelMode); break; } + +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr, true, videoImageSize, currentMIPS->pc); +#endif return videoImageSize; #endif // USE_FFMPEG return 0; @@ -653,6 +655,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineRGBA(imgbuf, data, width); data += m_desWidth * sizeof(u32); imgbuf += frameWidth * sizeof(u32); +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr + frameWidth * sizeof(u32), true, width * sizeof(u32), currentMIPS->pc); +#endif } videoImageSize = frameWidth * sizeof(u32) * m_desHeight; break; @@ -663,6 +668,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5650(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += frameWidth * sizeof(u16); +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr + frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); +#endif } videoImageSize = frameWidth * sizeof(u16) * m_desHeight; break; @@ -673,6 +681,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR5551(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += frameWidth * sizeof(u16); +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr + frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); +#endif } videoImageSize = frameWidth * sizeof(u16) * m_desHeight; break; @@ -683,6 +694,9 @@ int MediaEngine::writeVideoImageWithRange(u32 bufferPtr, int frameWidth, int vid writeVideoLineABGR4444(imgbuf, data, width); data += m_desWidth * sizeof(u16); imgbuf += frameWidth * sizeof(u16); +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr + frameWidth * sizeof(u16), true, width * sizeof(u16), currentMIPS->pc); +#endif } videoImageSize = frameWidth * sizeof(u16) * m_desHeight; break; @@ -729,7 +743,7 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { return 0; } - // When m_demux , increment pts + // When getting a frame, increment pts m_audiopts += 4180; // Demux now (rather than on add data) so that we select the right stream. @@ -737,9 +751,8 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { u8 *audioFrame = 0; int headerCode1, headerCode2; - int frameSize = m_demux->getNextaudioFrame(&audioFrame, &headerCode1, &headerCode2); + int frameSize = m_demux->getNextAudioFrame(&audioFrame, &headerCode1, &headerCode2); if (frameSize == 0) { - m_noAudioData = true; return 0; } int outbytes = 0; @@ -748,6 +761,9 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { if (!m_audioContext->Decode(audioFrame, frameSize, buffer, &outbytes)) { ERROR_LOG(ME, "Audio (%s) decode failed during video playback", GetCodecName(m_audioType)); } +#ifndef MOBILE_DEVICE + CBreakPoints::ExecMemCheck(bufferPtr, true, outbytes, currentMIPS->pc); +#endif } if (headerCode1 == 0x24) { @@ -761,10 +777,19 @@ int MediaEngine::getAudioSamples(u32 bufferPtr) { } } - m_noAudioData = false; return 0x2000; } +bool MediaEngine::IsNoAudioData() { + if (!m_demux) { + return true; + } + + // Let's double check. Here should be a safe enough place to demux. + m_demux->demux(m_audioStream); + return !m_demux->hasNextAudioFrame(NULL, NULL, NULL, NULL); +} + s64 MediaEngine::getVideoTimeStamp() { return m_videopts; } diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index aae6a528a..378ba1853 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -87,7 +87,7 @@ public: s64 getLastTimeStamp(); bool IsVideoEnd() { return m_isVideoEnd; } - bool IsNoAudioData() { return m_noAudioData; } + bool IsNoAudioData(); void DoState(PointerWrap &p); @@ -128,7 +128,6 @@ public: // TODO: Very little of this below should be public. s64 m_lastTimeStamp; bool m_isVideoEnd; - bool m_noAudioData; int m_ringbuffersize; u8 m_mpegheader[0x10000]; // TODO: Allocate separately diff --git a/Core/HW/MpegDemux.cpp b/Core/HW/MpegDemux.cpp index 1dd1fc8ed..02b39ba14 100644 --- a/Core/HW/MpegDemux.cpp +++ b/Core/HW/MpegDemux.cpp @@ -45,7 +45,7 @@ void MpegDemux::DoState(PointerWrap &p) { p.DoClass(m_audioStream); } -bool MpegDemux::addStreamData(u8* buf, int addSize) { +bool MpegDemux::addStreamData(const u8 *buf, int addSize) { if (m_readSize + addSize > m_len) return false; memcpy(m_buf + m_readSize, buf, addSize); @@ -234,25 +234,43 @@ static int getNextHeaderPosition(u8* audioStream, int curpos, int limit, int fra return -1; } -int MpegDemux::getNextaudioFrame(u8** buf, int *headerCode1, int *headerCode2) +int MpegDemux::getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2) { - int gotsize = m_audioStream.get_front(m_audioFrame, 0x2000); - if (gotsize == 0 || !isHeader(m_audioFrame, 0)) - return 0; - u8 Code1 = m_audioFrame[2]; - u8 Code2 = m_audioFrame[3]; - int frameSize = (((Code1 & 0x03) << 8) | ((Code2 & 0xFF) * 8)) + 0x10; - if (frameSize > gotsize) + int gotsize; + int frameSize; + if (!hasNextAudioFrame(&gotsize, &frameSize, headerCode1, headerCode2)) return 0; int audioPos = 8; int nextHeader = getNextHeaderPosition(m_audioFrame, audioPos, gotsize, frameSize); if (nextHeader >= 0) { audioPos = nextHeader; - } else + } else { audioPos = gotsize; + } m_audioStream.pop_front(0, audioPos); *buf = m_audioFrame + 8; - if (headerCode1) *headerCode1 = Code1; - if (headerCode2) *headerCode2 = Code2; return frameSize - 8; } + +bool MpegDemux::hasNextAudioFrame(int *gotsizeOut, int *frameSizeOut, int *headerCode1, int *headerCode2) +{ + int gotsize = m_audioStream.get_front(m_audioFrame, 0x2000); + if (gotsize == 0 || !isHeader(m_audioFrame, 0)) + return false; + u8 code1 = m_audioFrame[2]; + u8 code2 = m_audioFrame[3]; + int frameSize = (((code1 & 0x03) << 8) | ((code2 & 0xFF) * 8)) + 0x10; + if (frameSize > gotsize) + return false; + + if (gotsizeOut) + *gotsizeOut = gotsize; + if (frameSizeOut) + *frameSizeOut = frameSize; + if (headerCode1) + *headerCode1 = code1; + if (headerCode2) + *headerCode2 = code2; + + return true; +} diff --git a/Core/HW/MpegDemux.h b/Core/HW/MpegDemux.h index 0a973e9c5..11f2bccf8 100644 --- a/Core/HW/MpegDemux.h +++ b/Core/HW/MpegDemux.h @@ -14,11 +14,12 @@ public: MpegDemux(int size, int offset); ~MpegDemux(); - bool addStreamData(u8* buf, int addSize); + bool addStreamData(const u8 *buf, int addSize); void demux(int audioChannel); // return its framesize - int getNextaudioFrame(u8** buf, int *headerCode1, int *headerCode2); + int getNextAudioFrame(u8 **buf, int *headerCode1, int *headerCode2); + bool hasNextAudioFrame(int *gotsizeOut, int *frameSizeOut, int *headerCode1, int *headerCode2); inline int getRemainSize() { return m_len - m_readSize;