300 lines
7.1 KiB
Markdown
300 lines
7.1 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
docker build . -t intel-media-ffmpeg
|
|
```
|
|
|
|
or you can pull the container from Harbor:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
[ ! -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:
|
|
```bash
|
|
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/renderD128} \
|
|
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.)
|
|
|
|
```bash
|
|
$ 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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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:
|
|
|
|
```Dockerfile
|
|
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:
|
|
|
|
```bash
|
|
. 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
|
|
```
|
|
|