7.1 KiB
Intel Media FFMPEG Transcode Container
This project hosts a container demonstrating the use of ffmpeg using GPU offload for transcode operations.
Usage examples
Build the container
Build the container:
docker build . -t intel-media-ffmpeg
or you can pull the container from Harbor:
docker pull amr-registry.caas.intel.com/vtt-osgc/solutions/intel-media-ffmpeg
docker tag amr-registry.caas.intel.com/vtt-osgc/solutions/intel-media-ffmpeg intel-media-ffmpeg
Download test content
You will need some test content to run the following tests. You can download a test stream here:
[ ! -e media ] && mkdir media
wget -q -O media/AUD_MW_E.264 https://fate-suite.libav.org/h264-conformance/AUD_MW_E.264
Run a set of tests in sequence
The following will decode, encode, and transcode the sample content:
HOST_MEDIA=$(pwd)/media
[ ! -e "${HOST_MEDIA}/AUD_MW_E.264" ] && {
mkdir -p ${HOST_MEDIA}
wget -q -O ${HOST_MEDIA}/AUD_MW_E.264 https://fate-suite.libav.org/h264-conformance/AUD_MW_E.264
}
tests=(
"decode AUD_MW_E.264 AUD_MW.yuv"
"encode AUD_MW.yuv AUD_MW_E.h264"
"transcode AUD_MW_E.264 AUD_MW_E.hevc"
"1N_transcode AUD_MW_E.264 AUD_1N"
)
for test in "${tests[@]}"; do
test=($test)
docker run --rm \
--volume ${HOST_MEDIA}:/media -it \
--device=/dev/dri -e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/D128} \
intel-media-ffmpeg ${test[*]} || {
echo "Error: Test failed: ${test[*]}"
break
}
done
The above will download the file AUD_MW_E.264 and then perform various actions on it. If successful, looking in $(HOST_MEDIA} should show the following files (all with data in them.)
$ ls -1tr "${HOST_MEDIA}"
AUD_MW_E.264
AUD_MW.yuv
AUD_MW_E.h264
AUD_MW_E.hevc
AUD_1N-5M.mp4
AUD_1N-4M60FPS.h264
Launch a shell in the container
The examples below are all assumed to be running in the container's environment:
docker run \
--rm \
--device=/dev/dri \
-e QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} \
-it \
intel-media-ffmpeg \
shell
Decode
AVC (H.264) video decode and save as YUV 420P raw file:
IN_FILE=AUD_WM_E.264
OUT_FILE=AUD_MW.yuv
HOST_MEDIA=$(pwd)/media
docker run \
--rm \
--device /dev/dri \
--volume ${HOST_MEDIA}:/media \
-it \
intel-media-ffmpeg \
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:
IN_FILE=AUD_MW.yuv
OUT_FILE=AUD_MW_E.h264
HOST_MEDIA=$(pwd)/media
docker run \
--rm \
--device /dev/dri \
--volume ${HOST_MEDIA}:/media \
-it \
intel-media-ffmpeg \
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
IN_FILE=AUD_MW_E.264
OUT_FILE=AUD_MW_E.hevc
HOST_MEDIA=$(pwd)/media
docker run \
--rm \
--device=/dev/dri \
--volume ${HOST_MEDIA}:/media \
-it \
intel-media-ffmpeg \
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
IN_FILE=AUD_MW_E.264
OUT_FILE=AUD_1N_
HOST_MEDIA=$(pwd)/media
docker run \
--rm \
--device=/dev/dri \
--volume ${HOST_MEDIA}:/media \
-it \
intel-media-ffmpeg \
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"
Developing
The Dockerfile itself is constructed from re-usable snippets, located in the templates/ directory, and can be regenerated by running:
scripts/build-dockerfile
The above script uses environment substitution to stamp version information within the created Dockerfile. The files which declare the environment variables are in SOLUTION and MANIFEST.
After joining the template/* pieces together, the file Dockerfile.solution is then added to the Dockerfile with environment substitution.
SOLUTION
Solution specific definitions:
CONTAINER_IMAGE is used as the container tag name
OS_DISTRO is used as the base OS distribution. Possible values: ubuntu
OS_RELEASE is used as the OS version. Possible values: disco, eoan
MANIFEST
The version of MANIFEST is created by the set of Agama packages from the Agama repository and name-mangling them to be a VERSION declaration:
For example:
libgl1-mesa-glx_19.0.1-agama-109_amd64.deb
is changed to:
LIBGL1_MESA_GLX_VERSION=19.0.1-agama-109
The script you can use to recreate the OS_RELEASE defined in SOLUTION is:
NOTE: This only works on the Ubuntu releases.
AGAMA_VERSION=169
. SOLUTION
echo "AGAMA_VERSION=${AGAMA_VERSION}" > MANIFEST
wget -q -O - \
https://osgc.jf.intel.com/packages/agama/ubuntu/dists/${OS_RELEASE}/main/binary-amd64/Packages.bz2 |
bunzip2 |
sed -nE 's/^(Package|Version): (.*)/\2/p' |
paste -s -d' \n' |
while read package version rest; do
package=$(echo $package | sed -E -e s#-#_#g -e 's#(.*)#\U\1#g')_VERSION
echo $package=$version
done | grep ${AGAMA_VERSION}\$ >> MANIFEST
This allows the Dockerfile templates to then version pin Agama packages:
RUN apt-get install -y libgl1-mesa-glx=$LIBGL1_MESA_GLX_VERSION
The scripts/build-dockerfile
loads MANIFEST, which defines
LIBGL1_MESA_GLX_VERSION. That is then subsituted for the version in
the above Dockerfile snippet when being placed into the main Dockerfile.
Tagging
If the build succeeds, we want to be able to tag the git project as well as corresponding Docker images with the appropriate Agama tag:
. MANIFEST ; git tag -f agama-${AGAMA_VERSION}
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