1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 12:12:50 +01:00

cellAdec draft

This commit is contained in:
Nekotekina 2014-03-03 03:02:42 +04:00
parent c978fe377d
commit c064c701e2
8 changed files with 548 additions and 199 deletions

View File

@ -19,11 +19,6 @@ public:
{ {
while (true) while (true)
{ {
if (Emu.IsStopped())
{
return false;
}
if (m_mutex.GetOwner() == m_mutex.GetDeadValue()) if (m_mutex.GetOwner() == m_mutex.GetDeadValue())
{ {
return false; return false;
@ -31,6 +26,10 @@ public:
if (m_count >= SQSize) if (m_count >= SQSize)
{ {
if (Emu.IsStopped())
{
return false;
}
Sleep(1); Sleep(1);
continue; continue;
} }
@ -51,11 +50,6 @@ public:
{ {
while (true) while (true)
{ {
if (Emu.IsStopped())
{
return false;
}
if (m_mutex.GetOwner() == m_mutex.GetDeadValue()) if (m_mutex.GetOwner() == m_mutex.GetDeadValue())
{ {
return false; return false;
@ -63,6 +57,10 @@ public:
if (!m_count) if (!m_count)
{ {
if (Emu.IsStopped())
{
return false;
}
Sleep(1); Sleep(1);
continue; continue;
} }
@ -96,4 +94,10 @@ public:
SMutexLocker lock(m_mutex); SMutexLocker lock(m_mutex);
m_count = 0; m_count = 0;
} }
T& Peek(u32 pos = 0)
{
SMutexLocker lock(m_mutex);
return m_data[(m_pos + pos) % SQSize];
}
}; };

View File

@ -1,64 +1,226 @@
#include "stdafx.h" #include "stdafx.h"
#include "Emu/SysCalls/SysCalls.h" #include "Emu/SysCalls/SysCalls.h"
#include "Emu/SysCalls/SC_FUNC.h" #include "Emu/SysCalls/SC_FUNC.h"
#include "cellPamf.h"
extern "C"
{
#include "libavcodec\avcodec.h"
#include "libavformat\avformat.h"
}
#include "cellAdec.h" #include "cellAdec.h"
void cellAdec_init(); void cellAdec_init();
Module cellAdec(0x0006, 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<CellAdecType> type, mem_ptr_t<CellAdecAttr> attr) int cellAdecQueryAttr(mem_ptr_t<CellAdecType> type, mem_ptr_t<CellAdecAttr> 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; return CELL_OK;
} }
int cellAdecOpen(mem_ptr_t<CellAdecType> type, mem_ptr_t<CellAdecResource> res, mem_ptr_t<CellAdecCb> cb, mem32_t handle) int cellAdecOpen(mem_ptr_t<CellAdecType> type, mem_ptr_t<CellAdecResource> res, mem_ptr_t<CellAdecCb> 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()); 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; return CELL_OK;
} }
int cellAdecOpenEx(mem_ptr_t<CellAdecType> type, mem_ptr_t<CellAdecResourceEx> res, mem_ptr_t<CellAdecCb> cb, mem32_t handle) int cellAdecOpenEx(mem_ptr_t<CellAdecType> type, mem_ptr_t<CellAdecResourceEx> res, mem_ptr_t<CellAdecCb> 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()); 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; return CELL_OK;
} }
int cellAdecClose(u32 handle) 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; return CELL_OK;
} }
int cellAdecStartSeq(u32 handle, u32 param_addr) 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; return CELL_OK;
} }
int cellAdecEndSeq(u32 handle) int cellAdecEndSeq(u32 handle)
{ {
cellAdec.Error("cellAdecEndSeq(handle=0x%x)", handle); cellAdec.Error("cellAdecEndSeq(handle=%d)", handle);
return CELL_OK; return CELL_OK;
} }
int cellAdecDecodeAu(u32 handle, mem_ptr_t<CellAdecAuInfo> auInfo) int cellAdecDecodeAu(u32 handle, mem_ptr_t<CellAdecAuInfo> 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; return CELL_OK;
} }
int cellAdecGetPcm(u32 handle, u32 outBuffer_addr) 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; return CELL_OK;
} }
int cellAdecGetPcmItem(u32 handle, u32 pcmItem_ptr_addr) 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; return CELL_OK;
} }
@ -73,4 +235,7 @@ void cellAdec_init()
cellAdec.AddFunc(0x1529e506, cellAdecDecodeAu); cellAdec.AddFunc(0x1529e506, cellAdecDecodeAu);
cellAdec.AddFunc(0x97ff2af1, cellAdecGetPcm); cellAdec.AddFunc(0x97ff2af1, cellAdecGetPcm);
cellAdec.AddFunc(0xbd75f78b, cellAdecGetPcmItem); cellAdec.AddFunc(0xbd75f78b, cellAdecGetPcmItem);
av_register_all();
avcodec_register_all();
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "cellPamf.h"
#include "Utilities/SQueue.h"
// Error Codes // Error Codes
enum enum
@ -341,6 +342,17 @@ struct CellAdecResource
be_t<u32> ppuThreadStackSize; be_t<u32> ppuThreadStackSize;
}; };
struct CellAdecResourceEx
{
be_t<u32> totalMemSize;
be_t<u32> startAddr;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> spurs_addr;
u8 priority[8];
be_t<u32> maxContention;
};
// Callback Messages // Callback Messages
enum CellAdecMsgType enum CellAdecMsgType
{ {
@ -348,12 +360,14 @@ enum CellAdecMsgType
CELL_ADEC_MSG_TYPE_PCMOUT, CELL_ADEC_MSG_TYPE_PCMOUT,
CELL_ADEC_MSG_TYPE_ERROR, CELL_ADEC_MSG_TYPE_ERROR,
CELL_ADEC_MSG_TYPE_SEQDONE, CELL_ADEC_MSG_TYPE_SEQDONE,
}; };
typedef mem_func_ptr_t<int (*)(u32 handle, CellAdecMsgType msgType, int msgData, u32 cbArg)> CellAdecCbMsg;
struct CellAdecCb struct CellAdecCb
{ {
be_t<mem_func_ptr_t<int (*)(u32 handle, CellAdecMsgType msgType, int msgData, u32 cbArg_addr)>> cbFunc; be_t<u32> cbFunc;
be_t<u32> cbArg_addr; be_t<u32> cbArg;
}; };
typedef CellCodecTimeStamp CellAdecTimeStamp; typedef CellCodecTimeStamp CellAdecTimeStamp;
@ -399,17 +413,6 @@ struct CellAdecLpcmInfo
be_t<u32> outputDataSize; be_t<u32> outputDataSize;
}; };
struct CellAdecResourceEx
{
be_t<u32> totalMemSize;
be_t<u32> startAddr;
be_t<u32> ppuThreadPriority;
be_t<u32> ppuThreadStackSize;
be_t<u32> spurs_addr;
u8 priority[8];
be_t<u32> maxContention;
};
// CELP Excitation Mode // CELP Excitation Mode
enum CELP_ExcitationMode enum CELP_ExcitationMode
{ {
@ -985,3 +988,67 @@ struct CellAdecMpmcInfo
be_t<u32> lfePresent; be_t<u32> lfePresent;
be_t<u32> channelCoufiguration; be_t<u32> 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<AdecTask> job;
u32 id;
volatile bool is_running;
volatile bool is_finished;
SQueue<AdecFrame> 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()
{
}
};

View File

@ -18,7 +18,7 @@ void dmuxQueryEsAttr(u32 info_addr /* may be 0 */, const mem_ptr_t<CellCodecEsFi
const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> attr) const u32 esSpecificInfo_addr, mem_ptr_t<CellDmuxEsAttr> attr)
{ {
if (esFilterId->filterIdMajor >= 0xe0) if (esFilterId->filterIdMajor >= 0xe0)
attr->memSize = 0x600000; // 0x45fa49 from ps3 attr->memSize = 0x6000000; // 0x45fa49 from ps3
else else
attr->memSize = 0x10000; // 0x73d9 from ps3 attr->memSize = 0x10000; // 0x73d9 from ps3
@ -159,7 +159,7 @@ u32 dmuxOpen(Demuxer* data)
if (pes.new_au) 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()) if (es.isfull())
@ -167,8 +167,10 @@ u32 dmuxOpen(Demuxer* data)
stream = backup; stream = backup;
continue; 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 else
{ {

View File

@ -373,7 +373,7 @@ struct PesHeader
, dts(0xffffffffffffffff) , dts(0xffffffffffffffff)
, ch(0) , ch(0)
, size(0) , size(0)
, new_au(true) , new_au(false)
{ {
u16 header; u16 header;
stream.get(header); stream.get(header);

View File

@ -19,22 +19,54 @@ int vdecRead(void* opaque, u8* buf, int buf_size)
{ {
VideoDecoder& vdec = *(VideoDecoder*)opaque; 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) if (!buf_size)
{ {
return AVERROR_EOF; return res;
} }
else if (!Memory.CopyToReal(buf, vdec.reader.addr, buf_size)) else if (!Memory.CopyToReal(buf, vdec.reader.addr, buf_size))
{ {
ConLog.Error("vdecRead: data reading failed (buf_size=0x%x)", buf_size); ConLog.Error("vdecRead: data reading failed (buf_size=0x%x)", buf_size);
Emu.Pause(); Emu.Pause();
return 0; return res;
} }
else else
{ {
vdec.reader.addr += buf_size; vdec.reader.addr += buf_size;
vdec.reader.size -= 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) if (vdec.job.IsEmpty() && vdec.is_running)
{ {
// TODO: default task (not needed?)
Sleep(1); Sleep(1);
continue; continue;
} }
if (vdec.has_picture) // hack if (vdec.frames.GetCount() >= 50)
{ {
Sleep(1); Sleep(1);
continue; continue;
@ -100,55 +131,34 @@ u32 vdecOpen(VideoDecoder* data)
case vdecStartSeq: case vdecStartSeq:
{ {
// TODO: reset data // TODO: reset data
ConLog.Warning("vdecStartSeq()"); ConLog.Warning("vdecStartSeq:");
vdec.reader.addr = 0;
vdec.reader.size = 0;
vdec.is_running = true; vdec.is_running = true;
vdec.just_started = true;
} }
break; break;
case vdecEndSeq: case vdecEndSeq:
{ {
ConLog.Warning("vdecEndSeq:");
Callback cb; Callback cb;
cb.SetAddr(vdec.cbFunc); 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, 0, vdec.cbArg);
cb.Branch(false); cb.Branch(true); // ???
ConLog.Warning("vdecEndSeq()");
avcodec_close(vdec.ctx);
avformat_close_input(&vdec.fmt);
vdec.is_running = false; vdec.is_running = false;
} }
break; break;
case vdecDecodeAu: case vdecDecodeAu:
{ {
struct vdecPacket : AVPacket int err;
{
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;
}
if (task.mode != CELL_VDEC_DEC_MODE_NORMAL) if (task.mode != CELL_VDEC_DEC_MODE_NORMAL)
{ {
@ -159,11 +169,34 @@ u32 vdecOpen(VideoDecoder* data)
vdec.reader.addr = task.addr; vdec.reader.addr = task.addr;
vdec.reader.size = task.size; 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); AVPacketHolder(u32 size)
break; {
} 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; wxFile dump;
@ -172,54 +205,108 @@ u32 vdecOpen(VideoDecoder* data)
dump.Close(); dump.Close();
}*/ }*/
int got_picture = 0; if (vdec.just_started) // deferred initialization
//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)
{ {
ConLog.Error("vdecDecodeAu: AU decoding error(%d)", decode); err = avformat_open_input(&vdec.fmt, NULL, NULL, NULL);
break; if (err)
}
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)
{ {
ConLog.Error("vdecDecodeAu: av_image_alloc failed(%d)", err); ConLog.Error("vdecDecodeAu: avformat_open_input() failed");
Emu.Pause(); Emu.Pause();
return; 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);*/ static SMutexGeneral g_mutex_avcodec_open2;
vdec.buf_size = a128(av_image_get_buffer_size(vdec.ctx->pix_fmt, vdec.ctx->width, vdec.ctx->height, 1)); SMutexGeneralLocker lock(g_mutex_avcodec_open2);
// not multithread-safe
vdec.userdata = task.userData; err = avcodec_open2(vdec.ctx, codec, &vdec.opts);
vdec.has_picture = true; }
if (err)
Callback cb; {
cb.SetAddr(vdec.cbFunc); ConLog.Error("vdecDecodeAu: avcodec_open2() failed");
cb.Handle(vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, 0, vdec.cbArg); Emu.Pause();
cb.Branch(false); 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; Callback cb;
cb.SetAddr(vdec.cbFunc); cb.SetAddr(vdec.cbFunc);
@ -331,7 +418,7 @@ int cellVdecClose(u32 handle)
vdec->job.Push(VdecTask(vdecClose)); vdec->job.Push(VdecTask(vdecClose));
while (!vdec->is_finished) while (!vdec->is_finished || !vdec->frames.IsEmpty())
{ {
if (Emu.IsStopped()) if (Emu.IsStopped())
{ {
@ -361,7 +448,7 @@ int cellVdecStartSeq(u32 handle)
int cellVdecEndSeq(u32 handle) int cellVdecEndSeq(u32 handle)
{ {
cellVdec.Log("cellVdecEndSeq(handle=%d)", handle); cellVdec.Warning("cellVdecEndSeq(handle=%d)", handle);
VideoDecoder* vdec; VideoDecoder* vdec;
if (!Emu.GetIdManager().GetIDData(handle, vdec)) if (!Emu.GetIdManager().GetIDData(handle, vdec))
@ -369,6 +456,28 @@ int cellVdecEndSeq(u32 handle)
return CELL_VDEC_ERROR_ARG; 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)); vdec->job.Push(VdecTask(vdecEndSeq));
return CELL_OK; return CELL_OK;
} }
@ -399,7 +508,7 @@ int cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, const mem_ptr_t<CellVd
int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> format, u32 out_addr) int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> 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; VideoDecoder* vdec;
if (!Emu.GetIdManager().GetIDData(handle, vdec)) if (!Emu.GetIdManager().GetIDData(handle, vdec))
@ -412,14 +521,16 @@ int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> format, u3
return CELL_VDEC_ERROR_FATAL; return CELL_VDEC_ERROR_FATAL;
} }
if (!vdec->has_picture) if (vdec->frames.IsEmpty())
{ {
return CELL_VDEC_ERROR_EMPTY; return CELL_VDEC_ERROR_EMPTY;
} }
if (out_addr) 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; return CELL_VDEC_ERROR_FATAL;
} }
@ -429,54 +540,47 @@ int cellVdecGetPicture(u32 handle, const mem_ptr_t<CellVdecPicFormat> format, u3
cellVdec.Error("cellVdecGetPicture: TODO: unknown formatType(%d)", (u32)format->formatType); cellVdec.Error("cellVdecGetPicture: TODO: unknown formatType(%d)", (u32)format->formatType);
return CELL_OK; return CELL_OK;
} }
if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709) if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709)
{ {
cellVdec.Error("cellVdecGetPicture: TODO: unknown colorMatrixType(%d)", (u32)format->colorMatrixType); cellVdec.Error("cellVdecGetPicture: TODO: unknown colorMatrixType(%d)", (u32)format->colorMatrixType);
return CELL_OK; return CELL_OK;
} }
AVFrame& frame = *vdec->frame; VdecFrame vf;
u8* buf = (u8*)malloc(vdec->buf_size); vdec->frames.Pop(vf);
if (!buf)
{ AVFrame& frame = *vf.data;
cellVdec.Error("cellVdecGetPicture: malloc failed (out of memory)");
Emu.Pause(); u8* buf = (u8*)malloc(buf_size);
return CELL_OK;
}
// TODO: zero padding bytes // 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) if (err < 0)
{ {
cellVdec.Error("cellVdecGetPicture: av_image_copy_to_buffer failed(%d)", err); cellVdec.Error("cellVdecGetPicture: av_image_copy_to_buffer failed(%d)", err);
Emu.Pause(); 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"); cellVdec.Error("cellVdecGetPicture: data copying failed");
Emu.Pause(); Emu.Pause();
} }
/* av_frame_unref(vf.data);
u32 size0 = frame.linesize[0] * frame.height; av_frame_free(&vf.data);
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);
*/
free(buf); free(buf);
} }
vdec->has_picture = false;
return CELL_OK; return CELL_OK;
} }
int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr) 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; VideoDecoder* vdec;
if (!Emu.GetIdManager().GetIDData(handle, vdec)) if (!Emu.GetIdManager().GetIDData(handle, vdec))
@ -489,26 +593,31 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr)
return CELL_VDEC_ERROR_FATAL; 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; return CELL_VDEC_ERROR_EMPTY;
} }
AVFrame& frame = *vf.data;
mem_ptr_t<CellVdecPicItem> info(vdec->memAddr); mem_ptr_t<CellVdecPicItem> info(vdec->memAddr);
info->codecType = vdec->type; info->codecType = vdec->type;
info->startAddr = 0x00000123; // invalid value (no address for picture) 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->auNum = 1;
info->auPts[0].lower = vdec->pts; info->auPts[0].lower = vf.pts;
info->auPts[0].upper = vdec->pts >> 32; info->auPts[0].upper = vf.pts >> 32;
info->auPts[1].lower = 0xffffffff; info->auPts[1].lower = 0xffffffff;
info->auPts[1].upper = 0xffffffff; info->auPts[1].upper = 0xffffffff;
info->auDts[0].lower = vdec->dts; info->auDts[0].lower = vf.dts;
info->auDts[0].upper = vdec->dts >> 32; info->auDts[0].upper = vf.dts >> 32;
info->auDts[1].lower = 0xffffffff; info->auDts[1].lower = 0xffffffff;
info->auDts[1].upper = 0xffffffff; info->auDts[1].upper = 0xffffffff;
info->auUserData[0] = vdec->userdata; info->auUserData[0] = vf.userdata;
info->auUserData[1] = 0; info->auUserData[1] = 0;
info->status = CELL_OK; info->status = CELL_OK;
info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL; info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL;
@ -516,9 +625,9 @@ int cellVdecGetPicItem(u32 handle, mem32_t picItem_ptr)
mem_ptr_t<CellVdecAvcInfo> avc(vdec->memAddr + sizeof(CellVdecPicItem)); mem_ptr_t<CellVdecAvcInfo> avc(vdec->memAddr + sizeof(CellVdecPicItem));
avc->horizontalSize = vdec->frame->width; // ??? avc->horizontalSize = frame.width;
avc->verticalSize = vdec->frame->height; avc->verticalSize = frame.height;
switch (vdec->frame->pict_type) switch (frame.pict_type)
{ {
case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break; 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; 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(0x17c702b9, cellVdecGetPicItem);
cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate); cellVdec.AddFunc(0xe13ef6fc, cellVdecSetFrameRate);
av_register_all();
avcodec_register_all(); avcodec_register_all();
} }

View File

@ -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); int vdecRead(void* opaque, u8* buf, int buf_size);
class VideoDecoder class VideoDecoder
@ -688,19 +696,12 @@ public:
u32 id; u32 id;
volatile bool is_running; volatile bool is_running;
volatile bool is_finished; volatile bool is_finished;
bool just_started;
AVCodec* codec;
AVCodecContext* ctx; AVCodecContext* ctx;
AVFormatContext* fmt;
AVFrame* frame;
AVDictionary* opts; AVDictionary* opts;
AVFormatContext* fmt;
u8* io_buf; u8* io_buf;
u32 buf_size;
u64 pts;
u64 dts;
u64 pos;
u64 userdata;
volatile bool has_picture;
struct VideoReader struct VideoReader
{ {
@ -708,6 +709,8 @@ public:
u32 size; u32 size;
} reader; } reader;
SQueue<VdecFrame> frames;
const CellVdecCodecType type; const CellVdecCodecType type;
const u32 profile; const u32 profile;
const u32 memAddr; const u32 memAddr;
@ -724,38 +727,24 @@ public:
, cbArg(arg) , cbArg(arg)
, is_finished(false) , is_finished(false)
, is_running(false) , is_running(false)
, has_picture(false) , just_started(false)
, pos(0)
{ {
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) if (!codec)
{ {
ConLog.Error("VideoDecoder(): avcodec_find_decoder failed"); ConLog.Error("vdecDecodeAu: avcodec_find_decoder(H264) failed");
Emu.Pause(); Emu.Pause();
return; return;
} }
ctx = avcodec_alloc_context3(codec); ctx = nullptr; /*avcodec_alloc_context3(codec);
if (!ctx) if (!ctx)
{ {
ConLog.Error("VideoDecoder(): avcodec_alloc_context3 failed"); ConLog.Error("VideoDecoder(): avcodec_alloc_context3 failed");
Emu.Pause(); Emu.Pause();
return; 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(); fmt = avformat_alloc_context();
if (!fmt) if (!fmt)
{ {
@ -771,23 +760,32 @@ public:
Emu.Pause(); Emu.Pause();
return; return;
} }
//memset(&out_data, 0, sizeof(out_data));
//memset(&linesize, 0, sizeof(linesize));
} }
~VideoDecoder() ~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)
{ {
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt); avformat_free_context(fmt);
} }
if (frame) av_frame_free(&frame); if (!is_finished && ctx)
if (ctx)
{ {
avcodec_close(ctx); //avcodec_close(ctx); // crashes
av_free(ctx); //avformat_close_input(&fmt);
} }
//if (out_data[0]) av_freep(out_data[0]);
} }
}; };

View File

@ -139,31 +139,34 @@ int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpos
picInfo->reserved1 = 0; picInfo->reserved1 = 0;
picInfo->reserved2 = 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* pU = (u8*)malloc(w*h/4);
u8* pV = (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; const u8 alpha = ctrlParam->outAlpha;
if (!Memory.CopyToReal(pY, inPicBuff_addr, w*h)) if (!Memory.CopyToReal(pY, inPicBuff_addr, w*h))
{ {
cellVpost.Error("cellVpostExec: data copying failed(pY)"); cellVpost.Error("cellVpostExec: data copying failed(pY)");
Emu.Pause();
} }
if (!Memory.CopyToReal(pU, inPicBuff_addr + w*h, w*h/4)) if (!Memory.CopyToReal(pU, inPicBuff_addr + w*h, w*h/4))
{ {
cellVpost.Error("cellVpostExec: data copying failed(pU)"); cellVpost.Error("cellVpostExec: data copying failed(pU)");
Emu.Pause();
} }
if (!Memory.CopyToReal(pV, inPicBuff_addr + w*h + w*h/4, w*h/4)) if (!Memory.CopyToReal(pV, inPicBuff_addr + w*h + w*h/4, w*h/4))
{ {
cellVpost.Error("cellVpostExec: data copying failed(pV)"); cellVpost.Error("cellVpostExec: data copying failed(pV)");
Emu.Pause();
} }
for (u32 i = 0; i < h; i++) for (u32 j = 0; j < w; j++) for (u32 i = 0; i < h; i++) for (u32 j = 0; j < w; j++)
{ {
float Cr = pV[(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]; float Cb = pU[(i/2)*(w/2)+j/2] - 128;
float Y = pY[i*w+j]; float Y = pY[i*w+j];
int R = Y + 1.5701f * Cr; int R = Y + 1.5701f * Cr;