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
+}