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; diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 916614cae241..0a27c082b66f 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) @@ -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); 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. 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) diff --git a/Core/HW/MediaEngine.cpp b/Core/HW/MediaEngine.cpp index 244feb3f234d..88e24b2f7ed9 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,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; } diff --git a/Core/HW/MediaEngine.h b/Core/HW/MediaEngine.h index 7af600096789..d060ca9a40b5 100644 --- a/Core/HW/MediaEngine.h +++ b/Core/HW/MediaEngine.h @@ -32,6 +32,9 @@ struct SwsContext; struct AVFrame; +struct AVIOContext; +struct AVFormatContext; +struct AVCodecContext; class MediaEngine { @@ -45,7 +48,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); @@ -58,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); @@ -72,11 +76,11 @@ class MediaEngine 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 @@ 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; @@ -97,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;