mirror of
https://github.com/libretro/ppsspp.git
synced 2024-12-01 04:10:30 +00:00
Merge pull request #5977 from unknownbrackets/psmfplayer
Fix video flicker, add memchecks and detect no audio better
This commit is contained in:
commit
7fd84046a4
@ -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;
|
||||
|
@ -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<PsmfVideoData>::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?
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user