Fixed Metal video decode presenting stale pictures, and added AV1 decode on Metal. (#4047)

This commit is contained in:
Branimir Karadžić
2026-10-12 21:20:21 -07:00
committed by GitHub
parent ec6e934a2d
commit cd84669abc
5 changed files with 3433 additions and 690 deletions

View File

@@ -258,11 +258,6 @@ namespace bgfx
return poc;
}
inline bool isInRange(int32_t _value, int32_t _min, int32_t _max)
{
return _min <= _value && _value <= _max;
}
static constexpr int32_t kVideoMaxH264SizeInMbsMinus1 = 1023;
inline bool isDecodableProfile(int32_t _profileIdc)
@@ -278,17 +273,17 @@ namespace bgfx
{
return true
&& isDecodableProfile(_sps.profile_idc)
&& isInRange(_sps.seq_parameter_set_id, 0, h264::MAX_SPS_ID)
&& isInRange(_sps.chroma_format_idc, 0, h264::MAX_CHROMA_FORMAT_IDC)
&& isInRange(_sps.bit_depth_luma_minus8, 0, h264::MAX_BIT_DEPTH_MINUS8)
&& isInRange(_sps.bit_depth_chroma_minus8, 0, h264::MAX_BIT_DEPTH_MINUS8)
&& isInRange(_sps.log2_max_frame_num_minus4, 0, h264::MAX_LOG2_MINUS4)
&& isInRange(_sps.pic_order_cnt_type, 0, h264::MAX_PIC_ORDER_CNT_TYPE)
&& isInRange(_sps.log2_max_pic_order_cnt_lsb_minus4, 0, h264::MAX_LOG2_MINUS4)
&& isInRange(_sps.num_ref_frames_in_pic_order_cnt_cycle, 0, h264::MAX_NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE)
&& isInRange(_sps.num_ref_frames, 0, h264::MAX_NUM_REF_FRAMES)
&& isInRange(_sps.pic_width_in_mbs_minus1, 0, kVideoMaxH264SizeInMbsMinus1)
&& isInRange(_sps.pic_height_in_map_units_minus1, 0, kVideoMaxH264SizeInMbsMinus1)
&& bx::isInRange(_sps.seq_parameter_set_id, 0, h264::MAX_SPS_ID)
&& bx::isInRange(_sps.chroma_format_idc, 0, h264::MAX_CHROMA_FORMAT_IDC)
&& bx::isInRange(_sps.bit_depth_luma_minus8, 0, h264::MAX_BIT_DEPTH_MINUS8)
&& bx::isInRange(_sps.bit_depth_chroma_minus8, 0, h264::MAX_BIT_DEPTH_MINUS8)
&& bx::isInRange(_sps.log2_max_frame_num_minus4, 0, h264::MAX_LOG2_MINUS4)
&& bx::isInRange(_sps.pic_order_cnt_type, 0, h264::MAX_PIC_ORDER_CNT_TYPE)
&& bx::isInRange(_sps.log2_max_pic_order_cnt_lsb_minus4, 0, h264::MAX_LOG2_MINUS4)
&& bx::isInRange(_sps.num_ref_frames_in_pic_order_cnt_cycle, 0, h264::MAX_NUM_REF_FRAMES_IN_PIC_ORDER_CNT_CYCLE)
&& bx::isInRange(_sps.num_ref_frames, 0, h264::MAX_NUM_REF_FRAMES)
&& bx::isInRange(_sps.pic_width_in_mbs_minus1, 0, kVideoMaxH264SizeInMbsMinus1)
&& bx::isInRange(_sps.pic_height_in_map_units_minus1, 0, kVideoMaxH264SizeInMbsMinus1)
;
}
@@ -298,16 +293,16 @@ namespace bgfx
const int32_t maxQpInit = h264::MAX_QP - h264::PIC_INIT_QP;
return true
&& isInRange(_pps.pic_parameter_set_id, 0, h264::MAX_PPS_ID)
&& isInRange(_pps.seq_parameter_set_id, 0, h264::MAX_SPS_ID)
&& bx::isInRange(_pps.pic_parameter_set_id, 0, h264::MAX_PPS_ID)
&& bx::isInRange(_pps.seq_parameter_set_id, 0, h264::MAX_SPS_ID)
&& 0 == _pps.num_slice_groups_minus1
&& isInRange(_pps.num_ref_idx_l0_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& isInRange(_pps.num_ref_idx_l1_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& isInRange(_pps.weighted_bipred_idc, 0, h264::MAX_WEIGHTED_BIPRED_IDC)
&& isInRange(_pps.pic_init_qp_minus26, -h264::PIC_INIT_QP - qpBdOffset, maxQpInit)
&& isInRange(_pps.pic_init_qs_minus26, -h264::PIC_INIT_QP, maxQpInit)
&& isInRange(_pps.chroma_qp_index_offset, -h264::MAX_CHROMA_QP_INDEX_OFFSET, h264::MAX_CHROMA_QP_INDEX_OFFSET)
&& isInRange(_pps.second_chroma_qp_index_offset, -h264::MAX_CHROMA_QP_INDEX_OFFSET, h264::MAX_CHROMA_QP_INDEX_OFFSET)
&& bx::isInRange(_pps.num_ref_idx_l0_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& bx::isInRange(_pps.num_ref_idx_l1_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& bx::isInRange(_pps.weighted_bipred_idc, 0, h264::MAX_WEIGHTED_BIPRED_IDC)
&& bx::isInRange(_pps.pic_init_qp_minus26, -h264::PIC_INIT_QP - qpBdOffset, maxQpInit)
&& bx::isInRange(_pps.pic_init_qs_minus26, -h264::PIC_INIT_QP, maxQpInit)
&& bx::isInRange(_pps.chroma_qp_index_offset, -h264::MAX_CHROMA_QP_INDEX_OFFSET, h264::MAX_CHROMA_QP_INDEX_OFFSET)
&& bx::isInRange(_pps.second_chroma_qp_index_offset, -h264::MAX_CHROMA_QP_INDEX_OFFSET, h264::MAX_CHROMA_QP_INDEX_OFFSET)
;
}
@@ -320,17 +315,17 @@ namespace bgfx
const int32_t mbaff = 0 != _sps.mb_adaptive_frame_field_flag && 0 == _sh.field_pic_flag ? 1 : 0;
return true
&& isInRange(_sh.first_mb_in_slice * (1 + mbaff), 0, picSizeInMbs - 1)
&& isInRange(_sh.slice_type, 0, h264::MAX_SLICE_TYPE)
&& isInRange(sliceQp, -qpBdOffset, h264::MAX_QP)
&& isInRange(_sh.num_ref_idx_l0_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& isInRange(_sh.num_ref_idx_l1_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& isInRange(_sh.cabac_init_idc, 0, h264::MAX_CABAC_INIT_IDC)
&& isInRange(_sh.disable_deblocking_filter_idc, 0, h264::MAX_DISABLE_DEBLOCKING_FILTER_IDC)
&& isInRange(_sh.slice_alpha_c0_offset_div2, -h264::MAX_DEBLOCKING_OFFSET_DIV2, h264::MAX_DEBLOCKING_OFFSET_DIV2)
&& isInRange(_sh.slice_beta_offset_div2, -h264::MAX_DEBLOCKING_OFFSET_DIV2, h264::MAX_DEBLOCKING_OFFSET_DIV2)
&& isInRange(_sh.idr_pic_id, 0, h264::MAX_IDR_PIC_ID)
&& isInRange(_sh.redundant_pic_cnt, 0, h264::MAX_REDUNDANT_PIC_CNT)
&& bx::isInRange(_sh.first_mb_in_slice * (1 + mbaff), 0, picSizeInMbs - 1)
&& bx::isInRange(_sh.slice_type, 0, h264::MAX_SLICE_TYPE)
&& bx::isInRange(sliceQp, -qpBdOffset, h264::MAX_QP)
&& bx::isInRange(_sh.num_ref_idx_l0_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& bx::isInRange(_sh.num_ref_idx_l1_active_minus1, 0, h264::MAX_NUM_REF_IDX_ACTIVE_MINUS1)
&& bx::isInRange(_sh.cabac_init_idc, 0, h264::MAX_CABAC_INIT_IDC)
&& bx::isInRange(_sh.disable_deblocking_filter_idc, 0, h264::MAX_DISABLE_DEBLOCKING_FILTER_IDC)
&& bx::isInRange(_sh.slice_alpha_c0_offset_div2, -h264::MAX_DEBLOCKING_OFFSET_DIV2, h264::MAX_DEBLOCKING_OFFSET_DIV2)
&& bx::isInRange(_sh.slice_beta_offset_div2, -h264::MAX_DEBLOCKING_OFFSET_DIV2, h264::MAX_DEBLOCKING_OFFSET_DIV2)
&& bx::isInRange(_sh.idr_pic_id, 0, h264::MAX_IDR_PIC_ID)
&& bx::isInRange(_sh.redundant_pic_cnt, 0, h264::MAX_REDUNDANT_PIC_CNT)
;
}
@@ -434,7 +429,7 @@ namespace bgfx
h264::PPS pps = {};
h264::read_pps(&pps, &nalBs);
if (isInRange(pps.seq_parameter_set_id, 0, int32_t(_spsArrayLen) - 1)
if (bx::isInRange(pps.seq_parameter_set_id, 0, int32_t(_spsArrayLen) - 1)
&& isPpsInRange(pps, _spsArray[pps.seq_parameter_set_id])
&& pps.pic_parameter_set_id < (int)_ppsArrayLen)
{

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,7 @@ static const bgfx::EmbeddedShader s_embeddedShaders[] =
BGFX_EMBEDDED_SHADER_END()
};
static bx::StringView s_supportedExt[32];
static bx::StringView s_supportedExt[64];
static uint32_t s_numSupportedExt = 0;
static int32_t compareExt(const void* _lhs, const void* _rhs)
@@ -850,29 +850,55 @@ struct View
&& bx::FileType::File == fi.type)
{
bx::StringView ext = fi.filePath.getExt();
bool supported = false;
if (!ext.isEmpty() )
{
ext.set(ext.getPtr()+1, ext.getTerm() );
supported = isExtSupported(ext);
}
else
{
bx::FilePath fullPath = m_path;
fullPath.join(fi.filePath.getFileName() );
supported = VideoPlayer::isVideoFile(fullPath);
}
if (isExtSupported(ext) )
{
const bx::StringView fileName = fi.filePath.getFileName();
m_fileList.push_back(std::string(fileName.getPtr(), fileName.getTerm() ) );
}
if (supported)
{
const bx::StringView fileName = fi.filePath.getFileName();
m_fileList.push_back(std::string(fileName.getPtr(), fileName.getTerm() ) );
}
}
}
bx::close(&dr);
const bx::StringView fileName = _filePath.getFileName();
{
bx::FileInfo fi;
bool listed = false;
for (FileList::const_iterator it = m_fileList.begin(); it != m_fileList.end(); ++it)
{
listed |= 0 == bx::strCmp(it->c_str(), fileName);
}
if (!listed
&& !fileName.isEmpty()
&& bx::stat(fi, _filePath)
&& bx::FileType::File == fi.type)
{
m_fileList.push_back(std::string(fileName.getPtr(), fileName.getTerm() ) );
}
}
std::sort(m_fileList.begin(), m_fileList.end(), sortNameAscending);
m_fileIndex = 0;
uint32_t idx = 0;
const bx::StringView fileName = _filePath.getFileName();
for (FileList::const_iterator it = m_fileList.begin(); it != m_fileList.end(); ++it, ++idx)
{
if (0 == bx::strCmpI(it->c_str(), fileName) )
@@ -1564,7 +1590,41 @@ int _main_(int _argc, char** _argv)
if (0 != (caps->supported & BGFX_CAPS_VIDEO_DECODE) )
{
s_supportedExt[s_numSupportedExt++] = "mp4";
struct CodecExt
{
bgfx::VideoCodec::Enum codec;
const char* const* ext;
};
static const char* const s_containerExt[] = { "mp4", "m4v", "mov", NULL };
static const char* const s_h264Ext[] = { "264", "h264", NULL };
static const char* const s_h265Ext[] = { "265", "h265", "hevc", NULL };
static const char* const s_av1Ext[] = { "av1", "obu", "ivf", NULL };
static const CodecExt s_codecExt[] =
{
{ bgfx::VideoCodec::Count, s_containerExt },
{ bgfx::VideoCodec::H264, s_h264Ext },
{ bgfx::VideoCodec::H265, s_h265Ext },
{ bgfx::VideoCodec::AV1, s_av1Ext },
};
for (uint32_t ii = 0; ii < BX_COUNTOF(s_codecExt); ++ii)
{
const CodecExt& codecExt = s_codecExt[ii];
if (bgfx::VideoCodec::Count != codecExt.codec
&& 0 == caps->codecs[codecExt.codec])
{
continue;
}
for (const char* const* ext = codecExt.ext; NULL != *ext && s_numSupportedExt < BX_COUNTOF(s_supportedExt); ++ext)
{
s_supportedExt[s_numSupportedExt++] = *ext;
}
}
bx::quickSort(s_supportedExt, s_numSupportedExt, sizeof(s_supportedExt[0]), compareExt);
}
@@ -2453,18 +2513,7 @@ int _main_(int _argc, char** _argv)
bx::FilePath fp = view.m_path;
fp.join(view.m_fileList[view.m_fileIndex].c_str() );
bx::StringView fileName(view.m_fileList[view.m_fileIndex].c_str() );
bx::StringView ext;
if (fileName.getLength() >= 4)
{
ext.set(fileName.getTerm() - 4, fileName.getTerm() );
}
const bool nextIsVideo = false
|| 0 == bx::strCmpI(ext, ".mp4")
|| 0 == bx::strCmpI(ext, ".m4v")
;
const bool nextIsVideo = VideoPlayer::isVideoFile(fp);
if (videoOwnsTexture)
{

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
#define TEXTUREV_VIDEO_PLAYER_H_HEADER_GUARD
#include <bgfx/bgfx.h>
#include <bx/string.h>
#include <bx/filepath.h>
class VideoPlayer
{
@@ -15,7 +15,7 @@ public:
VideoPlayer();
~VideoPlayer();
static bool isVideoExt(const bx::StringView& _ext);
static bool isVideoFile(const bx::FilePath& _filePath);
bool open(const char* _filePath);
void close();