diff --git a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp index ebff0e2de6..d784a9524d 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp @@ -3,6 +3,8 @@ #include "Emu/SysCalls/SC_FUNC.h" #include "cellPamf.h" +extern SMutexGeneral g_mutex_avcodec_open2; + extern "C" { #include "libavcodec\avcodec.h" @@ -14,6 +16,33 @@ extern "C" void cellAdec_init(); Module cellAdec(0x0006, cellAdec_init); +int adecRead(void* opaque, u8* buf, int buf_size) +{ + AudioDecoder& adec = *(AudioDecoder*)opaque; + + if (adec.reader.size < (u32)buf_size) + { + buf_size = adec.reader.size; + } + + if (!buf_size) + { + return 0; + } + else if (!Memory.CopyToReal(buf, adec.reader.addr, buf_size)) + { + ConLog.Error("adecRead: data reading failed (buf_size=0x%x)", buf_size); + Emu.Pause(); + return 0; + } + else + { + adec.reader.addr += buf_size; + adec.reader.size -= buf_size; + return 0 + buf_size; + } +} + u32 adecOpen(AudioDecoder* data) { AudioDecoder& adec = *data; @@ -56,16 +85,205 @@ u32 adecOpen(AudioDecoder* data) { case adecStartSeq: { + // TODO: reset data + ConLog.Warning("adecStartSeq:"); + + adec.reader.addr = 0; + adec.reader.size = 0; + adec.is_running = true; + adec.just_started = true; } break; case adecEndSeq: { + // TODO: finalize + ConLog.Warning("adecEndSeq:"); + + Callback cb; + cb.SetAddr(adec.cbFunc); + cb.Handle(adec.id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, adec.cbArg); + cb.Branch(true); // ??? + + avcodec_close(adec.ctx); + avformat_close_input(&adec.fmt); + + adec.is_running = false; } break; case adecDecodeAu: { + int err; + + adec.reader.addr = task.au.addr; + adec.reader.size = task.au.size; + + u64 last_pts = task.au.pts; + + struct AVPacketHolder : AVPacket + { + 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; + dump.Open(wxString::Format("audio pts-0x%llx.dump", task.au.pts), wxFile::write); + u8* buf = (u8*)malloc(task.au.size); + if (Memory.CopyToReal(buf, task.au.addr, task.au.size)) dump.Write(buf, task.au.size); + free(buf); + dump.Close(); + } + + if (adec.just_started) // deferred initialization + { + err = avformat_open_input(&adec.fmt, NULL, NULL, NULL); + if (err) + { + ConLog.Error("adecDecodeAu: avformat_open_input() failed"); + Emu.Pause(); + break; + } + err = avformat_find_stream_info(adec.fmt, NULL); + if (err) + { + ConLog.Error("adecDecodeAu: avformat_find_stream_info() failed"); + Emu.Pause(); + break; + } + if (!adec.fmt->nb_streams) + { + ConLog.Error("adecDecodeAu: no stream found"); + Emu.Pause(); + break; + } + adec.ctx = adec.fmt->streams[0]->codec; // TODO: check data + + AVCodec* codec = avcodec_find_decoder(adec.ctx->codec_id); // ??? + if (!codec) + { + ConLog.Error("adecDecodeAu: avcodec_find_decoder() failed"); + Emu.Pause(); + break; + } + + AVDictionary* opts; + av_dict_set(&opts, "refcounted_frames", "1", 0); + { + SMutexGeneralLocker lock(g_mutex_avcodec_open2); + // not multithread-safe + err = avcodec_open2(adec.ctx, codec, &opts); + } + if (err) + { + ConLog.Error("adecDecodeAu: avcodec_open2() failed"); + Emu.Pause(); + break; + } + adec.just_started = false; + } + + while (av_read_frame(adec.fmt, &au) >= 0)*/ while (true) + { + if (!adec.ctx) // fake + { + AdecFrame frame; + frame.pts = task.au.pts; + frame.auAddr = task.au.addr; + frame.auSize = task.au.size; + frame.userdata = task.au.userdata; + frame.size = 2048; + frame.data = nullptr; + adec.frames.Push(frame); + + Callback cb; + cb.SetAddr(adec.cbFunc); + cb.Handle(adec.id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, adec.cbArg); + cb.Branch(false); + + break; + } + + struct VdecFrameHolder : AdecFrame + { + VdecFrameHolder() + { + data = av_frame_alloc(); + } + + ~VdecFrameHolder() + { + if (data) + { + av_frame_unref(data); + av_frame_free(&data); + } + } + + } frame; + + if (!frame.data) + { + ConLog.Error("adecDecodeAu: av_frame_alloc() failed"); + Emu.Pause(); + break; + } + + int got_frame = 0; + + int decode = avcodec_decode_audio4(adec.ctx, frame.data, &got_frame, &au); + + if (decode < 0) + { + ConLog.Error("adecDecodeAu: AU decoding error(0x%x)", decode); + break; + } + + if (got_frame) + { + ConLog.Write("got_frame (%d, vdec: pts=0x%llx, dts=0x%llx)", got_frame, au.pts, au.dts); + + frame.pts = task.au.pts; // ??? + frame.auAddr = task.au.addr; + frame.auSize = task.au.size; + frame.userdata = task.au.userdata; + frame.size = 32768; // ???? + adec.frames.Push(frame); + frame.data = nullptr; // to prevent destruction + + Callback cb; + cb.SetAddr(adec.cbFunc); + cb.Handle(adec.id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, adec.cbArg); + cb.Branch(false); + } + } + + Callback cb; + cb.SetAddr(adec.cbFunc); + cb.Handle(adec.id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, adec.cbArg); + cb.Branch(false); } break; @@ -78,9 +296,9 @@ u32 adecOpen(AudioDecoder* data) default: ConLog.Error("Audio Decoder error: unknown task(%d)", task.type); - return; } } + adec.is_finished = true; ConLog.Warning("Audio Decoder aborted"); }); @@ -196,31 +414,158 @@ int cellAdecClose(u32 handle) int cellAdecStartSeq(u32 handle, u32 param_addr) { - cellAdec.Error("cellAdecStartSeq(handle=%d, param_addr=0x%x)", handle, param_addr); + cellAdec.Log("cellAdecStartSeq(handle=%d, param_addr=0x%x)", handle, param_addr); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + AdecTask task(adecStartSeq); + /*if (adec->type == CELL_ADEC_TYPE_ATRACX_2CH) + { + + } + else*/ + { + cellAdec.Warning("cellAdecStartSeq: (TODO) initialization"); + } + + adec->job.Push(task); return CELL_OK; } int cellAdecEndSeq(u32 handle) { - cellAdec.Error("cellAdecEndSeq(handle=%d)", handle); + cellAdec.Warning("cellAdecEndSeq(handle=%d)", handle); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + adec->job.Push(AdecTask(adecEndSeq)); return CELL_OK; } int cellAdecDecodeAu(u32 handle, mem_ptr_t auInfo) { - cellAdec.Error("cellAdecDecodeAu(handle=%d, auInfo_addr=0x%x)", handle, auInfo.GetAddr()); + cellAdec.Log("cellAdecDecodeAu(handle=%d, auInfo_addr=0x%x)", handle, auInfo.GetAddr()); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + if (!auInfo.IsGood()) + { + return CELL_ADEC_ERROR_FATAL; + } + + AdecTask task(adecDecodeAu); + task.au.auInfo_addr = auInfo.GetAddr(); + task.au.addr = auInfo->startAddr; + task.au.size = auInfo->size; + task.au.pts = ((u64)auInfo->pts.upper << 32) | (u64)auInfo->pts.lower; + task.au.userdata = auInfo->userData; + + adec->job.Push(task); return CELL_OK; } int cellAdecGetPcm(u32 handle, u32 outBuffer_addr) { - cellAdec.Error("cellAdecGetPcm(handle=%d, outBuffer_addr=0x%x)", handle, outBuffer_addr); - return CELL_OK; + cellAdec.Log("cellAdecGetPcm(handle=%d, outBuffer_addr=0x%x)", handle, outBuffer_addr); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + if (adec->frames.IsEmpty()) + { + return CELL_ADEC_ERROR_EMPTY; + } + + AdecFrame af; + adec->frames.Pop(af); + //AVFrame& frame = *af.data; + + int result = CELL_OK; + + if (!Memory.IsGoodAddr(outBuffer_addr, af.size)) + { + result = CELL_ADEC_ERROR_FATAL; + } + else + { + // copy data + if (!af.data) // fake: empty data + { + return CELL_OK; + } + } + + if (af.data) + { + av_frame_unref(af.data); + av_frame_free(&af.data); + } + return result; } -int cellAdecGetPcmItem(u32 handle, u32 pcmItem_ptr_addr) +int cellAdecGetPcmItem(u32 handle, mem32_t pcmItem_ptr) { - cellAdec.Error("cellAdecGetPcmItem(handle=%d, pcmItem_ptr_addr=0x%x)", handle, pcmItem_ptr_addr); + cellAdec.Log("cellAdecGetPcmItem(handle=%d, pcmItem_ptr_addr=0x%x)", handle, pcmItem_ptr.GetAddr()); + + AudioDecoder* adec; + if (!Emu.GetIdManager().GetIDData(handle, adec)) + { + return CELL_ADEC_ERROR_ARG; + } + + if (!pcmItem_ptr.IsGood()) + { + return CELL_ADEC_ERROR_FATAL; + } + + AdecFrame& af = adec->frames.Peek(); + + if (adec->frames.IsEmpty()) + { + return CELL_ADEC_ERROR_EMPTY; + } + + //AVFrame& frame = *af.data; + + mem_ptr_t pcm(adec->memAddr + adec->memBias); + + adec->memBias += 512; + if (adec->memBias + 512 > adec->memSize) + adec->memBias = 0; + + pcm->pcmHandle = 0; // ??? + pcm->pcmAttr.bsiInfo_addr = pcm.GetAddr() + sizeof(CellAdecPcmItem); + pcm->startAddr = 0x00000312; // invalid address (no output) + pcm->size = af.size; + pcm->status = CELL_OK; + pcm->auInfo.pts.lower = af.pts; // ??? + pcm->auInfo.pts.upper = af.pts >> 32; + pcm->auInfo.size = af.auSize; + pcm->auInfo.startAddr = af.auAddr; + pcm->auInfo.userData = af.userdata; + + mem_ptr_t atx(pcm.GetAddr() + sizeof(CellAdecPcmItem)); + atx->samplingFreq = 48000; // ??? + atx->nbytes = 2048; // ??? + atx->channelConfigIndex = CELL_ADEC_CH_STEREO; // ??? + + pcmItem_ptr = pcm.GetAddr(); + return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellAdec.h b/rpcs3/Emu/SysCalls/Modules/cellAdec.h index 833042396c..50ace2eb07 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.h +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.h @@ -1002,7 +1002,17 @@ enum AdecJobType : u32 struct AdecTask { AdecJobType type; - // ... + union + { + struct + { + u32 auInfo_addr; + u32 addr; + u32 size; + u64 pts; + u64 userdata; + } au; + }; AdecTask(AdecJobType type) : type(type) @@ -1016,11 +1026,16 @@ struct AdecTask struct AdecFrame { - // under construction + AVFrame* data; u64 pts; u64 userdata; + u32 auAddr; + u32 auSize; + u32 size; }; +int adecRead(void* opaque, u8* buf, int buf_size); + class AudioDecoder { public: @@ -1028,6 +1043,17 @@ public: u32 id; volatile bool is_running; volatile bool is_finished; + bool just_started; + + AVCodecContext* ctx; + AVFormatContext* fmt; + u8* io_buf; + + struct AudioReader + { + u32 addr; + u32 size; + } reader; SQueue frames; @@ -1036,19 +1062,66 @@ public: const u32 memSize; const u32 cbFunc; const u32 cbArg; + u32 memBias; AudioDecoder(AudioCodecType type, u32 addr, u32 size, u32 func, u32 arg) : type(type) , memAddr(addr) , memSize(size) + , memBias(0) , cbFunc(func) , cbArg(arg) , is_running(false) , is_finished(false) + , just_started(false) + , ctx(nullptr) + , fmt(nullptr) { + AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P); + if (!codec) + { + ConLog.Error("AudioDecoder(): avcodec_find_decoder(ATRAC3P) failed"); + Emu.Pause(); + return; + } + fmt = avformat_alloc_context(); + if (!fmt) + { + ConLog.Error("AudioDecoder(): avformat_alloc_context failed"); + Emu.Pause(); + return; + } + io_buf = (u8*)av_malloc(4096); + fmt->pb = avio_alloc_context(io_buf, 4096, 0, this, adecRead, NULL, NULL); + if (!fmt->pb) + { + ConLog.Error("AudioDecoder(): avio_alloc_context failed"); + Emu.Pause(); + return; + } } ~AudioDecoder() { + if (ctx) + { + for (u32 i = frames.GetCount() - 1; ~i; i--) + { + AdecFrame& af = frames.Peek(i); + av_frame_unref(af.data); + av_frame_free(&af.data); + } + avcodec_close(ctx); + avformat_close_input(&fmt); + } + if (fmt) + { + if (io_buf) + { + av_free(io_buf); + } + if (fmt->pb) av_free(fmt->pb); + avformat_free_context(fmt); + } } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp index 4e1afc2923..326f2923c9 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp @@ -18,9 +18,9 @@ void dmuxQueryEsAttr(u32 info_addr /* may be 0 */, const mem_ptr_t attr) { if (esFilterId->filterIdMajor >= 0xe0) - attr->memSize = 0x6000000; // 0x45fa49 from ps3 + attr->memSize = 0x2000000; // 0x45fa49 from ps3 else - attr->memSize = 0x10000; // 0x73d9 from ps3 + attr->memSize = 0x400000; // 0x73d9 from ps3 cellDmux.Warning("*** filter(0x%x, 0x%x, 0x%x, 0x%x)", (u32)esFilterId->filterIdMajor, (u32)esFilterId->filterIdMinor, (u32)esFilterId->supplementalInfo1, (u32)esFilterId->supplementalInfo2); @@ -102,12 +102,51 @@ u32 dmuxOpen(Demuxer* data) case PRIVATE_STREAM_1: { + DemuxerStream backup = stream; + // audio AT3+ (and probably LPCM or user data) stream.skip(4); stream.get(len); - // skipping... - stream.skip(len); + PesHeader pes(stream); + + if (!pes.new_au) // temporarily + { + ConLog.Error("No pts info found"); + } + + // read additional header: + stream.peek(ch); + //stream.skip(4); + //pes.size += 4; + + if (esATX[ch]) + { + ElementaryStream& es = *esATX[ch]; + if (es.isfull()) + { + stream = backup; + Sleep(1); + continue; + } + + //ConLog.Write("*** AT3+ AU sent (pts=0x%llx, dts=0x%llx)", pes.pts, pes.dts); + + es.push(stream, len - pes.size - 3, pes); + es.finish(stream); + + mem_ptr_t esMsg(a128(dmux.memAddr) + (cb_add ^= 16)); + esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND; + esMsg->supplementalInfo = stream.userdata; + Callback cb; + cb.SetAddr(es.cbFunc); + cb.Handle(dmux.id, es.id, esMsg.GetAddr(), es.cbArg); + cb.Branch(false); + } + else + { + stream.skip(len - pes.size - 3); + } } break; @@ -133,12 +172,6 @@ u32 dmuxOpen(Demuxer* data) stream.get(len); PesHeader pes(stream); - if (!pes.new_au && !es.hasdata()) // fatal error - { - ConLog.Error("PES not found"); - return; - } - if (pes.new_au && es.hasdata()) // new AU detected { if (es.hasunseen()) // hack, probably useless @@ -285,6 +318,13 @@ task: { esAVC[es.fidMajor - 0xe0] = task.es.es_ptr; } + else if (es.fidMajor == 0xbd && + es.fidMinor == 0 && + es.sup1 == 0 && + es.sup2 == 0) + { + esATX[0] = task.es.es_ptr; + } else { ConLog.Warning("dmuxEnableEs: (TODO) unsupported filter (0x%x, 0x%x, 0x%x, 0x%x)", es.fidMajor, es.fidMinor, es.sup1, es.sup2); @@ -853,7 +893,7 @@ int cellDmuxPeekAuEx(u32 esHandle, mem32_t auInfoEx_ptr, mem32_t auSpecificInfo_ int cellDmuxReleaseAu(u32 esHandle) { - cellDmux.Warning("(disabled) cellDmuxReleaseAu(esHandle=0x%x)", esHandle); + cellDmux.Log("cellDmuxReleaseAu(esHandle=0x%x)", esHandle); return CELL_OK; diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.h b/rpcs3/Emu/SysCalls/Modules/cellDmux.h index cd417b5915..d03b4e7e00 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.h +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.h @@ -329,11 +329,11 @@ struct DemuxerStream } template - bool peek(T& out) + bool peek(T& out, u32 shift = 0) { - if (sizeof(T) > size) return false; + if (sizeof(T) + shift > size) return false; - out = *(T*)Memory.VirtualToRealAddr(addr); + out = *(T*)Memory.VirtualToRealAddr(addr + shift); return true; } @@ -364,14 +364,12 @@ struct PesHeader { u64 pts; u64 dts; - u8 ch; u8 size; bool new_au; PesHeader(DemuxerStream& stream) : pts(0xffffffffffffffff) , dts(0xffffffffffffffff) - , ch(0) , size(0) , new_au(false) { @@ -380,29 +378,40 @@ struct PesHeader stream.get(size); if (size) { - //ConLog.Write(">>>>> Pes Header (size=%d)", size); - if (size < 10) - { - stream.skip(size); - return; - } - new_au = true; + u8 empty = 0; u8 v; - stream.get(v); - if ((v & 0xF0) != 0x30) + while (true) { - ConLog.Error("Pts not found"); - Emu.Pause(); - } - pts = stream.get_ts(v); - stream.get(v); - if ((v & 0xF0) != 0x10) + stream.get(v); + if (v != 0xFF) break; // skip padding bytes + empty++; + if (empty = size) return; + }; + + if ((v & 0xF0) == 0x20 && (size - empty) >= 5) // pts only { - ConLog.Error("Dts not found"); - Emu.Pause(); + new_au = true; + pts = stream.get_ts(v); + stream.skip(size - empty - 5); + } + else + { + new_au = true; + if ((v & 0xF0) != 0x30 || (size - empty) < 10) + { + ConLog.Error("PesHeader(): pts not found"); + Emu.Pause(); + } + pts = stream.get_ts(v); + stream.get(v); + if ((v & 0xF0) != 0x10) + { + ConLog.Error("PesHeader(): dts not found"); + Emu.Pause(); + } + dts = stream.get_ts(v); + stream.skip(size - empty - 10); } - dts = stream.get_ts(v); - stream.skip(size - 10); } } }; @@ -497,8 +506,8 @@ public: ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, u32 cbFunc, u32 cbArg, u32 spec) : dmux(dmux) - , memAddr(addr) - , memSize(size) + , memAddr(a128(addr)) + , memSize(size - (addr - memAddr)) , fidMajor(fidMajor) , fidMinor(fidMinor) , sup1(sup1) @@ -508,7 +517,7 @@ public: , spec(spec) , first_addr(0) , peek_addr(0) - , last_addr(a128(addr)) + , last_addr(memAddr) , last_size(0) { } @@ -590,7 +599,7 @@ public: mem_ptr_t info(last_addr); info->auAddr = last_addr + 128; info->auSize = last_size; - if (pes.size) + if (pes.new_au) { info->dts.lower = (u32)pes.dts; info->dts.upper = (u32)(pes.dts >> 32); @@ -607,7 +616,7 @@ public: mem_ptr_t inf(last_addr + 64); inf->auAddr = last_addr + 128; inf->auSize = last_size; - if (pes.size) + if (pes.new_au) { inf->dtsLower = (u32)pes.dts; inf->dtsUpper = (u32)(pes.dts >> 32); @@ -687,7 +696,7 @@ public: SMutexLocker lock(mutex); first_addr = 0; peek_addr = 0; - last_addr = a128(memAddr); + last_addr = memAddr; last_size = 0; } }; diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp index 6333851bd1..f325f3faa3 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp @@ -3,6 +3,8 @@ #include "Emu/SysCalls/SC_FUNC.h" #include "cellPamf.h" +SMutexGeneral g_mutex_avcodec_open2; + extern "C" { #include "libavcodec\avcodec.h" @@ -21,35 +23,56 @@ int vdecRead(void* opaque, u8* buf, int buf_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; + while (vdec.job.IsEmpty()) + { + if (Emu.IsStopped()) + { + ConLog.Warning("vdecRead() aborted"); + return 0; + } + Sleep(1); + } + + switch (vdec.job.Peek().type) + { + case vdecEndSeq: + { + buf_size = vdec.reader.size; + } + break; + case vdecDecodeAu: + { + if (!Memory.CopyToReal(buf, vdec.reader.addr, vdec.reader.size)) + { + ConLog.Error("vdecRead: data reading failed (reader.size=0x%x)", vdec.reader.size); + Emu.Pause(); + return 0; + } + + buf += vdec.reader.size; + buf_size -= vdec.reader.size; + res += vdec.reader.size; + + Callback cb; + cb.SetAddr(vdec.cbFunc); + cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg); + cb.Branch(false); + + vdec.job.Pop(vdec.task); + + vdec.reader.addr = vdec.task.addr; + vdec.reader.size = vdec.task.size; + + vdec.last_pts = vdec.task.pts; + vdec.last_dts = vdec.task.dts; + } + break; + default: + ConLog.Error("vdecRead(): sequence error (task %d)", vdec.job.Peek().type); + return 0; + } } if (!buf_size) @@ -60,7 +83,7 @@ int vdecRead(void* opaque, u8* buf, int buf_size) { ConLog.Error("vdecRead: data reading failed (buf_size=0x%x)", buf_size); Emu.Pause(); - return res; + return 0; } else { @@ -100,7 +123,7 @@ u32 vdecOpen(VideoDecoder* data) { ConLog.Write("Video Decoder enter()"); - VdecTask task; + VdecTask& task = vdec.task; while (true) { @@ -142,11 +165,12 @@ u32 vdecOpen(VideoDecoder* data) case vdecEndSeq: { + // TODO: finalize ConLog.Warning("vdecEndSeq:"); Callback cb; cb.SetAddr(vdec.cbFunc); - cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, 0, vdec.cbArg); + cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, vdec.cbArg); cb.Branch(true); // ??? avcodec_close(vdec.ctx); @@ -169,7 +193,8 @@ u32 vdecOpen(VideoDecoder* data) vdec.reader.addr = task.addr; vdec.reader.size = task.size; - u64 last_pts = task.pts, last_dts = task.dts; + vdec.last_pts = task.pts; + vdec.last_dts = task.dts; struct AVPacketHolder : AVPacket { @@ -198,13 +223,6 @@ u32 vdecOpen(VideoDecoder* data) } au(0); - /*{ - wxFile dump; - dump.Open(wxString::Format("0x%llx-0x%llx.dump", au.pts, au.dts), wxFile::write); - dump.Write(au.data, task.size + FF_INPUT_BUFFER_PADDING_SIZE); - dump.Close(); - }*/ - if (vdec.just_started) // deferred initialization { err = avformat_open_input(&vdec.fmt, NULL, NULL, NULL); @@ -212,20 +230,20 @@ u32 vdecOpen(VideoDecoder* data) { ConLog.Error("vdecDecodeAu: avformat_open_input() failed"); Emu.Pause(); - return; + break; } err = avformat_find_stream_info(vdec.fmt, NULL); if (err) { ConLog.Error("vdecDecodeAu: avformat_find_stream_info() failed"); Emu.Pause(); - return; + break; } if (!vdec.fmt->nb_streams) { ConLog.Error("vdecDecodeAu: no stream found"); Emu.Pause(); - return; + break; } vdec.ctx = vdec.fmt->streams[0]->codec; // TODO: check data @@ -234,26 +252,37 @@ u32 vdecOpen(VideoDecoder* data) { ConLog.Error("vdecDecodeAu: avcodec_find_decoder() failed"); Emu.Pause(); - return; + break; } + AVDictionary* opts = nullptr; + av_dict_set(&opts, "refcounted_frames", "1", 0); { - static SMutexGeneral g_mutex_avcodec_open2; SMutexGeneralLocker lock(g_mutex_avcodec_open2); // not multithread-safe - err = avcodec_open2(vdec.ctx, codec, &vdec.opts); + err = avcodec_open2(vdec.ctx, codec, &opts); } if (err) { ConLog.Error("vdecDecodeAu: avcodec_open2() failed"); Emu.Pause(); - return; + break; } vdec.just_started = false; } - while (av_read_frame(vdec.fmt, &au) >= 0) + bool last_frame = false; + + while (true) { + last_frame = av_read_frame(vdec.fmt, &au) < 0; + if (last_frame) + { + av_free(au.data); + au.data = NULL; + au.size = 0; + } + struct VdecFrameHolder : VdecFrame { VdecFrameHolder() @@ -276,41 +305,43 @@ u32 vdecOpen(VideoDecoder* data) { ConLog.Error("vdecDecodeAu: av_frame_alloc() failed"); Emu.Pause(); - return; + break; } int got_picture = 0; int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &au); - if (decode < 0) + if (decode <= 0) { - ConLog.Error("vdecDecodeAu: AU decoding error(0x%x)", decode); - break; + if (!last_frame && decode < 0) + { + ConLog.Error("vdecDecodeAu: AU decoding error(0x%x)", decode); + break; + } + if (!got_picture && vdec.reader.size == 0) break; // video end? } if (got_picture) { - ConLog.Write("got_picture (%d, vdec: pts=0x%llx, dts=0x%llx)", got_picture, au.pts, au.dts); + //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.dts = vdec.last_dts; vdec.last_dts += 3003; // + duration??? + frame.pts = vdec.last_pts; vdec.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.Handle(vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, 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); - cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, 0, vdec.cbArg); + cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg); cb.Branch(false); } break; @@ -325,15 +356,15 @@ u32 vdecOpen(VideoDecoder* data) case vdecSetFrameRate: { ConLog.Error("TODO: vdecSetFrameRate(%d)", task.frc); - return; } + break; default: ConLog.Error("Video Decoder error: unknown task(%d)", task.type); - return; } } + vdec.is_finished = true; ConLog.Warning("Video Decoder aborted"); }); @@ -603,7 +634,11 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) AVFrame& frame = *vf.data; - mem_ptr_t info(vdec->memAddr); + mem_ptr_t info(vdec->memAddr + vdec->memBias); + + vdec->memBias += 512; + if (vdec->memBias + 512 > vdec->memSize) + vdec->memBias = 0; info->codecType = vdec->type; info->startAddr = 0x00000123; // invalid value (no address for picture) @@ -621,9 +656,9 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) info->auUserData[1] = 0; info->status = CELL_OK; info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL; - info->picInfo_addr = vdec->memAddr + sizeof(CellVdecPicItem); + info->picInfo_addr = info.GetAddr() + sizeof(CellVdecPicItem); - mem_ptr_t avc(vdec->memAddr + sizeof(CellVdecPicItem)); + mem_ptr_t avc(info.GetAddr() + sizeof(CellVdecPicItem)); avc->horizontalSize = frame.width; avc->verticalSize = frame.height; diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.h b/rpcs3/Emu/SysCalls/Modules/cellVdec.h index 8068b2e8b4..7542607d59 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.h +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.h @@ -699,7 +699,6 @@ public: bool just_started; AVCodecContext* ctx; - AVDictionary* opts; AVFormatContext* fmt; u8* io_buf; @@ -717,34 +716,31 @@ public: const u32 memSize; const u32 cbFunc; const u32 cbArg; + u32 memBias; + + VdecTask task; // reference to current task variable + u64 last_pts, last_dts; VideoDecoder(CellVdecCodecType type, u32 profile, u32 addr, u32 size, u32 func, u32 arg) : type(type) , profile(profile) , memAddr(addr) , memSize(size) + , memBias(0) , cbFunc(func) , cbArg(arg) , is_finished(false) , is_running(false) , just_started(false) + , ctx(nullptr) { - opts = nullptr; - av_dict_set(&opts, "refcounted_frames", "1", 0); AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!codec) { - ConLog.Error("vdecDecodeAu: avcodec_find_decoder(H264) failed"); + ConLog.Error("VideoDecoder(): avcodec_find_decoder(H264) failed"); Emu.Pause(); return; } - ctx = nullptr; /*avcodec_alloc_context3(codec); - if (!ctx) - { - ConLog.Error("VideoDecoder(): avcodec_alloc_context3 failed"); - Emu.Pause(); - return; - }*/ fmt = avformat_alloc_context(); if (!fmt) { @@ -764,7 +760,7 @@ public: ~VideoDecoder() { - if (!is_finished && ctx) + if (ctx) { for (u32 i = frames.GetCount() - 1; ~i; i--) { @@ -772,20 +768,17 @@ public: av_frame_unref(vf.data); av_frame_free(&vf.data); } - } - if (io_buf) - { - av_free(io_buf); + avcodec_close(ctx); + avformat_close_input(&fmt); } if (fmt) { + if (io_buf) + { + av_free(io_buf); + } if (fmt->pb) av_free(fmt->pb); avformat_free_context(fmt); } - if (!is_finished && ctx) - { - //avcodec_close(ctx); // crashes - //avformat_close_input(&fmt); - } } }; \ No newline at end of file diff --git a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp index 586fa349d4..92ead9c6a4 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVpost.cpp @@ -77,7 +77,7 @@ int cellVpostClose(u32 handle) int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t ctrlParam, u32 outPicBuff_addr, mem_ptr_t picInfo) { - cellVpost.Warning("cellVpostExec(handle=0x%x, inPicBuff_addr=0x%x, ctrlParam_addr=0x%x, outPicBuff_addr=0x%x, picInfo_addr=0x%x)", + cellVpost.Log("cellVpostExec(handle=0x%x, inPicBuff_addr=0x%x, ctrlParam_addr=0x%x, outPicBuff_addr=0x%x, picInfo_addr=0x%x)", handle, inPicBuff_addr, ctrlParam.GetAddr(), outPicBuff_addr, picInfo.GetAddr()); VpostInstance* vpost;