Added the bit stream reader feature. (#433)
* * rocDecode/ES parser: Added elementary stream file parser for HEVC and AVC.
* * rocDecode/ES parser: Added elementary stream file parser for AV1. Also cleaned up the bitstream ring buffer code.
* * rocDecode/ES parser: Added the IVF container file parser for AV1. Also fixed a bug in fill ring buffer function.
* * rocDecode/ES file parder: Added supported stream type detection.
- The stream type detection checks the unique syntax patterns of the stream type and calculate the likeliheed score. Based on the score, the most likely type is determined.
- The current supported stream types are: AVC/HEVC/AV1 elementary streams, IVF AV1 streams.
* * rocDecode/ES file parser: Fixed an AVC decode regression due to a copy and paste error.
* * rocDecode/ES file parser: Added bit depth parsing for codec support check; Added stronger AV1 detection for IVF AV1 stream type.
* * rocDecode/ES file parser: Removed debugging logs.
* * rocDecode/ES file parser: Added exmaple code to use the built-in file parser.
* * rocDecode/Bitstream reader: Renamed the elementary parser feature to bitstream reader and re-organized the code.
- Moved the bitstream reader code to rocDecode core lib from utility.
- Added bitstream reader interface in parallel with rocDecode parser and decoder interfaces.
* * rocDecode/Bitstream reader: Added sample to use bitstream reader, instead of FFMPEG demuxer, to get picture data. Also reverted the original sample app back to using FFMPEG demuxer only.
* * rocDecode/Bitstream reader: Renamed the new sample app.
* * rocDecode/Bitstream reader: FFMPEG dependency reduction.
- Moved MD5 functions out of RocVideoDecoder utility class. This removed RocVideoDecoder's dependency on FFMPEG.
- Added the new MD5 utility, which depends on FFMPEG lib. MD5 message digest generation is now performed in the MD5 utility.
- Modified decode sampples that uses MD5 generation function.
- Removed FFMPEG dependency from video decoder basic sample.
* * rocDecode/Bitstream reader: Added option to use bitstream reader to video decode sample and conformance test script. Added the missing destroy bitstream reader call in video decode basic sample.
* * rocDecode/Bitstream reader: Minor format change. No functional changes.
* * rocDecode/Bitstream reader: Added handling of unsupported stream file type by the bitstream reader to decode sample apps.
* * rocDecode/Bitstream reader: Fixed build errors of several samples.
* * rocDecode/Bitstream reader: Added changes based on review comments.
* * rocDecode/Bitstream reader: File name changes based on review comments.
* * rocDecode/Bitstream reader: Moved MD5 code into single header file. Added changes based on review comments.
* * rocDecode/Bitstream reader: Removed redundant path.
* * rocDecode/Bitstream reader: Changed rocDecode version to 0.10.0. Added minor changes based on review comments.
---------
Co-authored-by: Kiriti Gowda <kiritigowda@gmail.com>
[ROCm/rocdecode commit: e62aa3e09b]
This commit is contained in:
zatwierdzone przez
GitHub
rodzic
fb82691ef2
commit
c8a16141e4
@@ -23,6 +23,7 @@ THE SOFTWARE.
|
||||
#pragma once
|
||||
|
||||
#include "roc_video_dec.h"
|
||||
#include "md5.h"
|
||||
|
||||
typedef enum ReconfigFlushMode_enum {
|
||||
RECONFIG_FLUSH_MODE_NONE = 0, /**< Just flush to get the frame count */
|
||||
@@ -34,6 +35,7 @@ typedef enum ReconfigFlushMode_enum {
|
||||
typedef struct ReconfigDumpFileStruct_t {
|
||||
bool b_dump_frames_to_file;
|
||||
std::string output_file_name;
|
||||
void *md5_generator_handle;
|
||||
} ReconfigDumpFileStruct;
|
||||
|
||||
|
||||
@@ -53,13 +55,14 @@ int ReconfigureFlushCallback(void *p_viddec_obj, uint32_t flush_mode, void *p_us
|
||||
int64_t pts;
|
||||
while ((pframe = viddec->GetFrame(&pts))) {
|
||||
if (flush_mode != RECONFIG_FLUSH_MODE_NONE) {
|
||||
ReconfigDumpFileStruct *p_dump_file_struct = static_cast<ReconfigDumpFileStruct *>(p_user_struct);
|
||||
if (flush_mode == ReconfigFlushMode::RECONFIG_FLUSH_MODE_DUMP_TO_FILE) {
|
||||
ReconfigDumpFileStruct *p_dump_file_struct = static_cast<ReconfigDumpFileStruct *>(p_user_struct);
|
||||
if (p_dump_file_struct->b_dump_frames_to_file) {
|
||||
viddec->SaveFrameToFile(p_dump_file_struct->output_file_name, pframe, surf_info);
|
||||
}
|
||||
} else if (flush_mode == ReconfigFlushMode::RECONFIG_FLUSH_MODE_CALCULATE_MD5) {
|
||||
viddec->UpdateMd5ForFrame(pframe, surf_info);
|
||||
MD5Generator *md5_generator = static_cast<MD5Generator*>(p_dump_file_struct->md5_generator_handle);
|
||||
md5_generator->UpdateMd5ForFrame(pframe, surf_info);
|
||||
}
|
||||
}
|
||||
// release and flush frame
|
||||
|
||||
@@ -38,4 +38,5 @@ make -j
|
||||
-md5_check MD5_File_Path <generate MD5 message digest on the decoded YUV image sequence and compare to the reference MD5 string in a file [optional]>
|
||||
-crop <crop rectangle for output (not used when using interopped decoded frame) [optional - default: 0,0,0,0]>
|
||||
-m <output_surface_memory_type - decoded surface memory [optional - default: 0][0 : OUT_SURFACE_MEM_DEV_INTERNAL/ 1 : OUT_SURFACE_MEM_DEV_COPIED/ 2 : OUT_SURFACE_MEM_HOST_COPIED/3 : OUT_SURFACE_MEM_NOT_MAPPED]>
|
||||
-no_ffmpeg_demux <use the built-in bitstream reader instead of FFMPEG demuxer to obtain picture data [optional]>
|
||||
```
|
||||
@@ -37,6 +37,7 @@ THE SOFTWARE.
|
||||
#include <experimental/filesystem>
|
||||
#endif
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_bitstream_reader.h"
|
||||
#include "roc_video_dec.h"
|
||||
#include "ffmpeg_video_dec.h"
|
||||
#include "common.h"
|
||||
@@ -59,7 +60,8 @@ void ShowHelpAndExit(const char *option = NULL) {
|
||||
<< "-seek_criteria - Demux seek criteria & value - optional; default - 0,0; "
|
||||
<< "[0: no seek; 1: SEEK_CRITERIA_FRAME_NUM, frame number; 2: SEEK_CRITERIA_TIME_STAMP, frame number (time calculated internally)]" << std::endl
|
||||
<< "-seek_mode - Seek to previous key frame or exact - optional; default - 0"
|
||||
<< "[0: SEEK_MODE_PREV_KEY_FRAME; 1: SEEK_MODE_EXACT_FRAME]" << std::endl;
|
||||
<< "[0: SEEK_MODE_PREV_KEY_FRAME; 1: SEEK_MODE_EXACT_FRAME]" << std::endl
|
||||
<< "-no_ffmpeg_demux - use the built-in bitstream reader instead of FFMPEG demuxer to obtain picture data; optional." << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ int main(int argc, char **argv) {
|
||||
// seek options
|
||||
uint64_t seek_to_frame = 0;
|
||||
int seek_criteria = 0, seek_mode = 0;
|
||||
bool b_use_ffmpeg_demuxer = true; // true by default to use FFMPEG demuxer. set to false to use the built-in bitstream reader.
|
||||
|
||||
// Parse command-line arguments
|
||||
if(argc <= 1) {
|
||||
@@ -207,6 +210,13 @@ int main(int argc, char **argv) {
|
||||
ShowHelpAndExit("-seek_mode");
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-no_ffmpeg_demux")) {
|
||||
if (i == argc) {
|
||||
ShowHelpAndExit("-no_ffmpeg_demux");
|
||||
}
|
||||
b_use_ffmpeg_demuxer = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
ShowHelpAndExit(argv[i]);
|
||||
}
|
||||
@@ -214,10 +224,38 @@ int main(int argc, char **argv) {
|
||||
try {
|
||||
std::size_t found_file = input_file_path.find_last_of('/');
|
||||
std::cout << "info: Input file: " << input_file_path.substr(found_file + 1) << std::endl;
|
||||
VideoDemuxer *demuxer;
|
||||
RocdecBitstreamReader bs_reader = nullptr;
|
||||
rocDecVideoCodec rocdec_codec_id;
|
||||
int bit_depth;
|
||||
|
||||
if (b_use_ffmpeg_demuxer) {
|
||||
std::cout << "info: Using FFMPEG demuxer" << std::endl;
|
||||
demuxer = new VideoDemuxer(input_file_path.c_str());
|
||||
rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer->GetCodecID());
|
||||
bit_depth = demuxer->GetBitDepth();
|
||||
} else {
|
||||
std::cout << "info: Using built-in bitstream reader" << std::endl;
|
||||
if (rocDecCreateBitstreamReader(&bs_reader, input_file_path.c_str()) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to create the bitstream reader." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocDecGetBitstreamCodecType(bs_reader, &rocdec_codec_id) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get stream codec type." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocdec_codec_id >= rocDecVideoCodec_NumCodecs) {
|
||||
std::cerr << "Unsupported stream file type or codec type by the bitstream reader. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocDecGetBitstreamBitDepth(bs_reader, &bit_depth) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get stream bit depth." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
RocVideoDecoder *viddec;
|
||||
VideoDemuxer demuxer(input_file_path.c_str());
|
||||
VideoSeekContext video_seek_ctx;
|
||||
rocDecVideoCodec rocdec_codec_id = AVCodec2RocDecVideoCodec(demuxer.GetCodecID());
|
||||
if (!backend) // gpu backend
|
||||
viddec = new RocVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay);
|
||||
else {
|
||||
@@ -230,10 +268,10 @@ int main(int argc, char **argv) {
|
||||
viddec = new FFMpegVideoDecoder(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay, true);
|
||||
}
|
||||
|
||||
if(!viddec->CodecSupported(device_id, rocdec_codec_id, demuxer.GetBitDepth())) {
|
||||
if(!viddec->CodecSupported(device_id, rocdec_codec_id, bit_depth)) {
|
||||
std::cerr << "rocDecode doesn't support codec!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
std::string device_name, gcn_arch_name;
|
||||
int pci_bus_id, pci_domain_id, pci_device_id;
|
||||
|
||||
@@ -253,6 +291,8 @@ int main(int argc, char **argv) {
|
||||
uint32_t width, height;
|
||||
double total_dec_time = 0;
|
||||
bool first_frame = true;
|
||||
MD5Generator *md5_generator = nullptr;
|
||||
|
||||
// initialize reconfigure params: the following is configured to dump to output which is relevant for this sample
|
||||
reconfig_params.p_fn_reconfigure_flush = ReconfigureFlushCallback;
|
||||
reconfig_user_struct.b_dump_frames_to_file = dump_output_frames;
|
||||
@@ -267,32 +307,41 @@ int main(int argc, char **argv) {
|
||||
reconfig_params.p_reconfig_user_struct = &reconfig_user_struct;
|
||||
|
||||
if (b_generate_md5) {
|
||||
viddec->InitMd5();
|
||||
md5_generator = new MD5Generator();
|
||||
md5_generator->InitMd5();
|
||||
reconfig_user_struct.md5_generator_handle = static_cast<void*>(md5_generator);
|
||||
}
|
||||
viddec->SetReconfigParams(&reconfig_params);
|
||||
|
||||
do {
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
if (seek_criteria == 1 && first_frame) {
|
||||
// use VideoSeekContext class to seek to given frame number
|
||||
video_seek_ctx.seek_frame_ = seek_to_frame;
|
||||
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_FRAME_NUM;
|
||||
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
|
||||
demuxer.Seek(video_seek_ctx, &pvideo, &n_video_bytes);
|
||||
pts = video_seek_ctx.out_frame_pts_;
|
||||
std::cout << "info: Number of frames that were decoded during seek - " << video_seek_ctx.num_frames_decoded_ << std::endl;
|
||||
first_frame = false;
|
||||
} else if (seek_criteria == 2 && first_frame) {
|
||||
// use VideoSeekContext class to seek to given timestamp
|
||||
video_seek_ctx.seek_frame_ = seek_to_frame;
|
||||
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_TIME_STAMP;
|
||||
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
|
||||
demuxer.Seek(video_seek_ctx, &pvideo, &n_video_bytes);
|
||||
pts = video_seek_ctx.out_frame_pts_;
|
||||
std::cout << "info: Duration of frame found after seek - " << video_seek_ctx.out_frame_duration_ << " ms" << std::endl;
|
||||
first_frame = false;
|
||||
if (b_use_ffmpeg_demuxer) {
|
||||
if (seek_criteria == 1 && first_frame) {
|
||||
// use VideoSeekContext class to seek to given frame number
|
||||
video_seek_ctx.seek_frame_ = seek_to_frame;
|
||||
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_FRAME_NUM;
|
||||
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
|
||||
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
|
||||
pts = video_seek_ctx.out_frame_pts_;
|
||||
std::cout << "info: Number of frames that were decoded during seek - " << video_seek_ctx.num_frames_decoded_ << std::endl;
|
||||
first_frame = false;
|
||||
} else if (seek_criteria == 2 && first_frame) {
|
||||
// use VideoSeekContext class to seek to given timestamp
|
||||
video_seek_ctx.seek_frame_ = seek_to_frame;
|
||||
video_seek_ctx.seek_crit_ = SEEK_CRITERIA_TIME_STAMP;
|
||||
video_seek_ctx.seek_mode_ = (seek_mode ? SEEK_MODE_EXACT_FRAME : SEEK_MODE_PREV_KEY_FRAME);
|
||||
demuxer->Seek(video_seek_ctx, &pvideo, &n_video_bytes);
|
||||
pts = video_seek_ctx.out_frame_pts_;
|
||||
std::cout << "info: Duration of frame found after seek - " << video_seek_ctx.out_frame_duration_ << " ms" << std::endl;
|
||||
first_frame = false;
|
||||
} else {
|
||||
demuxer->Demux(&pvideo, &n_video_bytes, &pts);
|
||||
}
|
||||
} else {
|
||||
demuxer.Demux(&pvideo, &n_video_bytes, &pts);
|
||||
if (rocDecGetBitstreamPicData(bs_reader, &pvideo, &n_video_bytes, &pts) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get picture data." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Treat 0 bitstream size as end of stream indicator
|
||||
if (n_video_bytes == 0) {
|
||||
@@ -307,7 +356,7 @@ int main(int argc, char **argv) {
|
||||
for (int i = 0; i < n_frame_returned; i++) {
|
||||
pframe = viddec->GetFrame(&pts);
|
||||
if (b_generate_md5) {
|
||||
viddec->UpdateMd5ForFrame(pframe, surf_info);
|
||||
md5_generator->UpdateMd5ForFrame(pframe, surf_info);
|
||||
}
|
||||
if (dump_output_frames && mem_type != OUT_SURFACE_MEM_NOT_MAPPED) {
|
||||
viddec->SaveFrameToFile(output_file_path, pframe, surf_info);
|
||||
@@ -323,7 +372,6 @@ int main(int argc, char **argv) {
|
||||
if (num_decoded_frames && num_decoded_frames <= n_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (n_video_bytes);
|
||||
|
||||
n_frame += viddec->GetNumOfFlushedFrames();
|
||||
@@ -343,7 +391,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (b_generate_md5) {
|
||||
uint8_t *digest;
|
||||
viddec->FinalizeMd5(&digest);
|
||||
md5_generator->FinalizeMd5(&digest);
|
||||
std::cout << "MD5 message digest: ";
|
||||
for (int i = 0; i < 16; i++) {
|
||||
std::cout << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
@@ -371,9 +419,15 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
std::cout << "MD5 digest does not match the reference MD5 digest: ";
|
||||
}
|
||||
std::cout << ref_md5_string << std::endl;
|
||||
std::cout << ref_md5_string.c_str() << std::endl;
|
||||
ref_md5_file.close();
|
||||
}
|
||||
delete md5_generator;
|
||||
}
|
||||
if (b_use_ffmpeg_demuxer && demuxer) {
|
||||
delete demuxer;
|
||||
} else if (bs_reader) {
|
||||
rocDecDestroyBitstreamReader(bs_reader);
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
std::cout << ex.what() << std::endl;
|
||||
|
||||
@@ -37,6 +37,7 @@ THE SOFTWARE.
|
||||
#endif
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_video_dec.h"
|
||||
#include "md5.h"
|
||||
|
||||
class FileStreamProvider : public VideoDemuxer::StreamProvider {
|
||||
public:
|
||||
@@ -210,9 +211,11 @@ int main(int argc, char **argv) {
|
||||
OutputSurfaceInfo *surf_info;
|
||||
uint32_t width, height;
|
||||
double total_dec_time = 0;
|
||||
MD5Generator *md5_generator = nullptr;
|
||||
|
||||
if (b_generate_md5) {
|
||||
viddec.InitMd5();
|
||||
md5_generator = new MD5Generator();
|
||||
md5_generator->InitMd5();
|
||||
}
|
||||
|
||||
do {
|
||||
@@ -233,7 +236,7 @@ int main(int argc, char **argv) {
|
||||
for (int i = 0; i < n_frame_returned; i++) {
|
||||
pframe = viddec.GetFrame(&pts);
|
||||
if (b_generate_md5) {
|
||||
viddec.UpdateMd5ForFrame(pframe, surf_info);
|
||||
md5_generator->UpdateMd5ForFrame(pframe, surf_info);
|
||||
}
|
||||
if (dump_output_frames && mem_type != OUT_SURFACE_MEM_NOT_MAPPED) {
|
||||
viddec.SaveFrameToFile(output_file_path, pframe, surf_info);
|
||||
@@ -258,7 +261,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (b_generate_md5) {
|
||||
uint8_t *digest;
|
||||
viddec.FinalizeMd5(&digest);
|
||||
md5_generator->FinalizeMd5(&digest);
|
||||
std::cout << "MD5 message digest: ";
|
||||
for (int i = 0; i < 16; i++) {
|
||||
std::cout << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
@@ -289,6 +292,7 @@ int main(int argc, char **argv) {
|
||||
std::cout << ref_md5_string << std::endl;
|
||||
ref_md5_file.close();
|
||||
}
|
||||
delete md5_generator;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
std::cout << ex.what() << std::endl;
|
||||
|
||||
@@ -42,6 +42,7 @@ THE SOFTWARE.
|
||||
#include "video_demuxer.h"
|
||||
#include "roc_video_dec.h"
|
||||
#include "video_post_process.h"
|
||||
#include "md5.h"
|
||||
|
||||
std::vector<std::string> st_output_format_name = {"native", "bgr", "bgr48", "rgb", "rgb48", "bgra", "bgra64", "rgba", "rgba64"};
|
||||
|
||||
@@ -65,7 +66,7 @@ std::condition_variable cv[frame_buffers_size];
|
||||
|
||||
void ColorSpaceConversionThread(std::atomic<bool>& continue_processing, bool convert_to_rgb, Dim *p_resize_dim, OutputSurfaceInfo **surf_info, OutputSurfaceInfo **res_surf_info,
|
||||
OutputFormatEnum e_output_format, uint8_t *p_rgb_dev_mem, uint8_t *p_resize_dev_mem, bool dump_output_frames,
|
||||
std::string &output_file_path, RocVideoDecoder &viddec, VideoPostProcess &post_proc, bool b_generate_md5) {
|
||||
std::string &output_file_path, RocVideoDecoder &viddec, VideoPostProcess &post_proc, MD5Generator *md5_gen_handle, bool b_generate_md5) {
|
||||
|
||||
size_t rgb_image_size, resize_image_size;
|
||||
hipError_t hip_status = hipSuccess;
|
||||
@@ -138,7 +139,7 @@ void ColorSpaceConversionThread(std::atomic<bool>& continue_processing, bool con
|
||||
viddec.SaveFrameToFile(output_file_path, out_frame, p_surf_info);
|
||||
}
|
||||
if(b_generate_md5 && convert_to_rgb){
|
||||
viddec.UpdateMd5ForDataBuffer(p_rgb_dev_mem, rgb_image_size);
|
||||
md5_gen_handle->UpdateMd5ForDataBuffer(p_rgb_dev_mem, rgb_image_size);
|
||||
}
|
||||
|
||||
|
||||
@@ -270,6 +271,7 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
VideoPostProcess post_process;
|
||||
MD5Generator *md5_generator = nullptr;
|
||||
|
||||
std::string device_name, gcn_arch_name;
|
||||
int pci_bus_id, pci_domain_id, pci_device_id;
|
||||
@@ -282,7 +284,8 @@ int main(int argc, char **argv) {
|
||||
std::cout << "info: decoding started, please wait!" << std::endl;
|
||||
|
||||
if (b_generate_md5) {
|
||||
viddec.InitMd5();
|
||||
md5_generator = new MD5Generator();
|
||||
md5_generator->InitMd5();
|
||||
}
|
||||
|
||||
int n_video_bytes = 0, n_frames_returned = 0, n_frame = 0;
|
||||
@@ -296,7 +299,7 @@ int main(int argc, char **argv) {
|
||||
convert_to_rgb = e_output_format != native;
|
||||
std::atomic<bool> continue_processing(true);
|
||||
std::thread color_space_conversion_thread(ColorSpaceConversionThread, std::ref(continue_processing), std::ref(convert_to_rgb), &resize_dim, &surf_info, &resize_surf_info, std::ref(e_output_format),
|
||||
std::ref(p_rgb_dev_mem), std::ref(p_resize_dev_mem), std::ref(dump_output_frames), std::ref(output_file_path), std::ref(viddec), std::ref(post_process), b_generate_md5);
|
||||
std::ref(p_rgb_dev_mem), std::ref(p_resize_dev_mem), std::ref(dump_output_frames), std::ref(output_file_path), std::ref(viddec), std::ref(post_process), md5_generator, b_generate_md5);
|
||||
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
do {
|
||||
@@ -379,7 +382,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (b_generate_md5) {
|
||||
uint8_t *digest;
|
||||
viddec.FinalizeMd5(&digest);
|
||||
md5_generator->FinalizeMd5(&digest);
|
||||
std::cout << "MD5 message digest: ";
|
||||
for (int i = 0; i < 16; i++) {
|
||||
std::cout << std::setfill('0') << std::setw(2) << std::hex << static_cast<int>(digest[i]);
|
||||
@@ -410,6 +413,7 @@ int main(int argc, char **argv) {
|
||||
std::cout << ref_md5_string << std::endl;
|
||||
ref_md5_file.close();
|
||||
}
|
||||
delete md5_generator;
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
std::cout << ex.what() << std::endl;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
################################################################################
|
||||
# Copyright (c) 2023 - 2024 Advanced Micro Devices, Inc.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
|
||||
# ROCM Path
|
||||
if(DEFINED ENV{ROCM_PATH})
|
||||
set(ROCM_PATH $ENV{ROCM_PATH} CACHE PATH "${White}${PROJECT_NAME}: Default ROCm installation path${ColourReset}")
|
||||
elseif(ROCM_PATH)
|
||||
message("-- ${White}${PROJECT_NAME} :ROCM_PATH Set -- ${ROCM_PATH}${ColourReset}")
|
||||
else()
|
||||
set(ROCM_PATH /opt/rocm CACHE PATH "${White}${PROJECT_NAME}: Default ROCm installation path${ColourReset}")
|
||||
endif()
|
||||
# Set AMD Clang as default compiler
|
||||
if (NOT DEFINED CMAKE_CXX_COMPILER)
|
||||
set(CMAKE_CXX_COMPILER ${ROCM_PATH}/bin/amdclang++)
|
||||
endif()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake)
|
||||
list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/hip ${ROCM_PATH})
|
||||
|
||||
project(videodecoderaw)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# rocDecode sample build type
|
||||
set(DEFAULT_BUILD_TYPE "Release")
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "rocDecode Default Build Type" FORCE)
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release")
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
# -O0 -- Don't Optimize output file
|
||||
# -gdwarf-4 -- generate debugging information, dwarf-4 for making valgrind work
|
||||
# -Og -- Optimize for debugging experience rather than speed or size
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -gdwarf-4 -Og")
|
||||
else()
|
||||
# -O3 -- Optimize output file
|
||||
# -DNDEBUG -- turn off asserts
|
||||
# -fPIC -- Generate position-independent code if possible
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG -fPIC")
|
||||
endif()
|
||||
|
||||
find_package(HIP QUIET)
|
||||
find_package(rocDecode QUIET)
|
||||
|
||||
if(HIP_FOUND AND ROCDECODE_FOUND)
|
||||
# HIP
|
||||
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} hip::host)
|
||||
# rocDecode and utils
|
||||
include_directories (${ROCDECODE_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../../utils ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/rocvideodecode)
|
||||
set(LINK_LIBRARY_LIST ${LINK_LIBRARY_LIST} ${ROCDECODE_LIBRARY})
|
||||
# sample app exe
|
||||
list(APPEND SOURCES ${PROJECT_SOURCE_DIR} videodecoderaw.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../utils/rocvideodecode/roc_video_dec.cpp)
|
||||
add_executable(${PROJECT_NAME} ${SOURCES})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17")
|
||||
target_link_libraries(${PROJECT_NAME} ${LINK_LIBRARY_LIST})
|
||||
else()
|
||||
message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!")
|
||||
if (NOT HIP_FOUND)
|
||||
message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!")
|
||||
endif()
|
||||
if (NOT ROCDECODE_FOUND)
|
||||
message(FATAL_ERROR "-- ERROR!: rocDecode Not Found! - please install rocDecode!")
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,29 @@
|
||||
# Video decode sample
|
||||
|
||||
The video decode raw sample illustrates decoding a single packetized video stream using the built-in bitstream reader, video parser, and rocDecoder to get the individual decoded frames in YUV format. This sample can be configured with a device ID and optionally able to dump the output to a file. This sample uses the high-level RocVideoDecoder class which connects both the video parser and Rocdecoder. This process repeats in a loop until all frames have been decoded.
|
||||
|
||||
## Prerequisites:
|
||||
|
||||
* Install [rocDecode](../../README.md#build-and-install-instructions)
|
||||
|
||||
## Build
|
||||
|
||||
```shell
|
||||
mkdir video_decode_raw_sample && cd video_decode_raw_sample
|
||||
cmake ../
|
||||
make -j
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```shell
|
||||
./videodecoderaw -i <input video file [required]>
|
||||
-o <output path to save decoded YUV frames [optional]>
|
||||
-d <GPU device ID - 0:device 0 / 1:device 1/ ... [optional - default:0]>
|
||||
-f <Number of decoded frames - specify the number of pictures to be decoded [optional]>
|
||||
-z <force_zero_latency - Decoded frames will be flushed out for display immediately [optional]>
|
||||
-disp_delay <display delay - specify the number of frames to be delayed for display [optional - default: 1]>
|
||||
-sei <extract SEI messages [optional]>
|
||||
-crop <crop rectangle for output (not used when using interopped decoded frame) [optional - default: 0,0,0,0]>
|
||||
-m <output_surface_memory_type - decoded surface memory [optional - default: 0][0 : OUT_SURFACE_MEM_DEV_INTERNAL/ 1 : OUT_SURFACE_MEM_DEV_COPIED/ 2 : OUT_SURFACE_MEM_HOST_COPIED/3 : OUT_SURFACE_MEM_NOT_MAPPED]>
|
||||
```
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright (c) 2023 - 2024 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <sys/stat.h>
|
||||
#include <libgen.h>
|
||||
#if __cplusplus >= 201703L && __has_include(<filesystem>)
|
||||
#include <filesystem>
|
||||
#else
|
||||
#include <experimental/filesystem>
|
||||
#endif
|
||||
|
||||
#include "roc_bitstream_reader.h"
|
||||
#include "roc_video_dec.h"
|
||||
|
||||
typedef enum ReconfigFlushMode_enum {
|
||||
RECONFIG_FLUSH_MODE_NONE = 0, /**< Just flush to get the frame count */
|
||||
RECONFIG_FLUSH_MODE_DUMP_TO_FILE = 1, /**< The remaining frames will be dumped to file in this mode */
|
||||
RECONFIG_FLUSH_MODE_CALCULATE_MD5 = 2, /**< Calculate the MD5 of the flushed frames */
|
||||
} ReconfigFlushMode;
|
||||
|
||||
// this struct is used by videodecode and videodecodeMultiFiles to dump last frames to file
|
||||
typedef struct ReconfigDumpFileStruct_t {
|
||||
bool b_dump_frames_to_file;
|
||||
std::string output_file_name;
|
||||
void *md5_generator_handle;
|
||||
} ReconfigDumpFileStruct;
|
||||
|
||||
|
||||
// callback function to flush last frames and save it to file when reconfigure happens
|
||||
int ReconfigureFlushCallback(void *p_viddec_obj, uint32_t flush_mode, void *p_user_struct) {
|
||||
int n_frames_flushed = 0;
|
||||
if ((p_viddec_obj == nullptr) || (p_user_struct == nullptr)) return n_frames_flushed;
|
||||
|
||||
RocVideoDecoder *viddec = static_cast<RocVideoDecoder *> (p_viddec_obj);
|
||||
OutputSurfaceInfo *surf_info;
|
||||
if (!viddec->GetOutputSurfaceInfo(&surf_info)) {
|
||||
std::cerr << "Error: Failed to get Output Surface Info!" << std::endl;
|
||||
return n_frames_flushed;
|
||||
}
|
||||
|
||||
uint8_t *pframe = nullptr;
|
||||
int64_t pts;
|
||||
while ((pframe = viddec->GetFrame(&pts))) {
|
||||
if (flush_mode != RECONFIG_FLUSH_MODE_NONE) {
|
||||
ReconfigDumpFileStruct *p_dump_file_struct = static_cast<ReconfigDumpFileStruct *>(p_user_struct);
|
||||
if (flush_mode == ReconfigFlushMode::RECONFIG_FLUSH_MODE_DUMP_TO_FILE) {
|
||||
if (p_dump_file_struct->b_dump_frames_to_file) {
|
||||
viddec->SaveFrameToFile(p_dump_file_struct->output_file_name, pframe, surf_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
// release and flush frame
|
||||
viddec->ReleaseFrame(pts, true);
|
||||
n_frames_flushed ++;
|
||||
}
|
||||
|
||||
return n_frames_flushed;
|
||||
}
|
||||
|
||||
void ShowHelpAndExit(const char *option = NULL) {
|
||||
std::cout << "Options:" << std::endl
|
||||
<< "-i Input File Path - required" << std::endl
|
||||
<< "-o Output File Path - dumps output if requested; optional" << std::endl
|
||||
<< "-d GPU device ID (0 for the first device, 1 for the second, etc.); optional; default: 0" << std::endl
|
||||
<< "-f Number of decoded frames - specify the number of pictures to be decoded; optional" << std::endl
|
||||
<< "-z force_zero_latency (force_zero_latency, Decoded frames will be flushed out for display immediately); optional;" << std::endl
|
||||
<< "-disp_delay -specify the number of frames to be delayed for display; optional; default: 1" << std::endl
|
||||
<< "-sei extract SEI messages; optional;" << std::endl
|
||||
<< "-crop crop rectangle for output (not used when using interopped decoded frame); optional; default: 0" << std::endl
|
||||
<< "-m output_surface_memory_type - decoded surface memory; optional; default - 0"
|
||||
<< " [0 : OUT_SURFACE_MEM_DEV_INTERNAL/ 1 : OUT_SURFACE_MEM_DEV_COPIED/ 2 : OUT_SURFACE_MEM_HOST_COPIED/ 3 : OUT_SURFACE_MEM_NOT_MAPPED]" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::string input_file_path, output_file_path;
|
||||
int dump_output_frames = 0;
|
||||
int device_id = 0;
|
||||
int disp_delay = 1;
|
||||
bool b_force_zero_latency = false; // false by default: enabling this option might affect decoding performance
|
||||
bool b_extract_sei_messages = false;
|
||||
bool b_flush_frames_during_reconfig = true;
|
||||
Rect crop_rect = {};
|
||||
Rect *p_crop_rect = nullptr;
|
||||
OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal
|
||||
ReconfigParams reconfig_params = { 0 };
|
||||
ReconfigDumpFileStruct reconfig_user_struct = { 0 };
|
||||
uint32_t num_decoded_frames = 0; // default value is 0, meaning decode the entire stream
|
||||
|
||||
// Parse command-line arguments
|
||||
if(argc <= 1) {
|
||||
ShowHelpAndExit();
|
||||
}
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "-h")) {
|
||||
ShowHelpAndExit();
|
||||
}
|
||||
if (!strcmp(argv[i], "-i")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-i");
|
||||
}
|
||||
input_file_path = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-o")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-o");
|
||||
}
|
||||
output_file_path = argv[i];
|
||||
dump_output_frames = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-d")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-d");
|
||||
}
|
||||
device_id = atoi(argv[i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-disp_delay")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-disp_delay");
|
||||
}
|
||||
disp_delay = atoi(argv[i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-f")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-d");
|
||||
}
|
||||
num_decoded_frames = atoi(argv[i]);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-z")) {
|
||||
if (i == argc) {
|
||||
ShowHelpAndExit("-z");
|
||||
}
|
||||
b_force_zero_latency = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-sei")) {
|
||||
if (i == argc) {
|
||||
ShowHelpAndExit("-sei");
|
||||
}
|
||||
b_extract_sei_messages = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-crop")) {
|
||||
if (++i == argc || 4 != sscanf(argv[i], "%d,%d,%d,%d", &crop_rect.left, &crop_rect.top, &crop_rect.right, &crop_rect.bottom)) {
|
||||
ShowHelpAndExit("-crop");
|
||||
}
|
||||
if ((crop_rect.right - crop_rect.left) % 2 == 1 || (crop_rect.bottom - crop_rect.top) % 2 == 1) {
|
||||
std::cout << "output crop rectangle must have width and height of even numbers" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
p_crop_rect = &crop_rect;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "-m")) {
|
||||
if (++i == argc) {
|
||||
ShowHelpAndExit("-m");
|
||||
}
|
||||
mem_type = static_cast<OutputSurfaceMemoryType>(atoi(argv[i]));
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[i], "flush")) {
|
||||
b_flush_frames_during_reconfig = atoi(argv[i]) ? true : false;
|
||||
continue;
|
||||
}
|
||||
|
||||
ShowHelpAndExit(argv[i]);
|
||||
}
|
||||
|
||||
try {
|
||||
std::size_t found_file = input_file_path.find_last_of('/');
|
||||
std::cout << "info: Input file: " << input_file_path.substr(found_file + 1) << std::endl;
|
||||
std::cout << "info: Using built-in bitstream reader" << std::endl;
|
||||
RocdecBitstreamReader bs_reader = nullptr;
|
||||
rocDecVideoCodec rocdec_codec_id;
|
||||
int bit_depth;
|
||||
if (rocDecCreateBitstreamReader(&bs_reader, input_file_path.c_str()) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to create the bitstream reader." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocDecGetBitstreamCodecType(bs_reader, &rocdec_codec_id) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get stream codec type." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocdec_codec_id >= rocDecVideoCodec_NumCodecs) {
|
||||
std::cerr << "Unsupported stream file type or codec type by the bitstream reader. Exiting." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (rocDecGetBitstreamBitDepth(bs_reader, &bit_depth) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get stream bit depth." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
RocVideoDecoder viddec(device_id, mem_type, rocdec_codec_id, b_force_zero_latency, p_crop_rect, b_extract_sei_messages, disp_delay);
|
||||
if(!viddec.CodecSupported(device_id, rocdec_codec_id, bit_depth)) {
|
||||
std::cerr << "GPU doesn't support codec!" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
std::string device_name, gcn_arch_name;
|
||||
int pci_bus_id, pci_domain_id, pci_device_id;
|
||||
|
||||
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 n_video_bytes = 0, n_frame_returned = 0, n_frame = 0;
|
||||
int n_pic_decoded = 0, decoded_pics = 0;
|
||||
uint8_t *pvideo = nullptr;
|
||||
int pkg_flags = 0;
|
||||
uint8_t *pframe = nullptr;
|
||||
int64_t pts = 0;
|
||||
OutputSurfaceInfo *surf_info;
|
||||
uint32_t width, height;
|
||||
double total_dec_time = 0;
|
||||
bool first_frame = true;
|
||||
// initialize reconfigure params: the following is configured to dump to output which is relevant for this sample
|
||||
reconfig_params.p_fn_reconfigure_flush = ReconfigureFlushCallback;
|
||||
reconfig_user_struct.b_dump_frames_to_file = dump_output_frames;
|
||||
reconfig_user_struct.output_file_name = output_file_path;
|
||||
if (dump_output_frames) {
|
||||
reconfig_params.reconfig_flush_mode = RECONFIG_FLUSH_MODE_DUMP_TO_FILE;
|
||||
} else {
|
||||
reconfig_params.reconfig_flush_mode = RECONFIG_FLUSH_MODE_NONE;
|
||||
}
|
||||
reconfig_params.p_reconfig_user_struct = &reconfig_user_struct;
|
||||
|
||||
viddec.SetReconfigParams(&reconfig_params);
|
||||
|
||||
do {
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
if (rocDecGetBitstreamPicData(bs_reader, &pvideo, &n_video_bytes, &pts) != ROCDEC_SUCCESS) {
|
||||
std::cerr << "Failed to get picture data." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
// Treat 0 bitstream size as end of stream indicator
|
||||
if (n_video_bytes == 0) {
|
||||
pkg_flags |= ROCDEC_PKT_ENDOFSTREAM;
|
||||
}
|
||||
n_frame_returned = viddec.DecodeFrame(pvideo, n_video_bytes, pkg_flags, pts, &decoded_pics);
|
||||
|
||||
if (!n_frame && !viddec.GetOutputSurfaceInfo(&surf_info)) {
|
||||
std::cerr << "Error: Failed to get Output Surface Info!" << std::endl;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < n_frame_returned; i++) {
|
||||
pframe = viddec.GetFrame(&pts);
|
||||
if (dump_output_frames && mem_type != OUT_SURFACE_MEM_NOT_MAPPED) {
|
||||
viddec.SaveFrameToFile(output_file_path, pframe, surf_info);
|
||||
}
|
||||
// release frame
|
||||
viddec.ReleaseFrame(pts);
|
||||
}
|
||||
auto end_time = std::chrono::high_resolution_clock::now();
|
||||
auto time_per_decode = std::chrono::duration<double, std::milli>(end_time - start_time).count();
|
||||
total_dec_time += time_per_decode;
|
||||
n_frame += n_frame_returned;
|
||||
n_pic_decoded += decoded_pics;
|
||||
if (num_decoded_frames && num_decoded_frames <= n_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (n_video_bytes);
|
||||
|
||||
n_frame += viddec.GetNumOfFlushedFrames();
|
||||
std::cout << "info: Total pictures decoded: " << n_pic_decoded << std::endl;
|
||||
std::cout << "info: Total frames output/displayed: " << n_frame << std::endl;
|
||||
if (!dump_output_frames) {
|
||||
std::cout << "info: avg decoding time per picture: " << total_dec_time / n_pic_decoded << " ms" <<std::endl;
|
||||
std::cout << "info: avg decode FPS: " << (n_pic_decoded / total_dec_time) * 1000 << std::endl;
|
||||
std::cout << "info: avg output/display time per frame: " << total_dec_time / n_frame << " ms" <<std::endl;
|
||||
std::cout << "info: avg output/display FPS: " << (n_frame / total_dec_time) * 1000 << std::endl;
|
||||
} else {
|
||||
if (mem_type == OUT_SURFACE_MEM_NOT_MAPPED) {
|
||||
std::cout << "info: saving frames with -m 3 option is not supported!" << std::endl;
|
||||
} else {
|
||||
std::cout << "info: saved frames into " << output_file_path << std::endl;
|
||||
}
|
||||
}
|
||||
if (bs_reader) {
|
||||
rocDecDestroyBitstreamReader(bs_reader);
|
||||
}
|
||||
} catch (const std::exception &ex) {
|
||||
std::cout << ex.what() << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user