Merge pull request #5977 from unknownbrackets/psmfplayer

Fix video flicker, add memchecks and detect no audio better
This commit is contained in:
Henrik Rydgård 2014-04-28 09:21:46 +02:00
commit 7fd84046a4
6 changed files with 115 additions and 46 deletions

View File

@ -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;

View File

@ -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?

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;