diff --git a/README.md b/README.md index 943a992..716fc45 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,9 @@ This project hosts a container demonstrating the use of ffmpeg using GPU offload for transcode operations. -# Using +# Usage examples + +## Build the container Build the container: @@ -11,17 +13,13 @@ Build the container: docker build . -t intel-media-ffmpeg ``` -Run the container: +or you can pull the container from Harbor: ```bash -docker run \ - --rm \ - --device=/dev/dri \ - -it \ - intel-media-ffmpeg +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 ``` -# Usage examples ## Download test content @@ -33,9 +31,65 @@ test stream here: 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/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.) + +```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 -H264 video decode and save as raw file: +AVC (H.264) video decode and save as YUV 420P raw file: ```bash IN_FILE=AUD_WM_E.264 @@ -51,10 +105,10 @@ docker run \ -hwaccel qsv \ -qsv_device ${QSV_DEVICE:-/dev/dri/renderD128} \ -c:v h264_qsv \ - -i /media/${IN_FILE} \ + -i /media/"${IN_FILE}" \ -vf hwdownload,format=nv12 -pix_fmt yuv420p \ -y \ - /media/${OUT_FILE} + /media/"${OUT_FILE}" ``` ## Encode @@ -79,13 +133,13 @@ docker run \ -f rawvideo \ -pix_fmt yuv420p \ -s:v 176x144 \ - -i /media/${IN_FILE} \ + -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} + /media/"${OUT_FILE}" ``` ## Transcode @@ -111,15 +165,14 @@ docker run \ -c:v hevc_qsv \ -b:v 5M \ -y \ - /media/${OUT_FILE} + /media/"${OUT_FILE}" ``` ### 1:N transcoding ```bash -QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} IN_FILE=AUD_MW_E.264 -OUT_FILE_BASE=AUD_1N_ +OUT_FILE=AUD_1N_ HOST_MEDIA=$(pwd)/media docker run \ --rm \ @@ -129,14 +182,14 @@ docker run \ intel-media-ffmpeg \ ffmpeg \ -hwaccel qsv \ - -qsv_device ${QSV_DEVICE} \ + -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_BASE}5M.mp4" \ - -map [o2] -c:v h264_qsv -b:v 4M /media/"${OUT_FILE_BASE}4M60FPS.h264" + -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 diff --git a/assets/cmd_1N_transcode b/assets/cmd_1N_transcode new file mode 100755 index 0000000..3f077b5 --- /dev/null +++ b/assets/cmd_1N_transcode @@ -0,0 +1,127 @@ +#!/bin/bash + +PARAM=$0 +# Strip path and cmd_ prefix from command name +PARAM=${PARAM##*/cmd_} + +short() { + echo "Transcode AVC (H.264) to to streams: 1280x720@5Mbps and 60FPS@4Mbps" +} + +help() { +cat << EOF +$(short) + +$(info) + +IN_FILE AVC (H.264) format input stream +OUT_FILE Base filename. "-5M.mp4" and "-4M60FPS.h264" will be added. + +NOTE: Above paths are read from the container volume /media. + +EOF +} + +details() { +cat << EOF +$(short) + +This script provides a quick reference example using the following +ffmpeg command: + + 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]" \ + -y \ + -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" + +EOF +} + +arguments=() + +for arg in $*; do + [[ $arg =~ ^-- ]] && { + command=${arg/--} + case $command in + short) short + exit 0 + ;; + help) help + exit 0 + ;; + details) details + exit 0 + ;; + *) echo "Unrecognized command." + exit 1 + ;; + esac + } || { + arguments+=("$arg") + } +done + +info() { + cat << EOF +usage: $PARAM INPUT-FILE OUTPUT-FILE +EOF +} + +QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} + +[ "${arguments[0]}" == "" ] || [ "${arguments[1]}" == "" ] && { + info + exit 1 +} + +[ ! -e /media/"${arguments[0]}" ] && { + cat << EOF +Error: '${arguments[0]}' not found. + +Was the volume /media mounted when launching the container? + + docker ... --volume \$(pwd)/media:/media ... + +or similar? + +EOF + + exit 1 +} + +[ ! -e ${QSV_DEVICE} ] && { +cat << EOF + +Error: '${QSV_DEVICE}' not found. + +Did you pass /dev/dri to Docker? + + docker ... --device=/dev/dri ... + +or similar? + +EOF + exit 2 +} + +IN_FILE="${arguments[0]}" +OUT_FILE="${arguments[1]}" + + 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" + diff --git a/assets/cmd_decode b/assets/cmd_decode index a3ce413..5bb2bc0 100755 --- a/assets/cmd_decode +++ b/assets/cmd_decode @@ -5,7 +5,7 @@ PARAM=$0 PARAM=${PARAM##*/cmd_} short() { - echo "Decode H264 video and save as raw file" + echo "Decode AVC (H.264) video and save as YUV 420P raw file" } help() { @@ -14,8 +14,8 @@ $(short) $(info) -INFILE Input stream must be H264 video -OUTFILE Will be decoded and saved as YUV420P +IN_FILE Input stream must be AVC (H.264) video +OUT_FILE Output stream as YUV 420P NOTE: Above paths are read from the container volume /media. @@ -29,14 +29,14 @@ $(short) This script provides a quick reference example using the following ffmpeg command: -ffmpeg \ - -hwaccel qsv \ - -qsv_device ${QSV_DEVICE} \ - -c:v h264_qsv \ - -i /media/INFILE \ - -vf hwdownload,format=nv12 \ - -pix_fmt yuv420p \ - /media/OUTFILE + 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}" EOF } @@ -95,7 +95,7 @@ EOF [ ! -e ${QSV_DEVICE} ] && { cat << EOF -Error: '${QSV_DEVCIE}' not found. +Error: '${QSV_DEVICE}' not found. Did you pass /dev/dri to Docker? @@ -107,8 +107,15 @@ EOF exit 2 } -ffmpeg -hwaccel qsv -qsv_device ${QSV_DEVICE} \ - -c:v h264_qsv -i /media/"${arguments[0]}" \ - -vf hwdownload,format=nv12 -pix_fmt yuv420p \ - /media/"${arguments[1]}" +IN_FILE="${arguments[0]}" +OUT_FILE="${arguments[1]}" + + 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}" diff --git a/assets/cmd_encode b/assets/cmd_encode index f0bd1b5..b0ffcc6 100755 --- a/assets/cmd_encode +++ b/assets/cmd_encode @@ -14,8 +14,8 @@ $(short) $(info) -INFILE Must 720p raw -OUTFILE Will be encoded as H264 w/ 5Mbps using VBR +IN_FILE YUV 420p raw stream +OUT_FILE Encoded to AVC (H.264) with 5Mbps using VBR NOTE: Above paths are read from the container volume /media. @@ -29,17 +29,21 @@ $(short) This script provides a quick reference example using the following ffmpeg command: -ffmpeg -loglevel debug \ - -init_hw_device qsv=hw \ - -filter_hw_device hw \ - -f rawvideo -pix_fmt yuv420p \ - -s:v 176x144 \ - -i /media/INFILE \ - -vf hwupload=extra_hw_frames=64,format=qsv \ - -c:v h264_qsv \ - -b:v 5M \ - -frames:v 10 \ - -y /media/OUTFILE + 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} EOF } @@ -99,7 +103,7 @@ EOF [ ! -e ${QSV_DEVICE} ] && { cat << EOF -Error: '${QSV_DEVCIE}' not found. +Error: '${QSV_DEVICE}' not found. Did you pass /dev/dri to Docker? @@ -111,12 +115,21 @@ EOF exit 2 } -INFILE="${arguments[0]}" -OUTFILE="${arguments[1]}" +IN_FILE="${arguments[0]}" +OUT_FILE="${arguments[1]}" -ffmpeg -loglevel debug -init_hw_device qsv=hw \ - -filter_hw_device hw -f rawvideo -pix_fmt \ - yuv420p -s:v 176x144 -i /media/"${INFILE}" -vf \ - hwupload=extra_hw_frames=64,format=qsv \ - -c:v h264_qsv -b:v 5M -frames:v 10 \ - -y /media/"${OUTFILE}" +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} diff --git a/assets/cmd_transcode b/assets/cmd_transcode new file mode 100755 index 0000000..8b0543d --- /dev/null +++ b/assets/cmd_transcode @@ -0,0 +1,125 @@ +#!/bin/bash + +PARAM=$0 +# Strip path and cmd_ prefix from command name +PARAM=${PARAM##*/cmd_} + +short() { + echo "Transcode AVC (H.264) to HEVC (H.265) with 5Mbps using VBR" +} + +help() { +cat << EOF +$(short) + +$(info) + +IN_FILE AVC (H.264) format input stream +OUT_FILE HEVC (H.265) output stream + +NOTE: Above paths are read from the container volume /media. + +EOF +} + +details() { +cat << EOF +$(short) + +This script provides a quick reference example using the following +ffmpeg command: + + 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}" + +EOF +} + +arguments=() + +for arg in $*; do + [[ $arg =~ ^-- ]] && { + command=${arg/--} + case $command in + short) short + exit 0 + ;; + help) help + exit 0 + ;; + details) details + exit 0 + ;; + *) echo "Unrecognized command." + exit 1 + ;; + esac + } || { + arguments+=("$arg") + } +done + +info() { + cat << EOF +usage: $PARAM INPUT-FILE OUTPUT-FILE +EOF +} + +QSV_DEVICE=${QSV_DEVICE:-/dev/dri/renderD128} + +[ "${arguments[0]}" == "" ] || [ "${arguments[1]}" == "" ] && { + info + exit 1 +} + +[ ! -e /media/"${arguments[0]}" ] && { + cat << EOF +Error: '${arguments[0]}' not found. + +Was the volume /media mounted when launching the container? + + docker ... --volume \$(pwd)/media:/media ... + +or similar? + +EOF + + exit 1 +} + +[ ! -e ${QSV_DEVICE} ] && { +cat << EOF + +Error: '${QSV_DEVICE}' not found. + +Did you pass /dev/dri to Docker? + + docker ... --device=/dev/dri ... + +or similar? + +EOF + exit 2 +} + +IN_FILE="${arguments[0]}" +OUT_FILE="${arguments[1]}" + + 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}" diff --git a/scripts/build-images b/scripts/build-images index 8afeba8..2e6d513 100755 --- a/scripts/build-images +++ b/scripts/build-images @@ -6,6 +6,7 @@ export $(grep -v '^#' SOLUTION | xargs -d '\n') export $(grep -v '^#' MANIFEST | xargs -d '\n') docker build . -t ${CONTAINER}:agama-${AGAMA_VERSION} +docker tag ${CONTAINER}:agama-${AGAMA_VERSION} ${CONTAINER} cat << EOM