code cleanup - use google style for videodecode sample and RocVideoDecoder class - no functional change (#65)
* code cleanup - use google style for videodecode sample and RocVideoDecoder class - no functional change * rename SaveSurfToFile to SaveFrameToFile
This commit is contained in:
+1
-1
@@ -946,7 +946,7 @@ WARN_LOGFILE =
|
||||
INPUT = README.md \
|
||||
api/rocDecode.h \
|
||||
api/rocparser.h \
|
||||
utils/video_demuxer.hpp
|
||||
utils/video_demuxer.h
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
|
||||
@@ -128,7 +128,7 @@ if(HIP_FOUND AND Libva_FOUND AND Libdrm_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST})
|
||||
|
||||
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(FILES api/rocdecode.h api/rocparser.h utils/video_demuxer.hpp utils/rocvideodecode/roc_video_dec.h
|
||||
install(FILES api/rocdecode.h api/rocparser.h utils/video_demuxer.h utils/rocvideodecode/roc_video_dec.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
||||
install(DIRECTORY cmake DESTINATION ${CMAKE_INSTALL_DATADIR}/rocdecode)
|
||||
install(DIRECTORY utils/rocvideodecode DESTINATION ${CMAKE_INSTALL_DATADIR}/rocdecode/utils)
|
||||
|
||||
@@ -33,7 +33,7 @@ THE SOFTWARE.
|
||||
#else
|
||||
#include <experimental/filesystem>
|
||||
#endif
|
||||
#include "video_demuxer.hpp"
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_video_dec.h"
|
||||
|
||||
void ShowHelpAndExit(const char *option = NULL) {
|
||||
@@ -48,14 +48,13 @@ void ShowHelpAndExit(const char *option = NULL) {
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
std::string inputFilePath, outputFilePath;
|
||||
int dumpOutputFrames = 0;
|
||||
int isOutputRGB = 0;
|
||||
int deviceId = 0;
|
||||
std::string input_file_path, output_file_path;
|
||||
int dump_output_frames = 0;
|
||||
int device_id = 0;
|
||||
bool b_force_zero_latency = false; // false by default: enabling this option might affect decoding performance
|
||||
Rect crop_rect = {};
|
||||
Rect *p_crop_rect = nullptr;
|
||||
OUTPUT_SURF_MEMORY_TYPE mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
// Parse command-line arguments
|
||||
if(argc < 1) {
|
||||
ShowHelpAndExit();
|
||||
@@ -68,22 +67,22 @@ int main(int argc, char **argv) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-i");
|
||||
}
|
||||
inputFilePath = argv[i];
|
||||
input_file_path = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-o")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-o");
|
||||
}
|
||||
outputFilePath = argv[i];
|
||||
dumpOutputFrames = 1;
|
||||
output_file_path = argv[i];
|
||||
dump_output_frames = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-d")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-d");
|
||||
}
|
||||
deviceId = atoi(argv[i]);
|
||||
device_id = atoi(argv[i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-z")) {
|
||||
@@ -107,69 +106,60 @@ int main(int argc, char **argv) {
|
||||
ShowHelpAndExit(argv[i]);
|
||||
}
|
||||
try {
|
||||
VideoDemuxer demuxer(inputFilePath.c_str());
|
||||
VideoDemuxer demuxer(input_file_path.c_str());
|
||||
rocDecVideoCodec rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer.GetCodecID());
|
||||
RocVideoDecoder viddec(deviceId, mem_type, rocdec_codec_id, false, b_force_zero_latency, p_crop_rect);
|
||||
RocVideoDecoder viddec(device_id, mem_type, rocdec_codec_id, false, b_force_zero_latency, p_crop_rect);
|
||||
|
||||
std::string deviceName, gcnArchName, drmNode;
|
||||
int pciBusID, pciDomainID, pciDeviceID;
|
||||
std::string device_name, gcn_arch_name;
|
||||
int pci_bus_id, pci_domain_id, pci_device_id;
|
||||
|
||||
viddec.GetDeviceinfo(deviceName, gcnArchName, pciBusID, pciDomainID, pciDeviceID);
|
||||
std::cout << "info: Using GPU device " << deviceId << " - " << deviceName << "[" << gcnArchName << "] on PCI bus " <<
|
||||
std::setfill('0') << std::setw(2) << std::right << std::hex << pciBusID << ":" << std::setfill('0') << std::setw(2) <<
|
||||
std::right << std::hex << pciDomainID << "." << pciDeviceID << std::dec << std::endl;
|
||||
viddec.GetDeviceinfo(device_name, gcn_arch_name, pci_bus_id, pci_domain_id, pci_device_id);
|
||||
std::cout << "info: Using GPU device " << device_id << " - " << device_name << "[" << gcn_arch_name << "] on PCI bus " <<
|
||||
std::setfill('0') << std::setw(2) << std::right << std::hex << pci_bus_id << ":" << std::setfill('0') << std::setw(2) <<
|
||||
std::right << std::hex << pci_domain_id << "." << pci_device_id << std::dec << std::endl;
|
||||
std::cout << "info: decoding started, please wait!" << std::endl;
|
||||
|
||||
int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0;
|
||||
uint8_t *pVideo = nullptr;
|
||||
uint8_t *pFrame = nullptr;
|
||||
int n_video_bytes = 0, n_frame_returned = 0, n_frame = 0;
|
||||
uint8_t *pvideo = nullptr;
|
||||
uint8_t *pframe = nullptr;
|
||||
int64_t pts = 0;
|
||||
OutputSurfaceInfo *pSurfInfo;
|
||||
bool bDecodeOutSemiPlanar = false;
|
||||
OutputSurfaceInfo *surf_info;
|
||||
uint32_t width, height;
|
||||
double totalDecTime = 0;
|
||||
double total_dec_time = 0;
|
||||
|
||||
do {
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
demuxer.Demux(&pVideo, &nVideoBytes, &pts);
|
||||
nFrameReturned = viddec.DecodeFrame(pVideo, nVideoBytes, 0, pts);
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
auto timePerFrame = std::chrono::duration<double, std::milli>(endTime - startTime).count();
|
||||
totalDecTime += timePerFrame;
|
||||
if (!nFrame && !viddec.GetOutputSurfaceInfo(&pSurfInfo)) {
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
demuxer.Demux(&pvideo, &n_video_bytes, &pts);
|
||||
n_frame_returned = viddec.DecodeFrame(pvideo, n_video_bytes, 0, pts);
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
auto time_per_frame = std::chrono::duration<double, std::milli>(end_time - start_time).count();
|
||||
total_dec_time += time_per_frame;
|
||||
if (!n_frame && !viddec.GetOutputSurfaceInfo(&surf_info)) {
|
||||
std::cerr << "Error: Failed to get Output Surface Info!" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dumpOutputFrames) {
|
||||
for (int i = 0; i < nFrameReturned; i++) {
|
||||
pFrame = viddec.GetFrame(&pts);
|
||||
viddec.SaveSurfToFile(outputFilePath, pFrame, pSurfInfo);
|
||||
if (dump_output_frames) {
|
||||
for (int i = 0; i < n_frame_returned; i++) {
|
||||
pframe = viddec.GetFrame(&pts);
|
||||
viddec.SaveFrameToFile(output_file_path, pframe, surf_info);
|
||||
// release frame
|
||||
viddec.ReleaseFrame(pts);
|
||||
}
|
||||
}
|
||||
nFrame += nFrameReturned;
|
||||
} while (nVideoBytes);
|
||||
#if 0 // is flushing required?
|
||||
// Flush last frames from the decoder if any
|
||||
do {
|
||||
// send null packet to decoder to flush out
|
||||
pVideo = nullptr; nVideoBytes = 0;
|
||||
int64_t pts = 0;
|
||||
//nFrameReturned = viddec.decode(pVideo, nVideoBytes, pts);
|
||||
} while (nFrameReturned);
|
||||
#endif
|
||||
n_frame += n_frame_returned;
|
||||
} while (n_video_bytes);
|
||||
|
||||
std::cout << "info: Video codec format: " << viddec.GetCodecFmtName(viddec.GetCodecId()) << std::endl;
|
||||
std::cout << "info: Video size: [ " << pSurfInfo->output_width << ", " << pSurfInfo->output_height << " ]" << std::endl;
|
||||
std::cout << "info: Video surface format: " << viddec.GetSurfaceFmtName(pSurfInfo->surface_format) << std::endl;
|
||||
std::cout << "info: Video Bit depth: " << pSurfInfo->bit_depth << std::endl;
|
||||
std::cout << "info: Total frame decoded: " << nFrame << std::endl;
|
||||
if (!dumpOutputFrames) {
|
||||
std::cout << "info: avg decoding time per frame (ms): " << totalDecTime / nFrame << std::endl;
|
||||
std::cout << "info: avg FPS: " << (nFrame / totalDecTime) * 1000 << std::endl;
|
||||
std::cout << "info: Video size: [ " << surf_info->output_width << ", " << surf_info->output_height << " ]" << std::endl;
|
||||
std::cout << "info: Video surface format: " << viddec.GetSurfaceFmtName(surf_info->surface_format) << std::endl;
|
||||
std::cout << "info: Video Bit depth: " << surf_info->bit_depth << std::endl;
|
||||
std::cout << "info: Total frame decoded: " << n_frame << std::endl;
|
||||
if (!dump_output_frames) {
|
||||
std::cout << "info: avg decoding time per frame (ms): " << total_dec_time / n_frame << std::endl;
|
||||
std::cout << "info: avg FPS: " << (n_frame / total_dec_time) * 1000 << std::endl;
|
||||
}
|
||||
}catch (const std::exception &ex) {
|
||||
} catch (const std::exception &ex) {
|
||||
std::cout << ex.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ THE SOFTWARE.
|
||||
#else
|
||||
#include <experimental/filesystem>
|
||||
#endif
|
||||
#include "video_demuxer.hpp"
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_video_dec.h"
|
||||
|
||||
static int *n_total;
|
||||
@@ -67,7 +67,7 @@ int main(int argc, char **argv) {
|
||||
int n_fork = 4;
|
||||
int device_id = 0;
|
||||
Rect *p_crop_rect = nullptr;
|
||||
OUTPUT_SURF_MEMORY_TYPE mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
|
||||
// Parse command-line arguments
|
||||
if(argc < 1) {
|
||||
|
||||
@@ -33,7 +33,7 @@ THE SOFTWARE.
|
||||
#else
|
||||
#include <experimental/filesystem>
|
||||
#endif
|
||||
#include "video_demuxer.hpp"
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_video_dec.h"
|
||||
|
||||
void DecProc(RocVideoDecoder *p_dec, VideoDemuxer *demuxer, int *pn_frame, double *pn_fps) {
|
||||
@@ -73,7 +73,7 @@ int main(int argc, char **argv) {
|
||||
int device_id = 0;
|
||||
int n_thread = 4;
|
||||
Rect *p_crop_rect = nullptr;
|
||||
OUTPUT_SURF_MEMORY_TYPE mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
// Parse command-line arguments
|
||||
if(argc < 1) {
|
||||
ShowHelpAndExit();
|
||||
|
||||
@@ -22,10 +22,10 @@ THE SOFTWARE.
|
||||
|
||||
#include "roc_video_dec.h"
|
||||
|
||||
RocVideoDecoder::RocVideoDecoder(int device_id, OUTPUT_SURF_MEMORY_TYPE out_mem_type, rocDecVideoCodec codec, bool b_low_latency, bool force_zero_latency,
|
||||
const Rect *p_crop_rect, bool extract_user_SEI_Message, int max_width, int max_height,uint32_t clk_rate) :
|
||||
RocVideoDecoder::RocVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool b_low_latency, bool force_zero_latency,
|
||||
const Rect *p_crop_rect, bool extract_user_sei_Message, int max_width, int max_height,uint32_t clk_rate) :
|
||||
device_id_{device_id}, out_mem_type_(out_mem_type), codec_id_(codec), b_low_latency_(b_low_latency),
|
||||
b_force_zero_latency_(force_zero_latency), b_extract_sei_message_(extract_user_SEI_Message),
|
||||
b_force_zero_latency_(force_zero_latency), b_extract_sei_message_(extract_user_sei_Message),
|
||||
max_width_ (max_width), max_height_(max_height) {
|
||||
|
||||
if (!InitHIP(device_id_)) {
|
||||
@@ -82,9 +82,9 @@ RocVideoDecoder::~RocVideoDecoder() {
|
||||
}
|
||||
}
|
||||
|
||||
static const char * GetVideoCodecString(rocDecVideoCodec eCodec) {
|
||||
static const char * GetVideoCodecString(rocDecVideoCodec e_codec) {
|
||||
static struct {
|
||||
rocDecVideoCodec eCodec;
|
||||
rocDecVideoCodec e_codec;
|
||||
const char *name;
|
||||
} aCodecName [] = {
|
||||
{ rocDecVideoCodec_MPEG1, "MPEG-1" },
|
||||
@@ -99,12 +99,12 @@ static const char * GetVideoCodecString(rocDecVideoCodec eCodec) {
|
||||
{ rocDecVideoCodec_NumCodecs, "Invalid" },
|
||||
};
|
||||
|
||||
if (eCodec >= 0 && eCodec <= rocDecVideoCodec_NumCodecs) {
|
||||
return aCodecName[eCodec].name;
|
||||
if (e_codec >= 0 && e_codec <= rocDecVideoCodec_NumCodecs) {
|
||||
return aCodecName[e_codec].name;
|
||||
}
|
||||
for (int i = rocDecVideoCodec_NumCodecs + 1; i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) {
|
||||
if (eCodec == aCodecName[i].eCodec) {
|
||||
return aCodecName[eCodec].name;
|
||||
if (e_codec == aCodecName[i].e_codec) {
|
||||
return aCodecName[e_codec].name;
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
@@ -226,44 +226,44 @@ static void GetSurfaceStrideInternal(rocDecVideoSurfaceFormat surface_format, ui
|
||||
/* Return value from HandleVideoSequence() are interpreted as :
|
||||
* 0: fail, 1: succeeded, > 1: override dpb size of parser (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser)
|
||||
*/
|
||||
int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) {
|
||||
int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *p_video_format) {
|
||||
//START_TIMER
|
||||
input_video_info_str_.str("");
|
||||
input_video_info_str_.clear();
|
||||
input_video_info_str_ << "Input Video Information" << std::endl
|
||||
<< "\tCodec : " << GetCodecFmtName(pVideoFormat->codec) << std::endl
|
||||
<< "\tFrame rate : " << pVideoFormat->frame_rate.numerator << "/" << pVideoFormat->frame_rate.denominator
|
||||
<< " = " << 1.0 * pVideoFormat->frame_rate.numerator / pVideoFormat->frame_rate.denominator << " fps" << std::endl
|
||||
<< "\tSequence : " << (pVideoFormat->progressive_sequence ? "Progressive" : "Interlaced") << std::endl
|
||||
<< "\tCoded size : [" << pVideoFormat->coded_width << ", " << pVideoFormat->coded_height << "]" << std::endl
|
||||
<< "\tDisplay area : [" << pVideoFormat->display_area.left << ", " << pVideoFormat->display_area.top << ", "
|
||||
<< pVideoFormat->display_area.right << ", " << pVideoFormat->display_area.bottom << "]" << std::endl
|
||||
<< "\tChroma : " << GetVideoChromaFormatName(pVideoFormat->chroma_format) << std::endl
|
||||
<< "\tBit depth : " << pVideoFormat->bit_depth_luma_minus8 + 8
|
||||
<< "\tCodec : " << GetCodecFmtName(p_video_format->codec) << std::endl
|
||||
<< "\tFrame rate : " << p_video_format->frame_rate.numerator << "/" << p_video_format->frame_rate.denominator
|
||||
<< " = " << 1.0 * p_video_format->frame_rate.numerator / p_video_format->frame_rate.denominator << " fps" << std::endl
|
||||
<< "\tSequence : " << (p_video_format->progressive_sequence ? "Progressive" : "Interlaced") << std::endl
|
||||
<< "\tCoded size : [" << p_video_format->coded_width << ", " << p_video_format->coded_height << "]" << std::endl
|
||||
<< "\tDisplay area : [" << p_video_format->display_area.left << ", " << p_video_format->display_area.top << ", "
|
||||
<< p_video_format->display_area.right << ", " << p_video_format->display_area.bottom << "]" << std::endl
|
||||
<< "\tChroma : " << GetVideoChromaFormatName(p_video_format->chroma_format) << std::endl
|
||||
<< "\tBit depth : " << p_video_format->bit_depth_luma_minus8 + 8
|
||||
;
|
||||
input_video_info_str_ << std::endl;
|
||||
|
||||
int nDecodeSurface = pVideoFormat->min_num_decode_surfaces;
|
||||
int nDecodeSurface = p_video_format->min_num_decode_surfaces;
|
||||
|
||||
RocdecDecodeCaps decode_caps;
|
||||
memset(&decode_caps, 0, sizeof(decode_caps));
|
||||
decode_caps.eCodecType = pVideoFormat->codec;
|
||||
decode_caps.eChromaFormat = pVideoFormat->chroma_format;
|
||||
decode_caps.nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8;
|
||||
decode_caps.eCodecType = p_video_format->codec;
|
||||
decode_caps.eChromaFormat = p_video_format->chroma_format;
|
||||
decode_caps.nBitDepthMinus8 = p_video_format->bit_depth_luma_minus8;
|
||||
|
||||
ROCDEC_API_CALL(rocDecGetDecoderCaps(&decode_caps));
|
||||
|
||||
if(!decode_caps.bIsSupported){
|
||||
if(!decode_caps.bIsSupported) {
|
||||
ROCDEC_THROW("Rocdec:: Codec not supported on this GPU: ", ROCDEC_NOT_SUPPORTED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((pVideoFormat->coded_width > decode_caps.nMaxWidth) ||
|
||||
(pVideoFormat->coded_height > decode_caps.nMaxHeight)){
|
||||
if ((p_video_format->coded_width > decode_caps.nMaxWidth) ||
|
||||
(p_video_format->coded_height > decode_caps.nMaxHeight)) {
|
||||
|
||||
std::ostringstream errorString;
|
||||
errorString << std::endl
|
||||
<< "Resolution : " << pVideoFormat->coded_width << "x" << pVideoFormat->coded_height << std::endl
|
||||
<< "Resolution : " << p_video_format->coded_width << "x" << p_video_format->coded_height << std::endl
|
||||
<< "Max Supported (wxh) : " << decode_caps.nMaxWidth << "x" << decode_caps.nMaxHeight << std::endl
|
||||
<< "Resolution not supported on this GPU ";
|
||||
|
||||
@@ -276,22 +276,22 @@ int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) {
|
||||
|
||||
// rocdecCreateDecoder() has been called before, and now there's possible config change
|
||||
// todo:: support reconfigure
|
||||
//return ReconfigureDecoder(pVideoFormat);
|
||||
//return ReconfigureDecoder(p_video_format);
|
||||
}
|
||||
|
||||
// eCodec has been set in the constructor (for parser). Here it's set again for potential correction
|
||||
codec_id_ = pVideoFormat->codec;
|
||||
video_chroma_format_ = pVideoFormat->chroma_format;
|
||||
bitdepth_minus_8_ = pVideoFormat->bit_depth_luma_minus8;
|
||||
// e_codec has been set in the constructor (for parser). Here it's set again for potential correction
|
||||
codec_id_ = p_video_format->codec;
|
||||
video_chroma_format_ = p_video_format->chroma_format;
|
||||
bitdepth_minus_8_ = p_video_format->bit_depth_luma_minus8;
|
||||
byte_per_pixel_ = bitdepth_minus_8_ > 0 ? 2 : 1;
|
||||
|
||||
// Set the output surface format same as chroma format
|
||||
if (video_chroma_format_ == rocDecVideoChromaFormat_420 || rocDecVideoChromaFormat_Monochrome)
|
||||
video_surface_format_ = pVideoFormat->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_P016 : rocDecVideoSurfaceFormat_NV12;
|
||||
video_surface_format_ = p_video_format->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_P016 : rocDecVideoSurfaceFormat_NV12;
|
||||
else if (video_chroma_format_ == rocDecVideoChromaFormat_444)
|
||||
video_surface_format_ = pVideoFormat->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_YUV444_16Bit : rocDecVideoSurfaceFormat_YUV444;
|
||||
video_surface_format_ = p_video_format->bit_depth_luma_minus8 ? rocDecVideoSurfaceFormat_YUV444_16Bit : rocDecVideoSurfaceFormat_YUV444;
|
||||
else if (video_chroma_format_ == rocDecVideoChromaFormat_422)
|
||||
video_surface_format_ = rocDecVideoSurfaceFormat_NV12; // 422 output surface is not supported:: default to NV12
|
||||
video_surface_format_ = rocDecVideoSurfaceFormat_NV12;
|
||||
|
||||
// Check if output format supported. If not, check falback options
|
||||
if (!(decode_caps.nOutputFormatMask & (1 << video_surface_format_))){
|
||||
@@ -306,40 +306,37 @@ int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) {
|
||||
else
|
||||
ROCDEC_THROW("No supported output format found", ROCDEC_NOT_SUPPORTED);
|
||||
}
|
||||
video_format_ = *pVideoFormat;
|
||||
video_format_ = *p_video_format;
|
||||
|
||||
RocDecoderCreateInfo videoDecodeCreateInfo = { 0 };
|
||||
videoDecodeCreateInfo.CodecType = pVideoFormat->codec;
|
||||
videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format;
|
||||
videoDecodeCreateInfo.CodecType = p_video_format->codec;
|
||||
videoDecodeCreateInfo.ChromaFormat = p_video_format->chroma_format;
|
||||
videoDecodeCreateInfo.OutputFormat = video_surface_format_;
|
||||
videoDecodeCreateInfo.bitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8;
|
||||
videoDecodeCreateInfo.bitDepthMinus8 = p_video_format->bit_depth_luma_minus8;
|
||||
videoDecodeCreateInfo.ulNumOutputSurfaces = 2;
|
||||
videoDecodeCreateInfo.ulNumDecodeSurfaces = nDecodeSurface;
|
||||
videoDecodeCreateInfo.ulWidth = pVideoFormat->coded_width;
|
||||
videoDecodeCreateInfo.ulHeight = pVideoFormat->coded_height;
|
||||
videoDecodeCreateInfo.ulWidth = p_video_format->coded_width;
|
||||
videoDecodeCreateInfo.ulHeight = p_video_format->coded_height;
|
||||
// AV1 has max width/height of sequence in sequence header
|
||||
if (pVideoFormat->codec == rocDecVideoCodec_AV1 && pVideoFormat->seqhdr_data_length > 0) {
|
||||
if (p_video_format->codec == rocDecVideoCodec_AV1 && p_video_format->seqhdr_data_length > 0) {
|
||||
// dont overwrite if it is already set from cmdline or reconfig.txt
|
||||
if (!(max_width_ > pVideoFormat->coded_width || max_height_ > pVideoFormat->coded_height))
|
||||
{
|
||||
RocdecVideoFormatEx *vidFormatEx = (RocdecVideoFormatEx *)pVideoFormat;
|
||||
if (!(max_width_ > p_video_format->coded_width || max_height_ > p_video_format->coded_height)) {
|
||||
RocdecVideoFormatEx *vidFormatEx = (RocdecVideoFormatEx *)p_video_format;
|
||||
max_width_ = vidFormatEx->max_width;
|
||||
max_height_ = vidFormatEx->max_height;
|
||||
}
|
||||
}
|
||||
if (max_width_ < (int)pVideoFormat->coded_width)
|
||||
max_width_ = pVideoFormat->coded_width;
|
||||
if (max_height_ < (int)pVideoFormat->coded_height)
|
||||
max_height_ = pVideoFormat->coded_height;
|
||||
if (max_width_ < (int)p_video_format->coded_width)
|
||||
max_width_ = p_video_format->coded_width;
|
||||
if (max_height_ < (int)p_video_format->coded_height)
|
||||
max_height_ = p_video_format->coded_height;
|
||||
|
||||
videoDecodeCreateInfo.ulMaxWidth = max_width_;
|
||||
videoDecodeCreateInfo.ulMaxHeight = max_height_;
|
||||
|
||||
if (!(crop_rect_.r && crop_rect_.b)) {
|
||||
width_ = pVideoFormat->display_area.right - pVideoFormat->display_area.left;
|
||||
height_ = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top;
|
||||
//videoDecodeCreateInfo.ulTargetWidth = pVideoFormat->coded_width;
|
||||
//videoDecodeCreateInfo.ulTargetHeight = pVideoFormat->coded_height;
|
||||
width_ = p_video_format->display_area.right - p_video_format->display_area.left;
|
||||
height_ = p_video_format->display_area.bottom - p_video_format->display_area.top;
|
||||
videoDecodeCreateInfo.ulTargetWidth = width_;
|
||||
videoDecodeCreateInfo.ulTargetHeight = height_;
|
||||
} else {
|
||||
@@ -355,9 +352,9 @@ int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) {
|
||||
|
||||
chroma_height_ = (int)(ceil(height_ * GetChromaHeightFactor(video_surface_format_)));
|
||||
num_chroma_planes_ = GetChromaPlaneCount(video_surface_format_);
|
||||
if (pVideoFormat->chroma_format == rocDecVideoChromaFormat_Monochrome) num_chroma_planes_ = 0;
|
||||
if (p_video_format->chroma_format == rocDecVideoChromaFormat_Monochrome) num_chroma_planes_ = 0;
|
||||
if (out_mem_type_ == OUT_SURFACE_MEM_DEV_INTERNAL)
|
||||
GetSurfaceStrideInternal(video_surface_format_, pVideoFormat->coded_width, pVideoFormat->coded_height, &surface_stride_, &surface_vstride_);
|
||||
GetSurfaceStrideInternal(video_surface_format_, p_video_format->coded_width, p_video_format->coded_height, &surface_stride_, &surface_vstride_);
|
||||
else {
|
||||
surface_stride_ = videoDecodeCreateInfo.ulTargetWidth * byte_per_pixel_; // todo:: check if we need pitched memory for faster copy
|
||||
}
|
||||
@@ -401,7 +398,7 @@ int RocVideoDecoder::HandleVideoSequence(RocdecVideoFormat *pVideoFormat) {
|
||||
}
|
||||
|
||||
|
||||
int RocVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *pVideoFormat) {
|
||||
int RocVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *p_video_format) {
|
||||
ROCDEC_THROW("ReconfigureDecoder is not supported in this version: ", ROCDEC_NOT_SUPPORTED);
|
||||
return ROCDEC_NOT_SUPPORTED;
|
||||
}
|
||||
@@ -413,14 +410,12 @@ int RocVideoDecoder::ReconfigureDecoder(RocdecVideoFormat *pVideoFormat) {
|
||||
* @return int 1: success 0: fail
|
||||
*/
|
||||
int RocVideoDecoder::HandlePictureDecode(RocdecPicParams *pPicParams) {
|
||||
if (!roc_decoder_)
|
||||
{
|
||||
if (!roc_decoder_) {
|
||||
THROW("RocDecoder not initialized: failed with ErrCode: " + TOSTR(ROCDEC_NOT_INITIALIZED));
|
||||
}
|
||||
pic_num_in_dec_order_[pPicParams->CurrPicIdx] = decode_poc_++;
|
||||
ROCDEC_API_CALL(rocDecDecodeFrame(roc_decoder_, pPicParams));
|
||||
if (b_force_zero_latency_ && ((!pPicParams->field_pic_flag) || (pPicParams->second_field)))
|
||||
{
|
||||
if (b_force_zero_latency_ && ((!pPicParams->field_pic_flag) || (pPicParams->second_field))) {
|
||||
RocdecParserDispInfo disp_info;
|
||||
memset(&disp_info, 0, sizeof(disp_info));
|
||||
disp_info.picture_index = pPicParams->CurrPicIdx;
|
||||
@@ -453,13 +448,11 @@ int RocVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) {
|
||||
for (uint32_t i = 0; i < sei_num_messages; i++) {
|
||||
if (codec_id_ == rocDecVideoCodec_H264 || rocDecVideoCodec_HEVC) {
|
||||
switch (sei_message[i].sei_message_type) {
|
||||
case SEI_TYPE_TIME_CODE:
|
||||
{
|
||||
case SEI_TYPE_TIME_CODE: {
|
||||
//todo:: check if we need to write timecode
|
||||
}
|
||||
break;
|
||||
case SEI_TYPE_USER_DATA_UNREGISTERED:
|
||||
{
|
||||
case SEI_TYPE_USER_DATA_UNREGISTERED: {
|
||||
fwrite(sei_buffer, sei_message[i].sei_message_size, 1, fp_sei_);
|
||||
}
|
||||
break;
|
||||
@@ -523,7 +516,7 @@ int RocVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) {
|
||||
} else {
|
||||
// use 2d copy to copy an ROI
|
||||
HIP_API_CALL(hipMemcpy2DAsync(p_dec_frame, dst_pitch, src_dev_ptr[0], src_pitch[0], width_ * byte_per_pixel_, height_, hipMemcpyDeviceToDevice, hip_stream_));
|
||||
}
|
||||
}
|
||||
} else
|
||||
HIP_API_CALL(hipMemcpy2DAsync(p_dec_frame, width_ * byte_per_pixel_, src_dev_ptr[0], src_pitch[0], width_ * byte_per_pixel_, height_, hipMemcpyDeviceToHost, hip_stream_));
|
||||
|
||||
@@ -534,7 +527,7 @@ int RocVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) {
|
||||
if (src_pitch[1] == dst_pitch) {
|
||||
int chroma_size = chroma_height_ * dst_pitch;
|
||||
HIP_API_CALL(hipMemcpyDtoDAsync(p_frame_uv, src_dev_ptr[1], chroma_size, hip_stream_));
|
||||
}else {
|
||||
} else {
|
||||
// use 2d copy to copy an ROI
|
||||
HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[1], src_pitch[1], width_ * byte_per_pixel_, chroma_height_, hipMemcpyDeviceToDevice, hip_stream_));
|
||||
}
|
||||
@@ -551,7 +544,7 @@ int RocVideoDecoder::HandlePictureDisplay(RocdecParserDispInfo *pDispInfo) {
|
||||
// use 2d copy to copy an ROI
|
||||
HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[2], src_pitch[2], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToDevice, hip_stream_));
|
||||
}
|
||||
}else
|
||||
} else
|
||||
HIP_API_CALL(hipMemcpy2DAsync(p_frame_uv, dst_pitch, src_dev_ptr[2], src_pitch[2], width_*byte_per_pixel_, chroma_height_, hipMemcpyDeviceToHost, hip_stream_));
|
||||
}
|
||||
|
||||
@@ -622,7 +615,7 @@ uint8_t* RocVideoDecoder::GetFrame(int64_t *pts) {
|
||||
DecFrameBuffer *fb = &vp_frames_q_.front();
|
||||
if (pts) *pts = fb->pts;
|
||||
return fb->frame_ptr;
|
||||
}else {
|
||||
} else {
|
||||
if (pts) *pts = vp_frames_[decoded_frame_cnt_ret_].pts;
|
||||
return vp_frames_[decoded_frame_cnt_ret_++].frame_ptr;
|
||||
}
|
||||
@@ -659,7 +652,7 @@ bool RocVideoDecoder::ReleaseFrame(int64_t pTimestamp) {
|
||||
}
|
||||
|
||||
|
||||
void RocVideoDecoder::SaveSurfToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info) {
|
||||
void RocVideoDecoder::SaveFrameToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info) {
|
||||
uint8_t *hst_ptr = nullptr;
|
||||
uint64_t output_image_size = surf_info->output_surface_size_in_bytes;
|
||||
if (surf_info->mem_type == OUT_SURFACE_MEM_DEV_INTERNAL || surf_info->mem_type == OUT_SURFACE_MEM_DEV_COPIED) {
|
||||
|
||||
@@ -39,16 +39,16 @@ THE SOFTWARE.
|
||||
|
||||
#define MAX_FRAME_NUM 16
|
||||
|
||||
typedef enum{
|
||||
typedef enum SeiH264HevcPayloadType_enum {
|
||||
SEI_TYPE_TIME_CODE = 136,
|
||||
SEI_TYPE_USER_DATA_UNREGISTERED = 5
|
||||
}SEI_H264_HEVC_PAYLOAD_TYPE;
|
||||
} SeiH264HevcPayloadType;
|
||||
|
||||
typedef enum {
|
||||
OUT_SURFACE_MEM_DEV_INTERNAL = 0, /**< Internal interopped decoded surface memory(original mapped decoded surface) */
|
||||
OUT_SURFACE_MEM_DEV_COPIED = 1, /**< decoded output will be copied to a separate device memory (the user doesn't need to call release) **/
|
||||
OUT_SURFACE_MEM_HOST_COPIED = 2 /**< decoded output will be copied to a separate host memory (the user doesn't need to call release) **/
|
||||
} OUTPUT_SURF_MEMORY_TYPE;
|
||||
typedef enum OutputSurfaceMemoryType_enum {
|
||||
OUT_SURFACE_MEM_DEV_INTERNAL = 0, /**< Internal interopped decoded surface memory(original mapped decoded surface) */
|
||||
OUT_SURFACE_MEM_DEV_COPIED = 1, /**< decoded output will be copied to a separate device memory (the user doesn't need to call release) **/
|
||||
OUT_SURFACE_MEM_HOST_COPIED = 2 /**< decoded output will be copied to a separate host memory (the user doesn't need to call release) **/
|
||||
} OutputSurfaceMemoryType;
|
||||
|
||||
#define TOSTR(X) std::to_string(static_cast<int>(X))
|
||||
#define STR(X) std::string(X)
|
||||
@@ -61,44 +61,44 @@ typedef enum {
|
||||
#define ERR(X) std::cerr << "[ERR] " << " {" << __func__ <<"} " << " " << X << std::endl;
|
||||
|
||||
|
||||
class rocVideoDecodeException : public std::exception {
|
||||
class RocVideoDecodeException : public std::exception {
|
||||
public:
|
||||
|
||||
explicit rocVideoDecodeException(const std::string& message, const int errCode):_message(message), _err_code(errCode) {}
|
||||
explicit rocVideoDecodeException(const std::string& message):_message(message), _err_code(-1) {}
|
||||
explicit RocVideoDecodeException(const std::string& message, const int err_code):_message(message), _err_code(err_code) {}
|
||||
explicit RocVideoDecodeException(const std::string& message):_message(message), _err_code(-1) {}
|
||||
virtual const char* what() const throw() override {
|
||||
return _message.c_str();
|
||||
}
|
||||
int getErrorCode() const { return _err_code; }
|
||||
int Geterror_code() const { return _err_code; }
|
||||
private:
|
||||
std::string _message;
|
||||
int _err_code;
|
||||
};
|
||||
|
||||
#define ROCDEC_THROW(X, CODE) throw rocVideoDecodeException(" { " + std::string(__func__) + " } " + X , CODE);
|
||||
#define THROW(X) throw rocVideoDecodeException(" { " + std::string(__func__) + " } " + X);
|
||||
#define ROCDEC_THROW(X, CODE) throw RocVideoDecodeException(" { " + std::string(__func__) + " } " + X , CODE);
|
||||
#define THROW(X) throw RocVideoDecodeException(" { " + std::string(__func__) + " } " + X);
|
||||
|
||||
#define ROCDEC_API_CALL( rocDecAPI ) \
|
||||
do { \
|
||||
rocDecStatus errorCode = rocDecAPI; \
|
||||
if( errorCode != ROCDEC_SUCCESS) { \
|
||||
std::ostringstream errorLog; \
|
||||
errorLog << #rocDecAPI << " returned err " << errorCode << " at " <<__FILE__ <<":" << __LINE__; \
|
||||
ROCDEC_THROW(errorLog.str(), errorCode); \
|
||||
} \
|
||||
#define ROCDEC_API_CALL( rocDecAPI ) \
|
||||
do { \
|
||||
rocDecStatus error_code = rocDecAPI; \
|
||||
if( error_code != ROCDEC_SUCCESS) { \
|
||||
std::ostringstream error_log; \
|
||||
error_log << #rocDecAPI << " returned err " << error_code << " at " <<__FILE__ <<":" << __LINE__;\
|
||||
ROCDEC_THROW(error_log.str(), error_code); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define HIP_API_CALL( call ) \
|
||||
do { \
|
||||
hipError_t hip_status = call; \
|
||||
if (hip_status != hipSuccess) { \
|
||||
const char *szErrName = NULL; \
|
||||
szErrName = hipGetErrorName(hip_status); \
|
||||
std::ostringstream errorLog; \
|
||||
errorLog << "hip API error " << szErrName ; \
|
||||
ROCDEC_THROW(errorLog.str(), hip_status); \
|
||||
} \
|
||||
} \
|
||||
#define HIP_API_CALL( call ) \
|
||||
do { \
|
||||
hipError_t hip_status = call; \
|
||||
if (hip_status != hipSuccess) { \
|
||||
const char *sz_err_name = NULL; \
|
||||
sz_err_name = hipGetErrorName(hip_status); \
|
||||
std::ostringstream error_log; \
|
||||
error_log << "hip API error " << sz_err_name ; \
|
||||
ROCDEC_THROW(error_log.str(), hip_status); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ typedef struct OutputSurfaceInfoType {
|
||||
uint32_t num_chroma_planes; /**< Output Chroma number of planes*/
|
||||
uint64_t output_surface_size_in_bytes; /**< Output Image Size in Bytes; including both luma and chroma planes*/
|
||||
rocDecVideoSurfaceFormat surface_format; /**< Chroma format of the decoded image*/
|
||||
OUTPUT_SURF_MEMORY_TYPE mem_type; /**< Output mem_type of the surface*/
|
||||
OutputSurfaceMemoryType mem_type; /**< Output mem_type of the surface*/
|
||||
} OutputSurfaceInfo;
|
||||
|
||||
class RocVideoDecoder {
|
||||
@@ -148,7 +148,7 @@ class RocVideoDecoder {
|
||||
* @param clk_rate
|
||||
* @param force_zero_latency
|
||||
*/
|
||||
RocVideoDecoder(int device_id, OUTPUT_SURF_MEMORY_TYPE out_mem_type, rocDecVideoCodec codec, bool b_low_latency, bool force_zero_latency = false,
|
||||
RocVideoDecoder(int device_id, OutputSurfaceMemoryType out_mem_type, rocDecVideoCodec codec, bool b_low_latency, bool force_zero_latency = false,
|
||||
const Rect *p_crop_rect = nullptr, bool extract_user_SEI_Message = false, int max_width = 0, int max_height = 0,
|
||||
uint32_t clk_rate = 1000);
|
||||
~RocVideoDecoder();
|
||||
@@ -264,11 +264,11 @@ class RocVideoDecoder {
|
||||
/**
|
||||
* @brief Get the Device info for the current device
|
||||
*
|
||||
* @param device_name
|
||||
* @param gcn_arch_name
|
||||
* @param pci_bus_id
|
||||
* @param pci_domain_id
|
||||
* @param pci_device_id
|
||||
* @param device_name
|
||||
* @param gcn_arch_name
|
||||
* @param pci_bus_id
|
||||
* @param pci_domain_id
|
||||
* @param pci_device_id
|
||||
*/
|
||||
void GetDeviceinfo(std::string &device_name, std::string &gcn_arch_name, int &pci_bus_id, int &pci_domain_id, int &pci_device_id);
|
||||
|
||||
@@ -279,56 +279,56 @@ class RocVideoDecoder {
|
||||
* @param dev_mem - pointer to surface memory
|
||||
* @param surf_info - surface info
|
||||
*/
|
||||
void SaveSurfToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info);
|
||||
void SaveFrameToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info);
|
||||
|
||||
private:
|
||||
int decoder_session_id_; // Decoder session identifier. Used to gather session level stats.
|
||||
/**
|
||||
* @brief Callback function to be registered for getting a callback when decoding of sequence starts
|
||||
*/
|
||||
static int ROCDECAPI HandleVideoSequenceProc(void *pUserData, RocdecVideoFormat *pVideoFormat) { return ((RocVideoDecoder *)pUserData)->HandleVideoSequence(pVideoFormat); }
|
||||
static int ROCDECAPI HandleVideoSequenceProc(void *p_user_data, RocdecVideoFormat *p_video_format) { return ((RocVideoDecoder *)p_user_data)->HandleVideoSequence(p_video_format); }
|
||||
|
||||
/**
|
||||
* @brief Callback function to be registered for getting a callback when a decoded frame is ready to be decoded
|
||||
*/
|
||||
static int ROCDECAPI HandlePictureDecodeProc(void *pUserData, RocdecPicParams *pPicParams) { return ((RocVideoDecoder *)pUserData)->HandlePictureDecode(pPicParams); }
|
||||
static int ROCDECAPI HandlePictureDecodeProc(void *p_user_data, RocdecPicParams *p_pic_params) { return ((RocVideoDecoder *)p_user_data)->HandlePictureDecode(p_pic_params); }
|
||||
|
||||
/**
|
||||
* @brief Callback function to be registered for getting a callback when a decoded frame is available for display
|
||||
*/
|
||||
static int ROCDECAPI HandlePictureDisplayProc(void *pUserData, RocdecParserDispInfo *pDispInfo) { return ((RocVideoDecoder *)pUserData)->HandlePictureDisplay(pDispInfo); }
|
||||
static int ROCDECAPI HandlePictureDisplayProc(void *p_user_data, RocdecParserDispInfo *p_disp_info) { return ((RocVideoDecoder *)p_user_data)->HandlePictureDisplay(p_disp_info); }
|
||||
|
||||
/**
|
||||
* @brief Callback function to be registered for getting a callback when all the unregistered user SEI Messages are parsed for a frame.
|
||||
*/
|
||||
static int ROCDECAPI HandleSEIMessagesProc(void *pUserData, RocdecSeiMessageInfo *pSEIMessageInfo) { return ((RocVideoDecoder *)pUserData)->GetSEIMessage(pSEIMessageInfo); }
|
||||
static int ROCDECAPI HandleSEIMessagesProc(void *p_user_data, RocdecSeiMessageInfo *p_sei_message_info) { return ((RocVideoDecoder *)p_user_data)->GetSEIMessage(p_sei_message_info); }
|
||||
|
||||
/**
|
||||
* @brief This function gets called when a sequence is ready to be decoded. The function also gets called
|
||||
when there is format change
|
||||
*/
|
||||
int HandleVideoSequence(RocdecVideoFormat *pVideoFormat);
|
||||
int HandleVideoSequence(RocdecVideoFormat *p_video_format);
|
||||
|
||||
/**
|
||||
* @brief This function gets called when a picture is ready to be decoded. cuvidDecodePicture is called from this function
|
||||
* to decode the picture
|
||||
*/
|
||||
int HandlePictureDecode(RocdecPicParams *pPicParams);
|
||||
int HandlePictureDecode(RocdecPicParams *p_pic_params);
|
||||
|
||||
/**
|
||||
* @brief This function gets called after a picture is decoded and available for display. Frames are fetched and stored in
|
||||
internal buffer
|
||||
*/
|
||||
int HandlePictureDisplay(RocdecParserDispInfo *pDispInfo);
|
||||
int HandlePictureDisplay(RocdecParserDispInfo *p_disp_info);
|
||||
/**
|
||||
* @brief This function gets called when all unregistered user SEI messages are parsed for a frame
|
||||
*/
|
||||
int GetSEIMessage(RocdecSeiMessageInfo *pSEIMessageInfo);
|
||||
int GetSEIMessage(RocdecSeiMessageInfo *p_sei_message_info);
|
||||
|
||||
/**
|
||||
* @brief This function reconfigure decoder if there is a change in sequence params.
|
||||
*/
|
||||
int ReconfigureDecoder(RocdecVideoFormat *pVideoFormat);
|
||||
int ReconfigureDecoder(RocdecVideoFormat *p_video_format);
|
||||
|
||||
/**
|
||||
* @brief Function to Initialize GPU-HIP
|
||||
@@ -339,7 +339,7 @@ class RocVideoDecoder {
|
||||
int device_id_;
|
||||
RocdecVideoParser rocdec_parser_ = nullptr;
|
||||
rocDecDecoderHandle roc_decoder_ = nullptr;
|
||||
OUTPUT_SURF_MEMORY_TYPE out_mem_type_ = OUT_SURFACE_MEM_DEV_INTERNAL;
|
||||
OutputSurfaceMemoryType out_mem_type_ = OUT_SURFACE_MEM_DEV_INTERNAL;
|
||||
bool b_extract_sei_message_ = false;
|
||||
bool b_low_latency_ = true;
|
||||
bool b_force_zero_latency_ = true;
|
||||
|
||||
Reference in New Issue
Block a user