摘要:本文详细介绍了如何将功能强大的开源媒体播放器 mpv 适配到 鸿蒙PC 平台。文章将系统性地讲解如何利用 lycium_plusplus 构建框架,处理基于 Meson 构建系统的 C/C++ 项目在鸿蒙环境下的交叉编译流程,展示如何处理复杂的依赖链管理(FFmpeg、libplacebo、libass 等)、pkg-config 路径配置、头文件路径修复以及 HNP 包生成的完整实践。

本文是软件鸿蒙化迁移实践系列文章之一,专注于 C/C++ 原生库的鸿蒙适配,为开发者提供完整的迁移指南。

欢迎加入开源鸿蒙PC社区:https://harmonypc.csdn.net/

欢迎在PC社区平台申请新建项目:https://atomgit.com/OpenHarmonyPCDeveloper

AtomGit仓库地址:https://atomgit.com/OpenHarmonyPCDeveloper/ohos_mpv_pc

项目信息说明

项目 说明
名称 mpv (libmpv)
开源协议 LGPL-2.1-or-later
源码版本 0.41.0
目标平台 鸿蒙 PC
依赖项 Meson, Ninja, FFmpeg, libplacebo, libass
操作系统平台 WSL Ubuntu 24.04

一、背景介绍

1.0 功能与效果

mpv 在本实践中的预期能力如下:

功能:提供功能强大的媒体播放核心库 libmpv,支持广泛的音视频格式、字幕渲染(ASS/SSA)、硬件加速、GPU 渲染等核心功能。作为嵌入式播放器库,可被鸿蒙应用通过 NAPI 调用。

效果:在鸿蒙平台上提供高质量的音视频播放能力,便于在鸿蒙应用开发中实现视频播放器、媒体浏览器、字幕渲染等场景的多媒体功能。

1.1 什么是 鸿蒙PC HNP 生态

HNP(Harmony Native Package)是 鸿蒙PC 的原生包格式,lycium 是增强型构建框架,支持自动下载源码、交叉编译(arm64-v8a、armeabi-v7a)、一键生成 HNP 包以及开源声明聚合(README.OpenSource)。C/C++ 原生库的适配是鸿蒙系统生态建设中的重要一环。

1.2 为什么适配 C/C++ 原生库会有难度

常见挑战包括:

  • 构建系统差异:mpv 使用 Meson + Ninja 构建系统,需要配置交叉编译工具链(aarch64-linux-ohos-clang/clang++)和交叉编译配置文件。
  • 复杂依赖链:mpv 依赖 FFmpeg(音视频编解码)、libplacebo(GPU 渲染)、libass(字幕渲染),而 libass 又依赖 fontconfig、harfbuzz、fribidi、freetype2 等,形成多层依赖树。
  • pkg-config 路径管理:Meson 依赖 pkg-config 查找依赖库,需要正确配置所有依赖库的 pkgconfig 路径,包括传递依赖。
  • 头文件路径问题:部分依赖库(如 libplacebo、fribidi)的安装路径不规范,需要手动添加头文件搜索路径。
  • 链接路径配置:部分依赖库安装在非标准路径(如 usr/lib 而非 lib),需要在交叉编译配置中显式指定链接路径。
  • 鸿蒙平台特性限制:需要禁用 Android 特定的音频后端(opensles)、X11/Wayland 图形后端等鸿蒙不支持的特性。

1.3 mpv 简介

mpv 是一个由 mpv-player 社区开发的自由开源媒体播放器(以 libmpv 库形式提供)。其主要特点包括:

  • 强大的播放核心:基于 FFmpeg,支持广泛的音视频格式和编解码器。
  • GPU** 加速渲染**:通过 libplacebo 实现高质量的 GPU 加速视频渲染。
  • 字幕支持:内置 libass,完美支持 ASS/SSA 高级字幕格式。
  • 嵌入式****设计:libmpv.so 可被其他应用通过 API 调用,适合嵌入式场景。
  • 跨平台特性:原生支持 Windows、macOS、Linux,本次适配扩展到 鸿蒙PC 平台。
  • 开源地址
    • GitHub:https://github.com/mpv-player/mpv
    • AtomGit:https://atomgit.com/weixin_62765017/mpv

二、环境准备

2.0 系统要求

  • 开发环境:Ubuntu 24.04(推荐 WSL 2)
  • 核心工具:Meson(v0.60+)、Ninja、GCC/G++、Git
  • 构建框架:lycium_plusplus
  • 鸿蒙 SDK:鸿蒙PC SDK(提供交叉编译工具链 aarch64-linux-ohos-clang/clang++)
  • 目标架构:arm64-v8a(AArch64)

2.0.1 扩展阅读与参考教程

下方汇总展示了多位老师在鸿蒙 鸿蒙PC 适配方面的高质量教程。若在前提准备(环境、工具链、框架)部分还有不清楚的地方,可参考这些文章进一步学习。 以下资源不分先后顺序,均具有参考价值。

资源类型 描述 链接
三方库交叉编译环境(Ubuntu) 在 Ubuntu 中搭建鸿蒙PC 三方库交叉编译构建开发环境 👉 点击查看
三方库交叉编译环境(macOS) 在 macOS 中搭建鸿蒙PC 三方库交叉编译开发环境 👉 点击查看
基础环境搭建 Windows 10 上安装和使用 WSL 2、安装 Ubuntu 24 详细指南 👉 点击查看
Mac 移植指南 鸿蒙PC命令行适配指南(Mac 版) 👉 点击查看
Win 移植指南 鸿蒙PC 生态三方软件移植:开发环境搭建及三方库移植指南 👉 点击查看
全流程适配指南 OpenHarmony Linux 命令行工具适配实战:基于 Cursor × WSL 的 tree 2.2.1 交叉编译与 HNP 打包全流程指南 👉 点击查看
官方构建文档 新脚手架:社区维护的鸿蒙PC 生态命令行工具构建框架 lycium_plusplus(原 build 仓库为旧方式,请以本仓库为准) 👉 点击查看

2.1 lycium_plusplus 框架

lycium_plusplus 是本次适配工作的核心工具,主要用于统一管理各类第三方库的构建流程,通过规范编译、依赖与打包逻辑,实现三方库在目标平台上高效、稳定地编译与集成,是整个适配环节中保障构建一致性与可维护性的关键支撑。

# 克隆 lycium_plusplus 项目
git clone https://gitcode.com/OpenHarmonyPCDeveloper/lycium_plusplus.git
cd lycium_plusplus

2.2 Meson 与交叉编译环境

由于 mpv 是 C/C++ 项目,使用 Meson + Ninja 构建系统,需要 鸿蒙PC SDK 提供的交叉编译工具链。

# 安装 Meson 和 Ninja
sudo apt-get install -y meson ninja-build

# 验证安装
meson --version
ninja --version

# 配置 鸿蒙PC SDK 环境变量
export OHOS_SDK=/home/weishuo/ohos-sdk/linux

三、实战:以 mpv 为例的适配步骤

3.1 创建项目目录结构

在 lycium_plusplus/thirdparty/ 目录下为 mpv 创建专属目录。

cd lycium_plusplus/thirdparty
mkdir -p mpv
cd mpv

3.2 创建 HPKBUILD 文件

HPKBUILD 作为 lycium 框架的核心构建脚本,完整定义了三方库的元信息配置及下载、编译、打包等全流程构建逻辑。

#!/bin/bash

# -----------------------------------------------------------------------------
# mpv HPKBUILD - OpenHarmony 鸿蒙适配
# -----------------------------------------------------------------------------

pkgname=mpv
pkgver=0.41.0
pkgrel=0
pkgdesc="A free, open source, and cross-platform media player"
url="https://mpv.io"
archs=("arm64-v8a")
license=("LGPL-2.1-or-later")
depends=("FFmpeg" "libplacebo")
makedepends=("meson" "ninja")
autounpack=false
downloadpackage=false
buildtools="meson"
srcpath="${LYCIUM_ROOT}/../Projects/${pkgname}"
builddir="${pkgname}-${pkgver}"

# -----------------------------------------------------------------------------
# prepare():准备源码
# -----------------------------------------------------------------------------
prepare() {
    if [ -d "$srcpath" ]; then
        echo "Using local source from: $srcpath"
        mkdir -p "$builddir"
        cp -rf "$srcpath"/* "$builddir/"
        find "$builddir" -name "*.bak" -type f -delete 2>/dev/null || true
        find "$builddir" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true
        echo "Prepare completed in: $builddir"
    else
        echo "ERROR: Source not found at $srcpath"
        exit 1
    fi
}

# -----------------------------------------------------------------------------
# build():编译构建
# -----------------------------------------------------------------------------
build() {
    cd "$builddir"

    # 设置交叉编译环境变量
    export CC="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang"
    export CXX="${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++"
    export AR="${OHOS_SDK}/native/llvm/bin/llvm-ar"
    export RANLIB="${OHOS_SDK}/native/llvm/bin/llvm-ranlib"
    export STRIP="${OHOS_SDK}/native/llvm/bin/llvm-strip"

    export CFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
    export CXXFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
    export LDFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot"

    # 创建 Meson 交叉编译配置文件
    cat > ohos-cross.ini << EOF
[binaries]
c = '${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang'
cpp = '${OHOS_SDK}/native/llvm/bin/aarch64-linux-ohos-clang++'
ar = '${OHOS_SDK}/native/llvm/bin/llvm-ar'
strip = '${OHOS_SDK}/native/llvm/bin/llvm-strip'
pkgconfig = 'pkg-config'

[properties]
pkg_config_path = ['${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig', '${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig', '${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig']

[built-in options]
c_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']
cpp_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

[host_machine]
system = 'linux'
cpu_family = 'aarch64'
cpu = 'aarch64'
endian = 'little'
EOF

    # 设置 FFmpeg、libplacebo 和 libass 及其所有依赖的 pkg-config 路径
    export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig:${PKG_CONFIG_PATH}"
    export PKG_CONFIG_LIBDIR="${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libexpat/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig:${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig:${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig"
    
    echo "PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
    pkg-config --list-all | grep -E 'libav|libplacebo|libass' || true

    # 设置 LDFLAGS 包含 libplacebo 的库路径
    export LDFLAGS="-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib ${LDFLAGS}"

    # Meson 配置:禁用图形界面和不支持的特性
    meson setup ohos-build \
        --cross-file ohos-cross.ini \
        --prefix=/usr \
        --buildtype=release \
        -Dgpl=false \
        -Dcplayer=false \
        -Dlibmpv=true \
        -Dbuild-date=false \
        -Dtests=false \
        -Dfuzzers=false \
        -Dmanpage-build=disabled \
        -Dcdda=disabled \
        -Ddvbin=disabled \
        -Ddvdnav=disabled \
        -Djavascript=disabled \
        -Djpeg=disabled \
        -Dlcms2=disabled \
        -Dlibarchive=disabled \
        -Dlibavdevice=disabled \
        -Dlibbluray=disabled \
        -Dlua=disabled \
        -Drubberband=disabled \
        -Dsdl2-gamepad=disabled \
        -Dsdl2-audio=disabled \
        -Dsdl2-video=disabled \
        -Dsubrandr=disabled \
        -Duchardet=disabled \
        -Dvapoursynth=disabled \
        -Dzimg=disabled \
        -Dalsa=disabled \
        -Daudiounit=disabled \
        -Dcoreaudio=disabled \
        -Djack=disabled \
        -Dopenal=disabled \
        -Dopensles=disabled \
        -Doss-audio=disabled \
        -Dpipewire=disabled \
        -Dpulse=disabled \
        -Dsndio=disabled \
        -Dgl=disabled \
        -Degl-android=disabled \
        -Degl-x11=disabled \
        -Degl-wayland=disabled \
        -Ddrm=disabled \
        -Dgbm=disabled \
        -Dx11=disabled \
        -Dwayland=disabled \
        -Dcocoa=disabled \
        -Dmacos-media-player=disabled \
        -Dios-gl=disabled \
        -Dandroid-media-ndk=disabled \
        -Dcaca=disabled \
        -Dplain-gl=disabled \
        -Dzlib=enabled \
        > "$buildlog" 2>&1

    ret=$?
    if [ $ret -ne 0 ]; then
        echo "Meson configuration failed!"
        cat "$buildlog" >&2
        cd "$OLDPWD"
        return $ret
    fi

    # 编译
    ninja -C ohos-build >> "$buildlog" 2>&1
    ret=$?

    cd "$OLDPWD"
    return $ret
}

# -----------------------------------------------------------------------------
# check():验证构建产物
# -----------------------------------------------------------------------------
check() {
    echo "The test must be on an OpenHarmony device!"
}

# -----------------------------------------------------------------------------
# package():打包产物
# -----------------------------------------------------------------------------
package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 进入源码目录
    cd ${builddir}

    # 手动复制编译产物
    mkdir -p "${destdir}/usr/bin"
    mkdir -p "${destdir}/usr/lib"
    mkdir -p "${destdir}/usr/include/mpv"

    # 复制 mpv 可执行文件
    if [ -f ohos-build/mpv ]; then
        cp -f ohos-build/mpv "${destdir}/usr/bin/mpv"
        chmod +x "${destdir}/usr/bin/mpv"
        echo "  ✅ Installed mpv binary"
    fi

    # 复制 libmpv 库文件
    if ls ohos-build/libmpv.so* 1> /dev/null 2>&1; then
        find ohos-build -maxdepth 1 -name 'libmpv.so*' -type f -exec cp -f {} "${destdir}/usr/lib/" \;
        echo "  ✅ Installed libmpv library"
    fi

    # 复制头文件
    if [ -d include/mpv ]; then
        cp -rf include/mpv/* "${destdir}/usr/include/mpv/"
        echo "  ✅ Installed mpv headers"
    fi

    find "${destdir}" -name "*.bak" -type f -delete 2>/dev/null || true
    find "${destdir}" -name "*:Zone.Identifier" -type f -delete 2>/dev/null || true

    echo "Package completed!"
    return 0
}

# -----------------------------------------------------------------------------
# archive():打包成 tar.gz 和 HNP
# -----------------------------------------------------------------------------
archive() {
    export HNP_TOOL="${HNP_TOOL:-${OHOS_SDK}/toolchains/hnpcli}"

    mkdir -p ${LYCIUM_ROOT}/output/$ARCH

    # 打包 tar.gz
    pushd ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} > /dev/null 2>&1
    tar -zcf ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz .
    echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}_${pkgver}.tar.gz"
    popd > /dev/null 2>&1

    # 打包 HNP
    if [ -f "${HNP_TOOL}" ]; then
        cp ${LYCIUM_ROOT}/../thirdparty/${pkgname}/hnp.json ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}/
        ${HNP_TOOL} pack \
            -i ${LYCIUM_ROOT}/usr/${pkgname}/${ARCH} \
            -o ${LYCIUM_ROOT}/output/$ARCH/
        echo "Archive completed: ${LYCIUM_ROOT}/output/$ARCH/${pkgname}.hnp"
    else
        echo "Warning: hnpcli not found at ${HNP_TOOL}, skipping HNP generation"
    fi
}

# -----------------------------------------------------------------------------
# cleanbuild():清理构建产物
# -----------------------------------------------------------------------------
cleanbuild() {
    echo "Cleaning build artifacts for ${pkgname}..."

    # 清理构建目录
    rm -rf "${LYCIUM_ROOT}/../thirdparty/${pkgname}/${builddir}"

    # 清理 output
    rm -rf "${LYCIUM_ROOT}/output/${pkgname}"*

    # 清理 usr 产物
    rm -rf "${LYCIUM_ROOT}/usr/${pkgname}"

    echo "Clean completed"
}

3.3 创建 hnp.json(包元数据)

hnp.json 文件用于描述 HNP 包的元信息,类似于 package.json。

{
    "type": "hnp-config",
    "name": "mpv",
    "version": "0.41.0",
    "description": "A free, open source, and cross-platform media player",
    "license": "LGPL-2.1-or-later",
    "arch": "arm64-v8a",
    "install": {
        "bin": ["usr/bin/mpv"],
        "lib": ["usr/lib/libmpv.so*"],
        "include": ["usr/include/mpv"]
    }
}

3.4 创建 README.OpenSource(开源声明)

为了满足开源合规性,需要提供 README.OpenSource 文件来声明许可证和依赖库信息。

[
    {
        "Name": "mpv",
        "License": "LGPL-2.1-or-later",
        "License File": "https://github.com/mpv-player/mpv/blob/master/Copyright",
        "Version Number": "0.41.0",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/mpv-player/mpv/archive/refs/tags/v0.41.0.tar.gz",
        "Description": "mpv is a free (as in freedom) media player for the command line. It supports a wide variety of media file formats, audio and video codecs, and subtitle types."
    },
    {
        "Name": "FFmpeg",
        "License": "LGPL-2.1-or-later",
        "License File": "https://github.com/FFmpeg/FFmpeg/blob/master/LICENSE.md",
        "Version Number": "8.0",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://ffmpeg.org/releases/ffmpeg-8.0.tar.xz",
        "Description": "FFmpeg is a complete, cross-platform solution to record, convert and stream audio and video. It includes libavcodec, libavformat, libavutil, libswscale, libswresample and other libraries."
    },
    {
        "Name": "libplacebo",
        "License": "LGPL-2.1-or-later",
        "License File": "https://github.com/haasn/libplacebo/blob/master/LICENSE",
        "Version Number": "7.362",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/haasn/libplacebo/archive/refs/tags/v7.362.tar.gz",
        "Description": "Reusable library for GPU-accelerated video/image rendering primitives, with a focus on vulkan"
    },
    {
        "Name": "libass",
        "License": "ISC",
        "License File": "https://github.com/libass/libass/blob/master/COPYING",
        "Version Number": "0.17.1",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/libass/libass/releases/download/0.17.1/libass-0.17.1.tar.gz",
        "Description": "LibASS is an SSA/ASS subtitles rendering library"
    },
    {
        "Name": "fontconfig",
        "License": "MIT",
        "License File": "https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/main/COPYING",
        "Version Number": "2.14.2",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://www.freedesktop.org/software/fontconfig/release/fontconfig-2.14.2.tar.gz",
        "Description": "Font configuration and customization library"
    },
    {
        "Name": "freetype",
        "License": "FTL",
        "License File": "https://sourceforge.net/p/freetype/code/ci/master/docs/FTL.TXT",
        "Version Number": "2.13.0",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://sourceforge.net/projects/freetype/files/freetype2/2.13.0/freetype-2.13.0.tar.xz",
        "Description": "FreeType is a freely available software library to render fonts"
    },
    {
        "Name": "harfbuzz",
        "License": "MIT",
        "License File": "https://github.com/harfbuzz/harfbuzz/blob/main/COPYING",
        "Version Number": "7.1.0",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/harfbuzz/harfbuzz/releases/download/7.1.0/harfbuzz-7.1.0.tar.xz",
        "Description": "HarfBuzz is an OpenType text shaping engine"
    },
    {
        "Name": "fribidi",
        "License": "LGPL-2.1-or-later",
        "License File": "https://github.com/fribidi/fribidi/blob/master/COPYING",
        "Version Number": "1.0.13",
        "Owner": "your-email@example.com",
        "Upstream URL": "https://github.com/fribidi/fribidi/releases/download/v1.0.13/fribidi-1.0.13.tar.xz",
        "Description": "GNU FriBidi is an implementation of the Unicode Bidirectional Algorithm"
    }
]

3.5 创建 HPKCHECK 检查脚本

HPKCHECK 是 lycium 框架的构建检查脚本,用于在编译前验证环境是否满足构建要求。

#!/bin/bash
# HPKCHECK for mpv
# 检查 mpv 构建产物

echo "=== Checking mpv build artifacts ==="

# 检查 mpv 可执行文件
if [ -f "$1/usr/bin/mpv" ]; then
    echo "✓ mpv binary found"
    file "$1/usr/bin/mpv"
else
    echo "⚠ Warning: mpv binary not found"
fi

# 检查 libmpv 库文件
if [ -f "$1/usr/lib/libmpv.so" ]; then
    echo "✓ Shared library found: libmpv.so"
    file "$1/usr/lib/libmpv.so"
elif [ -f "$1/usr/lib/libmpv.a" ]; then
    echo "✓ Static library found: libmpv.a"
    file "$1/usr/lib/libmpv.a"
else
    echo "✗ ERROR: No libmpv library found!"
    exit 1
fi

# 检查头文件
if [ -d "$1/usr/include/mpv" ]; then
    echo "✓ Header directory found: mpv/"
    ls "$1/usr/include/mpv/"
else
    echo "✗ ERROR: Header directory not found!"
    exit 1
fi

echo "=== Check completed ==="

四、编译流程与完整示例

4.1 环境准备

配置用于前置环境检查与变量设置,通过指定 鸿蒙PC SDK 路径、验证 Meson/Ninja 环境,为 lycium_plusplus 构建三方库提供基础运行环境。

# 1. 确保 OpenHarmony SDK 已安装
export OHOS_SDK=/home/weishuo/ohos-sdk/linux

# 2. 确保 Meson 和 Ninja 已安装
meson --version
ninja --version

4.2 创建项目结构并执行编译

通过目录操作、脚本创建、清理缓存、执行构建指令,完成 lycium_plusplus 中 mpv 三方库的从零构建全流程。

cd lycium_plusplus/thirdparty
mkdir -p mpv
cd mpv
# 创建 HPKBUILD 文件(内容见第三章)
vim HPKBUILD

cd ../../lycium
# 清理历史构建记录(可选,但推荐)
grep -v 'mpv' usr/hpk_build.csv > usr/hpk_build.csv.tmp
mv usr/hpk_build.csv.tmp usr/hpk_build.csv
rm -rf ../thirdparty/mpv/mpv-0.41.0
rm -rf output/*/mpv*

# 开始构建
./build.sh mpv

4.3 构建成功输出示例

4.4 验证产物

构建成功后,编译产物会统一输出至 output 目录,包含标准 tar 压缩包与鸿蒙专用 hnp 格式包。

五、鸿蒙PC 上验证结果(测试)

先解压 mpv 编译产物,进入库目录后查看文件,验证该 arm64 架构 ELF 共享库的有效性,再为其添加执行权限,最后执行鸿蒙系统签名操作

# 解压mpv编译产物
tar -zxf mpv_0.41.0.tar.gz

# 进入库目录
cd usr/lib

# 查看文件是否存在
ls

# 验证是否为鸿蒙 arm64 有效库(核心验证)
file libmpv.so.2.5.0

# 查看ELF魔数,确认架构合法
od -N 4 libmpv.so.2.5.0

通过 strings 命令提取 libmpv.so.2.5.0 共享库中的字符串信息,并结合 grep 过滤出版本号相关内容,成功匹配到 mpv v0.41.0-UNKNOWN 标识,验证了该动态库的版本正确性与编译完整性

strings libmpv.so.2.5.0 | grep "0.41.0"

六、常见问题与解决方案(FAQ)

Q1:Meson 配置时报错 “ERROR: Unknown options: libplacebo_ldflags”?

A:这是因为 libplacebo_ldflags 不是有效的 Meson 选项。正确的做法是在交叉编译配置文件的 c_link_args 中添加库路径:

[built-in options]
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

不要在 meson setup 命令中使用 -Dlibplacebo_ldflags 参数。

Q2:编译时报错 “fatal error: libplacebo/colorspace.h: No such file or directory”?

A:这是因为 libplacebo 的头文件路径未正确配置。需要在 CFLAGS 和 CPPFLAGS 中添加:

export CFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"
export CXXFLAGS="--target=aarch64-linux-ohos --sysroot=${OHOS_SDK}/native/sysroot -O2 -fPIC -I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include"

同时在 Meson 交叉编译配置中添加:

[built-in options]
c_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']
cpp_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-O2', '-fPIC', '-I${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/include']

Q3:编译时报错 “fatal error: libplacebo/config.h: No such file or directory”?

A:这是因为 libplacebo 构建时生成的 config.h 没有被安装到产物目录。需要在 libplacebo 的 HPKBUILD 的 package() 函数中添加:

# 复制构建生成的 config.h
if [ -f ohos-build/src/include/libplacebo/config.h ]; then
    cp -f ohos-build/src/include/libplacebo/config.h "${destdir}/usr/include/libplacebo/"
    echo "  ✅ Installed config.h"
fi

然后重新构建 libplacebo:

# 清理 libplacebo 缓存
cat lycium/usr/hpk_build.csv | grep -v libplacebo > /tmp/hpk_temp.csv
mv /tmp/hpk_temp.csv lycium/usr/hpk_build.csv
rm -rf lycium/usr/libplacebo
rm -rf thirdparty/libplacebo/libplacebo-*/ohos-build

# 重新构建
./lycium/build.sh libplacebo

Q4:链接时报错 “ld.lld: error: unable to find library -lplacebo”?

A:这是因为链接器找不到 libplacebo 的库文件。libplacebo 安装在 usr/lib 而不是 lib。需要在 Meson 交叉编译配置中添加链接路径:

[built-in options]
c_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']
cpp_link_args = ['--target=aarch64-linux-ohos', '--sysroot=${OHOS_SDK}/native/sysroot', '-L${LYCIUM_ROOT}/usr/libplacebo/${ARCH}/usr/lib']

Q5:配置时报错 “ERROR: Dependency “libass” not found”?

A:这是因为 pkg-config 路径不完整。libass 依赖 fontconfig、harfbuzz、fribidi、freetype2 等库,需要在 pkg_config_path 中包含所有这些库的路径:

[properties]
pkg_config_path = [
    '${LYCIUM_ROOT}/usr/FFmpeg/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/libass/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/fontconfig/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/harfbuzz/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/freetype2/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/zlib/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/libpng/${ARCH}/lib/pkgconfig',
    '${LYCIUM_ROOT}/usr/brotli/${ARCH}/lib/pkgconfig'
]

注意:fribidi 的路径是 usr/lib/pkgconfig 而不是 lib/pkgconfig。

Q6:编译时报错 “fatal error: fribidi.h: No such file or directory”?

A:这是因为 fribidi 的头文件安装在 usr/include/fribidi/ 而不是 include/fribidi/。这是 fribidi 的 HPKBUILD 中 prefix 设置为 /usr 导致的路径问题。

解决方案是在依赖 libass 的项目中显式添加 fribidi 头文件路径:

export PKG_CONFIG_PATH="${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/lib/pkgconfig:${PKG_CONFIG_PATH}"

如果仍有问题,可以直接添加头文件路径到 CFLAGS:

export CFLAGS="${CFLAGS} -I${LYCIUM_ROOT}/usr/fribidi/${ARCH}/usr/include"

Q7:配置时报错 “dependency SLES/OpenSLES_Android.h not found”?

A:这是因为 mpv 默认启用了 Android 的 OpenSL ES 音频后端。鸿蒙不支持这个后端,需要显式禁用:

meson setup ohos-build \
    --cross-file ohos-cross.ini \
    ...
    -Dopensles=disabled \
    ...

Q8:package 阶段产物只有 hnp.json,没有实际的库文件?

A:这是因为 package() 函数中的路径问题。需要在 package 函数开头添加 cd ${builddir} 进入正确的构建目录:

package() {
    : ${destdir:=${LYCIUM_ROOT}/usr/${pkgname}/${ARCH}}

    # 进入源码目录(关键!)
    cd ${builddir}

    # 复制产物...
}

Q9:复制库文件时报错 “cp: -r not specified; omitting directory”?

A:这是因为通配符 libmpv.so* 匹配到了目录 libmpv.so.2.5.0.p。需要使用 find 命令只复制文件:

# 错误的做法
cp -f ohos-build/libmpv.so* "${destdir}/usr/lib/"

# 正确的做法
find ohos-build -maxdepth 1 -name 'libmpv.so*' -type f -exec cp -f {} "${destdir}/usr/lib/" \;

Q10:修改 HPKBUILD 后 lycium 直接跳过构建?

A:这是 lycium 的构建缓存机制导致的。lycium 使用 hpk_build.csv 文件记录构建状态,如果存在记录则会跳过构建。解决方案是删除缓存文件:

# 删除构建状态缓存
cat lycium/usr/hpk_build.csv | grep -v mpv > /tmp/hpk_temp.csv
mv /tmp/hpk_temp.csv lycium/usr/hpk_build.csv

# 同时清理旧产物
rm -rf thirdparty/mpv/mpv-*/ohos-build
rm -rf lycium/usr/mpv
rm -f lycium/output/*/*mpv*

# 重新构建
./lycium/build.sh mpv

Q11:为什么没有mpv可执行文件?

A:这是因为配置中使用了 -Dcplayer=false 禁用了命令行播放器。这是有意为之的,因为:

  1. 鸿蒙是图形化系统,没有传统终端环境
  2. 命令行播放器无法直接使用
  3. 缺少 X11/Wayland 图形后端
  4. 鸿蒙应用通过 NAPI 调用 libmpv.so 实现播放功能

如果需要 mpv 可执行文件,需要将 -Dcplayer=false 改为 -Dcplayer=true,并适配图形后端。

七、技术总结

本次成功将 mpv 适配至 鸿蒙PC 平台,形成了一套可复用的 Meson 类项目的鸿蒙移植范式。核心要点如下:

  • 标准化适配模板:建立了 Meson 类项目的 HPKBUILD 配置规范,统一交叉编译与依赖管理流程
  • 复杂依赖链管理:通过正确配置 pkg-config 路径,解决多层依赖树(FFmpeg、libplacebo、libass 等)的查找问题
  • 关键问题解决:通过修复 libplacebo 头文件路径、添加链接路径配置、禁用不支持的音频/图形后端,有效解决编译和链接问题
  • 产物规范化:使用 README.OpenSource 规范化开源声明格式,确保开源合规性
  • 通用适配能力:方案可直接迁移至 VLC、GStreamer 等同类多媒体播放器的鸿蒙移植

八、结语

本次实践成功将 mpv 移植至 鸿蒙PC 平台,验证了 lycium_plusplus 框架对 Meson 构建系统项目交叉编译的支撑能力。通过规范 HPKBUILD 构建流程、正确配置复杂依赖链的 pkg-config 路径、修复头文件和链接路径问题、禁用不支持的平台特性等关键措施,有效解决了适配中的兼容性与构建问题,为同类开源项目迁移至鸿蒙生态提供了可复用的实践参考,助力鸿蒙原生多媒体应用生态的完善与发展。

提示:本文基于 mpv 0.41.0 版本进行适配。不同版本的依赖和构建脚本可能有所差异,建议在适配前先熟悉目标项目的 meson.build 和构建配置文件。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐