# Intel Media FFMPEG Transcode Container This project hosts a container demonstrating the use of ffmpeg using GPU offload for transcode operations. The Dockerfile itself is constructed from templates/* and Dockerfile.solution, and provides a multi-stage Docker container with the final container being a minimal run-time installation on top of the base OS. # Usage examples ## Getting the container You can pull the container from Harbor: ```bash export OS_DISTRO=ubuntu export PACKAGE_STREAM=eoan TAG=${OS_DISTRO}-${PACKAGE_STREAM} docker pull amr-registry.caas.intel.com/vtt-osgc/solutions/intel-media-ffmpeg:${TAG} docker tag amr-registry.caas.intel.com/vtt-osgc/solutions/intel-media-ffmpeg:${TAG} intel-media-ffmpeg ``` ### Build Ubuntu container ```bash export OS_DISTRO=ubuntu export OS_RELEASE=eoan export PACKAGE_STREAM=eoan export PACKAGE_REPOSITORY=https://repositories.intel.com/graphics export TAG=test-build-${OS_DISTRO}-${PACKAGE_STREAM}-$(date +%Y%m%d) scripts/build-dockerfile.sh scripts/build-image.sh ``` ### Build Red Hat container ```bash export OS_DISTRO=rhel export OS_RELEASE=8.0 export PACKAGE_STREAM=8.0 export PACKAGE_REPOSITORY=https://repositories.intel.com/graphics export TAG=test-build-${OS_DISTRO}-${PACKAGE_STREAM}-$(date +%Y%m%d) scripts/build-dockerfile.sh scripts/build-image.sh ``` ## Verify hardware access ```bash TAG=${TAG:-latest} docker run \ --rm \ --device=/dev/dri \ -e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} \ -it \ intel-media-ffmpeg:${TAG} \ info ``` The above will provide information about the software in the container, as well as the detected Intel graphics hardware. If you are in a multicard environment, see Appendix A. ## Test hardware accelerated FFMPEG media operations First download test content into ./media, then launch the Docker container mounting that path to the /media volume, running the 'test' command. NOTE: The test media stream is currently hard coded to expect the name AUD_WM_E.264 in the container. ```bash TAG=${TAG:-latest} mkdir $(pwd)/media wget -O $(pwd)/media/AUD_MW_E.264 \ https://fate-suite.libav.org/h264-conformance/AUD_MW_E.264 docker run \ --rm \ --device=/dev/dri \ -e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} \ --volume $(pwd)/media:/media \ -it \ intel-media-ffmpeg:${TAG} \ test ``` The above will: 1. Download a test content file from fate-suite.libav.org into $(pwd)/media 2. Instantiate the 'intel-media-ffmpeg' container 3. Perfom the following tests: 1. decode AUD_MW_E.264 to AUD_MW.yuv 2. encode AUD_MW.yuv to AUD_MW_E.h264 3. transcode AUD_MW_E.264 to AUD_MW_E.hevc 4. transcode AUD_MW_E.264 to two streams at once, AUD_1N-5M.h264 and AUD_1N-4M60FPS.h264 Once completed, you can check the contents of $(pwd)/media for the following files: ``` AUD_MW_E.264 AUD_MW.yuv AUD_MW_E.h264 AUD_MW_E.hevc AUD_1N-5M.h264 AUD_1N-4M60FPS.h264 ``` ## Launch a shell in the container The examples below are all assumed to be running in the container's environment: ```bash TAG=${TAG:-latest} docker run \ --rm \ --device=/dev/dri \ -e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} \ -it \ intel-media-ffmpeg:${TAG} \ shell ``` ### Decode AVC (H.264) video decode and save as YUV 420P raw file: **NOTE**: Run the following in the container launched previously. ```bash IN_FILE=AUD_WM_E.264 OUT_FILE=AUD_MW.yuv ffmpeg \ -hwaccel qsv \ -qsv_device ${QSV_DEVICE:-/dev/dri/renderD128} \ -c:v h264_qsv \ -i /media/"${IN_FILE}" \ -vf hwdownload,format=nv12 -pix_fmt yuv420p \ -y \ /media/"${OUT_FILE}" ``` ### Encode Encode a 10 frames of 720p raw input as H264 with 5Mbps using VBR mode: **NOTE**: Run the following in the container launched previously. ```bash IN_FILE=AUD_MW.yuv OUT_FILE=AUD_MW_E.h264 ffmpeg \ -loglevel debug \ -init_hw_device vaapi=va:${QSV_DEVICE:-/dev/dri/renderD128} \ -init_hw_device qsv=hw@va \ -filter_hw_device hw \ -f rawvideo \ -pix_fmt yuv420p \ -s:v 176x144 \ -i /media/"${IN_FILE}" \ -vf hwupload=extra_hw_frames=64,format=qsv \ -c:v h264_qsv \ -b:v 5M \ -frames:v 10 \ -y \ /media/"${OUT_FILE}" ``` ### Transcode #### AVC (H.264) => HEVC (H.265) with 5Mbps using VBR **NOTE**: Run the following in the container launched previously. ```bash IN_FILE=AUD_MW_E.264 OUT_FILE=AUD_MW_E.hevc ffmpeg \ -loglevel debug \ -hwaccel qsv \ -qsv_device ${QSV_DEVICE:-/dev/dri/renderD128} \ -c:v h264_qsv \ -i /media/"${IN_FILE}" \ -c:v hevc_qsv \ -b:v 5M \ -y \ /media/"${OUT_FILE}" ``` #### 1:N transcoding **NOTE**: Run the following in the container launched previously. ```bash IN_FILE=AUD_MW_E.264 OUT_FILE=AUD_1N_ ffmpeg \ -hwaccel qsv \ -qsv_device ${QSV_DEVICE} \ -c:v h264_qsv \ -i /media/"${IN_FILE}" \ -filter_complex "split=2[s1][s2]; \ [s1]scale_qsv=1280:720[o1]; \ [s2]vpp_qsv=framerate=60[o2]" \ -map [o1] -c:v h264_qsv -b:v 5M /media/"${OUT_FILE}-5M.mp4" \ -map [o2] -c:v h264_qsv -b:v 4M /media/"${OUT_FILE}-4M60FPS.h264" ``` # Appendix A: Multicard Most of the filters and drivers for ffmpeg will default to connecting to /dev/dri/renderD128. If you have multiple cards, the card you want to connect to might be exposed on a different render interface. You can configure which interface is used by setting the QSV_DEVICE environment variable prior to running intel-docker (or by passing -e QSV_DEVICE to docker if you run it manually.) You can find out the correct path for your Intel Graphics card by running: ``` ls -l /dev/dri/by-path/pci-*$(lspci | grep Intel.*Graphics | cut -d " " -f1)* ``` If the interface is on /dev/dri/renderD129, set QSV_DEVICE as follows: ``` export QSV_DEVICE=/dev/dri/renderD129 ```