diff --git a/samples/jupyter_notebooks/videodecode.ipynb b/samples/jupyter_notebooks/videodecode.ipynb new file mode 100644 index 0000000..b822c2b --- /dev/null +++ b/samples/jupyter_notebooks/videodecode.ipynb @@ -0,0 +1,391 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n", + "\n", + "# rocPyDecode \n", + "##### rocDecode Python Binding\n", + "\n", + "> **NOTE**\n", + "> The published documentation is available at [rocPyDecode](https://rocm.docs.amd.com/projects/rocPyDecode/en/latest/index.html) in an organized, easy-to-read format, with search and a table of contents. The documentation source files reside in the `docs` folder of this repository. As with all ROCm projects, the documentation is open source. For more information on contributing to the documentation, see [Contribute to ROCm documentation](https://rocm.docs.amd.com/en/latest/contribute/contributing.html).\n", + "\n", + "\n", + "The rocDecode Python Binding, rocPyDecode, is a tool that allows users to access rocDecode APIs in both Python and C/C++ languages. It works by connecting Python and C/C++ libraries, enabling function calling and data passing between the two languages. The rocpydecode.so library is a wrapper that facilitates the use of rocDecode APIs that are written primarily in C/C++ language within Python.\n", + "\n", + "## Prerequisites\n", + "\n", + "* Linux distribution\n", + " * Ubuntu - `20.04` / `22.04`\n", + "\n", + "* [ROCm-supported hardware](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/reference/system-requirements.html)\n", + "> **IMPORTANT**\n", + "> `gfx908` or higher GPU required\n", + "\n", + "* Install ROCm `6.2.0` or later with [amdgpu-install](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/how-to/amdgpu-install.html): Required usecase - rocm\n", + "> **IMPORTANT**\n", + "> `sudo amdgpu-install --usecase=rocm`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Video Decode Python Sample" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Import rocPyDecode Modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pyRocVideoDecode.decoder as dec\n", + "import pyRocVideoDecode.demuxer as dmx" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Other needed modules" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "import sys\n", + "import argparse\n", + "import os.path" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Construct Demuxer & Decoder Instances " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Set parameters to construct demuxer and decoder instance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "input_file_path = \"../../data/videos/AMD_driving_virtual_20-H265.mp4\" # must be set to valid video existing video file\n", + "output_file_path = None\n", + "device_id = 0\n", + "mem_type = 0 \n", + "b_force_zero_latency = False\n", + "crop_rect = None\n", + "b_generate_md5 = False\n", + "ref_md5_file = None\n", + "seek_frame = -1\n", + "seek_mode = 1\n", + "seek_criteria = 0\n", + "resize_dim = None" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Instantiate demuxer and decoder" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# demuxer instance\n", + "demuxer = dmx.demuxer(input_file_path)\n", + "\n", + "# get the used coded id\n", + "codec_id = dec.GetRocDecCodecID(demuxer.GetCodecId())\n", + "\n", + "# decoder instance\n", + "viddec = dec.decoder(\n", + " codec_id,\n", + " device_id,\n", + " mem_type,\n", + " b_force_zero_latency,\n", + " crop_rect,\n", + " 0,\n", + " 0,\n", + " 1000)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Check if codec of the input video is supported" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Get GPU device information\n", + "cfg = viddec.GetGpuInfo()\n", + "\n", + "# check if codec is supported\n", + "if (viddec.IsCodecSupported(device_id, codec_id, demuxer.GetBitDepth()) == False):\n", + " print(\"ERROR: Codec is not supported on this GPU \" + cfg.device_name)\n", + " exit()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Printout system/env information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# print some GPU info out\n", + "print(\"\\ninfo: Input file: \" + \n", + " input_file_path + '\\n' + \"info: Using GPU device \" + str(device_id) + \" - \" +\n", + " cfg.device_name + \"[\" + cfg.gcn_arch_name + \"] on PCI bus \" + str(cfg.pci_bus_id) +\n", + " \":\" + str(cfg.pci_domain_id) + \".\" + str(cfg.pci_device_id))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setup MD5 if requested" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# md5 file full path & md5 flag\n", + "b_md5_check = False\n", + "if (ref_md5_file is not None):\n", + " if os.path.exists(ref_md5_file):\n", + " b_generate_md5 = True\n", + " b_md5_check = True\n", + "\n", + "# init MD5 if requested\n", + "if b_generate_md5:\n", + " viddec.InitMd5()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " Set reconfiguration params" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# set reconfiguration params based on user arguments\n", + "flush_mode = 0\n", + "if (output_file_path is not None):\n", + " flush_mode = 1\n", + "elif b_generate_md5:\n", + " flush_mode = 2\n", + "viddec.SetReconfigParams(flush_mode, output_file_path if (output_file_path is not None) else str(\"\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Start the decoding loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Setup vars for the decoding loop\n", + "n_frame = 0\n", + "total_dec_time = 0.0\n", + "frame_is_resized = False\n", + "not_seeking = True if (seek_frame == -1) else False\n", + "session_id = 0\n", + "if (resize_dim is not None):\n", + " resize_dim = None if(resize_dim[0] == 0 or resize_dim[1] == 0) else resize_dim\n", + "\n", + "# start decoding \n", + "print(\"info: decoding started, please wait! \\n\") \n", + " \n", + "while True:\n", + " start_time = datetime.datetime.now()\n", + " if(not_seeking):\n", + " packet = demuxer.DemuxFrame()\n", + " else:\n", + " packet = demuxer.SeekFrame(seek_frame, seek_mode, seek_criteria)\n", + " not_seeking = True\n", + " n_frame_returned = viddec.DecodeFrame(packet)\n", + " for i in range(n_frame_returned):\n", + " viddec.GetFrameYuv(packet)\n", + " if (b_generate_md5):\n", + " surface_info = viddec.GetOutputSurfaceInfo()\n", + " viddec.UpdateMd5ForFrame(packet.frame_adrs, surface_info)\n", + " if (resize_dim is not None):\n", + " surface_info = viddec.GetOutputSurfaceInfo()\n", + " if(viddec.ResizeFrame(packet, resize_dim, surface_info) != 0):\n", + " frame_is_resized = True\n", + " else:\n", + " frame_is_resized = False\n", + " if (output_file_path is not None):\n", + " if (frame_is_resized):\n", + " resized_surface_info = viddec.GetResizedOutputSurfaceInfo()\n", + " viddec.SaveFrameToFile(output_file_path, packet.frame_adrs_resized, resized_surface_info)\n", + " else:\n", + " viddec.SaveFrameToFile(output_file_path, packet.frame_adrs)\n", + "\n", + " # release frame\n", + " viddec.ReleaseFrame(packet)\n", + "\n", + " # measure after completing a whole frame\n", + " end_time = datetime.datetime.now()\n", + " time_per_frame = end_time - start_time\n", + " total_dec_time = total_dec_time + time_per_frame.total_seconds()\n", + "\n", + " # increament frames counter\n", + " n_frame += n_frame_returned\n", + " if (packet.bitstream_size <= 0): # EOF: no more to decode\n", + " break\n", + " \n", + "# decoding end\n", + "print(\"info: decoding ended! \\n\") " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Beyond the decoding loop" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Calculate overhead and FPS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# after the decoding loop\n", + "n_frame += viddec.GetNumOfFlushedFrames()\n", + "print(\"info: Total frame decoded: \" + str(n_frame))\n", + "\n", + "# Calculate if overhead\n", + "if (output_file_path is None):\n", + " if (n_frame > 0 and total_dec_time > 0):\n", + " time_per_frame = (total_dec_time / n_frame) * 1000\n", + " session_overhead = viddec.GetDecoderSessionOverHead(session_id)\n", + " if (session_overhead == None):\n", + " session_overhead = 0\n", + " time_per_frame -= (session_overhead / n_frame) # remove the overhead\n", + " frame_per_second = n_frame / total_dec_time\n", + " print(\"info: avg decoding time per frame: \" +\"{0:0.2f}\".format(round(time_per_frame, 2)) + \" ms\")\n", + " print(\"info: avg frame per second: \" +\"{0:0.2f}\".format(round(frame_per_second,2)) +\"\\n\")\n", + " else:\n", + " print(\"info: frame count= \", n_frame)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " MD5-Digest if requested" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# if MD5 check requested\n", + "if b_generate_md5:\n", + " digest = viddec.FinalizeMd5()\n", + " print(\"MD5 message digest: \", end=\" \")\n", + " str_digest = \"\"\n", + " for i in range(16):\n", + " str_digest = str_digest + str(format('%02x' % int(digest[i])))\n", + " print(str_digest)\n", + " if (b_md5_check):\n", + " f = open(ref_md5_file)\n", + " md5_from_file = f.read(16 * 2)\n", + " b_match = (md5_from_file == str_digest)\n", + " if (b_match):\n", + " print(\"MD5 digest matches the reference MD5 digest.\\n\")\n", + " else:\n", + " print(\n", + " \"MD5 digest does not match the reference MD5 digest: \",\n", + " md5_from_file)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}