From edffdf20506d494ace55f33d8badeb6762833cd3 Mon Sep 17 00:00:00 2001 From: kaienfr Date: Mon, 7 Apr 2014 18:26:11 +0200 Subject: [PATCH] fix pmp video quality issue. We have no more decoding errors on imcomplete frame now. --- Core/HLE/sceMpeg.cpp | 216 ++++++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 65 deletions(-) diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 93ab546327..8a587f4855 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -813,6 +813,88 @@ bool InitPmp(MpegContext * ctx){ #endif } +class H264Frames{ +public: + int size; + u8* stream; + + H264Frames() :size(0), stream(NULL){}; + + H264Frames(u8* str, int sz) :size(sz){ + stream = new u8[size]; + memcpy(stream, str, size); + }; + + H264Frames(H264Frames *frame){ + size = frame->size; + stream = new u8[size]; + memcpy(stream, frame->stream, size); + }; + + ~H264Frames(){ + size = 0; + if (stream){ + delete[] stream; + stream = NULL; + } + }; + + void add(H264Frames *p){ + add(p->stream, p->size); + }; + + void add(u8* str, int sz){ + int newsize = size + sz; + auto newstream = new u8[newsize]; + // join two streams + memcpy(newstream, stream, size); + memcpy(newstream + size, str, sz); + // delete old stream + if (stream) + delete[] stream; + // replace with new stream + stream = newstream; + size = newsize; + }; + + void remove(int pos){ + // remove stream from begining to pos + if (pos == 0){ + // nothing to remove + } + else if (pos >= size){ + // we remove all + size = 0; + if (stream){ + delete[] stream; + stream = NULL; + } + } + else{ + // we remove the front part + size -= pos; + auto str = new u8[size]; + memcpy(str, stream + pos, size); + delete[] stream; + stream = str; + } + }; +#ifndef USE_FFMPEG +#define FF_INPUT_BUFFER_PADDING_SIZE 16 +#endif + void addpadding(){ + auto str = new u8[size + FF_INPUT_BUFFER_PADDING_SIZE]; + memcpy(str, stream, size); + memset(str + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + size += FF_INPUT_BUFFER_PADDING_SIZE; + delete[] stream; + stream = str; + } +}; + +// collect pmp video frames +static H264Frames *pmpframes = new H264Frames(); + // decode pmp video to RGBA format bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxAddr){ // the current video is pmp iff pmp_videoSource is a valide addresse @@ -836,75 +918,79 @@ bool decodePmpVideo(PSPPointer ringbuffer, u32 pmpctxAddr){ auto pFrameRGB = mediaengine->m_pFrameRGB; auto pCodecCtx = mediaengine->m_pCodecCtxs[0]; - // decode all blocks + // joint all blocks into H264Frames SceMpegLLI lli; for (int i = 0; i < pmp_nBlocks; i++){ Memory::ReadStruct(pmp_videoSource, &lli); - - // initialize packet - AVPacket packet; - av_new_packet(&packet, pCodecCtx->width*pCodecCtx->height); - - // set packet to source block - packet.data = Memory::GetPointer(lli.pSrc); - packet.size = lli.iSize; - - // reuse pFrame and pFrameRGB - int got_picture = 0; - av_frame_unref(pFrame); - av_frame_unref(pFrameRGB); - - // hook pFrameRGB output to buffer - avpicture_fill((AVPicture *)pFrameRGB, mediaengine->m_buffer, pmp_want_pix_fmt, pCodecCtx->width, pCodecCtx->height); - - // decode video frame - auto len = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); - DEBUG_LOG(ME, "got_picture %d", got_picture); - if (got_picture){ - SwsContext *img_convert_ctx = NULL; - img_convert_ctx = sws_getContext( - pCodecCtx->width, - pCodecCtx->height, - pCodecCtx->pix_fmt, - pCodecCtx->width, - pCodecCtx->height, - pmp_want_pix_fmt, - SWS_BILINEAR, - NULL, NULL, NULL); - - if (!img_convert_ctx) { - ERROR_LOG(ME, "Cannot initialize sws conversion context"); - return false; - } - - // Convert to RGBA - int swsRet = sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, - pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); - if (swsRet < 0){ - ERROR_LOG(ME, "sws_scale: Error while converting %d", swsRet); - return false; - } - - // update timestamp - if (av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) != AV_NOPTS_VALUE) - mediaengine->m_videopts = av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) + av_frame_get_pkt_duration(mediaengine->m_pFrame) - mediaengine->m_firstTimeStamp; - else - mediaengine->m_videopts += av_frame_get_pkt_duration(mediaengine->m_pFrame); - - // push the decoded frame into pmp_queue - pmp_queue.push_back(pFrameRGB); - - // write frame into ppm file - // SaveFrame(pNewFrameRGB, pCodecCtx->width, pCodecCtx->height); - - // free some pointers - sws_freeContext(img_convert_ctx); - av_free_packet(&packet); - - // get next block - pmp_videoSource += sizeof(SceMpegLLI); - } + // add source block into pmpframes + pmpframes->add(Memory::GetPointer(lli.pSrc), lli.iSize); + // get next block + pmp_videoSource += sizeof(SceMpegLLI); } + + pmpframes->addpadding(); + + // initialize packet + AVPacket packet; + av_new_packet(&packet, pCodecCtx->width*pCodecCtx->height); + + // set packet to source block + packet.data = pmpframes->stream; + packet.size = pmpframes->size; + + // reuse pFrame and pFrameRGB + int got_picture = 0; + av_frame_unref(pFrame); + av_frame_unref(pFrameRGB); + + // hook pFrameRGB output to buffer + avpicture_fill((AVPicture *)pFrameRGB, mediaengine->m_buffer, pmp_want_pix_fmt, pCodecCtx->width, pCodecCtx->height); + + // decode video frame + auto len = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); + DEBUG_LOG(ME, "got_picture %d", got_picture); + if (got_picture){ + SwsContext *img_convert_ctx = NULL; + img_convert_ctx = sws_getContext( + pCodecCtx->width, + pCodecCtx->height, + pCodecCtx->pix_fmt, + pCodecCtx->width, + pCodecCtx->height, + pmp_want_pix_fmt, + SWS_BILINEAR, + NULL, NULL, NULL); + + if (!img_convert_ctx) { + ERROR_LOG(ME, "Cannot initialize sws conversion context"); + return false; + } + + // Convert to RGBA + int swsRet = sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, + pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); + if (swsRet < 0){ + ERROR_LOG(ME, "sws_scale: Error while converting %d", swsRet); + return false; + } + // free sws context + sws_freeContext(img_convert_ctx); + + // update timestamp + if (av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) != AV_NOPTS_VALUE) + mediaengine->m_videopts = av_frame_get_best_effort_timestamp(mediaengine->m_pFrame) + av_frame_get_pkt_duration(mediaengine->m_pFrame) - mediaengine->m_firstTimeStamp; + else + mediaengine->m_videopts += av_frame_get_pkt_duration(mediaengine->m_pFrame); + + // push the decoded frame into pmp_queue + pmp_queue.push_back(pFrameRGB); + + // write frame into ppm file + // SaveFrame(pNewFrameRGB, pCodecCtx->width, pCodecCtx->height); + } + // free some pointers + av_free_packet(&packet); + pmpframes->~H264Frames(); // must reset pmp_VideoSource address to zero after decoding. pmp_videoSource = 0; return true;