#include "stdafx.h" #include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SC_FUNC.h" #include "cellPamf.h" extern "C" { #include "libavcodec\avcodec.h" } #include "cellVdec.h" void cellVdec_init(); Module cellVdec(0x0005, cellVdec_init); u32 vdecQueryAttr(CellVdecCodecType type, u32 profile, u32 spec_addr /* may be 0 */, mem_ptr_t attr) { switch (type) // TODO: check profile levels { case CELL_VDEC_CODEC_TYPE_AVC: cellVdec.Warning("cellVdecQueryAttr: AVC (profile=%d)", profile); break; case CELL_VDEC_CODEC_TYPE_MPEG2: cellVdec.Error("TODO: MPEG2 not supported"); break; case CELL_VDEC_CODEC_TYPE_DIVX: cellVdec.Error("TODO: DIVX not supported"); break; default: return CELL_VDEC_ERROR_ARG; } // TODO: check values attr->decoderVerLower = 0x280000; // from dmux attr->decoderVerUpper = 0x260000; attr->memSize = 64 * 1024 * 1024; attr->cmdDepth = 15; return CELL_OK; } u32 vdecOpen(VideoDecoder* data) { VideoDecoder& vdec = *data; u32 vdec_id = cellVdec.GetNewId(data); vdec.id = vdec_id; thread t("Video Decoder[" + std::to_string(vdec_id) + "] Thread", [&]() { ConLog.Write("Video Decoder enter()"); VdecTask task; while (true) { if (Emu.IsStopped()) { break; } if (vdec.job.IsEmpty() && vdec.is_running) { // TODO: default task (not needed?) Sleep(1); continue; } if (!vdec.job.Pop(task)) { break; } switch (task.type) { case vdecStartSeq: { // TODO: reset data ConLog.Warning("vdecStartSeq()"); vdec.is_running = true; } break; case vdecEndSeq: { // TODO: send callback ConLog.Warning("vdecEndSeq()"); vdec.is_running = false; } break; case vdecDecodeAu: { struct vdecPacket : AVPacket { vdecPacket(u32 size) { av_new_packet(this, size + FF_INPUT_BUFFER_PADDING_SIZE); memset(data + size, 0, FF_INPUT_BUFFER_PADDING_SIZE); } ~vdecPacket() { av_free_packet(this); } } au(task.size); au.pts = task.pts; au.dts = task.dts; if (task.mode != CELL_VDEC_DEC_MODE_NORMAL) { ConLog.Error("vdecDecodeAu: unsupported decoding mode(%d)", task.mode); break; } if (!Memory.CopyToReal(au.data, task.addr, task.size)) { ConLog.Error("vdecDecodeAu: AU data accessing failed(addr=0x%x, size=0x%x)", task.addr, task.size); break; } int got_picture = 0; int decode = avcodec_decode_video2(vdec.ctx, vdec.frame, &got_picture, &au); if (decode < 0) { ConLog.Error("vdecDecodeAu: AU decoding error(%d)", decode); break; } ConLog.Write("Frame decoded (%d)", decode); } break; case vdecClose: { vdec.is_finished = true; ConLog.Write("Video Decoder exit"); return; } case vdecSetFrameRate: { ConLog.Error("TODO: vdecSetFrameRate(%d)", task.frc); } default: ConLog.Error("Video Decoder error: unknown task(%d)", task.type); return; } } ConLog.Warning("Video Decoder aborted"); }); t.detach(); return vdec_id; } int cellVdecQueryAttr(const mem_ptr_t type, mem_ptr_t attr) { cellVdec.Warning("cellVdecQueryAttr(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr()); if (!type.IsGood() || !attr.IsGood()) { return CELL_VDEC_ERROR_FATAL; } return vdecQueryAttr(type->codecType, type->profileLevel, 0, attr); } int cellVdecQueryAttrEx(const mem_ptr_t type, mem_ptr_t attr) { cellVdec.Warning("cellVdecQueryAttrEx(type_addr=0x%x, attr_addr=0x%x)", type.GetAddr(), attr.GetAddr()); if (!type.IsGood() || !attr.IsGood()) { return CELL_VDEC_ERROR_FATAL; } return vdecQueryAttr(type->codecType, type->profileLevel, type->codecSpecificInfo_addr, attr); } int cellVdecOpen(const mem_ptr_t type, const mem_ptr_t res, const mem_ptr_t cb, mem32_t handle) { cellVdec.Warning("cellVdecOpen(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_VDEC_ERROR_FATAL; } if (!Memory.IsGoodAddr(res->memAddr, res->memSize) || !Memory.IsGoodAddr(cb->cbFunc)) { return CELL_VDEC_ERROR_FATAL; } handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg)); return CELL_OK; } int cellVdecOpenEx(const mem_ptr_t type, const mem_ptr_t res, const mem_ptr_t cb, mem32_t handle) { cellVdec.Warning("cellVdecOpenEx(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_VDEC_ERROR_FATAL; } if (!Memory.IsGoodAddr(res->memAddr, res->memSize) || !Memory.IsGoodAddr(cb->cbFunc)) { return CELL_VDEC_ERROR_FATAL; } handle = vdecOpen(new VideoDecoder(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg)); return CELL_OK; } int cellVdecClose(u32 handle) { cellVdec.Warning("cellVdecClose(handle=%d)", handle); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) { return CELL_VDEC_ERROR_ARG; } vdec->job.Push(VdecTask(vdecClose)); while (!vdec->is_finished) { if (Emu.IsStopped()) { ConLog.Warning("cellVdecClose(%d) aborted", handle); break; } Sleep(1); } Emu.GetIdManager().RemoveID(handle); return CELL_OK; } int cellVdecStartSeq(u32 handle) { cellVdec.Log("cellVdecStartSeq(handle=%d)", handle); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) { return CELL_VDEC_ERROR_ARG; } vdec->job.Push(VdecTask(vdecStartSeq)); return CELL_OK; } int cellVdecEndSeq(u32 handle) { cellVdec.Log("cellVdecEndSeq(handle=%d)", handle); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) { return CELL_VDEC_ERROR_ARG; } vdec->job.Push(VdecTask(vdecEndSeq)); return CELL_OK; } int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, const mem_ptr_t auInfo) { cellVdec.Log("cellVdecDecodeAu(handle=%d, mode=0x%x, auInfo_addr=0x%x)", handle, mode, auInfo.GetAddr()); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) { return CELL_VDEC_ERROR_ARG; } // TODO: check info VdecTask task(vdecDecodeAu); task.mode = mode; task.addr = auInfo->startAddr; task.size = auInfo->size; task.dts = (u64)auInfo->dts.lower | ((u64)auInfo->dts.upper << 32); task.pts = (u64)auInfo->pts.lower | ((u64)auInfo->pts.upper << 32); task.userData = auInfo->userData; task.specData = auInfo->codecSpecificData; vdec->job.Push(task); return CELL_OK; } int cellVdecGetPicture(u32 handle, const mem_ptr_t format, u32 out_addr) { cellVdec.Error("cellVdecGetPicture(handle=%d, format_addr=0x%x, out_addr=0x%x)", handle, format.GetAddr(), out_addr); return CELL_OK; } int cellVdecGetPicItem(u32 handle, const u32 picItem_ptr_addr) { cellVdec.Error("cellVdecGetPicItem(handle=%d, picItem_ptr_addr=0x%x)", handle, picItem_ptr_addr); return CELL_OK; } int cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc) { cellVdec.Log("cellVdecSetFrameRate(handle=%d, frc=0x%x)", handle, frc); VideoDecoder* vdec; if (!Emu.GetIdManager().GetIDData(handle, vdec)) { return CELL_VDEC_ERROR_ARG; } // TODO: check frc value and set frame rate VdecTask task(vdecSetFrameRate); task.frc = frc; vdec->job.Push(task); return CELL_OK; } void cellVdec_init() { cellVdec.AddFunc(0xff6f6ebe, cellVdecQueryAttr); cellVdec.AddFunc(0xc982a84a, cellVdecQueryAttrEx); cellVdec.AddFunc(0xb6bbcd5d, cellVdecOpen); cellVdec.AddFunc(0x0053e2d8, cellVdecOpenEx); cellVdec.AddFunc(0x16698e83, cellVdecClose); cellVdec.AddFunc(0xc757c2aa, cellVdecStartSeq); cellVdec.AddFunc(0x824433f0, cellVdecEndSeq); cellVdec.AddFunc(0x2bf4ddd2, cellVdecDecodeAu); cellVdec.AddFunc(0x807c861a, cellVdecGetPicture); cellVdec.AddFunc(0x17c702b9, cellVdecGetPicItem); cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate); avcodec_register_all(); }