It can play video, but it can't

I don't know how to disable aggressive data caching that occures in
vdecRead(). Also ReleaseAu function is disabled because it breaks
everything.
This commit is contained in:
Nekotekina 2014-03-04 03:21:34 +04:00
parent c064c701e2
commit 8a4c67deab
7 changed files with 630 additions and 135 deletions

View file

@ -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<CellAdecAuInfo> 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<CellAdecPcmItem> 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<CellAdecAtracXInfo> atx(pcm.GetAddr() + sizeof(CellAdecPcmItem));
atx->samplingFreq = 48000; // ???
atx->nbytes = 2048; // ???
atx->channelConfigIndex = CELL_ADEC_CH_STEREO; // ???
pcmItem_ptr = pcm.GetAddr();
return CELL_OK;
}

View file

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

View file

@ -18,9 +18,9 @@ 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 = 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<CellDmuxEsMsg> 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;

View file

@ -329,11 +329,11 @@ struct DemuxerStream
}
template<typename T>
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<CellDmuxAuInfoEx> 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<CellDmuxAuInfo> 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;
}
};

View file

@ -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<CellVdecPicItem> info(vdec->memAddr);
mem_ptr_t<CellVdecPicItem> 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<CellVdecAvcInfo> avc(vdec->memAddr + sizeof(CellVdecPicItem));
mem_ptr_t<CellVdecAvcInfo> avc(info.GetAddr() + sizeof(CellVdecPicItem));
avc->horizontalSize = frame.width;
avc->verticalSize = frame.height;

View file

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

View file

@ -77,7 +77,7 @@ int cellVpostClose(u32 handle)
int cellVpostExec(u32 handle, const u32 inPicBuff_addr, const mem_ptr_t<CellVpostCtrlParam> ctrlParam,
u32 outPicBuff_addr, mem_ptr_t<CellVpostPictureInfo> 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;