# Intel Media FFMPEG Transcode Container This project hosts a container demonstrating the use of ffmpeg using GPU offload for transcode operations. For information on how the Dockerfile in this container is generated, see [xe-solutions](https://gitlab.devtools.intel.com/vtt/sws/osgc/solutions/xe-solutions). # Usage examples Two step from zero-to-hero: 1. Download some test content 2. Transcode with a pre-built container ## Download content The following will download the Blender TearsOfSteel video: ```bash mkdir $(pwd)media wget -O $(pwd)/media/TearsOfSteel.mp4 \ http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4 ``` ## Transcode with a pre-built container The following will transcode 'TearsOfSteal.mp4' from AVC (H.264) to a 5Mbps HEVC (H.265) stream using the GPU. ```bash docker run \ --device=/dev/dri \ --volume $(pwd)/media:/media \ amr-registry.caas.intel.com/vtt-osgc/solutions/intel-media-ffmpeg:ubuntu-eoan \ ffmpeg \ -hwaccel qsv \ -qsv_device ${QSV_DEVICE:-/dev/dri/renderD128} \ -c:v h264_qsv \ -i /media/TearsOfSteel.mp4 \ -c:v hevc_qsv \ -b:v 5M \ -y \ /media/TearsOfSteel-5M.mp4 ``` # Getting the container You can get the intel-media-ffmpeg container by either building it yourself from the Dockerfile, or by pulling a pre-built image from Intel's Harbor instance. ## Pull 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 NOTE: Ubuntu 19.10 eoan packages have not been pushed to repositories.intel.com yet. 'PACKAGE_REPOSITORY' is set to osgc.jf.intel.com/internal below until the packages are published (by end of November'19) ```bash export OS_DISTRO=ubuntu export OS_RELEASE=eoan export PACKAGE_STREAM=eoan export PACKAGE_REPOSITORY=https://osgc.jf.intel.com/internal 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 ``` # Want a longer transcode test? The following snippet will download the Blender video 'TearsOfSteel' which is available as H.264 AVC, and then use a pre-built ffmpeg container to transcode the stream to 5Mbps H.265 HEVC using the GPU. ```bash TAG=${TAG:-latest} mkdir $(pwd)/media wget -O $(pwd)/media/TearsOfSteel.mp4 \ http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4 docker run \ --rm \ --device=/dev/dri \ -e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} \ -it \ --volume $(pwd)/media:/media \ intel-media-ffmpeg:${TAG} \ transcode TearsOfSteel.mp4 TearsOfSteel-5M.mp4 ``` # 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 ```