#!/usr/bin/env bash # # SPDX-License-Identifier: GPL-2.0 # # Copyright (c) 2026 AMLOGIC # # gst-aml-vendor build script for AMLbian # Cross-compiles vendor gst plugins (amlv4l2h264dec et al) from aml-comp # sources, packages a .deb into BUILD_DEBS. The install side lives in # lib/function-img.sh as install_gst_aml_vendor_deb, mirroring libencoder. # ============================================================================== # Section 2: Default Configuration # ============================================================================== # BOARD and ARCH are set (often readonly) by the armbian framework before this # script is sourced -- do not reassign. DEB_VERSION is our own variable. DEB_VERSION="${GST_AML_VENDOR_VERSION:-1.0.0}" # Cross-compile toolchain prefix; inherits from framework if already set. : "${CROSS_COMPILE:=aarch64-linux-gnu-}" # Kernel source sub-directory inside aml-comp (provides vendor DRM uapi headers). : "${AML_KERNEL_SUBDIR:=aml-6.12}" # libsecmem prebuilt sub-path inside aml-comp/multimedia/libmediadrm/libsecmem-bin/prebuilt/ : "${LIBSECMEM_VERSION:=${PLATFORM_TDK_VERSION:-v4.4.0}}" : "${LIBSECMEM_ARCH_DIR:=aarch64.lp64.}" # ============================================================================== # Section 4: Build Functions # ============================================================================== function __gst_aml_vendor_prepare_source { local rootfs="$1" comp_dir="$2" work="$3" local _gnu_triplet="${CROSS_COMPILE%%-}" # dev headers/.pc files needed for cross-compile. --force-overwrite: vendor # aml-libgles ships a libgbm that collides with Debian libgbm1 pulled in via # libgstreamer-plugins-base1.0-dev. mount_chroot "$rootfs" chroot "$rootfs" /bin/bash -c " export DEBIAN_FRONTEND=noninteractive TMPDIR=/tmp apt-get update -y apt-get install -y --no-install-recommends \ -o Dpkg::Options::=--force-overwrite \ libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \ libdrm-dev libgudev-1.0-dev libv4l-dev pkg-config " local rc=$? umount_chroot "$rootfs" [[ $rc -eq 0 ]] || return 1 # Vendor DRM uapi headers + libsecmem prebuilt blob. install -m 0644 -D \ "${comp_dir}/kernel/${AML_KERNEL_SUBDIR}/common_drivers/include/uapi/amlogic/drm/meson_drm.h" \ "${rootfs}/usr/include/meson_drm.h" mkdir -p "${rootfs}/usr/include/drm" cp -a "${comp_dir}/kernel/${AML_KERNEL_SUBDIR}/include/uapi/drm/." "${rootfs}/usr/include/drm/" local secmem="${comp_dir}/multimedia/libmediadrm/libsecmem-bin/prebuilt" install -m 0644 -D "${secmem}/${LIBSECMEM_VERSION}/${LIBSECMEM_ARCH_DIR}/libsecmem.so" \ "${rootfs}/usr/lib/${_gnu_triplet}/libsecmem.so" cp -a "${secmem}/noarch/include/." "${rootfs}/usr/include/" mkdir -p "${rootfs}/usr/lib/${_gnu_triplet}/pkgconfig" cp -a "${secmem}/noarch/pkgconfig/." \ "${rootfs}/usr/lib/${_gnu_triplet}/pkgconfig/" # Stage three vendor source subtrees. rm -rf "${work}" && mkdir -p "${work}/src" cp -r "${comp_dir}/multimedia/gst-aml-drmbufferpool1/gst-aml-drmbufferpool-1.0" "${work}/src/drmbufferpool" cp -r "${comp_dir}/multimedia/gst-aml-drm-plugins1/gst-aml-drm-plugins-1.0/src/secmem" "${work}/src/secmem" cp -r "${comp_dir}/multimedia/gst-plugin-aml-v4l2dec" "${work}/src/v4l2dec" # Vendor version_config.sh needs git history we don't have; pre-write the # generated headers and disable the script. cat > "${work}/src/drmbufferpool/src/bufferpool/aml_version.h" <<'EOF' #ifndef AML_VERSION_H #define AML_VERSION_H static const char* gst_drmbufferpool_version __attribute__((used)) = "MM-module-name:gst-plugin-drmbufferpool,version:1.1-amlbian"; #endif EOF cat > "${work}/src/v4l2dec/src/aml_version.h" <<'EOF' #ifndef AML_VERSION_H #define AML_VERSION_H static const char* gst_amlv4l2_version __attribute__((used)) = "MM-module-name:gst-plugin-aml-v4l2dec,version:1.1-amlbian"; #endif EOF chmod -x "${work}/src/drmbufferpool/version_config.sh" \ "${work}/src/v4l2dec/version_config.sh" 2>/dev/null || true # aml_gst_shim.o: two yocto-only buffer pool symbols not in Debian gst 1.22. # Linked into libgstamlv4l2.so via LDFLAGS; hidden visibility keeps them # out of the exported symbol space. cat > "${work}/aml_gst_shim.c" <<'EOF' #include typedef void GstBufferPool; typedef int gboolean; __attribute__((visibility("hidden"))) int gst_buffer_pool_get_outstanding_num(GstBufferPool *p) { (void)p; return INT_MAX; } __attribute__((visibility("hidden"))) gboolean gst_buffer_pool_increase_max_num(GstBufferPool *p, unsigned int n) { (void)p; (void)n; return 1; } EOF # libsecmem.so DT_NEEDED's libmediahal_resman.so (closed source, no blob); # no-op stubs are enough for non-secure decode. cat > "${work}/libmediahal_resman_stub.c" <<'EOF' #include int resman_init(int a, int b) { (void)a; (void)b; return 0; } int resman_close(int a) { (void)a; return 0; } int resman_query(int a, int b, void *c) { (void)a; (void)b; (void)c; return -1; } int resman_acquire_para(int a, void *b) { (void)a; (void)b; return -1; } int resman_acquire_wait(int a) { (void)a; return -1; } int resman_release(int a) { (void)a; return 0; } int resman_resource_support(int a) { (void)a; return 0; } int resman_setappinfo(int a, void *b) { (void)a; (void)b; return 0; } int resman_support(int a) { (void)a; return 0; } EOF cat > "${work}/libteec_stub.c" <<'EOF' #include unsigned int TEEC_InitializeContext(const char *n, void *c) { (void)n; (void)c; return 0xFFFF0008U; } void TEEC_FinalizeContext(void *c) { (void)c; } unsigned int TEEC_OpenSession(void *c, void *s, const void *d, unsigned int m, const void *p, void *o, unsigned int *e) { (void)c; (void)s; (void)d; (void)m; (void)p; (void)o; if (e) *e = 0; return 0xFFFF0008U; } void TEEC_CloseSession(void *s) { (void)s; } unsigned int TEEC_InvokeCommand(void *s, unsigned int c, void *p, unsigned int *e) { (void)s; (void)c; (void)p; if (e) *e = 0; return 0xFFFF0008U; } unsigned int TEEC_AllocateSharedMemory(void *c, void *m) { (void)c; (void)m; return 0xFFFF0008U; } void TEEC_ReleaseSharedMemory(void *m) { (void)m; } EOF } function __gst_aml_vendor_build { local work="$1" rootfs="$2" local _gnu_triplet="${CROSS_COMPILE%%-}" local libdir="$rootfs/usr/lib/${_gnu_triplet}" local _build_triplet _build_triplet="$(gcc -dumpmachine)" if [[ ! -f "$libdir/libmediahal_resman.so" && ! -f "$rootfs/usr/lib/libmediahal_resman.so" ]]; then ${CROSS_COMPILE}gcc -shared -fPIC -O2 \ -Wl,-soname,libmediahal_resman.so \ -o "$libdir/libmediahal_resman.so" "$work/libmediahal_resman_stub.c" GST_AML_VENDOR_BUNDLE_RESMAN=1 fi if [[ ! -f "$libdir/libteec.so.2" && ! -f "$rootfs/usr/lib/libteec.so.2" && ! -f "$rootfs/usr/lib/libteec.so.2.0.0" ]]; then ${CROSS_COMPILE}gcc -shared -fPIC -O2 \ -Wl,-soname,libteec.so.2 \ -o "$libdir/libteec.so.2" "$work/libteec_stub.c" ln -sf libteec.so.2 "$libdir/libteec.so" GST_AML_VENDOR_BUNDLE_LIBTEEC=1 fi ${CROSS_COMPILE}gcc -fPIC -O2 -c \ -o "$work/aml_gst_shim.o" "$work/aml_gst_shim.c" export CC=${CROSS_COMPILE}gcc export AR=${CROSS_COMPILE}ar export STRIP=${CROSS_COMPILE}strip export PKG_CONFIG=pkg-config export PKG_CONFIG_SYSROOT_DIR="$rootfs" export PKG_CONFIG_LIBDIR="$rootfs/usr/lib/${_gnu_triplet}/pkgconfig:$rootfs/usr/share/pkgconfig:$rootfs/usr/lib/pkgconfig" unset PKG_CONFIG_PATH # drmbufferpool + drmallocator (autotools) ( cd "$work/src/drmbufferpool" && mkdir -p m4 build-aux && autoreconf -fi \ && ./configure --host=${_gnu_triplet} --build=${_build_triplet} \ --prefix=/usr --libdir=/usr/lib/${_gnu_triplet} \ --with-sysroot="$rootfs" \ CFLAGS="--sysroot=$rootfs" CPPFLAGS="--sysroot=$rootfs" \ LDFLAGS="--sysroot=$rootfs" \ && make -j"$(nproc)" \ && make install DESTDIR="$rootfs" ) || return 1 # secmemallocator (single .c) local CFLAGS_PKG LIBS_PKG CFLAGS_PKG=$(pkg-config --cflags gstreamer-1.0 gstreamer-base-1.0 \ gstreamer-allocators-1.0 libsecmem) || return 1 LIBS_PKG=$(pkg-config --libs gstreamer-1.0 gstreamer-base-1.0 \ gstreamer-allocators-1.0 libsecmem) || return 1 ( cd "$work/src/secmem" \ && ${CROSS_COMPILE}gcc -shared -fPIC -O2 --sysroot="$rootfs" \ $CFLAGS_PKG gstsecmemallocator.c $LIBS_PKG \ -Wl,-soname,libgstsecmemallocator.so.0 \ -o "$libdir/libgstsecmemallocator.so.0.0.0" ) || return 1 ln -sf libgstsecmemallocator.so.0.0.0 "$libdir/libgstsecmemallocator.so.0" ln -sf libgstsecmemallocator.so.0.0.0 "$libdir/libgstsecmemallocator.so" install -m 0644 -D "$work/src/secmem/gstsecmemallocator.h" \ "$rootfs/usr/include/gstreamer-1.0/gst/allocators/gstsecmemallocator.h" cat > "$libdir/pkgconfig/gstsecmemallocator.pc" < "${deb_dir}/DEBIAN/control" < Section: libs Priority: optional Depends: libc6, libgstreamer1.0-0, libgstreamer-plugins-base1.0-0, libdrm2, libgudev-1.0-0, libv4l-0 Description: Amlogic vendor GStreamer plugins (amlv4l2h264dec et al) Vendor GStreamer plugins for hardware video decode on Amlogic SoCs. EOF cat > "${deb_dir}/DEBIAN/postinst" <<'EOF' #!/bin/sh set -e if [ "$1" = "configure" ]; then command -v ldconfig >/dev/null 2>&1 && ldconfig rm -f /root/.cache/gstreamer-1.0/registry.* 2>/dev/null || true fi EOF chmod 0755 "${deb_dir}/DEBIAN/postinst" ( cd "${build_root}/deb" && dpkg-deb --build "${pkg_name}-${DEB_VERSION}" ) local out="${BUILD_DEBS:-${SRC}/output}" mkdir -p "${out}" mv "${build_root}/deb/${pkg_name}-${DEB_VERSION}.deb" \ "${out}/linux-${pkg_name}-${BOARD}-amlbian_${DEB_VERSION}_${ARCH}.deb" } function __gst_aml_vendor_cleanup_rootfs { local rootfs="$1" local _gnu_triplet="${CROSS_COMPILE%%-}" mount_chroot "$rootfs" # `remove` not `autoremove` -- autoremove greedily purges packages it # considers orphans, including PACKAGE_LIST_AML runtime libs. # Do NOT apt-get remove dev packages here: subsequent build steps # (venc, jpeg, dma-allocator, gfx2d, conv) still need libdrm-dev, # libgstreamer-plugins-base1.0-dev, pkg-config headers/pc-files. # Only clean the apt cache to reclaim space. chroot "$rootfs" /bin/bash -c " export DEBIAN_FRONTEND=noninteractive apt-get clean rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb " || true umount_chroot "$rootfs" # Compile-only artifacts (headers + .pc). The .so files stay -- dpkg # owns them now via the .deb just installed. local rdir="$rootfs/usr/lib/${_gnu_triplet}" rm -f "$rdir/pkgconfig/libsecmem.pc" \ "$rdir/pkgconfig/gstsecmemallocator.pc" \ "$rootfs/usr/include/secmem.h" \ "$rootfs/usr/include/secmem-ca.h" \ "$rootfs/usr/include/meson_drm.h" \ "$rootfs/usr/include/gstreamer-1.0/gst/allocators/gstsecmemallocator.h" 2>/dev/null rm -rf "$rootfs/usr/include/drm" 2>/dev/null } # ============================================================================== # Section 5: Main # ============================================================================== function amlbian_build_gst_aml_vendor { is_unit_enabled "gst-aml-vendor" || { display_alert "[SKIP] >> gst-aml-vendor disable in BUILD_UNITS" "" "info" return 0 } local rootfs="${ROOTFS_TEMP:-${SDCARD:-}}" [[ -d "${rootfs}" ]] || { display_alert "GST-AML-VENDOR" "no rootfs" "err"; return 1; } display_alert "AMLOGIC: Building gst-aml-vendor ${DEB_VERSION} ${BOARD} ${ARCH}" "info" local comp_dir="$(amlbian_get_aml_comp_dir)" local build_root="${SRC:-/tmp}/.tmp/gst-aml-vendor-build-${ARCH}" local work="${build_root}/work" local out="${BUILD_DEBS:-${SRC}/output}" local build_log="${out}/gst-aml-vendor-build.log" rm -rf "${build_root}" && mkdir -p "${work}" "${out}" local rc ( set -x __gst_aml_vendor_prepare_source "$rootfs" "$comp_dir" "$work" || exit 1 __gst_aml_vendor_build "$work" "$rootfs" || exit 1 __gst_aml_vendor_create_deb "$rootfs" "$build_root" || exit 1 __gst_aml_vendor_cleanup_rootfs "$rootfs" || true ) > "$build_log" 2>&1 rc=$? rm -rf "$work" if [[ $rc -ne 0 ]]; then display_alert "GST-AML-VENDOR" "build FAILED -- log: $build_log" "err" return 1 fi display_alert "AMLOGIC: gst-aml-vendor build completed" "$out" "info" return 0 } # Build runs in docker phase (needs rootfs as sysroot), so this script does # NOT auto-invoke; install_gst_aml_vendor_deb in lib/function-img.sh drives it. # ============================================================================== # End of build-gst-aml-vendor.sh # ==============================================================================