Skip to content

Commit

Permalink
Merge branch 'master' into ffmpeg-stringapi
Browse files Browse the repository at this point in the history
  • Loading branch information
uartie authored Aug 16, 2024
2 parents ffdc211 + 2844ddc commit 0e0c47b
Show file tree
Hide file tree
Showing 17 changed files with 222 additions and 25 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: unit

on: [ push, pull_request ]

jobs:
test:
strategy:
fail-fast: false
matrix:
ubuntu-version: [ "ubuntu-22.04", "ubuntu-24.04", "ubuntu-latest" ]
python-version: [ "3.10", "3.11", "3.12" ]
runs-on: ${{ matrix.ubuntu-version }}

steps:
- name: Checkout
uses: actions/checkout@v4
with:
lfs: 'true'
- name: Setup python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt setuptools
sudo sysctl kernel.dmesg_restrict=0
- name: Test "list"
run: ./vaapi-fits list
- name: Test "self"
run: ./vaapi-fits run --pl NA --device /dev/null -vv --parallel-metrics --call-timeout 10 --ctapr 0 test/self
2 changes: 1 addition & 1 deletion lib/codecs.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ class Codec(str, enum.Enum):
MJPEG = "mjpeg"
MPEG2 = "mpeg2"
VC1 = "vc1"

VVC = "vvc"
def __str__(self):
return self.value
5 changes: 3 additions & 2 deletions lib/ffmpeg/decoderbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Decoder(PropertyHandler, BaseFormatMapper):
decoded = property(lambda s: s._decoded)
osdecoded = property(lambda s: filepath2os(s.decoded))
hwaccel = property(lambda s: s.props["hwaccel"])
strict = property(lambda s: s.ifprop("strict", "-strict {strict}"))

width = property(lambda s: s.props["width"])
height = property(lambda s: s.props["height"])
Expand Down Expand Up @@ -82,7 +83,7 @@ def decode(self):
self._statsfile = get_media().artifacts.reserve(mtype)

return call(
f"{exe2os('ffmpeg')} -v verbose {self.hwinit}"
f"{exe2os('ffmpeg')} -v verbose {self.strict} {self.hwinit}"
f" {self.ffdecoder} -r:v {fps} -i {self.ossource}"
f" -f rawvideo -pix_fmt {self.format} -s:v {self.width}x{self.height}"
f" -r:v {fps} {self.refseek} -i {self.osreference}"
Expand All @@ -91,7 +92,7 @@ def decode(self):
)

return call(
f"{exe2os('ffmpeg')} -v verbose {self.hwinit}"
f"{exe2os('ffmpeg')} -v verbose {self.strict} {self.hwinit}"
f" {self.ffdecoder} -i {self.ossource} -lavfi '{self.scale_range}'"
f" -c:v rawvideo -pix_fmt {self.format} -fps_mode passthrough"
f" -noautoscale -vframes {self.frames} -y {self.ffoutput}"
Expand Down
2 changes: 1 addition & 1 deletion lib/ffmpeg/encoderbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def check_bitrate(self):
# acceptable bitrate within 10% of bitrate
assert(bitrate_gap <= 0.10)

elif "vbr" == self.rcmode and vars(self).get("maxframesize", None) is None:
elif "vbr" in self.rcmode and vars(self).get("maxframesize", None) is None:
# acceptable bitrate within 25% of minrate and 10% of maxrate
if vars(self).get("maxrate", None) is not None:
assert(self.minrate * 0.75 <= bitrate_actual <= self.maxrate * 1.10)
Expand Down
2 changes: 1 addition & 1 deletion lib/gstreamer/encoderbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def check_bitrate(self):
# acceptable bitrate within 10% of bitrate
assert(bitrate_gap <= 0.10)

elif self.rcmode in ["vbr", "la_vbr"] and vars(self).get("maxframesize", None) is None:
elif "vbr" in self.rcmode and vars(self).get("maxframesize", None) is None:
# acceptable bitrate within 25% of minrate and 10% of maxrate
assert(self.minrate * 0.75 <= bitrate_actual <= self.maxrate * 1.10)

Expand Down
18 changes: 17 additions & 1 deletion lib/gstreamer/msdk/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import slash

from ....lib.gstreamer.encoderbase import BaseEncoderTest, Encoder as GstEncoder
from ....lib.gstreamer.util import have_gst_element
from ....lib.gstreamer.util import have_gst_element, get_elements
from ....lib.gstreamer.msdk.util import using_compatible_driver, mapprofile, map_best_hw_format, mapformat
from ....lib.gstreamer.msdk.decoder import Decoder
from ....lib import platform
Expand Down Expand Up @@ -142,6 +142,22 @@ def before(self):
super().before()
os.environ["GST_MSDK_DRM_DEVICE"] = get_media().render_device

self.__rank_before = os.environ.get("GST_PLUGIN_FEATURE_RANK", None)
# WA: Fix the gst-discoverer-1.0.exe report Missing plugins problem on Windows OS
if platform.info()['os'] == 'wsl':
ranks = [] if self.__rank_before is None else self.__rank_before.split(',')
ranks += [f"{e}:MAX" for e in get_elements("msdk")]
os.environ["GST_PLUGIN_FEATURE_RANK"] = ','.join(ranks)

def after(self):
super().after()

if None == self.__rank_before:
if 'GST_PLUGIN_FEATURE_RANK' in os.environ:
del os.environ["GST_PLUGIN_FEATURE_RANK"]
else:
os.environ["GST_PLUGIN_FEATURE_RANK"] = self.__rank_before

def map_profile(self):
return mapprofile(self.codec, self.profile)

Expand Down
4 changes: 2 additions & 2 deletions lib/gstreamer/msdk/transcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ class TranscoderTest(BaseTranscoderTest):
),
),
Codec.VP9 : dict(
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"msdkvp9dec"), f"matroskademux ! vp9parse ! msdkvp9dec"),
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"msdkvp9dec"), "vp9parse ! msdkvp9dec"),
),
Codec.AV1 : dict(
hw = (platform.get_caps("decode", "av1_8"), have_gst_element(f"msdkav1dec"), f"matroskademux ! av1parse ! msdkav1dec"),
hw = (platform.get_caps("decode", "av1_8"), have_gst_element(f"msdkav1dec"), "av1parse ! msdkav1dec"),
),
},
encode = {
Expand Down
24 changes: 17 additions & 7 deletions lib/gstreamer/transcoderbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,21 @@ def validate_caps(self):

self.post_validate()

def get_demuxer(self, fname):
ext = os.path.splitext(fname)[1]
return {
".ivf" : " ! ivfparse",
".webm" : " ! matroskademux",
".ts" : " ! tsdemux"
}.get(ext, "")

def gen_input_opts(self):
self.ossource = filepath2os(self.source)

opts = "filesrc location={ossource}"
if (os.path.splitext(self.source)[1]) == ".ts":
opts += " ! tsdemux"
opts += self.get_demuxer(self.source)
opts += " ! " + self.get_decoder(self.codec, self.mode)

if self.mode in ["hw", "sw"]:
opts += " ! 'video/x-raw(ANY),format={mformat}'"

Expand Down Expand Up @@ -182,12 +191,12 @@ def transcode(self):
get_media()._set_test_details(**{"output.{}".format(n) : output})
for channel in range(output.get("channels", 1)):
encoded = self.goutputs[n][channel]
osencoded = filepath2os(encoded)
self.check_resolution(output, osencoded)
self.check_metrics(output, osencoded, refctx = [(n, channel)])
self.check_resolution(output, encoded)
self.check_metrics(output, encoded, refctx = [(n, channel)])

def check_resolution(self, output, encoded):
props = [l.strip() for l in gst_discover(encoded).split('\n')]
osencoded = filepath2os(encoded)
props = [l.strip() for l in gst_discover(osencoded).split('\n')]
width = output.get("width", self.width)
height = output.get("height", self.height)

Expand All @@ -197,13 +206,14 @@ def check_resolution(self, output, encoded):
def check_metrics(self, output, encoded, refctx):
ocodec = output["codec"]
odecoder = self.get_decoder(ocodec, "hw")
osencoded = filepath2os(encoded)

vppscale = self.get_vpp_scale(self.width, self.height, "hw")
statsfile = get_media().artifacts.reserve("psnr")
osstatsfile = filepath2os(statsfile)

iopts = (
f"filesrc location={encoded} ! {odecoder} ! {vppscale}"
f"filesrc location={osencoded} {self.get_demuxer(encoded)} ! {odecoder} ! {vppscale}"
f" ! videorate ! 'video/x-raw(ANY),framerate={self.gstfps}' ! cmp."
f" {self.gen_input_opts()} ! {vppscale} ! cmp."
f" avvideocompare method=psnr stats-file={osstatsfile} name=cmp"
Expand Down
7 changes: 6 additions & 1 deletion lib/gstreamer/va/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,14 @@ def before(self):
def decode_test_class(codec, bitdepth, **kwargs):
# caps lookup translation
capcodec = codec
if codec in [Codec.HEVC, Codec.VP9, Codec.AV1]:
if codec in [Codec.HEVC, Codec.VP9, Codec.AV1, Codec.VVC]:
capcodec = f"{codec}_{bitdepth}"

# gst element codec translation
gstcodec = {
Codec.AVC : "h264",
Codec.HEVC : "h265",
Codec.VVC : "h266",
}.get(codec, codec)

gstparser = {
Expand Down Expand Up @@ -90,3 +91,7 @@ def validate_caps(self):

## MPEG2 ##
MPEG2DecoderTest = decode_test_class(codec = Codec.MPEG2, bitdepth = 8)

## VVC ##
VVC_8DecoderTest = decode_test_class(codec = Codec.VVC, bitdepth = 8)
VVC_10DecoderTest = decode_test_class(codec = Codec.VVC, bitdepth = 10)
14 changes: 7 additions & 7 deletions lib/gstreamer/va/transcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ def make_requirements():
sw = (dict(maxres = (16384, 16384)), have_gst_element("libde265dec"), "h265parse ! libde265dec ! videoconvert"),
hw = (platform.get_caps("decode", "hevc_8"), have_gst_element(f"va{hw}h265dec"), f"h265parse ! va{hw}h265dec"),
),
Codec.AV1 : dict(
hw = (platform.get_caps("decode", "av1_8"), have_gst_element(f"va{hw}av1dec"), f"matroskademux ! av1parse ! va{hw}av1dec"),
),
Codec.MPEG2 : dict(
sw = (dict(maxres = (2048, 2048)), have_gst_element("mpeg2dec"), "mpegvideoparse ! mpeg2dec ! videoconvert"),
hw = (platform.get_caps("decode", "mpeg2"), have_gst_element(f"va{hw}mpeg2dec"), f"mpegvideoparse ! va{hw}mpeg2dec"),
Expand All @@ -39,7 +36,10 @@ def make_requirements():
hw = (platform.get_caps("decode", "jpeg"), have_gst_element(f"va{hw}jpegdec"), f"jpegparse ! va{hw}jpegdec"),
),
Codec.VP9 : dict(
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"va{hw}vp9dec"), f"matroskademux ! vp9parse ! va{hw}vp9dec"),
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"va{hw}vp9dec"), f"vp9parse ! va{hw}vp9dec"),
),
Codec.AV1 : dict(
hw = (platform.get_caps("decode", "av1_8"), have_gst_element(f"va{hw}av1dec"), f"av1parse ! va{hw}av1dec"),
),
},
encode = {
Expand All @@ -53,9 +53,6 @@ def make_requirements():
hw = (platform.get_caps("encode", "hevc_8"), have_gst_element(f"va{hw}h265enc"), f"va{hw}h265enc ! video/x-h265,profile=main ! h265parse"),
lp = (platform.get_caps("vdenc", "hevc_8"), have_gst_element(f"va{hw}h265lpenc"), f"va{hw}h265lpenc ! video/x-h265,profile=main ! h265parse"),
),
Codec.AV1 : dict(
lp = (platform.get_caps("vdenc", "av1_8"), have_gst_element(f"va{hw}av1lpenc"), f"va{hw}av1lpenc ! video/x-av1,profile=main ! av1parse ! matroskamux"),
),
Codec.MPEG2 : dict(
sw = (dict(maxres = (2048, 2048)), have_gst_element("avenc_mpeg2video"), "avenc_mpeg2video ! mpegvideoparse"),
hw = (platform.get_caps("encode", "mpeg2"), have_gst_element(f"va{hw}mpeg2enc"), f"va{hw}mpeg2enc ! mpegvideoparse"),
Expand All @@ -67,6 +64,9 @@ def make_requirements():
Codec.VP9 : dict(
lp = (platform.get_caps("vdenc", "vp9_8"), have_gst_element(f"va{hw}vp9lpenc"), f"va{hw}vp9lpenc ! vp9parse ! matroskamux"),
),
Codec.AV1 : dict(
lp = (platform.get_caps("vdenc", "av1_8"), have_gst_element(f"va{hw}av1lpenc"), f"va{hw}av1lpenc ! video/x-av1,profile=main ! av1parse ! matroskamux"),
),
},
vpp = {
"scale" : dict(
Expand Down
5 changes: 4 additions & 1 deletion lib/gstreamer/vaapi/transcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ class TranscoderTest(BaseTranscoderTest):
),
),
Codec.VP9 : dict(
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"vaapivp9dec"), f"matroskademux ! vp9parse ! vaapivp9dec"),
hw = (platform.get_caps("decode", "vp9_8"), have_gst_element(f"vaapivp9dec"), f"vp9parse ! vaapivp9dec"),
),
Codec.AV1 : dict(
hw = (platform.get_caps("decode", "av1_8"), have_gst_element("vaapiav1dec"), "av1parse ! vaapiav1dec"),
),
},
encode = {
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
slash>=1.12.0
slash >= 1.12.0
numpy
scikit-image
distro
Expand Down
29 changes: 29 additions & 0 deletions test/ffmpeg-vaapi/decode/10bit/vvc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###
### Copyright (C) 2024 Intel Corporation
###
### SPDX-License-Identifier: BSD-3-Clause
###

from .....lib import *
from .....lib.ffmpeg.vaapi.util import *
from .....lib.ffmpeg.vaapi.decoder import DecoderTest

spec = load_test_spec("vvc", "decode", "10bit")

@slash.requires(*platform.have_caps("decode", "vvc_10"))
@slash.requires(*have_ffmpeg_decoder("vvc"))
class default(DecoderTest):
def before(self):
vars(self).update(
caps = platform.get_caps("decode", "vvc_10"),
ffdecoder = "vvc",
metric = dict(type = "ssim", miny = 1.0, minu = 1.0, minv = 1.0), # default metric
strict = -2, # Currently, VVC decoder is experimental. Enable it with strict.
)
super().before()

@slash.parametrize(("case"), sorted(spec.keys()))
def test(self, case):
vars(self).update(spec[case].copy())
self.case = case
self.decode()
29 changes: 29 additions & 0 deletions test/ffmpeg-vaapi/decode/vvc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
###
### Copyright (C) 2024 Intel Corporation
###
### SPDX-License-Identifier: BSD-3-Clause
###

from ....lib import *
from ....lib.ffmpeg.vaapi.util import *
from ....lib.ffmpeg.vaapi.decoder import DecoderTest

spec = load_test_spec("vvc", "decode", "8bit")

@slash.requires(*platform.have_caps("decode", "vvc_8"))
@slash.requires(*have_ffmpeg_decoder("vvc"))
class default(DecoderTest):
def before(self):
vars(self).update(
caps = platform.get_caps("decode", "vvc_8"),
ffdecoder = "vvc",
metric = dict(type = "ssim", miny = 1.0, minu = 1.0, minv = 1.0), # default metric
strict = -2, # Currently, VVC decoder is experimental. Enable it with strict.
)
super().before()

@slash.parametrize(("case"), sorted(spec.keys()))
def test(self, case):
vars(self).update(spec[case].copy())
self.case = case
self.decode()
25 changes: 25 additions & 0 deletions test/gst-va/decode/10bit/vvc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
###
### Copyright (C) 2024 Intel Corporation
###
### SPDX-License-Identifier: BSD-3-Clause
###

from .....lib import *
from .....lib.gstreamer.va.util import *
from .....lib.gstreamer.va.decoder import VVC_10DecoderTest as DecoderTest

spec = load_test_spec("vvc", "decode", "10bit")

class default(DecoderTest):
def before(self):
# default metric
vars(self).update(
metric = dict(type = "ssim", miny = 1.0, minu = 1.0, minv = 1.0),
)
super().before()

@slash.parametrize(("case"), sorted(spec.keys()))
def test(self, case):
vars(self).update(spec[case].copy())
self.case = case
self.decode()
25 changes: 25 additions & 0 deletions test/gst-va/decode/vvc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
###
### Copyright (C) 2024 Intel Corporation
###
### SPDX-License-Identifier: BSD-3-Clause
###

from ....lib import *
from ....lib.gstreamer.va.util import *
from ....lib.gstreamer.va.decoder import VVC_8DecoderTest as DecoderTest

spec = load_test_spec("vvc", "decode", "8bit")

class default(DecoderTest):
def before(self):
# default metric
vars(self).update(
metric = dict(type = "ssim", miny = 1.0, minu = 1.0, minv = 1.0),
)
super().before()

@slash.parametrize(("case"), sorted(spec.keys()))
def test(self, case):
vars(self).update(spec[case].copy())
self.case = case
self.decode()
Loading

0 comments on commit 0e0c47b

Please sign in to comment.