From c064c701e2d35b10629de79311e69671ef6a9bcc Mon Sep 17 00:00:00 2001 From: Nekotekina Date: Mon, 3 Mar 2014 03:02:42 +0400 Subject: [PATCH] cellAdec draft --- Utilities/SQueue.h | 24 +- rpcs3/Emu/SysCalls/Modules/cellAdec.cpp | 183 +++++++++++- rpcs3/Emu/SysCalls/Modules/cellAdec.h | 97 ++++++- rpcs3/Emu/SysCalls/Modules/cellDmux.cpp | 10 +- rpcs3/Emu/SysCalls/Modules/cellDmux.h | 2 +- rpcs3/Emu/SysCalls/Modules/cellVdec.cpp | 346 +++++++++++++++-------- rpcs3/Emu/SysCalls/Modules/cellVdec.h | 74 +++-- rpcs3/Emu/SysCalls/Modules/cellVpost.cpp | 11 +- 8 files changed, 548 insertions(+), 199 deletions(-) diff --git a/Utilities/SQueue.h b/Utilities/SQueue.h index 1fee6f6727..aa6b3d3851 100644 --- a/Utilities/SQueue.h +++ b/Utilities/SQueue.h @@ -19,11 +19,6 @@ public: { while (true) { - if (Emu.IsStopped()) - { - return false; - } - if (m_mutex.GetOwner() == m_mutex.GetDeadValue()) { return false; @@ -31,6 +26,10 @@ public: if (m_count >= SQSize) { + if (Emu.IsStopped()) + { + return false; + } Sleep(1); continue; } @@ -51,11 +50,6 @@ public: { while (true) { - if (Emu.IsStopped()) - { - return false; - } - if (m_mutex.GetOwner() == m_mutex.GetDeadValue()) { return false; @@ -63,6 +57,10 @@ public: if (!m_count) { + if (Emu.IsStopped()) + { + return false; + } Sleep(1); continue; } @@ -96,4 +94,10 @@ public: SMutexLocker lock(m_mutex); m_count = 0; } + + T& Peek(u32 pos = 0) + { + SMutexLocker lock(m_mutex); + return m_data[(m_pos + pos) % SQSize]; + } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp index 5829ac0e29..ebff0e2de6 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp @@ -1,64 +1,226 @@ #include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" +#include "cellPamf.h" + +extern "C" +{ +#include "libavcodec\avcodec.h" +#include "libavformat\avformat.h" +} + #include "cellAdec.h" void cellAdec_init(); Module cellAdec(0x0006, cellAdec_init); +u32 adecOpen(AudioDecoder* data) +{ + AudioDecoder& adec = *data; + + u32 adec_id = cellAdec.GetNewId(data); + + adec.id = adec_id; + + thread t("Audio Decoder[" + std::to_string(adec_id) + "] Thread", [&]() + { + ConLog.Write("Audio Decoder enter()"); + + AdecTask task; + + while (true) + { + if (Emu.IsStopped()) + { + break; + } + + if (adec.job.IsEmpty() && adec.is_running) + { + Sleep(1); + continue; + } + + if (adec.frames.GetCount() >= 50) + { + Sleep(1); + continue; + } + + if (!adec.job.Pop(task)) + { + break; + } + + switch (task.type) + { + case adecStartSeq: + { + } + break; + + case adecEndSeq: + { + } + break; + + case adecDecodeAu: + { + } + break; + + case adecClose: + { + adec.is_finished = true; + ConLog.Write("Audio Decoder exit"); + return; + } + + default: + ConLog.Error("Audio Decoder error: unknown task(%d)", task.type); + return; + } + } + ConLog.Warning("Audio Decoder aborted"); + }); + + t.detach(); + + return adec_id; +} + +bool adecCheckType(AudioCodecType type) +{ + switch (type) + { + case CELL_ADEC_TYPE_ATRACX: ConLog.Write("*** (???) type: ATRAC3plus"); break; + case CELL_ADEC_TYPE_ATRACX_2CH: ConLog.Write("*** type: ATRAC3plus 2ch"); break; + + case CELL_ADEC_TYPE_ATRACX_6CH: + case CELL_ADEC_TYPE_ATRACX_8CH: + case CELL_ADEC_TYPE_LPCM_PAMF: + case CELL_ADEC_TYPE_AC3: + case CELL_ADEC_TYPE_MP3: + case CELL_ADEC_TYPE_ATRAC3: + case CELL_ADEC_TYPE_MPEG_L2: + case CELL_ADEC_TYPE_CELP: + case CELL_ADEC_TYPE_M4AAC: + case CELL_ADEC_TYPE_CELP8: + cellAdec.Error("Unimplemented audio codec type (%d)", type); + break; + default: + return false; + } + + return true; +} + int cellAdecQueryAttr(mem_ptr_t type, mem_ptr_t attr) { - cellAdec.Error("cellAdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr()); + cellAdec.Warning("cellAdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr()); + + if (!type.IsGood() || !attr.IsGood()) + { + return CELL_ADEC_ERROR_FATAL; + } + + if (!adecCheckType(type->audioCodecType)) return CELL_ADEC_ERROR_ARG; + + // TODO: check values + attr->adecVerLower = 0x280000; // from dmux + attr->adecVerUpper = 0x260000; + attr->workMemSize = 4 * 1024 * 1024; + return CELL_OK; } int cellAdecOpen(mem_ptr_t type, mem_ptr_t res, mem_ptr_t cb, mem32_t handle) { - cellAdec.Error("cellAdecOpen(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)", + cellAdec.Warning("cellAdecOpen(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)", type.GetAddr(), res.GetAddr(), cb.GetAddr(), handle.GetAddr()); + + if (!type.IsGood() || !res.IsGood() || !cb.IsGood() || !handle.IsGood()) + { + return CELL_ADEC_ERROR_FATAL; + } + + if (!adecCheckType(type->audioCodecType)) return CELL_ADEC_ERROR_ARG; + + handle = adecOpen(new AudioDecoder(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg)); + return CELL_OK; } int cellAdecOpenEx(mem_ptr_t type, mem_ptr_t res, mem_ptr_t cb, mem32_t handle) { - cellAdec.Error("cellAdecOpenEx(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)", + cellAdec.Warning("cellAdecOpenEx(type_addr=0x%x, res_addr=0x%x, cb_addr=0x%x, handle_addr=0x%x)", type.GetAddr(), res.GetAddr(), cb.GetAddr(), handle.GetAddr()); + + if (!type.IsGood() || !res.IsGood() || !cb.IsGood() || !handle.IsGood()) + { + return CELL_ADEC_ERROR_FATAL; + } + + if (!adecCheckType(type->audioCodecType)) return CELL_ADEC_ERROR_ARG; + + handle = adecOpen(new AudioDecoder(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg)); + return CELL_OK; } int cellAdecClose(u32 handle) { - cellAdec.Error("cellAdecClose(handle=0x%x)", handle); + cellAdec.Warning("cellAdecClose(handle=%d)", handle); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + adec->job.Push(AdecTask(adecClose)); + + while (!adec->is_finished || !adec->frames.IsEmpty()) + { + if (Emu.IsStopped()) + { + ConLog.Warning("cellAdecClose(%d) aborted", handle); + break; + } + Sleep(1); + } + + Emu.GetIdManager().RemoveID(handle); return CELL_OK; } int cellAdecStartSeq(u32 handle, u32 param_addr) { - cellAdec.Error("cellAdecStartSeq(handle=0x%x, param_addr=0x%x)", handle, param_addr); + cellAdec.Error("cellAdecStartSeq(handle=%d, param_addr=0x%x)", handle, param_addr); return CELL_OK; } int cellAdecEndSeq(u32 handle) { - cellAdec.Error("cellAdecEndSeq(handle=0x%x)", handle); + cellAdec.Error("cellAdecEndSeq(handle=%d)", handle); return CELL_OK; } int cellAdecDecodeAu(u32 handle, mem_ptr_t auInfo) { - cellAdec.Error("cellAdecDecodeAu(handle=0x%x, auInfo_addr=0x%x)", handle, auInfo.GetAddr()); + cellAdec.Error("cellAdecDecodeAu(handle=%d, auInfo_addr=0x%x)", handle, auInfo.GetAddr()); return CELL_OK; } int cellAdecGetPcm(u32 handle, u32 outBuffer_addr) { - cellAdec.Error("cellAdecGetPcm(handle=0x%x, outBuffer_addr=0x%x)", handle, outBuffer_addr); + cellAdec.Error("cellAdecGetPcm(handle=%d, outBuffer_addr=0x%x)", handle, outBuffer_addr); return CELL_OK; } int cellAdecGetPcmItem(u32 handle, u32 pcmItem_ptr_addr) { - cellAdec.Error("cellAdecGetPcmItem(handle=0x%x, pcmItem_ptr_addr=0x%x)", handle, pcmItem_ptr_addr); + cellAdec.Error("cellAdecGetPcmItem(handle=%d, pcmItem_ptr_addr=0x%x)", handle, pcmItem_ptr_addr); return CELL_OK; } @@ -73,4 +235,7 @@ void cellAdec_init() cellAdec.AddFunc(0x1529e506, cellAdecDecodeAu); cellAdec.AddFunc(0x97ff2af1, cellAdecGetPcm); cellAdec.AddFunc(0xbd75f78b, cellAdecGetPcmItem); + + av_register_all(); + avcodec_register_all(); } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellAdec.h b/rpcs3/Emu/SysCalls/Modules/cellAdec.h index 2aa17ead21..833042396c 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.h +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.h @@ -1,5 +1,6 @@ #pragma once -#include "cellPamf.h" + +#include "Utilities/SQueue.h" // Error Codes enum @@ -341,6 +342,17 @@ struct CellAdecResource be_t ppuThreadStackSize; }; +struct CellAdecResourceEx +{ + be_t totalMemSize; + be_t startAddr; + be_t ppuThreadPriority; + be_t ppuThreadStackSize; + be_t spurs_addr; + u8 priority[8]; + be_t maxContention; +}; + // Callback Messages enum CellAdecMsgType { @@ -348,12 +360,14 @@ enum CellAdecMsgType CELL_ADEC_MSG_TYPE_PCMOUT, CELL_ADEC_MSG_TYPE_ERROR, CELL_ADEC_MSG_TYPE_SEQDONE, -}; +}; + +typedef mem_func_ptr_t CellAdecCbMsg; struct CellAdecCb { - be_t> cbFunc; - be_t cbArg_addr; + be_t cbFunc; + be_t cbArg; }; typedef CellCodecTimeStamp CellAdecTimeStamp; @@ -399,17 +413,6 @@ struct CellAdecLpcmInfo be_t outputDataSize; }; -struct CellAdecResourceEx -{ - be_t totalMemSize; - be_t startAddr; - be_t ppuThreadPriority; - be_t ppuThreadStackSize; - be_t spurs_addr; - u8 priority[8]; - be_t maxContention; -}; - // CELP Excitation Mode enum CELP_ExcitationMode { @@ -985,3 +988,67 @@ struct CellAdecMpmcInfo be_t lfePresent; be_t channelCoufiguration; }; + +/* Audio Decoder Thread Classes */ + +enum AdecJobType : u32 +{ + adecStartSeq, + adecEndSeq, + adecDecodeAu, + adecClose, +}; + +struct AdecTask +{ + AdecJobType type; + // ... + + AdecTask(AdecJobType type) + : type(type) + { + } + + AdecTask() + { + } +}; + +struct AdecFrame +{ + // under construction + u64 pts; + u64 userdata; +}; + +class AudioDecoder +{ +public: + SQueue job; + u32 id; + volatile bool is_running; + volatile bool is_finished; + + SQueue frames; + + const AudioCodecType type; + const u32 memAddr; + const u32 memSize; + const u32 cbFunc; + const u32 cbArg; + + AudioDecoder(AudioCodecType type, u32 addr, u32 size, u32 func, u32 arg) + : type(type) + , memAddr(addr) + , memSize(size) + , cbFunc(func) + , cbArg(arg) + , is_running(false) + , is_finished(false) + { + } + + ~AudioDecoder() + { + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp index aadfaf1a85..4e1afc2923 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp @@ -18,7 +18,7 @@ void dmuxQueryEsAttr(u32 info_addr /* may be 0 */, const mem_ptr_t attr) { if (esFilterId->filterIdMajor >= 0xe0) - attr->memSize = 0x600000; // 0x45fa49 from ps3 + attr->memSize = 0x6000000; // 0x45fa49 from ps3 else attr->memSize = 0x10000; // 0x73d9 from ps3 @@ -159,7 +159,7 @@ u32 dmuxOpen(Demuxer* data) if (pes.new_au) { - ConLog.Write("*** AVC AU detected (pts=0x%llx, dts=0x%llx)", pes.pts, pes.dts); + //ConLog.Write("*** AVC AU detected (pts=0x%llx, dts=0x%llx)", pes.pts, pes.dts); } if (es.isfull()) @@ -167,8 +167,10 @@ u32 dmuxOpen(Demuxer* data) stream = backup; continue; } - //stream = backup; - es.push(stream, len - pes.size - 3, pes); + + //hack: reconstruction of MPEG2-PS stream for vdec module (seems it works without it too) + stream = backup; + es.push(stream, len + 6 /*- pes.size - 3*/, pes); } else { diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.h b/rpcs3/Emu/SysCalls/Modules/cellDmux.h index 51e805ab07..cd417b5915 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.h +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.h @@ -373,7 +373,7 @@ struct PesHeader , dts(0xffffffffffffffff) , ch(0) , size(0) - , new_au(true) + , new_au(false) { u16 header; stream.get(header); diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp index c0dd39dcfe..6333851bd1 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp @@ -19,22 +19,54 @@ int vdecRead(void* opaque, u8* buf, int buf_size) { VideoDecoder& vdec = *(VideoDecoder*)opaque; - if (vdec.reader.size < (u32)buf_size) buf_size = vdec.reader.size; + int res = 0; + + /*if (vdec.reader.header_size) + { + assert(vdec.reader.header_size == 14); + res = buf_size; + if (vdec.reader.header_size < (u32)buf_size) + res = vdec.reader.header_size; + + buf[0] = 0; + buf[1] = 0; + buf[2] = 1; + buf[3] = 0xba; + buf[4] = 0x44; + buf[5] = 0; + buf[6] = 0x07; + buf[7] = 0xaa; + buf[8] = 0x75; + buf[9] = 0xb1; + buf[10] = 0x07; + buf[11] = 0x53; + buf[12] = 0x03; + buf[13] = 0xf8; + vdec.reader.header_size -= res; + buf_size -= res; + buf += res; + }*/ + + if (vdec.reader.size < (u32)buf_size) + { + buf_size = vdec.reader.size; + } + if (!buf_size) { - return AVERROR_EOF; + return res; } else if (!Memory.CopyToReal(buf, vdec.reader.addr, buf_size)) { ConLog.Error("vdecRead: data reading failed (buf_size=0x%x)", buf_size); Emu.Pause(); - return 0; + return res; } else { vdec.reader.addr += buf_size; vdec.reader.size -= buf_size; - return buf_size; + return res + buf_size; } } @@ -79,12 +111,11 @@ u32 vdecOpen(VideoDecoder* data) if (vdec.job.IsEmpty() && vdec.is_running) { - // TODO: default task (not needed?) Sleep(1); continue; } - if (vdec.has_picture) // hack + if (vdec.frames.GetCount() >= 50) { Sleep(1); continue; @@ -100,55 +131,34 @@ u32 vdecOpen(VideoDecoder* data) case vdecStartSeq: { // TODO: reset data - ConLog.Warning("vdecStartSeq()"); + ConLog.Warning("vdecStartSeq:"); + + vdec.reader.addr = 0; + vdec.reader.size = 0; vdec.is_running = true; + vdec.just_started = true; } break; case vdecEndSeq: { + ConLog.Warning("vdecEndSeq:"); + Callback cb; cb.SetAddr(vdec.cbFunc); cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, 0, vdec.cbArg); - cb.Branch(false); - ConLog.Warning("vdecEndSeq()"); + cb.Branch(true); // ??? + + avcodec_close(vdec.ctx); + avformat_close_input(&vdec.fmt); + vdec.is_running = false; } break; case vdecDecodeAu: { - struct vdecPacket : AVPacket - { - vdecPacket(u32 size) - { - av_init_packet(this); - data = (u8*)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); - memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); - this->size = size + FF_INPUT_BUFFER_PADDING_SIZE; - } - - ~vdecPacket() - { - av_free(data); - //av_free_packet(this); - } - - } au(task.size); - - if ((task.pts || task.dts) && task.pts != ~0 && task.dts != ~0) - { - vdec.pts = task.pts; - vdec.dts = task.dts; - au.pts = vdec.pts; - au.dts = vdec.dts; - au.flags = AV_PKT_FLAG_KEY; - } - else - { - au.pts = vdec.pts; - au.dts = vdec.dts; - } + int err; if (task.mode != CELL_VDEC_DEC_MODE_NORMAL) { @@ -159,11 +169,34 @@ u32 vdecOpen(VideoDecoder* data) vdec.reader.addr = task.addr; vdec.reader.size = task.size; - if (!Memory.CopyToReal(au.data, task.addr, task.size)) + u64 last_pts = task.pts, last_dts = task.dts; + + struct AVPacketHolder : AVPacket { - ConLog.Error("vdecDecodeAu: AU data accessing failed(addr=0x%x, size=0x%x)", task.addr, task.size); - break; - } + AVPacketHolder(u32 size) + { + av_init_packet(this); + + if (size) + { + data = (u8*)av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE); + memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); + this->size = size + FF_INPUT_BUFFER_PADDING_SIZE; + } + else + { + data = NULL; + size = 0; + } + } + + ~AVPacketHolder() + { + av_free(data); + //av_free_packet(this); + } + + } au(0); /*{ wxFile dump; @@ -172,54 +205,108 @@ u32 vdecOpen(VideoDecoder* data) dump.Close(); }*/ - int got_picture = 0; - - //vdec.ctx->flags |= CODEC_FLAG_TRUNCATED; - //vdec.ctx->flags2 |= CODEC_FLAG2_CHUNKS; - vdec.ctx->flags2 |= CODEC_FLAG2_LOCAL_HEADER; - vdec.ctx->codec_tag = *(u32*)"DAVC"; - //vdec.ctx->stream_codec_tag = *(u32*)"DAVC"; - - //avcodec_get_frame_defaults(vdec.frame); - - - int decode = avcodec_decode_video2(vdec.ctx, vdec.frame, &got_picture, &au); - if (decode < 0) + if (vdec.just_started) // deferred initialization { - ConLog.Error("vdecDecodeAu: AU decoding error(%d)", decode); - break; - } - - if (got_picture) - { - ConLog.Write("got_picture (%d, vdec: pts=0x%llx, dts=0x%llx)", got_picture, vdec.pts, vdec.dts); - - /*if (vdec.out_data[0]) av_freep(vdec.out_data[0]); - - int err = av_image_alloc(vdec.out_data, vdec.linesize, vdec.ctx->width, vdec.ctx->height, vdec.ctx->pix_fmt, 1); - if (err < 0) + err = avformat_open_input(&vdec.fmt, NULL, NULL, NULL); + if (err) { - ConLog.Error("vdecDecodeAu: av_image_alloc failed(%d)", err); + ConLog.Error("vdecDecodeAu: avformat_open_input() failed"); Emu.Pause(); return; } + err = avformat_find_stream_info(vdec.fmt, NULL); + if (err) + { + ConLog.Error("vdecDecodeAu: avformat_find_stream_info() failed"); + Emu.Pause(); + return; + } + if (!vdec.fmt->nb_streams) + { + ConLog.Error("vdecDecodeAu: no stream found"); + Emu.Pause(); + return; + } + vdec.ctx = vdec.fmt->streams[0]->codec; // TODO: check data - vdec.buf_size = err; + AVCodec* codec = avcodec_find_decoder(vdec.ctx->codec_id); // ??? + if (!codec) + { + ConLog.Error("vdecDecodeAu: avcodec_find_decoder() failed"); + Emu.Pause(); + return; + } - av_image_copy(vdec.out_data, vdec.linesize, (const u8**)(vdec.frame->data), vdec.frame->linesize, - vdec.ctx->pix_fmt, vdec.ctx->width, vdec.ctx->height);*/ - vdec.buf_size = a128(av_image_get_buffer_size(vdec.ctx->pix_fmt, vdec.ctx->width, vdec.ctx->height, 1)); - - vdec.userdata = task.userData; - vdec.has_picture = true; - - Callback cb; - cb.SetAddr(vdec.cbFunc); - cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, 0, vdec.cbArg); - cb.Branch(false); + { + static SMutexGeneral g_mutex_avcodec_open2; + SMutexGeneralLocker lock(g_mutex_avcodec_open2); + // not multithread-safe + err = avcodec_open2(vdec.ctx, codec, &vdec.opts); + } + if (err) + { + ConLog.Error("vdecDecodeAu: avcodec_open2() failed"); + Emu.Pause(); + return; + } + vdec.just_started = false; } - - ConLog.Write("Frame decoded (pts=0x%llx, dts=0x%llx, addr=0x%x, result=0x%x)", au.pts, au.dts, task.addr, decode); + + while (av_read_frame(vdec.fmt, &au) >= 0) + { + struct VdecFrameHolder : VdecFrame + { + VdecFrameHolder() + { + data = av_frame_alloc(); + } + + ~VdecFrameHolder() + { + if (data) + { + av_frame_unref(data); + av_frame_free(&data); + } + } + + } frame; + + if (!frame.data) + { + ConLog.Error("vdecDecodeAu: av_frame_alloc() failed"); + Emu.Pause(); + return; + } + + int got_picture = 0; + + int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &au); + + if (decode < 0) + { + ConLog.Error("vdecDecodeAu: AU decoding error(0x%x)", decode); + break; + } + + if (got_picture) + { + ConLog.Write("got_picture (%d, vdec: pts=0x%llx, dts=0x%llx)", got_picture, au.pts, au.dts); + + frame.dts = last_dts; last_dts += 3003; // + duration??? + frame.pts = last_pts; last_pts += 3003; + frame.userdata = task.userData; + vdec.frames.Push(frame); + frame.data = nullptr; // to prevent destruction + + Callback cb; + cb.SetAddr(vdec.cbFunc); + cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, 0, vdec.cbArg); + cb.Branch(false); + } + } + + ConLog.Write("AU decoded (pts=0x%llx, dts=0x%llx, addr=0x%x, size=0x%x)", task.pts, task.dts, task.addr, task.size); Callback cb; cb.SetAddr(vdec.cbFunc); @@ -331,7 +418,7 @@ int cellVdecClose(u32 handle) vdec->job.Push(VdecTask(vdecClose)); - while (!vdec->is_finished) + while (!vdec->is_finished || !vdec->frames.IsEmpty()) { if (Emu.IsStopped()) { @@ -361,7 +448,7 @@ int cellVdecStartSeq(u32 handle) int cellVdecEndSeq(u32 handle) { - cellVdec.Log("cellVdecEndSeq(handle=%d)", handle); + cellVdec.Warning("cellVdecEndSeq(handle=%d)", handle); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) @@ -369,6 +456,28 @@ int cellVdecEndSeq(u32 handle) return CELL_VDEC_ERROR_ARG; } + /*if (!vdec->job.IsEmpty()) + { + Sleep(1); + return CELL_VDEC_ERROR_BUSY; // ??? + } + + if (!vdec->frames.IsEmpty()) + { + Sleep(1); + return CELL_VDEC_ERROR_BUSY; // ??? + }*/ + + while (!vdec->job.IsEmpty() || !vdec->frames.IsEmpty()) + { + if (Emu.IsStopped()) + { + ConLog.Warning("cellVdecEndSeq(%d) aborted", handle); + return CELL_OK; + } + Sleep(1); + } + vdec->job.Push(VdecTask(vdecEndSeq)); return CELL_OK; } @@ -399,7 +508,7 @@ int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, const mem_ptr_t format, u32 out_addr) { - cellVdec.Warning("cellVdecGetPicture(handle=%d, format_addr=0x%x, out_addr=0x%x)", handle, format.GetAddr(), out_addr); + cellVdec.Log("cellVdecGetPicture(handle=%d, format_addr=0x%x, out_addr=0x%x)", handle, format.GetAddr(), out_addr); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) @@ -412,14 +521,16 @@ int cellVdecGetPicture(u32 handle, const mem_ptr_t format, u3 return CELL_VDEC_ERROR_FATAL; } - if (!vdec->has_picture) + if (vdec->frames.IsEmpty()) { return CELL_VDEC_ERROR_EMPTY; } if (out_addr) { - if (!Memory.IsGoodAddr(out_addr, vdec->buf_size)) + u32 buf_size = a128(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1)); + + if (!Memory.IsGoodAddr(out_addr, buf_size)) { return CELL_VDEC_ERROR_FATAL; } @@ -429,54 +540,47 @@ int cellVdecGetPicture(u32 handle, const mem_ptr_t format, u3 cellVdec.Error("cellVdecGetPicture: TODO: unknown formatType(%d)", (u32)format->formatType); return CELL_OK; } + if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709) { cellVdec.Error("cellVdecGetPicture: TODO: unknown colorMatrixType(%d)", (u32)format->colorMatrixType); return CELL_OK; } - AVFrame& frame = *vdec->frame; + VdecFrame vf; - u8* buf = (u8*)malloc(vdec->buf_size); - if (!buf) - { - cellVdec.Error("cellVdecGetPicture: malloc failed (out of memory)"); - Emu.Pause(); - return CELL_OK; - } + vdec->frames.Pop(vf); + + AVFrame& frame = *vf.data; + + u8* buf = (u8*)malloc(buf_size); // TODO: zero padding bytes - int err = av_image_copy_to_buffer(buf, vdec->buf_size, frame.data, frame.linesize, vdec->ctx->pix_fmt, frame.width, frame.height, 1); + int err = av_image_copy_to_buffer(buf, buf_size, frame.data, frame.linesize, vdec->ctx->pix_fmt, frame.width, frame.height, 1); if (err < 0) { cellVdec.Error("cellVdecGetPicture: av_image_copy_to_buffer failed(%d)", err); Emu.Pause(); } - if (!Memory.CopyFromReal(out_addr, buf, vdec->buf_size)) + if (!Memory.CopyFromReal(out_addr, buf, buf_size)) { cellVdec.Error("cellVdecGetPicture: data copying failed"); Emu.Pause(); } - /* - u32 size0 = frame.linesize[0] * frame.height; - u32 size1 = frame.linesize[1] * frame.height / 2; - u32 size2 = frame.linesize[2] * frame.height / 2; - ConLog.Write("*** size0=0x%x, size1=0x%x, size2=0x%x, buf_size=0x%x (res=0x%x)", size0, size1, size2, vdec->buf_size, err); - */ - + av_frame_unref(vf.data); + av_frame_free(&vf.data); free(buf); } - vdec->has_picture = false; return CELL_OK; } int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) { - cellVdec.Warning("cellVdecGetPicItem(handle=%d, picItem_ptr_addr=0x%x)", handle, picItem_ptr.GetAddr()); + cellVdec.Log("cellVdecGetPicItem(handle=%d, picItem_ptr_addr=0x%x)", handle, picItem_ptr.GetAddr()); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) @@ -489,26 +593,31 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) return CELL_VDEC_ERROR_FATAL; } - if (!vdec->has_picture) + VdecFrame& vf = vdec->frames.Peek(); + + if (vdec->frames.IsEmpty()) { + Sleep(1); return CELL_VDEC_ERROR_EMPTY; } + AVFrame& frame = *vf.data; + mem_ptr_t info(vdec->memAddr); info->codecType = vdec->type; info->startAddr = 0x00000123; // invalid value (no address for picture) - info->size = vdec->buf_size; + info->size = a128(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1)); info->auNum = 1; - info->auPts[0].lower = vdec->pts; - info->auPts[0].upper = vdec->pts >> 32; + info->auPts[0].lower = vf.pts; + info->auPts[0].upper = vf.pts >> 32; info->auPts[1].lower = 0xffffffff; info->auPts[1].upper = 0xffffffff; - info->auDts[0].lower = vdec->dts; - info->auDts[0].upper = vdec->dts >> 32; + info->auDts[0].lower = vf.dts; + info->auDts[0].upper = vf.dts >> 32; info->auDts[1].lower = 0xffffffff; info->auDts[1].upper = 0xffffffff; - info->auUserData[0] = vdec->userdata; + info->auUserData[0] = vf.userdata; info->auUserData[1] = 0; info->status = CELL_OK; info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL; @@ -516,9 +625,9 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) mem_ptr_t avc(vdec->memAddr + sizeof(CellVdecPicItem)); - avc->horizontalSize = vdec->frame->width; // ??? - avc->verticalSize = vdec->frame->height; - switch (vdec->frame->pict_type) + avc->horizontalSize = frame.width; + avc->verticalSize = frame.height; + switch (frame.pict_type) { case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break; case AV_PICTURE_TYPE_P: avc->pictureType[0] = CELL_VDEC_AVC_PCT_P; break; @@ -590,5 +699,6 @@ void cellVdec_init() cellVdec.AddFunc(0x17c702b9, cellVdecGetPicItem); cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate); + av_register_all(); avcodec_register_all(); } \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.h b/rpcs3/Emu/SysCalls/Modules/cellVdec.h index f2ca2320fd..8068b2e8b4 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.h +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.h @@ -679,6 +679,14 @@ struct VdecTask } }; +struct VdecFrame +{ + AVFrame* data; + u64 dts; + u64 pts; + u64 userdata; +}; + int vdecRead(void* opaque, u8* buf, int buf_size); class VideoDecoder @@ -688,19 +696,12 @@ public: u32 id; volatile bool is_running; volatile bool is_finished; + bool just_started; - AVCodec* codec; AVCodecContext* ctx; - AVFormatContext* fmt; - AVFrame* frame; AVDictionary* opts; + AVFormatContext* fmt; u8* io_buf; - u32 buf_size; - u64 pts; - u64 dts; - u64 pos; - u64 userdata; - volatile bool has_picture; struct VideoReader { @@ -708,6 +709,8 @@ public: u32 size; } reader; + SQueue frames; + const CellVdecCodecType type; const u32 profile; const u32 memAddr; @@ -724,38 +727,24 @@ public: , cbArg(arg) , is_finished(false) , is_running(false) - , has_picture(false) - , pos(0) + , just_started(false) { - codec = avcodec_find_decoder(AV_CODEC_ID_H264); + opts = nullptr; + av_dict_set(&opts, "refcounted_frames", "1", 0); + AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!codec) { - ConLog.Error("VideoDecoder(): avcodec_find_decoder failed"); + ConLog.Error("vdecDecodeAu: avcodec_find_decoder(H264) failed"); Emu.Pause(); return; } - ctx = avcodec_alloc_context3(codec); + ctx = nullptr; /*avcodec_alloc_context3(codec); if (!ctx) { ConLog.Error("VideoDecoder(): avcodec_alloc_context3 failed"); Emu.Pause(); return; - } - opts = nullptr; - int err = avcodec_open2(ctx, codec, &opts); - if (err) // TODO: not multithread safe - { - ConLog.Error("VideoDecoder(): avcodec_open2 failed(%d)", err); - Emu.Pause(); - return; - } - frame = av_frame_alloc(); - if (!frame) - { - ConLog.Error("VideoDecoder(): av_frame_alloc failed"); - Emu.Pause(); - return; - } + }*/ fmt = avformat_alloc_context(); if (!fmt) { @@ -771,23 +760,32 @@ public: Emu.Pause(); return; } - //memset(&out_data, 0, sizeof(out_data)); - //memset(&linesize, 0, sizeof(linesize)); } ~VideoDecoder() { - if (io_buf) av_free(io_buf); + if (!is_finished && ctx) + { + for (u32 i = frames.GetCount() - 1; ~i; i--) + { + VdecFrame& vf = frames.Peek(i); + av_frame_unref(vf.data); + av_frame_free(&vf.data); + } + } + if (io_buf) + { + av_free(io_buf); + } if (fmt) { + if (fmt->pb) av_free(fmt->pb); avformat_free_context(fmt); } - if (frame) av_frame_free(&frame); - if (ctx) + if (!is_finished && ctx) { - avcodec_close(ctx); - av_free(ctx); + //avcodec_close(ctx); // crashes + //avformat_close_input(&fmt); } - //if (out_data[0]) av_freep(out_data[0]); } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp index 8073080424..586fa349d4 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp @@ -139,31 +139,34 @@ int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_treserved1 = 0; picInfo->reserved2 = 0; - u8* pY = (u8*)malloc(w*h); + u8* pY = (u8*)malloc(w*h); // color planes u8* pU = (u8*)malloc(w*h/4); u8* pV = (u8*)malloc(w*h/4); - u32* res = (u32*)malloc(w*h*4); + u32* res = (u32*)malloc(w*h*4); // RGBA interleaved output const u8 alpha = ctrlParam->outAlpha; if (!Memory.CopyToReal(pY, inPicBuff_addr, w*h)) { cellVpost.Error("cellVpostExec: data copying failed(pY)"); + Emu.Pause(); } if (!Memory.CopyToReal(pU, inPicBuff_addr + w*h, w*h/4)) { cellVpost.Error("cellVpostExec: data copying failed(pU)"); + Emu.Pause(); } if (!Memory.CopyToReal(pV, inPicBuff_addr + w*h + w*h/4, w*h/4)) { cellVpost.Error("cellVpostExec: data copying failed(pV)"); + Emu.Pause(); } for (u32 i = 0; i < h; i++) for (u32 j = 0; j < w; j++) { - float Cr = pV[(i/2)*(w/2)+j/2]; - float Cb = pU[(i/2)*(w/2)+j/2]; + float Cr = pV[(i/2)*(w/2)+j/2] - 128; + float Cb = pU[(i/2)*(w/2)+j/2] - 128; float Y = pY[i*w+j]; int R = Y + 1.5701f * Cr;