1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 04:02:42 +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)
{
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];
}
};

View File

@ -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<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;
}
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());
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<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());
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<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;
}
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();
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "cellPamf.h"
#include "Utilities/SQueue.h"
// Error Codes
enum
@ -341,6 +342,17 @@ struct CellAdecResource
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
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<int (*)(u32 handle, CellAdecMsgType msgType, int msgData, u32 cbArg)> CellAdecCbMsg;
struct CellAdecCb
{
be_t<mem_func_ptr_t<int (*)(u32 handle, CellAdecMsgType msgType, int msgData, u32 cbArg_addr)>> cbFunc;
be_t<u32> cbArg_addr;
be_t<u32> cbFunc;
be_t<u32> cbArg;
};
typedef CellCodecTimeStamp CellAdecTimeStamp;
@ -399,17 +413,6 @@ struct CellAdecLpcmInfo
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
enum CELP_ExcitationMode
{
@ -985,3 +988,67 @@ struct CellAdecMpmcInfo
be_t<u32> lfePresent;
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)
{
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
{

View File

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

View File

@ -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<CellVd
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;
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;
}
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<CellVdecPicFormat> 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<CellVdecPicItem> 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<CellVdecAvcInfo> 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();
}

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);
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<VdecFrame> 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]);
}
};

View File

@ -139,31 +139,34 @@ int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpos
picInfo->reserved1 = 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;