From 67c5b7780638a81707a6f47af9daf5aa3de6a026 Mon Sep 17 00:00:00 2001 From: jeffqjiangNew <142832361+jeffqjiangNew@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:40:45 -0500 Subject: [PATCH] MD5 digest generation support (#109) * * rocDecode/HEVC: Added support for MD5 message digest generation. - Command option "-md5" triggers MD5 calculation and output. * * rocDecode/HEVC: Changed C type casting to C++ type casting. [ROCm/rocdecode commit: ad20961c4ab194aa51ad94769d2624c732d41477] --- .../samples/videoDecode/videodecode.cpp | 25 ++++++ .../utils/rocvideodecode/roc_video_dec.cpp | 80 ++++++++++++++++++- .../utils/rocvideodecode/roc_video_dec.h | 26 ++++++ 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/projects/rocdecode/samples/videoDecode/videodecode.cpp b/projects/rocdecode/samples/videoDecode/videodecode.cpp index 54b14a30c2..7da7d51109 100644 --- a/projects/rocdecode/samples/videoDecode/videodecode.cpp +++ b/projects/rocdecode/samples/videoDecode/videodecode.cpp @@ -43,6 +43,7 @@ void ShowHelpAndExit(const char *option = NULL) { << "-d GPU device ID (0 for the first device, 1 for the second, etc.); optional; default: 0" << std::endl << "-z force_zero_latency (force_zero_latency, Decoded frames will be flushed out for display immediately); optional;" << std::endl << "-sei extract SEI messages; optional;" << std::endl + << "-md5 generate MD5 message digest on the decoded YUV image sequence; 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]" << std::endl; @@ -56,6 +57,7 @@ int main(int argc, char **argv) { int device_id = 0; bool b_force_zero_latency = false; // false by default: enabling this option might affect decoding performance bool b_extract_sei_messages = false; + bool b_generate_md5 = false; Rect crop_rect = {}; Rect *p_crop_rect = nullptr; OutputSurfaceMemoryType mem_type = OUT_SURFACE_MEM_DEV_INTERNAL; // set to internal @@ -103,6 +105,13 @@ int main(int argc, char **argv) { b_extract_sei_messages = true; continue; } + if (!strcmp(argv[i], "-md5")) { + if (i == argc) { + ShowHelpAndExit("-md5"); + } + b_generate_md5 = true; + continue; + } if (!strcmp(argv[i], "-crop")) { if (++i == argc || 4 != sscanf(argv[i], "%d,%d,%d,%d", &crop_rect.l, &crop_rect.t, &crop_rect.r, &crop_rect.b)) { ShowHelpAndExit("-crop"); @@ -146,6 +155,10 @@ int main(int argc, char **argv) { uint32_t width, height; double total_dec_time = 0; + if (b_generate_md5) { + viddec.InitMd5(); + } + do { auto start_time = std::chrono::high_resolution_clock::now(); demuxer.Demux(&pvideo, &n_video_bytes, &pts); @@ -163,6 +176,9 @@ 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); + } if (dump_output_frames) { viddec.SaveFrameToFile(output_file_path, pframe, surf_info); } @@ -177,6 +193,15 @@ int main(int argc, char **argv) { 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; } + if (b_generate_md5) { + uint8_t *digest; + viddec.FinalizeMd5(&digest); + std::cout << "MD5 message digest: "; + for (int i = 0; i < 16; i++) { + std::cout << std::hex << static_cast(digest[i]); + } + std::cout << std::endl; + } } catch (const std::exception &ex) { std::cout << ex.what() << std::endl; exit(1); diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp index 1f087fa9a6..ce763be003 100644 --- a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.cpp @@ -669,7 +669,7 @@ void RocVideoDecoder::SaveFrameToFile(std::string output_file_name, void *surf_m return; } } else - hst_ptr = (uint8_t *)surf_mem; + hst_ptr = static_cast (surf_mem); uint8_t *tmp_hst_ptr = hst_ptr; @@ -730,6 +730,84 @@ void RocVideoDecoder::SaveFrameToFile(std::string output_file_name, void *surf_m } } +void RocVideoDecoder::InitMd5() { + md5_ctx_ = av_md5_alloc(); + av_md5_init(md5_ctx_); +} + +void RocVideoDecoder::UpdateMd5ForFrame(void *surf_mem, OutputSurfaceInfo *surf_info) { + int i; + 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) { + if (hst_ptr == nullptr) { + hst_ptr = new uint8_t [output_image_size]; + } + hipError_t hip_status = hipSuccess; + hip_status = hipMemcpyDtoH((void *)hst_ptr, surf_mem, output_image_size); + if (hip_status != hipSuccess) { + std::cout << "ERROR: hipMemcpyDtoH failed! (" << hip_status << ")" << std::endl; + delete [] hst_ptr; + return; + } + } else + hst_ptr = static_cast (surf_mem); + + // Need to covert interleaved planar to stacked planar, assuming 4:2:0 chroma sampling. + uint8_t *stacked_ptr = new uint8_t [output_image_size]; + + uint8_t *tmp_hst_ptr = hst_ptr; + uint8_t *tmp_stacked_ptr = stacked_ptr; + int img_width = surf_info->output_width; + int img_height = surf_info->output_height; + int output_stride = surf_info->output_pitch; + // Luma + if (img_width * surf_info->bytes_per_pixel == output_stride && img_height == surf_info->output_vstride) { + memcpy(stacked_ptr, hst_ptr, img_width * surf_info->bytes_per_pixel * img_height); + } else { + for (i = 0; i < img_height; i++) { + memcpy(tmp_stacked_ptr, tmp_hst_ptr, img_width * surf_info->bytes_per_pixel); + tmp_hst_ptr += output_stride; + tmp_stacked_ptr += img_width * surf_info->bytes_per_pixel; + } + } + // Chroma + int img_width_chroma = img_width >> 1; + tmp_hst_ptr = hst_ptr + output_stride * surf_info->output_vstride; + tmp_stacked_ptr = stacked_ptr + img_width * surf_info->bytes_per_pixel * img_height; // Cb + uint8_t *tmp_stacked_ptr_v = tmp_stacked_ptr + img_width_chroma * surf_info->bytes_per_pixel * chroma_height_; // Cr + for (i = 0; i < chroma_height_; i++) { + for ( int j = 0; j < img_width_chroma; j++) { + uint8_t *src_ptr, *dst_ptr; + // Cb + src_ptr = &tmp_hst_ptr[j * surf_info->bytes_per_pixel * 2]; + dst_ptr = &tmp_stacked_ptr[j * surf_info->bytes_per_pixel]; + memcpy(dst_ptr, src_ptr, surf_info->bytes_per_pixel); + // Cr + src_ptr += surf_info->bytes_per_pixel; + dst_ptr = &tmp_stacked_ptr_v[j * surf_info->bytes_per_pixel]; + memcpy(dst_ptr, src_ptr, surf_info->bytes_per_pixel); + } + tmp_hst_ptr += output_stride; + tmp_stacked_ptr += img_width_chroma * surf_info->bytes_per_pixel; + tmp_stacked_ptr_v += img_width_chroma * surf_info->bytes_per_pixel; + } + + int img_size = img_width * surf_info->bytes_per_pixel * (img_height + chroma_height_); + av_md5_update(md5_ctx_, stacked_ptr, img_size); + + if (hst_ptr && (surf_info->mem_type != OUT_SURFACE_MEM_HOST_COPIED)) { + delete [] hst_ptr; + } + delete [] stacked_ptr; +} + +void RocVideoDecoder::FinalizeMd5(uint8_t **digest) { + av_md5_final(md5_ctx_, md5_digest_); + av_freep(&md5_ctx_); + *digest = md5_digest_; +} + void RocVideoDecoder::GetDeviceinfo(std::string &device_name, std::string &gcn_arch_name, int &pci_bus_id, int &pci_domain_id, int &pci_device_id) { device_name = hip_dev_prop_.name; gcn_arch_name = hip_dev_prop_.gcnArchName; diff --git a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h index 2b6b643458..fded0cc36e 100644 --- a/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h +++ b/projects/rocdecode/utils/rocvideodecode/roc_video_dec.h @@ -34,6 +34,10 @@ THE SOFTWARE. #include #include #include +extern "C" { +#include "libavutil/md5.h" +#include "libavutil/mem.h" +} #include "rocdecode.h" #include "rocparser.h" @@ -281,6 +285,26 @@ class RocVideoDecoder { */ void SaveFrameToFile(std::string output_file_name, void *surf_mem, OutputSurfaceInfo *surf_info); + /** + * @brief Helper function to start MD5 calculation + */ + void InitMd5(); + + /** + * @brief Helper function to dump decoded output surface to file + * + * @param dev_mem - pointer to surface memory + * @param surf_info - surface info + */ + void UpdateMd5ForFrame(void *surf_mem, OutputSurfaceInfo *surf_info); + + /** + * @brief Helper function to complete MD5 calculation + * + * @param [out] digest Pointer to the 16 byte message digest + */ + void FinalizeMd5(uint8_t **digest); + private: int decoder_session_id_; // Decoder session identifier. Used to gather session level stats. /** @@ -375,4 +399,6 @@ class RocVideoDecoder { Rect crop_rect_ = {}; FILE *fp_sei_ = NULL; FILE *fp_out_ = NULL; + struct AVMD5 *md5_ctx_; + uint8_t md5_digest_[16]; }; \ No newline at end of file