Demuxer improved

This commit is contained in:
Nekotekina 2014-12-12 03:21:34 +03:00
parent 8557614322
commit 058eb9e709
5 changed files with 306 additions and 265 deletions

View file

@ -10,52 +10,96 @@
Module *cellDmux = nullptr;
#define DMUX_ERROR(...) { cellDmux->Error(__VA_ARGS__); Emu.Pause(); return; } // only for demuxer thread
PesHeader::PesHeader(DemuxerStream& stream)
: pts(0xffffffffffffffffull)
, dts(0xffffffffffffffffull)
: pts(CODEC_TS_INVALID)
, dts(CODEC_TS_INVALID)
, size(0)
, has_ts(false)
, is_ok(false)
{
u16 header;
stream.get(header);
stream.get(size);
if (size)
if (!stream.get(header))
{
DMUX_ERROR("PesHeader(): end of stream (header)");
}
if (!stream.get(size))
{
DMUX_ERROR("PesHeader(): end of stream (size)");
}
if (!stream.check(size))
{
DMUX_ERROR("PesHeader(): end of stream (size=%d)", size);
}
u8 pos = 0;
while (pos++ < size)
{
u8 empty = 0;
u8 v;
while (true)
if (!stream.get(v))
{
stream.get(v);
if (v != 0xFF) break; // skip padding bytes
empty++;
if (empty == size) return;
};
return; // should never occur
}
if ((v & 0xF0) == 0x20 && (size - empty) >= 5) // pts only
if (v == 0xff) // skip padding bytes
{
has_ts = true;
continue;
}
if ((v & 0xf0) == 0x20 && (size - pos) >= 4) // pts only
{
pos += 4;
pts = stream.get_ts(v);
stream.skip(size - empty - 5);
has_ts = true;
}
else if ((v & 0xf0) == 0x30 && (size - pos) >= 9) // pts and dts
{
pos += 5;
pts = stream.get_ts(v);
stream.get(v);
has_ts = true;
if ((v & 0xf0) != 0x10)
{
cellDmux->Error("PesHeader(): dts not found (v=0x%x, size=%d, pos=%d)", v, size, pos - 1);
stream.skip(size - pos);
return;
}
pos += 4;
dts = stream.get_ts(v);
}
else
{
has_ts = true;
if ((v & 0xF0) != 0x30 || (size - empty) < 10)
{
cellDmux->Error("PesHeader(): pts not found");
Emu.Pause();
}
pts = stream.get_ts(v);
stream.get(v);
if ((v & 0xF0) != 0x10)
{
cellDmux->Error("PesHeader(): dts not found");
Emu.Pause();
}
dts = stream.get_ts(v);
stream.skip(size - empty - 10);
cellDmux->Notice("PesHeader(): unknown code (v=0x%x, size=%d, pos=%d)", v, size, pos - 1);
stream.skip(size - pos);
pos = size;
break;
}
}
is_ok = true;
}
ElementaryStream::ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec)
: dmux(dmux)
, memAddr(a128(addr))
, memSize(size - (addr - memAddr))
, fidMajor(fidMajor)
, fidMinor(fidMinor)
, sup1(sup1)
, sup2(sup2)
, cbFunc(cbFunc)
, cbArg(cbArg)
, spec(spec)
, put(memAddr)
, put_count(0)
, got_count(0)
, released(0)
, raw_pos(0)
, last_dts(CODEC_TS_INVALID)
, last_pts(CODEC_TS_INVALID)
{
}
bool ElementaryStream::is_full(u32 space)
@ -70,7 +114,8 @@ bool ElementaryStream::is_full(u32 space)
u32 first = 0;
if (!entries.Peek(first, &dmux->is_closed) || !first)
{
throw "es::is_full() error: entries.Peek() failed";
assert(!"es::is_full() error: entries.Peek() failed");
return false;
}
else if (first >= put)
{
@ -103,10 +148,7 @@ void ElementaryStream::push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool ra
{
std::lock_guard<std::mutex> lock(m_mutex);
if (is_full(size))
{
throw "es::push_au() error: buffer is full";
}
assert(!is_full(size));
if (put + size + 128 > memAddr + memSize)
{
@ -148,7 +190,7 @@ void ElementaryStream::push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool ra
}
if (!entries.Push(addr, &dmux->is_closed))
{
throw "es::push_au() error: entries.Push() failed";
assert(!"es::push_au() error: entries.Push() failed");
}
}
@ -277,13 +319,13 @@ u32 dmuxOpen(Demuxer* data)
DemuxerTask task;
DemuxerStream stream = {};
ElementaryStream* esALL[192]; memset(esALL, 0, sizeof(esALL));
ElementaryStream** esAVC = &esALL[0]; // AVC (max 16)
ElementaryStream** esM2V = &esALL[16]; // MPEG-2 (max 16)
ElementaryStream* esALL[96]; memset(esALL, 0, sizeof(esALL));
ElementaryStream** esAVC = &esALL[0]; // AVC (max 16 minus M2V count)
ElementaryStream** esM2V = &esALL[16]; // M2V (max 16 minus AVC count)
ElementaryStream** esDATA = &esALL[32]; // user data (max 16)
ElementaryStream** esATX = &esALL[48]; // ATRAC3+ (max 48)
ElementaryStream** esAC3 = &esALL[96]; // AC3 (max 48)
ElementaryStream** esPCM = &esALL[144]; // LPCM (max 48)
ElementaryStream** esATX = &esALL[48]; // ATRAC3+ (max 16)
ElementaryStream** esAC3 = &esALL[64]; // AC3 (max 16)
ElementaryStream** esPCM = &esALL[80]; // LPCM (max 16)
u32 cb_add = 0;
@ -299,7 +341,6 @@ u32 dmuxOpen(Demuxer* data)
// default task (demuxing) (if there is no other work)
be_t<u32> code;
be_t<u16> len;
u8 ch;
if (!stream.peek(code))
{
@ -310,53 +351,104 @@ u32 dmuxOpen(Demuxer* data)
dmux.cbFunc.call(*dmux.dmuxCb, dmux.id, dmuxMsg, dmux.cbArg);
dmux.is_running = false;
continue;
}
else switch (code.ToLE())
switch (code.ToLE())
{
case PACK_START_CODE:
{
if (!stream.check(14))
{
DMUX_ERROR("End of stream (PACK_START_CODE)");
}
stream.skip(14);
break;
}
break;
case SYSTEM_HEADER_START_CODE:
{
if (!stream.check(18))
{
DMUX_ERROR("End of stream (SYSTEM_HEADER_START_CODE)");
}
stream.skip(18);
break;
}
break;
case PADDING_STREAM:
{
if (!stream.check(6))
{
DMUX_ERROR("End of stream (PADDING_STREAM)");
}
stream.skip(4);
stream.get(len);
if (!stream.check(len.ToLE()))
{
DMUX_ERROR("End of stream (PADDING_STREAM, len=%d)", len.ToLE());
}
stream.skip(len);
break;
}
break;
case PRIVATE_STREAM_2:
{
if (!stream.check(6))
{
DMUX_ERROR("End of stream (PRIVATE_STREAM_2)");
}
stream.skip(4);
stream.get(len);
cellDmux->Notice("PRIVATE_STREAM_2 (%d)", len.ToLE());
if (!stream.check(len.ToLE()))
{
DMUX_ERROR("End of stream (PRIVATE_STREAM_2, len=%d)", len.ToLE());
}
stream.skip(len);
break;
}
break;
case PRIVATE_STREAM_1:
{
// audio and user data stream
DemuxerStream backup = stream;
// audio AT3+ (and probably LPCM or user data)
if (!stream.check(6))
{
DMUX_ERROR("End of stream (PRIVATE_STREAM_1)");
}
stream.skip(4);
stream.get(len);
PesHeader pes(stream);
if (!stream.check(len.ToLE()))
{
DMUX_ERROR("End of stream (PRIVATE_STREAM_1, len=%d)", len.ToLE());
}
// read additional header:
stream.peek(ch); // ???
//stream.skip(4);
//pes.size += 4;
const PesHeader pes(stream);
if (!pes.is_ok)
{
DMUX_ERROR("PesHeader error (PRIVATE_STREAM_1, len=%d)", len.ToLE());
}
if (esATX[ch])
if (len < pes.size + 4)
{
DMUX_ERROR("End of block (PRIVATE_STREAM_1, PesHeader + fid_minor, len=%d)", len.ToLE());
}
len -= pes.size + 4;
u8 fid_minor;
if (!stream.get(fid_minor))
{
DMUX_ERROR("End of stream (PRIVATE_STREAM1, fid_minor)");
}
const u32 ch = fid_minor % 16;
if ((fid_minor & -0x10) == 0 && esATX[ch])
{
ElementaryStream& es = *esATX[ch];
if (es.raw_data.size() > 1024 * 1024)
@ -366,8 +458,12 @@ u32 dmuxOpen(Demuxer* data)
continue;
}
stream.skip(4);
len -= 4;
if (len < 3 || !stream.check(3))
{
DMUX_ERROR("End of block (ATX, unknown header, len=%d)", len.ToLE());
}
len -= 3;
stream.skip(3);
if (pes.has_ts)
{
@ -375,7 +471,7 @@ u32 dmuxOpen(Demuxer* data)
es.last_pts = pes.pts;
}
es.push(stream, len - pes.size - 3);
es.push(stream, len);
while (true)
{
@ -386,9 +482,7 @@ u32 dmuxOpen(Demuxer* data)
if (data[0] != 0x0f || data[1] != 0xd0)
{
cellDmux->Error("ATX: 0x0fd0 header not found (ats=0x%llx)", ((be_t<u64>*)data)->ToLE());
Emu.Pause();
return;
DMUX_ERROR("ATX: 0x0fd0 header not found (ats=0x%llx)", ((be_t<u64>*)data)->ToLE());
}
u32 frame_size = ((((u32)data[2] & 0x3) << 8) | (u32)data[3]) * 8 + 8;
@ -399,6 +493,8 @@ u32 dmuxOpen(Demuxer* data)
es.push_au(frame_size + 8, es.last_dts, es.last_pts, stream.userdata, false /* TODO: set correct value */, 0);
cellDmux->Notice("ATX AU pushed (ats=0x%llx, frame_size=%d)", ((be_t<u64>*)data)->ToLE(), frame_size);
auto esMsg = vm::ptr<CellDmuxEsMsg>::make(a128(dmux.memAddr) + (cb_add ^= 16));
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_AU_FOUND;
esMsg->supplementalInfo = stream.userdata;
@ -407,26 +503,48 @@ u32 dmuxOpen(Demuxer* data)
}
else
{
stream.skip(len - pes.size - 3);
cellDmux->Notice("PRIVATE_STREAM_1 (len=%d, fid_minor=0x%x)", len.ToLE(), fid_minor);
stream.skip(len);
}
break;
}
break;
case 0x1e0: case 0x1e1: case 0x1e2: case 0x1e3:
case 0x1e4: case 0x1e5: case 0x1e6: case 0x1e7:
case 0x1e8: case 0x1e9: case 0x1ea: case 0x1eb:
case 0x1ec: case 0x1ed: case 0x1ee: case 0x1ef:
{
// video AVC
ch = code - 0x1e0;
// video stream (AVC or M2V)
DemuxerStream backup = stream;
if (!stream.check(6))
{
DMUX_ERROR("End of stream (video, code=0x%x)", code.ToLE());
}
stream.skip(4);
stream.get(len);
if (!stream.check(len.ToLE()))
{
DMUX_ERROR("End of stream (video, code=0x%x, len=%d)", code.ToLE(), len.ToLE());
}
const PesHeader pes(stream);
if (!pes.is_ok)
{
DMUX_ERROR("PesHeader error (video, code=0x%x, len=%d)", code.ToLE(), len.ToLE());
}
if (len < pes.size + 3)
{
DMUX_ERROR("End of block (video, code=0x%x, PesHeader)", code.ToLE());
}
len -= pes.size + 3;
const u32 ch = code.ToLE() % 16;
if (esAVC[ch])
{
ElementaryStream& es = *esAVC[ch];
DemuxerStream backup = stream;
stream.skip(4);
stream.get(len);
PesHeader pes(stream);
const u32 old_size = (u32)es.raw_data.size();
if (es.isfull(old_size))
@ -436,9 +554,9 @@ u32 dmuxOpen(Demuxer* data)
continue;
}
if ((pes.has_ts && old_size) || old_size >= 0x70000)
if ((pes.has_ts && old_size) || old_size >= 0x69800)
{
// push AU if it becomes too big or the next packet contains ts data
// push AU if it becomes too big or the next packet contains PTS/DTS
es.push_au(old_size, es.last_dts, es.last_pts, stream.userdata, false /* TODO: set correct value */, 0);
// callback
@ -456,53 +574,34 @@ u32 dmuxOpen(Demuxer* data)
}
// reconstruction of MPEG2-PS stream for vdec module
const u32 size = len + 6 /*- pes.size - 3*/;
const u32 size = (u32)len.ToLE() + pes.size + 9;
stream = backup;
es.push(stream, size);
}
else
{
stream.skip(4);
stream.get(len);
cellDmux->Notice("Video stream (code=0x%x, len=%d)", code.ToLE(), len.ToLE());
stream.skip(len);
}
}
break;
case 0x1c0: case 0x1c1: case 0x1c2: case 0x1c3:
case 0x1c4: case 0x1c5: case 0x1c6: case 0x1c7:
case 0x1c8: case 0x1c9: case 0x1ca: case 0x1cb:
case 0x1cc: case 0x1cd: case 0x1ce: case 0x1cf:
case 0x1d0: case 0x1d1: case 0x1d2: case 0x1d3:
case 0x1d4: case 0x1d5: case 0x1d6: case 0x1d7:
case 0x1d8: case 0x1d9: case 0x1da: case 0x1db:
case 0x1dc: case 0x1dd: case 0x1de: case 0x1df:
{
// unknown
cellDmux->Warning("Unknown MPEG stream found");
stream.skip(4);
stream.get(len);
stream.skip(len);
}
break;
case USER_DATA_START_CODE:
{
cellDmux->Error("USER_DATA_START_CODE found");
Emu.Pause();
return;
break;
}
default:
{
if ((code.ToLE() & PACKET_START_CODE_MASK) == PACKET_START_CODE_PREFIX)
{
DMUX_ERROR("Unknown code found (0x%x)", code.ToLE());
}
// search
stream.skip(1);
}
}
continue;
}
// wait for task with yielding (if no default work)
// wait for task if no work
if (!dmux.job.Pop(task, &dmux.is_closed))
{
break; // Emu is stopped
@ -515,7 +614,7 @@ u32 dmuxOpen(Demuxer* data)
if (task.stream.discontinuity)
{
cellDmux->Warning("dmuxSetStream (beginning)");
for (u32 i = 0; i < 192; i++)
for (u32 i = 0; i < sizeof(esALL) / sizeof(esALL[0]); i++)
{
if (esALL[i])
{
@ -527,8 +626,8 @@ u32 dmuxOpen(Demuxer* data)
stream = task.stream;
//LOG_NOTICE(HLE, "*** stream updated(addr=0x%x, size=0x%x, discont=%d, userdata=0x%llx)",
//stream.addr, stream.size, stream.discontinuity, stream.userdata);
break;
}
break;
case dmuxResetStream:
case dmuxResetStreamAndWaitDone:
@ -543,44 +642,55 @@ u32 dmuxOpen(Demuxer* data)
//if (task.type == dmuxResetStreamAndWaitDone)
//{
//}
break;
}
break;
case dmuxEnableEs:
{
ElementaryStream& es = *task.es.es_ptr;
if (es.fidMajor >= 0xe0 &&
es.fidMajor <= 0xef &&
es.fidMinor == 0 &&
es.sup1 == 1 &&
es.sup2 == 0)
// TODO: uncomment when ready to use
if ((es.fidMajor & -0x10) == 0xe0 && es.fidMinor == 0 && es.sup1 == 1 && !es.sup2)
{
esAVC[es.fidMajor - 0xe0] = task.es.es_ptr;
esAVC[es.fidMajor % 16] = task.es.es_ptr;
}
else if (es.fidMajor == 0xbd &&
es.fidMinor == 0 &&
es.sup1 == 0 &&
es.sup2 == 0)
//else if ((es.fidMajor & -0x10) == 0xe0 && es.fidMinor == 0 && !es.sup1 && !es.sup2)
//{
// esM2V[es.fidMajor % 16] = task.es.es_ptr;
//}
else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0 && !es.sup1 && !es.sup2)
{
esATX[0] = task.es.es_ptr;
esATX[es.fidMinor % 16] = task.es.es_ptr;
}
//else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x20 && !es.sup1 && !es.sup2)
//{
// esDATA[es.fidMinor % 16] = task.es.es_ptr;
//}
//else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x30 && !es.sup1 && !es.sup2)
//{
// esAC3[es.fidMinor % 16] = task.es.es_ptr;
//}
//else if (es.fidMajor == 0xbd && (es.fidMinor & -0x10) == 0x40 && !es.sup1 && !es.sup2)
//{
// esPCM[es.fidMinor % 16] = task.es.es_ptr;
//}
else
{
cellDmux->Warning("dmuxEnableEs: (TODO) unsupported filter (0x%x, 0x%x, 0x%x, 0x%x)", es.fidMajor, es.fidMinor, es.sup1, es.sup2);
DMUX_ERROR("dmuxEnableEs: unknown filter (0x%x, 0x%x, 0x%x, 0x%x)", es.fidMajor, es.fidMinor, es.sup1, es.sup2);
}
es.dmux = &dmux;
break;
}
break;
case dmuxDisableEs:
{
ElementaryStream& es = *task.es.es_ptr;
if (es.dmux != &dmux)
{
cellDmux->Warning("dmuxDisableEs: invalid elementary stream");
break;
DMUX_ERROR("dmuxDisableEs: invalid elementary stream");
}
for (u32 i = 0; i < 192; i++)
for (u32 i = 0; i < sizeof(esALL) / sizeof(esALL[0]); i++)
{
if (esALL[i] == &es)
{
@ -589,15 +699,15 @@ u32 dmuxOpen(Demuxer* data)
}
es.dmux = nullptr;
Emu.GetIdManager().RemoveID(task.es.es);
break;
}
break;
case dmuxFlushEs:
{
ElementaryStream& es = *task.es.es_ptr;
const u32 old_size = (u32)es.raw_data.size();
if (old_size && (es.fidMajor & 0xf0) == 0xe0)
if (old_size && (es.fidMajor & -0x10) == 0xe0)
{
// TODO (it's only for AVC, some ATX data may be lost)
while (es.isfull(old_size))
@ -626,22 +736,23 @@ u32 dmuxOpen(Demuxer* data)
esMsg->msgType = CELL_DMUX_ES_MSG_TYPE_FLUSH_DONE;
esMsg->supplementalInfo = stream.userdata;
es.cbFunc.call(*dmux.dmuxCb, dmux.id, es.id, esMsg, es.cbArg);
break;
}
break;
case dmuxResetEs:
{
task.es.es_ptr->reset();
break;
}
case dmuxClose:
{
break;
}
break;
case dmuxClose: break;
default:
{
cellDmux->Error("Demuxer thread error: unknown task(%d)", task.type);
Emu.Pause();
return;
DMUX_ERROR("Demuxer thread error: unknown task (0x%x)", task.type);
}
}
}