mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-10 01:24:41 -06:00
Merge 4a5e79f0a4 into 1c7fbafb00
This commit is contained in:
commit
216a4b09e1
@ -189,46 +189,122 @@ namespace H264
|
||||
return H264DEC_STATUS::BAD_STREAM;
|
||||
}
|
||||
|
||||
H264DEC_STATUS H264DECGetImageSize(uint8* stream, uint32 length, uint32 offset, uint32be* outputWidth, uint32be* outputHeight)
|
||||
/**
|
||||
* Changed to detect 3 or 4 emulation prevention bytes.
|
||||
* The "escape character" is 0x03 - also known as emulation_prevention_three_byte
|
||||
* ([0x000001][first NAL unit]) | ([0x000001][second NAL unit]) | ([0x000001][third NAL unit])
|
||||
*/
|
||||
H264DEC_STATUS H264DECGetImageSize(
|
||||
uint8* stream,
|
||||
uint32 length,
|
||||
uint32 offset,
|
||||
uint32be* outputWidth,
|
||||
uint32be* outputHeight)
|
||||
{
|
||||
if(!stream || length < 4 || !outputWidth || !outputHeight)
|
||||
if (!stream || length < 4 || !outputWidth || !outputHeight)
|
||||
return H264DEC_STATUS::INVALID_PARAM;
|
||||
if( (offset+4) > length )
|
||||
|
||||
if ((offset + 4) > length)
|
||||
return H264DEC_STATUS::INVALID_PARAM;
|
||||
|
||||
uint8* cur = stream + offset;
|
||||
uint8* end = stream + length;
|
||||
cur += 2; // we access cur[-2] and cur[-1] so we need to start at offset 2
|
||||
while(cur < end-2)
|
||||
|
||||
while (cur < end - 4)
|
||||
{
|
||||
// check for start code
|
||||
if(*cur != 1)
|
||||
// Detect start code (3-byte or 4-byte)
|
||||
uint32 startCodeSize = 0;
|
||||
|
||||
if (cur[0] == 0x00 && cur[1] == 0x00)
|
||||
{
|
||||
if (cur[2] == 0x01)
|
||||
{
|
||||
startCodeSize = 3;
|
||||
}
|
||||
else if (cur[2] == 0x00 && cur[3] == 0x01)
|
||||
{
|
||||
startCodeSize = 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (startCodeSize == 0)
|
||||
{
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
// check if this is a valid NAL header
|
||||
if(cur[-2] != 0 || cur[-1] != 0 || cur[0] != 1)
|
||||
|
||||
uint8* nalStart = cur + startCodeSize;
|
||||
if (nalStart >= end)
|
||||
break;
|
||||
|
||||
uint8 nalHeader = nalStart[0];
|
||||
|
||||
// Check if SPS (NAL type 7)
|
||||
if ((nalHeader & 0x1F) == 7)
|
||||
{
|
||||
cur++;
|
||||
continue;
|
||||
// Find end of this NAL (next start code)
|
||||
uint8* nalEnd = nalStart + 1;
|
||||
while (nalEnd < end - 3)
|
||||
{
|
||||
if (nalEnd[0] == 0x00 && nalEnd[1] == 0x00 &&
|
||||
(nalEnd[2] == 0x01 || (nalEnd[2] == 0x00 && nalEnd[3] == 0x01)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
nalEnd++;
|
||||
}
|
||||
|
||||
size_t nalSize = nalEnd - (nalStart + 1);
|
||||
|
||||
// --- EBSP -> RBSP ---
|
||||
uint8_t rbsp[2048]; // consider dynamic if needed
|
||||
size_t rbspSize = 0;
|
||||
|
||||
int zeroCount = 0;
|
||||
for (size_t i = 0; i < nalSize; i++)
|
||||
{
|
||||
uint8_t b = nalStart[1 + i];
|
||||
|
||||
if (zeroCount == 2 && b == 0x03)
|
||||
{
|
||||
zeroCount = 0;
|
||||
continue; // skip emulation prevention byte
|
||||
}
|
||||
|
||||
rbsp[rbspSize++] = b;
|
||||
|
||||
if (b == 0x00)
|
||||
zeroCount++;
|
||||
else
|
||||
zeroCount = 0;
|
||||
}
|
||||
|
||||
// Parse SPS
|
||||
h264State_seq_parameter_set_t psp;
|
||||
bool r = h264Parser_ParseSPS(rbsp, (uint32)rbspSize, psp);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return H264DEC_STATUS::BAD_STREAM;
|
||||
}
|
||||
|
||||
// Width
|
||||
*outputWidth = (psp.pic_width_in_mbs_minus1 + 1) * 16;
|
||||
|
||||
// Height (correct handling)
|
||||
uint32 height = (psp.pic_height_in_map_units_minus1 + 1) * 16;
|
||||
if (!psp.frame_mbs_only_flag)
|
||||
height *= 2;
|
||||
|
||||
*outputHeight = height;
|
||||
|
||||
return H264DEC_STATUS::SUCCESS;
|
||||
}
|
||||
uint8 nalHeader = cur[1];
|
||||
if((nalHeader & 0x1F) != 7)
|
||||
{
|
||||
cur++;
|
||||
continue;
|
||||
}
|
||||
h264State_seq_parameter_set_t psp;
|
||||
bool r = h264Parser_ParseSPS(cur+2, end-cur-2, psp);
|
||||
if(!r)
|
||||
{
|
||||
cemu_assert_suspicious(); // should not happen
|
||||
return H264DEC_STATUS::BAD_STREAM;
|
||||
}
|
||||
*outputWidth = (psp.pic_width_in_mbs_minus1 + 1) * 16;
|
||||
*outputHeight = (psp.pic_height_in_map_units_minus1 + 1) * 16; // affected by frame_mbs_only_flag?
|
||||
return H264DEC_STATUS::SUCCESS;
|
||||
|
||||
cur += startCodeSize;
|
||||
}
|
||||
|
||||
return H264DEC_STATUS::BAD_STREAM;
|
||||
}
|
||||
|
||||
@ -272,7 +348,6 @@ namespace H264
|
||||
static void _ReleaseDecoderSession(H264DecoderBackend* session)
|
||||
{
|
||||
std::unique_lock _lock(sDecoderSessionsMutex);
|
||||
|
||||
}
|
||||
|
||||
static void _DestroyDecoderSession(uint32 handle)
|
||||
@ -336,10 +411,10 @@ namespace H264
|
||||
coreinit::OSResetEvent(flushEvt);
|
||||
session->QueueFlush();
|
||||
coreinit::OSWaitEvent(flushEvt);
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
H264DecoderBackend::DecodeResult decodeResult;
|
||||
if( !session->GetFrameOutputIfReady(decodeResult) )
|
||||
if (!session->GetFrameOutputIfReady(decodeResult))
|
||||
break;
|
||||
// todo - output all frames in a single callback?
|
||||
H264DoFrameOutputCallback(ctx, decodeResult);
|
||||
@ -500,15 +575,15 @@ namespace H264
|
||||
// H264DECExecute is synchronous and will return a frame after either every call (non-buffered) or after 6 calls (buffered)
|
||||
// normally frame decoding happens only during H264DECExecute, but in order to hide the latency of our CPU decoder we will decode asynchronously in buffered mode
|
||||
uint32 numFramesToBuffer = (ctx->Param.outputPerFrame == 0) ? 5 : 0;
|
||||
if(ctx->decoderState.numFramesInFlight > numFramesToBuffer)
|
||||
if (ctx->decoderState.numFramesInFlight > numFramesToBuffer)
|
||||
{
|
||||
ctx->decoderState.numFramesInFlight--;
|
||||
while(true)
|
||||
while (true)
|
||||
{
|
||||
coreinit::OSEvent& evt = session->GetFrameOutputEvent();
|
||||
coreinit::OSWaitEvent(&evt);
|
||||
H264DecoderBackend::DecodeResult decodeResult;
|
||||
if( !session->GetFrameOutputIfReady(decodeResult) )
|
||||
if (!session->GetFrameOutputIfReady(decodeResult))
|
||||
continue;
|
||||
H264DoFrameOutputCallback(ctx, decodeResult);
|
||||
break;
|
||||
@ -558,7 +633,7 @@ namespace H264
|
||||
maxLength -= startCodeOffset;
|
||||
|
||||
// parse NAL data
|
||||
while (true)
|
||||
while(true)
|
||||
{
|
||||
if (nalStream.isEndOfStream())
|
||||
break;
|
||||
@ -619,7 +694,7 @@ namespace H264
|
||||
|
||||
class : public COSModule
|
||||
{
|
||||
public:
|
||||
public:
|
||||
std::string_view GetName() override
|
||||
{
|
||||
return "h264";
|
||||
|
||||
@ -194,8 +194,19 @@ namespace H264
|
||||
double decodeTime = bt.GetElapsedMilliseconds();
|
||||
|
||||
cemu_assert(s_dec_op.u4_frame_decoded_flag);
|
||||
cemu_assert_debug(s_dec_op.u4_num_bytes_consumed == decodedSlice.dataToDecode.m_length);
|
||||
|
||||
|
||||
/**
|
||||
* While testing Cod:Ghosts i found that ih264d may not consume the entire buffer.
|
||||
* So transformed into a Log not an assert.
|
||||
*/
|
||||
if (s_dec_op.u4_num_bytes_consumed < decodedSlice.dataToDecode.m_length)
|
||||
{
|
||||
// log instead of asserting
|
||||
cemuLog_log(LogType::H264, "Partial consumption: {} / {}",
|
||||
s_dec_op.u4_num_bytes_consumed,
|
||||
decodedSlice.dataToDecode.m_length);
|
||||
}
|
||||
|
||||
cemu_assert_debug(m_isBufferedMode || s_dec_op.u4_output_present); // if buffered mode is disabled, then every input should output a frame (except for partial slices?)
|
||||
|
||||
if (s_dec_op.u4_output_present)
|
||||
|
||||
@ -163,7 +163,6 @@ bool parseNAL_seq_parameter_set_rbsp(h264ParserState_t* h264ParserState, h264Par
|
||||
if (h264ParserState->sps.frame_mbs_only_flag == 0)
|
||||
{
|
||||
h264ParserState->sps.mb_adaptive_frame_field_flag = nalStream.readBit();
|
||||
cemu_assert_debug(false);
|
||||
}
|
||||
else
|
||||
h264ParserState->sps.mb_adaptive_frame_field_flag = 0; // default is zero?
|
||||
|
||||
Loading…
Reference in New Issue
Block a user