From 1ba367988d6514b2f7f8b4038c0e0871db4b5a7a Mon Sep 17 00:00:00 2001 From: Aryan Date: Wed, 26 Jul 2023 17:47:47 -0400 Subject: [PATCH] Add initial cmake support for building the rocdecode library --- .gitignore | 3 + CMakeLists.txt | 65 ++++++ README.md | 29 +++ cmake/FindFFmpeg.cmake | 163 +++++++++++++++ cmake/FindLibdrm.cmake | 34 +++ cmake/FindLibva.cmake | 47 +++++ docker/README.md | 13 ++ docker/vcnDECODE-on-ubuntu20.dockerfile | 28 +++ docker/vcnDECODE-on-ubuntu22.dockerfile | 27 +++ rocDecode-setup.py | 215 +++++++++++++++++++ samples/videoDecode/CMakeLists.txt | 54 +++++ samples/videoDecode/README.md | 11 + samples/videoDecode/videodecode.cpp | 146 +++++++++++++ src/rocdecode_api.cpp | 21 ++ src/{rocdecoder.h => rocdecoder.hpp} | 1 - utils/ffmpeg_demux.h | 23 --- utils/videoDemuxer.hpp | 263 ++++++++++++++++++++++++ 17 files changed, 1119 insertions(+), 24 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 cmake/FindFFmpeg.cmake create mode 100644 cmake/FindLibdrm.cmake create mode 100644 cmake/FindLibva.cmake create mode 100644 docker/README.md create mode 100644 docker/vcnDECODE-on-ubuntu20.dockerfile create mode 100644 docker/vcnDECODE-on-ubuntu22.dockerfile create mode 100644 rocDecode-setup.py create mode 100644 samples/videoDecode/CMakeLists.txt create mode 100644 samples/videoDecode/README.md create mode 100644 samples/videoDecode/videodecode.cpp rename src/{rocdecoder.h => rocdecoder.hpp} (97%) delete mode 100644 utils/ffmpeg_demux.h create mode 100644 utils/videoDemuxer.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..6975aef378 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +samples/*/build +.vscode/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..c3026a375b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +################################################################################ +# Copyright (c) 2023 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.0) +project(rocdecode) +set(VERSION "0.2.0") + +set(CMAKE_INSTALL_LIBDIR "lib" CACHE STRING "Library install directory") +set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE STRING "Include install directory") +set(CMAKE_CXX_STANDARD 17) +set(ROCM_PATH /opt/rocm CACHE PATH "Default ROCm installation path") + +# avoid setting the default installation path to /usr/local +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX ${ROCM_PATH} CACHE PATH "rocDecode default installation path" FORCE) +endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) +list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/hip ${ROCM_PATH}) +set(CMAKE_CXX_COMPILER ${ROCM_PATH}/llvm/bin/clang++) + +set(DEFAULT_AMDGPU_TARGETS "gfx803;gfx900;gfx906;gfx908;gfx90a;gfx940;gfx1030;gfx1031;gfx1032") +set(AMDGPU_TARGETS "${DEFAULT_AMDGPU_TARGETS}" CACHE STRING "List of specific machine types for library to target") + +find_package(HIP QUIET) +find_package(Libva QUIET) + +if(HIP_FOUND AND Libva_FOUND) + list(APPEND SOURCES ${PROJECT_SOURCE_DIR} src/rocdecode_api.cpp src/rocdecoder.cpp) + add_library(${PROJECT_NAME} SHARED ${SOURCES}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17") + target_link_libraries(${PROJECT_NAME} Libva::va Libva::va_drm hip::device) + install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES api/rocdecode.h utils/videoDemuxer.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) + +else() + if (NOT HIP_FOUND) + message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!") + endif() + if (NOT Libva_FOUND) + message(FATAL_ERROR "-- ERROR!: libva-dev Not Found - please install libva-dev!") + endif() + message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!") +endif() \ No newline at end of file diff --git a/README.md b/README.md index 62b3fe52ca..2633a7093c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,31 @@ # rocDecode rocDecode is a high performance video decode SDK for AMD hardware + +## Prerequisites: + +* One of the supported GPUs by ROCm: [AMD Radeon™ Graphics](https://docs.amd.com/bundle/Hardware_and_Software_Reference_Guide/page/Hardware_and_Software_Support.html) +* Linux distribution + + **Ubuntu** - `20.04` / `22.04` +* Install [ROCm5.5 or later](https://docs.amd.com) + + **Note** - both graphics and rocm use-cases must be installed (i.e., sudo amdgpu-install --usecase=graphics,rocm). +* CMake 3.0 or later +* libva-dev 2.7 or later +* [FFMPEG n4.4.2 or later](https://github.com/FFmpeg/FFmpeg/releases/tag/n4.4.2) + +* **Note** [vcnDECODE-setup.py](vcnDECODE-setup.py) script can be used for installing all the dependencies + +## Build instructions: +Please follow the instructions below to build and install the vcndecode library. +``` + cd rocDecode + mkdir build; cd build + cmake .. + make -j8 + sudo make install +``` + +## Samples: +The tool provides a few samples to decode videos [here](samples/). Please refer to the individual folders to build and run the samples. + +## Docker: +Docker files to build vcnDECODE containers are available [here](docker/) \ No newline at end of file diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake new file mode 100644 index 0000000000..bfa9239594 --- /dev/null +++ b/cmake/FindFFmpeg.cmake @@ -0,0 +1,163 @@ +################################################################################ +# Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved. +################################################################################ +# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil) +# Once done this will define +# +# FFMPEG_FOUND - system has ffmpeg or libav +# FFMPEG_INCLUDE_DIR - the ffmpeg include directory +# FFMPEG_LIBRARIES - Link these to use ffmpeg +################################################################################ + +set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/usr/local/lib/pkgconfig") +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + FFmpeg + FOUND_VAR FFMPEG_FOUND + REQUIRED_VARS + FFMPEG_LIBRARIES + FFMPEG_INCLUDE_DIR + AVCODEC_INCLUDE_DIR + AVCODEC_LIBRARY + AVFORMAT_INCLUDE_DIR + AVFORMAT_LIBRARY + AVUTIL_INCLUDE_DIR + AVUTIL_LIBRARY + SWSCALE_INCLUDE_DIR + SWSCALE_LIBRARY + VERSION_VAR FFMPEG_VERSION +) + +if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR) + set(FFMPEG_FOUND TRUE) +else() + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(_FFMPEG_AVCODEC libavcodec) + pkg_check_modules(_FFMPEG_AVFORMAT libavformat) + pkg_check_modules(_FFMPEG_AVUTIL libavutil) + endif() + + # AVCODEC + find_path(AVCODEC_INCLUDE_DIR + NAMES libavcodec/avcodec.h + PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS} + /usr/local/include + /usr/include + /opt/local/include + /sw/include + PATH_SUFFIXES ffmpeg libav + ) + mark_as_advanced(AVCODEC_INCLUDE_DIR) + find_library(AVCODEC_LIBRARY + NAMES avcodec + PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS} + /usr/local/lib + /usr/lib + /opt/local/lib + /sw/lib + ) + mark_as_advanced(AVCODEC_LIBRARY) + + # AVFORMAT + find_path(AVFORMAT_INCLUDE_DIR + NAMES libavformat/avformat.h + PATHS ${_FFMPEG_AVFORMAT_INCLUDE_DIRS} + /usr/local/include + /usr/include + /opt/local/include + /sw/include + PATH_SUFFIXES ffmpeg libav + ) + mark_as_advanced(AVFORMAT_INCLUDE_DIR) + find_library(AVFORMAT_LIBRARY + NAMES avformat + PATHS ${_FFMPEG_AVFORMAT_LIBRARY_DIRS} + /usr/local/lib + /usr/lib + /opt/local/lib + /sw/lib + ) + mark_as_advanced(AVFORMAT_LIBRARY) + + # AVUTIL + find_path(AVUTIL_INCLUDE_DIR + NAMES libavutil/avutil.h + PATHS ${_FFMPEG_AVUTIL_INCLUDE_DIRS} + /usr/local/include + /usr/include + /opt/local/include + /sw/include + PATH_SUFFIXES ffmpeg libav + ) + mark_as_advanced(AVUTIL_INCLUDE_DIR) + find_library(AVUTIL_LIBRARY + NAMES avutil + PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS} + /usr/local/lib + /usr/lib + /opt/local/lib + /sw/lib + ) + mark_as_advanced(AVUTIL_LIBRARY) + + # SWSCALE + find_path(SWSCALE_INCLUDE_DIR + NAMES libswscale/swscale.h + PATHS ${_FFMPEG_SWSCALE_INCLUDE_DIRS} + /usr/local/include + /usr/include + /opt/local/include + /sw/include + PATH_SUFFIXES ffmpeg libav sw + ) + mark_as_advanced(SWSCALE_INCLUDE_DIR) + find_library(SWSCALE_LIBRARY + NAMES swscale + PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} + /usr/local/lib + /usr/lib + /opt/local/lib + /sw/lib + ) + mark_as_advanced(SWSCALE_LIBRARY) + + if(AVCODEC_LIBRARY AND AVFORMAT_LIBRARY) + set(FFMPEG_FOUND TRUE) + endif() + + if(_FFMPEG_AVCODEC_VERSION VERSION_LESS 58.18.100 OR _FFMPEG_AVFORMAT_VERSION VERSION_LESS 58.12.100 OR _FFMPEG_AVUTIL_VERSION VERSION_LESS 56.14.100) + if(FFMPEG_FOUND) + message("-- ${White}FFMPEG required min version - 4.0.4 Found:${FFMPEG_VERSION}") + message("-- ${White}AVCODEC required min version - 58.18.100 Found:${_FFMPEG_AVCODEC_VERSION}${ColourReset}") + message("-- ${White}AVFORMAT required min version - 58.12.100 Found:${_FFMPEG_AVFORMAT_VERSION}${ColourReset}") + message("-- ${White}AVUTIL required min version - 56.14.100 Found:${_FFMPEG_AVUTIL_VERSION}${ColourReset}") + endif() + set(FFMPEG_FOUND FALSE) + message( "-- ${Yellow}NOTE: FindFFmpeg failed to find -- FFMPEG${ColourReset}" ) + endif() + + if(FFMPEG_FOUND) + set(FFMPEG_INCLUDE_DIR ${AVFORMAT_INCLUDE_DIR} CACHE INTERNAL "") + set(FFMPEG_LIBRARIES + ${AVCODEC_LIBRARY} + ${AVFORMAT_LIBRARY} + ${AVUTIL_LIBRARY} + ${SWSCALE_LIBRARY} + CACHE INTERNAL "" + ) + endif() + + if(FFMPEG_FOUND) + if(NOT FFMPEG_FIND_QUIETLY) + message("-- ${White}Using FFMPEG -- Libraries:${FFMPEG_LIBRARIES} Includes:${FFMPEG_INCLUDE_DIR}${ColourReset}") + endif() + else() + if(FFMPEG_FIND_REQUIRED) + message(FATAL_ERROR "{Red}FindFFmpeg -- libavcodec or libavformat or libavutil NOT FOUND${ColourReset}") + endif() + endif() +endif() diff --git a/cmake/FindLibdrm.cmake b/cmake/FindLibdrm.cmake new file mode 100644 index 0000000000..8ccc45f5ba --- /dev/null +++ b/cmake/FindLibdrm.cmake @@ -0,0 +1,34 @@ +################################################################################ +# +# MIT License +# +# Copyright (c) 2022 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. +# + +find_package(PkgConfig) +pkg_check_modules(Libdrm libdrm) +if(Libdrm_FOUND) + if(NOT TARGET Libdrm::drm) + add_library(Libdrm::drm UNKNOWN IMPORTED) + set_target_properties(Libdrm::drm PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIRS}" + IMPORTED_LOCATION "${Libdrm_LINK_LIBRARIES}") + endif() +endif() \ No newline at end of file diff --git a/cmake/FindLibva.cmake b/cmake/FindLibva.cmake new file mode 100644 index 0000000000..4662cb7e7e --- /dev/null +++ b/cmake/FindLibva.cmake @@ -0,0 +1,47 @@ +################################################################################ +# +# MIT License +# +# Copyright (c) 2022 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. +# + +find_package(Libdrm REQUIRED) +find_library(LIBVA_LIBRARY NAMES va) +find_library(LIBVA_DRM_LIBRARY NAMES va-drm) +find_path(LIBVA_INCLUDE_DIR NAMES va/va.h) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Libva DEFAULT_MSG LIBVA_INCLUDE_DIR LIBVA_LIBRARY LIBVA_DRM_LIBRARY) +mark_as_advanced(LIBVA_INCLUDE_DIR LIBVA_LIBRARY LIBVA_DRM_LIBRARY) + +if(Libva_FOUND) + if(NOT TARGET Libva::va) + add_library(Libva::va UNKNOWN IMPORTED) + set_target_properties(Libva::va PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBVA_INCLUDE_DIR}" + IMPORTED_LOCATION "${LIBVA_LIBRARY}") + endif() + if(NOT TARGET Libva::va_drm) + add_library(Libva::va_drm UNKNOWN IMPORTED) + target_link_libraries(Libva::va_drm INTERFACE Libdrm::drm) + set_target_properties(Libva::va_drm PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${LIBVA_INCLUDE_DIR}" + IMPORTED_LOCATION "${LIBVA_DRM_LIBRARY}") + endif() +endif() \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000000..ec1fd9b50f --- /dev/null +++ b/docker/README.md @@ -0,0 +1,13 @@ +# rocDecoder Docker + +## Build - dockerfiles + +``` +sudo docker build -f {DOCKER_FILE_NAME}.dockerfile -t {DOCKER_IMAGE_NAME} . +``` + +## Run - docker + +``` +sudo docker run -it --device=/dev/kfd --device=/dev/dri --cap-add=SYS_RAWIO --device=/dev/mem --group-add video --network host --env DISPLAY=unix$DISPLAY --privileged --volume $XAUTH:/root/.Xauthority --volume /tmp/.X11-unix/:/tmp/.X11-unix {DOCKER_IMAGE_NAME} +``` \ No newline at end of file diff --git a/docker/vcnDECODE-on-ubuntu20.dockerfile b/docker/vcnDECODE-on-ubuntu20.dockerfile new file mode 100644 index 0000000000..37955f80bd --- /dev/null +++ b/docker/vcnDECODE-on-ubuntu20.dockerfile @@ -0,0 +1,28 @@ +FROM ubuntu:20.04 + +# install base dependencies +RUN apt-get update -y +#RUN apt-get dist-upgrade -y +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install gcc g++ cmake pkg-config git apt-utils sudo vainfo dialog + +RUN DEBIAN_FRONTEND=noninteractive dpkg --add-architecture i386 + +# install ROCm +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install initramfs-tools libnuma-dev wget keyboard-configuration && \ + wget https://repo.radeon.com/amdgpu-install/5.5/ubuntu/focal/amdgpu-install_5.5.50500-1_all.deb && \ + sudo apt-get install ./amdgpu-install_5.5.50500-1_all.deb && \ + sudo amdgpu-install -y --usecase=graphics,rocm + +# install FFMPEG, and other dependencies +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install autoconf automake build-essential cmake git-core libass-dev libfreetype6-dev libsdl2-dev libtool libva-dev \ + libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo wget zlib1g-dev \ + nasm yasm libx264-dev libx265-dev libnuma-dev libfdk-aac-dev unzip && \ + wget https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n4.4.2.zip && unzip n4.4.2.zip && cd FFmpeg-n4.4.2/ && sudo ldconfig && \ + export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/" && \ + ./configure --enable-shared --disable-static && \ + make -j8 && sudo make install && cd + +ENV LD_LIBRARY_PATH="/usr/local/lib:/usr/local/lib64/:/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH" + +WORKDIR /workspace + diff --git a/docker/vcnDECODE-on-ubuntu22.dockerfile b/docker/vcnDECODE-on-ubuntu22.dockerfile new file mode 100644 index 0000000000..4b7404c0a9 --- /dev/null +++ b/docker/vcnDECODE-on-ubuntu22.dockerfile @@ -0,0 +1,27 @@ +FROM ubuntu:22.04 + +# install base dependencies +RUN apt-get update -y +#RUN apt-get dist-upgrade -y +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install gcc g++ cmake pkg-config git apt-utils sudo vainfo dialog libstdc++-12-dev + +RUN DEBIAN_FRONTEND=noninteractive dpkg --add-architecture i386 + +# install ROCm +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install initramfs-tools libnuma-dev wget keyboard-configuration && \ + wget https://repo.radeon.com/amdgpu-install/5.5/ubuntu/jammy/amdgpu-install_5.5.50500-1_all.deb && \ + sudo apt-get install ./amdgpu-install_5.5.50500-1_all.deb && \ + sudo amdgpu-install -y --usecase=graphics,rocm + +# install FFMPEG, and other dependencies +RUN DEBIAN_FRONTEND=noninteractive apt-get -y install autoconf automake build-essential cmake git-core libass-dev libfreetype6-dev libsdl2-dev libtool libva-dev \ + libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo wget zlib1g-dev \ + nasm yasm libx264-dev libx265-dev libnuma-dev libfdk-aac-dev unzip && \ + wget https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n4.4.2.zip && unzip n4.4.2.zip && cd FFmpeg-n4.4.2/ && sudo ldconfig && \ + export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/" && \ + ./configure --enable-shared --disable-static && \ + make -j8 && sudo make install && cd + +ENV LD_LIBRARY_PATH="/usr/local/lib:/usr/local/lib64/:/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH" + +WORKDIR /workspace diff --git a/rocDecode-setup.py b/rocDecode-setup.py new file mode 100644 index 0000000000..e7d6c6a575 --- /dev/null +++ b/rocDecode-setup.py @@ -0,0 +1,215 @@ +# Copyright (c) 2023 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. + +import os +import sys +import argparse +import platform +if sys.version_info[0] < 3: + import commands +else: + import subprocess + +__version__ = "1.0" +__status__ = "Shipping" + +# Arguments +parser = argparse.ArgumentParser() +parser.add_argument('--directory', type=str, default='~/rocDecode-deps', + help='Setup home directory - optional (default:~/)') +parser.add_argument('--ffmpeg', type=str, default='yes', + help='FFMPEG V4.4.2 Installation (default:yes) [options:yes/no]') +parser.add_argument('--reinstall', type=str, default='no', + help='Remove previous setup and reinstall - optional (default:no) [options:yes/no]') + +args = parser.parse_args() + +setupDir = args.directory +ffmpegInstall = args.ffmpeg +reinstall = args.reinstall + +if ffmpegInstall not in ('no', 'yes'): + print( + "ERROR: FFMPEG Install Option Not Supported - [Supported Options: no or yes]") + exit() + +# get platfrom info +platfromInfo = platform.platform() + +# sudo requirement check +sudoLocation = '' +userName = '' +if sys.version_info[0] < 3: + status, sudoLocation = commands.getstatusoutput("which sudo") + if sudoLocation != '/usr/bin/sudo': + status, userName = commands.getstatusoutput("whoami") +else: + status, sudoLocation = subprocess.getstatusoutput("which sudo") + if sudoLocation != '/usr/bin/sudo': + status, userName = subprocess.getstatusoutput("whoami") + +# Setup Directory for Deps +if setupDir == '~/rocDecode-deps': + setupDir_deps = setupDir +else: + setupDir_deps = setupDir+'/rocDecode-deps' + +# setup directory path +deps_dir = os.path.expanduser(setupDir_deps) +deps_dir = os.path.abspath(deps_dir) + +# setup for Linux +linuxSystemInstall = '' +linuxCMake = 'cmake' +linuxSystemInstall_check = '' +linuxFlag = '' +if "centos" in platfromInfo or "redhat" in platfromInfo: + linuxSystemInstall = 'yum -y' + linuxSystemInstall_check = '--nogpgcheck' + if "centos-7" in platfromInfo or "redhat-7" in platfromInfo: + linuxCMake = 'cmake3' + os.system(linuxSystemInstall+' install cmake3') +elif "Ubuntu" in platfromInfo or os.path.exists('/usr/bin/apt-get'): + linuxSystemInstall = 'apt-get -y' + linuxSystemInstall_check = '--allow-unauthenticated' + linuxFlag = '-S' + if not "Ubuntu" in platfromInfo: + platfromInfo = platfromInfo+'-Ubuntu' +elif os.path.exists('/usr/bin/zypper'): + linuxSystemInstall = 'zypper -n' + linuxSystemInstall_check = '--no-gpg-checks' + platfromInfo = platfromInfo+'-SLES' +else: + print("\nrocDecode Setup on "+platfromInfo+" is unsupported\n") + print("\nrocDecode Setup Supported on: Ubuntu 20/22; CentOS 7/8; RedHat 7/8; & SLES 15-SP2\n") + exit() + +# rocDecode Setup +print("\nrocDecode Setup on: "+platfromInfo+"\n") + +if userName == 'root': + os.system(linuxSystemInstall+' update') + os.system(linuxSystemInstall+' install sudo') + +# Delete previous install +if os.path.exists(deps_dir) and reinstall == 'yes': + os.system('sudo -v') + os.system('sudo rm -rf '+deps_dir) + print("\nrocDecode Setup: Removing Previous Install -- "+deps_dir+"\n") + +# Re-Install +if os.path.exists(deps_dir): + print("\nrocDecode Setup: Re-Installing Libraries from -- "+deps_dir+"\n") + if ffmpegInstall == 'yes': + # FFMPEG + if os.path.exists(deps_dir+'/FFmpeg-n4.4.2'): + os.system('sudo -v') + os.system('(cd '+deps_dir+'/FFmpeg-n4.4.2; sudo ' + + linuxFlag+' make install -j8)') + + print("\nrocDecode Dependencies Re-Installed with rocDecode-setup.py V-"+__version__+"\n") + exit() +# Clean Install +else: + print("\nrocDecode Dependencies Installation with rocDecode-setup.py V-"+__version__+"\n") + os.system('mkdir '+deps_dir) + # Create Build folder + os.system('(cd '+deps_dir+'; mkdir build )') + # install pre-reqs + os.system('sudo -v') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' ' + + linuxSystemInstall_check+' install gcc cmake git wget unzip pkg-config inxi vainfo') + + # Get Installation Source + if ffmpegInstall == 'yes': + os.system( + '(cd '+deps_dir+'; wget https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n4.4.2.zip && unzip n4.4.2.zip )') + + # Install ffmpeg + if ffmpegInstall == 'yes': + if "Ubuntu" in platfromInfo: + os.system('sudo -v') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install autoconf automake build-essential git-core libass-dev libfreetype6-dev') + os.system('sudo -v') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install libsdl2-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev') + os.system('sudo -v') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev') + os.system('sudo -v') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install nasm yasm libx264-dev libx265-dev libnuma-dev libfdk-aac-dev') + else: + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install autoconf automake bzip2 bzip2-devel freetype-devel') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install gcc-c++ libtool make pkgconfig zlib-devel') + # Nasm + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install nasm') + if "centos-7" in platfromInfo or "redhat-7" in platfromInfo: + # Yasm + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install http://repo.okay.com.mx/centos/7/x86_64/release/okay-release-1-1.noarch.rpm') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' --enablerepo=extras install epel-release') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install yasm') + # libx264 & libx265 + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install libx264-devel libx265-devel') + # libfdk_aac + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install https://forensics.cert.org/cert-forensics-tools-release-el7.rpm') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' --enablerepo=forensics install fdk-aac') + # libASS + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install libass-devel') + elif "centos-8" in platfromInfo or "redhat-8" in platfromInfo: + # el8 x86_64 packages + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-8.noarch.rpm') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install http://mirror.centos.org/centos/8/PowerTools/x86_64/os/Packages/SDL2-2.0.10-2.el8.x86_64.rpm') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install ffmpeg ffmpeg-devel') + elif "SLES" in platfromInfo: + # FFMPEG-4 packages + os.system( + 'sudo zypper ar -cfp 90 \'https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Leap_$releasever/Essentials\' packman-essentials') + os.system('sudo '+linuxFlag+' '+linuxSystemInstall+' '+linuxSystemInstall_check + + ' install ffmpeg-4') + + # FFMPEG 4 from source -- for Ubuntu, CentOS 7, & RedHat 7 + if "Ubuntu" in platfromInfo or "centos-7" in platfromInfo or "redhat-7" in platfromInfo: + os.system('sudo -v') + os.system( + '(cd '+deps_dir+'/FFmpeg-n4.4.2; sudo '+linuxFlag+' ldconfig )') + os.system('(cd '+deps_dir+'/FFmpeg-n4.4.2; export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig/"; ./configure --enable-shared --disable-static)') + os.system('(cd '+deps_dir+'/FFmpeg-n4.4.2; make -j8 )') + os.system('sudo -v') + os.system('(cd '+deps_dir+'/FFmpeg-n4.4.2; sudo ' + + linuxFlag+' make install )') + + print("\nrocDecode Dependencies Installed with rocDecode-setup.py V-"+__version__+"\n") diff --git a/samples/videoDecode/CMakeLists.txt b/samples/videoDecode/CMakeLists.txt new file mode 100644 index 0000000000..181ab18675 --- /dev/null +++ b/samples/videoDecode/CMakeLists.txt @@ -0,0 +1,54 @@ +################################################################################ +# Copyright (c) 2023 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.0) +project(videodecode) +set(CMAKE_CXX_STANDARD 17) +set(ROCM_PATH /opt/rocm CACHE PATH "default ROCm installation path") +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../cmake) +list(APPEND CMAKE_PREFIX_PATH ${ROCM_PATH}/hip ${ROCM_PATH}) +set(CMAKE_CXX_COMPILER ${ROCM_PATH}/llvm/bin/clang++) + +set(DEFAULT_AMDGPU_TARGETS "gfx803;gfx900;gfx906;gfx908;gfx90a;gfx940;gfx1030;gfx1031;gfx1032") +set(AMDGPU_TARGETS "${DEFAULT_AMDGPU_TARGETS}" CACHE STRING "List of specific machine types for library to target") +find_package(HIP QUIET) +find_package(FFmpeg QUIET) +find_package(Libva QUIET) +if(HIP_FOUND AND FFMPEG_FOUND AND Libva_FOUND) + include_directories (${ROCM_PATH}/include/rocdecode) + list(APPEND SOURCES ${PROJECT_SOURCE_DIR} videodecode.cpp) + add_executable(${PROJECT_NAME} ${SOURCES}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17") + target_link_libraries(${PROJECT_NAME} ${AVUTIL_LIBRARY} ${AVCODEC_LIBRARY} ${AVFORMAT_LIBRARY} Libva::va Libva::va_drm hip::device rocdecode) +else() + if (NOT HIP_FOUND) + message(FATAL_ERROR "-- ERROR!: HIP Not Found! - please install ROCm and HIP!") + endif() + if (NOT FFMPEG_FOUND) + message(FATAL_ERROR "-- ERROR!: FFMPEG Not Found! - please insatll FFMPEG!") + endif() + if (NOT Libva_FOUND) + message(FATAL_ERROR "-- ERROR!: libva-dev Not Found - please install libva-dev!") + endif() + message("-- ERROR!: ${PROJECT_NAME} excluded! please install all the dependencies and try again!") +endif() \ No newline at end of file diff --git a/samples/videoDecode/README.md b/samples/videoDecode/README.md new file mode 100644 index 0000000000..9cd72a75d4 --- /dev/null +++ b/samples/videoDecode/README.md @@ -0,0 +1,11 @@ +# Video Decode Sample +This sample illustrates the FFMPEG demuxer to get the individual frames which are then decoded on AMD hardware using VAAPI. This sample supports both YUV420 8-bit and 10-bit streams. + +## Build and run the sample: +``` +mkdir build +cd build +cmake .. +make -j +./videodecode -i -o -d +``` \ No newline at end of file diff --git a/samples/videoDecode/videodecode.cpp b/samples/videoDecode/videodecode.cpp new file mode 100644 index 0000000000..80506297ec --- /dev/null +++ b/samples/videoDecode/videodecode.cpp @@ -0,0 +1,146 @@ +/* +Copyright (c) 2023 - 2023 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 +#include +#include +#include +#include +#include +#include +#include +#include "videoDemuxer.hpp" +#include "rocdecode.h" + +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; + exit(0); +} + +int main(int argc, char **argv) { + + std::string inputFilePath, outputFilePath; + int dumpOutputFrames = 0; + int isOutputRGB = 0; + int deviceId = 0; + // 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"); + } + inputFilePath = argv[i]; + continue; + } + if (!strcmp(argv[i], "-o")) { + if (++i == argc) { + ShowHelpAndExit("-o"); + } + outputFilePath = argv[i]; + dumpOutputFrames = 1; + continue; + } + if (!strcmp(argv[i], "-d")) { + if (++i == argc) { + ShowHelpAndExit("-d"); + } + deviceId = atoi(argv[i]); + continue; + } + ShowHelpAndExit(argv[i]); + } + + VideoDemuxer demuxer(inputFilePath.c_str()); + //VideoDecode viddec(deviceId); + + std::string deviceName, gcnArchName, drmNode; + int pciBusID, pciDomainID, pciDeviceID; + + /*viddec.getDeviceinfo(deviceName, gcnArchName, pciBusID, pciDomainID, pciDeviceID, drmNode); + std::cout << "info: Using GPU device " << deviceId << ": (drm node: " << drmNode << ") " << 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; + std::cout << "info: decoding started, please wait!" << std::endl;*/ + + int nVideoBytes = 0, nFrameReturned = 0, nFrame = 0; + uint8_t *pVideo = nullptr; + uint8_t *pFrame = nullptr; + int64_t pts = 0; + //outputImageInfo *pImageInfo; + bool bDecodeOutSemiPlanar = false; + + uint32_t width, height; + //vcnImageFormat_t subsampling; + double totalDecTime = 0; + + do { + auto startTime = std::chrono::high_resolution_clock::now(); + demuxer.demux(&pVideo, &nVideoBytes, &pts); + //nFrameReturned = viddec.decode(pVideo, nVideoBytes, pts); + auto endTime = std::chrono::high_resolution_clock::now(); + auto timePerFrame = std::chrono::duration(endTime - startTime).count(); + totalDecTime += timePerFrame; + /*if (!nFrame && !viddec.getOutputImageInfo(&pImageInfo)){ + std::cerr << "Error: Failed to get Output Image Info!" << std::endl; + break; + }*/ + + if (dumpOutputFrames) { + for (int i = 0; i < nFrameReturned; i++) { + /*pFrame = viddec.getFrame(&pts); + viddec.saveImage(outputFilePath, pFrame, pImageInfo, false); + // release frame + viddec.releaseFrame(pts);*/ + } + } + nFrame += nFrameReturned; + } while (nVideoBytes); + + // 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); + + /*std::cout << "info: Video codec format: " << viddec.getCodecFmtName(viddec.getVcnVideoCodecId()) << std::endl; + std::cout << "info: Video size: [ " << pImageInfo->nOutputWidth << ", " << pImageInfo->nOutputHeight << " ]" << std::endl; + std::cout << "info: Video pix format: " << viddec.getPixFmtName(pImageInfo->chromaFormat) << std::endl; + std::cout << "info: Video Bit depth: " << pImageInfo->nBitDepth << 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; + }*/ + + return 0; +} diff --git a/src/rocdecode_api.cpp b/src/rocdecode_api.cpp index e69de29bb2..d1b9877593 100644 --- a/src/rocdecode_api.cpp +++ b/src/rocdecode_api.cpp @@ -0,0 +1,21 @@ +/* +Copyright (c) 2023 - 2023 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. +*/ diff --git a/src/rocdecoder.h b/src/rocdecoder.hpp similarity index 97% rename from src/rocdecoder.h rename to src/rocdecoder.hpp index 26f4bed400..247c5ab27e 100644 --- a/src/rocdecoder.h +++ b/src/rocdecoder.hpp @@ -30,5 +30,4 @@ THE SOFTWARE. #include #include #include "../api/rocdecode.h" -#include "../utils/ffmpeg_demux.h" #include diff --git a/utils/ffmpeg_demux.h b/utils/ffmpeg_demux.h deleted file mode 100644 index 6b61e2e660..0000000000 --- a/utils/ffmpeg_demux.h +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright (c) 2023 - 2023 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. -*/ - -#pragma once diff --git a/utils/videoDemuxer.hpp b/utils/videoDemuxer.hpp new file mode 100644 index 0000000000..adc1758945 --- /dev/null +++ b/utils/videoDemuxer.hpp @@ -0,0 +1,263 @@ +/* +Copyright (c) 2023 - 2023 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. +*/ + +#pragma once + +#include +extern "C" { + #include + #include + #include + #include + #include + #include + #include +} + +// Video Demuxer Interface class +class VideoDemuxer { + public: + class StreamProvider { + public: + virtual ~StreamProvider() {} + virtual int getData(uint8_t *pBuf, int nBuf) = 0; + }; + VideoDemuxer(const char *inputFilePath) : VideoDemuxer(createFmtContextUtil(inputFilePath)) {} + VideoDemuxer(StreamProvider *pStreamProvider) : VideoDemuxer(createFmtContextUtil(pStreamProvider)) {av_io_ctx_ = av_fmt_input_ctx_->pb;} + ~VideoDemuxer(); + bool demux(uint8_t **ppVideo, int *pnVideoBytes, int64_t *pts = nullptr); + private: + VideoDemuxer(AVFormatContext *av_fmt_input_ctx_); + AVFormatContext *createFmtContextUtil(StreamProvider *pStreamProvider); + AVFormatContext *createFmtContextUtil(const char *inputFilePath); + static int readPacket(void *pData, uint8_t *pBuf, int nBuf); + AVFormatContext *av_fmt_input_ctx_ = nullptr; + AVIOContext *av_io_ctx_ = nullptr; + AVPacket* packet_ = nullptr; + AVPacket* packetFiltered_ = nullptr; + AVBSFContext *av_bsf_ctx_ = nullptr; + AVCodecID av_videoCodecID_; + uint8_t *pDataWithHeader = nullptr; + int av_stream_ = 0; + bool isH264_ = false; + bool isHEVC_ = false; + bool isMPEG4_ = false; + int64_t defaultTimeScale = 1000; + double timeBase = 0.0; + unsigned int frameCnt = 0; +}; + +VideoDemuxer::~VideoDemuxer() { + if (!av_fmt_input_ctx_) { + return; + } + if (packet_) { + av_packet_free(&packet_); + } + if (packetFiltered_) { + av_packet_free(&packetFiltered_); + } + if (av_bsf_ctx_) { + av_bsf_free(&av_bsf_ctx_); + } + avformat_close_input(&av_fmt_input_ctx_); + if (av_io_ctx_) { + av_freep(&av_io_ctx_->buffer); + av_freep(&av_io_ctx_); + } + if (pDataWithHeader) { + av_free(pDataWithHeader); + } +} + +bool VideoDemuxer::demux(uint8_t **ppVideo, int *pnVideoBytes, int64_t *pts) { + if (!av_fmt_input_ctx_) { + return false; + } + *pnVideoBytes = 0; + if (packet_->data) { + av_packet_unref(packet_); + } + int ret = 0; + while ((ret = av_read_frame(av_fmt_input_ctx_, packet_)) >= 0 && packet_->stream_index != av_stream_) { + av_packet_unref(packet_); + } + if (ret < 0) { + return false; + } + if (isH264_ || isHEVC_) { + if (packetFiltered_->data) { + av_packet_unref(packetFiltered_); + } + if (av_bsf_send_packet(av_bsf_ctx_, packet_) != 0) { + std::cerr << "ERROR: av_bsf_send_packet failed!" << std::endl; + return false; + } + if (av_bsf_receive_packet(av_bsf_ctx_, packetFiltered_) != 0) { + std::cerr << "ERROR: av_bsf_receive_packet failed!" << std::endl; + return false; + } + *ppVideo = packetFiltered_->data; + *pnVideoBytes = packetFiltered_->size; + if (pts) + *pts = (int64_t) (packetFiltered_->pts * defaultTimeScale * timeBase); + } else { + if (isMPEG4_ && (frameCnt == 0)) { + int extDataSize = av_fmt_input_ctx_->streams[av_stream_]->codecpar->extradata_size; + if (extDataSize > 0) { + pDataWithHeader = (uint8_t *)av_malloc(extDataSize + packet_->size - 3 * sizeof(uint8_t)); + if (!pDataWithHeader) { + std::cerr << "ERROR: av_malloc failed!" << std::endl; + return false; + } + memcpy(pDataWithHeader, av_fmt_input_ctx_->streams[av_stream_]->codecpar->extradata, extDataSize); + memcpy(pDataWithHeader + extDataSize, packet_->data + 3, packet_->size - 3 * sizeof(uint8_t)); + *ppVideo = pDataWithHeader; + *pnVideoBytes = extDataSize + packet_->size - 3 * sizeof(uint8_t); + } + } else { + *ppVideo = packet_->data; + *pnVideoBytes = packet_->size; + } + if (pts) + *pts = (int64_t)(packet_->pts * defaultTimeScale * timeBase); + } + frameCnt++; + return true; +} + +VideoDemuxer::VideoDemuxer(AVFormatContext *av_fmt_input_ctx_) : av_fmt_input_ctx_(av_fmt_input_ctx_) { + if (!av_fmt_input_ctx_) { + std::cerr << "ERROR: av_fmt_input_ctx_ is not vaild!" << std::endl; + return; + } + packet_ = av_packet_alloc(); + packetFiltered_ = av_packet_alloc(); + if (!packet_ || !packetFiltered_) { + std::cerr << "ERROR: av_packet_alloc failed!" << std::endl; + return; + } + if (avformat_find_stream_info(av_fmt_input_ctx_, nullptr) < 0) { + std::cerr << "ERROR: avformat_find_stream_info failed!" << std::endl; + return; + } + av_stream_ = av_find_best_stream(av_fmt_input_ctx_, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0); + if (av_stream_ < 0) { + std::cerr << "ERROR: av_find_best_stream failed!" << std::endl; + av_packet_free(&packet_); + av_packet_free(&packetFiltered_); + return; + } + av_videoCodecID_ = av_fmt_input_ctx_->streams[av_stream_]->codecpar->codec_id; + AVRational rTimeBase = av_fmt_input_ctx_->streams[av_stream_]->time_base; + timeBase = av_q2d(rTimeBase); + + isH264_ = av_videoCodecID_ == AV_CODEC_ID_H264 && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM")); + isHEVC_ = av_videoCodecID_ == AV_CODEC_ID_HEVC && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM")); + isMPEG4_ = av_videoCodecID_ == AV_CODEC_ID_MPEG4 && (!strcmp(av_fmt_input_ctx_->iformat->long_name, "QuickTime / MOV") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "FLV (Flash Video)") + || !strcmp(av_fmt_input_ctx_->iformat->long_name, "Matroska / WebM")); + + if (isH264_) { + const AVBitStreamFilter *bsf = av_bsf_get_by_name("h264_mp4toannexb"); + if (!bsf) { + std::cerr << "ERROR: av_bsf_get_by_name() failed" << std::endl; + av_packet_free(&packet_); + av_packet_free(&packetFiltered_); + return; + } + if (av_bsf_alloc(bsf, &av_bsf_ctx_) != 0) { + std::cerr << "ERROR: av_bsf_alloc failed!" << std::endl; + return; + } + avcodec_parameters_copy(av_bsf_ctx_->par_in, av_fmt_input_ctx_->streams[av_stream_]->codecpar); + if (av_bsf_init(av_bsf_ctx_) < 0) { + std::cerr << "ERROR: av_bsf_init failed!" << std::endl; + return; + } + } + if (isHEVC_) { + const AVBitStreamFilter *bsf = av_bsf_get_by_name("hevc_mp4toannexb"); + if (!bsf) { + std::cerr << "ERROR: av_bsf_get_by_name() failed" << std::endl; + av_packet_free(&packet_); + av_packet_free(&packetFiltered_); + return; + } + if (av_bsf_alloc(bsf, &av_bsf_ctx_) != 0 ) { + std::cerr << "ERROR: av_bsf_alloc failed!" << std::endl; + return; + } + avcodec_parameters_copy(av_bsf_ctx_->par_in, av_fmt_input_ctx_->streams[av_stream_]->codecpar); + if (av_bsf_init(av_bsf_ctx_) < 0) { + std::cerr << "ERROR: av_bsf_init failed!" << std::endl; + return; + } + } +} + +AVFormatContext *VideoDemuxer::createFmtContextUtil(StreamProvider *pStreamProvider) { + AVFormatContext *ctx = nullptr; + if (!(ctx = avformat_alloc_context())) { + std::cerr << "ERROR: avformat_alloc_context failed" << std::endl; + return nullptr; + } + uint8_t *avioc_buffer = nullptr; + int avioc_buffer_size = 8 * 1024 * 1024; + avioc_buffer = (uint8_t *)av_malloc(avioc_buffer_size); + if (!avioc_buffer) { + std::cerr << "ERROR: av_malloc failed!" << std::endl;; + return nullptr; + } + av_io_ctx_ = avio_alloc_context(avioc_buffer, avioc_buffer_size, + 0, pStreamProvider, &readPacket, nullptr, nullptr); + if (!av_io_ctx_) { + std::cerr << "ERROR: avio_alloc_context failed!" << std::endl; + return nullptr; + } + ctx->pb = av_io_ctx_; + + if (avformat_open_input(&ctx, nullptr, nullptr, nullptr) != 0) { + std::cerr << "ERROR: avformat_open_input failed!" << std::endl; + return nullptr; + } + return ctx; +} + +AVFormatContext *VideoDemuxer::createFmtContextUtil(const char *inputFilePath) { + avformat_network_init(); + av_log_set_level(AV_LOG_QUIET); + AVFormatContext *ctx = nullptr; + if (avformat_open_input(&ctx, inputFilePath, nullptr, nullptr) != 0 ) { + std::cerr << "ERROR: avformat_open_input failed!" << std::endl; + return nullptr; + } + return ctx; +} + +int VideoDemuxer::readPacket(void *pData, uint8_t *pBuf, int nBuf) { + return ((StreamProvider *)pData)->getData(pBuf, nBuf); +}