【FFmpeg+Surface 底层渲染,实现超低延迟100ms】

➡️最近在做一个智能挖机的操控平板,视频板块的实时渲染,需要低于160ms,虽然在局域网下,但是平常的播放器 【VLC、Medio3等等....】均不能达到效果,并且采集端的分辨率是1080x1920.在这样的分辨率下更加能达到超低延迟效果,硬件供应商并没有提供能够改变分辨率的接口。

➡️最终采用NDK交叉编译,生成so文件, FFmpeg+Surface实现底层渲染。

准备材料:

FFmpeg(下载地址): https://ffmpeg.org/download.html
MSYS(下载地址): https://www.msys2.org/
NDK(下载地址): https://developer.android.com/ndk/downloads

⬆️将上面的材料准备好,ndk、ffmpeg放在同一个文件夹。

⬆️完成以上三步,就具备了执行FFmpeg for Android编译的所有基础材料。之后,在MSYS2环⬆️境中运行您的编译脚本即可

生成一个build_ffmpeg.sh脚本:

运行:bush

cpp 复制代码
./build_ffmpeg.sh

编写:build_ffmpeg.sh

cpp 复制代码
#!/bin/bash
# FFmpeg智能编译脚本 - 避免重复下载
# 适用于Windows MSYS2环境

# 设置变量
NDK_PATH="/C/Android/android-ndk-r21e"
API_LEVEL=21
FFMPEG_VERSION="n6.0"
FFMPEG_SOURCE_DIR="$HOME/ffmpeg-source"
BUILD_DIR="$HOME/ffmpeg-build"
OUTPUT_DIR="/c/ffmpeg-android"
STATE_FILE="$HOME/ffmpeg_build_state.txt"
CACHE_DIR="$HOME/.ffmpeg-cache"

# 创建必要的目录
mkdir -p "$FFMPEG_SOURCE_DIR"
mkdir -p "$BUILD_DIR"
mkdir -p "$OUTPUT_DIR"
mkdir -p "$CACHE_DIR"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -s "${RED}[ERROR]${NC} $1" >&2; }

# 检查状态文件
check_ffmpeg_source() {
    if [ -f "${CACHE_DIR}/ffmpeg_${FFMPEG_VERSION}_downloaded" ]; then
        if [ -d "${FFMPEG_SOURCE_DIR}/.git" ]; then
            log_success "FFmpeg源码已存在 (版本: $FFMPEG_VERSION)"
            cd "${FFMPEG_SOURCE_DIR}"
            CURRENT_VERSION=$(git describe --tags 2>/dev/null || echo "unknown")
            if [ "$CURRENT_VERSION" != "$FFMPEG_VERSION" ] && [ "$CURRENT_VERSION" != "unknown" ]; then
                log_warn "检测到不同版本: $CURRENT_VERSION,需要切换..."
                return 1
            fi
            return 0
        fi
    fi
    return 1
}

# 标记下载完成
mark_downloaded() {
    echo "FFMPEG_VERSION=${FFMPEG_VERSION}" > "${CACHE_DIR}/ffmpeg_${FFMPEG_VERSION}_downloaded"
    echo "DOWNLOAD_DATE=$(date +%Y-%m-%d_%H:%M:%S)" >> "${CACHE_DIR}/ffmpeg_${FFMPEG_VERSION}_downloaded"
    echo "SOURCE_DIR=${FFMPEG_SOURCE_DIR}" >> "${CACHE_DIR}/ffmpeg_${FFMPEG_VERSION}_downloaded"
    log_success "已标记FFmpeg源码下载完成"
}

# 下载FFmpeg源码
download_ffmpeg() {
    log_info "开始下载FFmpeg源码 (版本: $FFMPEG_VERSION)..."
    
    if [ -d "${FFMPEG_SOURCE_DIR}" ] && [ "$(ls -A ${FFMPEG_SOURCE_DIR})" ]; then
        log_info "清理旧源码目录..."
        rm -rf "${FFMPEG_SOURCE_DIR}"
    fi
    
    # 使用git克隆
    git clone --depth 1 --branch "${FFMPEG_VERSION}" https://git.ffmpeg.org/ffmpeg.git "${FFMPEG_SOURCE_DIR}"
    
    if [ $? -ne 0 ]; then
        log_error "git克隆失败,尝试git clone master分支..."
        git clone --depth 1 https://git.ffmpeg.org/ffmpeg.git "${FFMPEG_SOURCE_DIR}"
        if [ $? -ne 0 ]; then
            log_error "FFmpeg源码下载失败!"
            exit 1
        fi
    fi
    
    cd "${FFMPEG_SOURCE_DIR}"
    log_success "FFmpeg源码下载完成"
    mark_downloaded
}

# 检查编译状态
check_build_status() {
    local arch="$1"
    if [ -f "${CACHE_DIR}/build_${arch}_success" ]; then
        if [ -d "${OUTPUT_DIR}/${arch}" ] && [ "$(ls -A ${OUTPUT_DIR}/${arch}/*.so 2>/dev/null)" ]; then
            log_info "架构 $arch 已编译,跳过..."
            return 0
        else
            log_warn "标记已编译但未找到so文件,重新编译..."
            rm -f "${CACHE_DIR}/build_${arch}_success"
        fi
    fi
    return 1
}

# 标记编译完成
mark_build_complete() {
    local arch="$1"
    echo "ARCH=${arch}" > "${CACHE_DIR}/build_${arch}_success"
    echo "BUILD_DATE=$(date +%Y-%m-%d_%H:%M:%S)" >> "${CACHE_DIR}/build_${arch}_success"
    echo "FFMPEG_VERSION=${FFMPEG_VERSION}" >> "${CACHE_DIR}/build_${arch}_success"
}

# 编译函数
build_for_arch() {
    local arch="$1"
    local cpu="$2"
    
    log_info "开始编译架构: $arch"
    
    # 检查是否已编译
    if check_build_status "$arch"; then
        return 0
    fi
    
    # 设置编译参数
    case "$arch" in
        "armeabi-v7a")
            TARGET_CPU="armv7-a"
            TOOLCHAIN_PREFIX="armv7a-linux-androideabi"
            EXTRA_CFLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=softfp -Wl,--fix-cortex-a8"
            SYSROOT="${NDK_PATH}/sysroot/usr/lib/arm-linux-androideabi"
            ;;
        "arm64-v8a")
            TARGET_CPU="armv8-a"
            TOOL	_PREFIX="aarch64-linux-android"
            EXTRA_CFLAGS=""
            SYSROOT="${NDK_PATH}/sysroot/usr/lib/aarch64-linux-android"
            ;;
        "x86")
            TARGET_CPU="i686"
            TOOLCHAIN_PREFIX="i686-linux-android"
            SYSROOT="${NDK_PATH}/sysroot/usr/lib/i686-linux-android"
            ;;
        "x86_64")
            TARGET_CPU="x86-64"
            TOOLCHAIN_PREFIX="x86_64-linux-android"
            EXTRA_CFLAGS="-march=x86-64 -msse4.2 -mpopcnt"
            SYSROOT="${NDK_PATH}/sysroot/usr/lib/x86_64-linux-android"
            ;;
        *)
            log_error "不支持的架构: $arch"
            return 1
            ;;
    esac
    
    local TOOLCHAIN="${NDK_PATH}/toolchains/llvm/prebuilt/windows-x86_64"
    local BUILD_DIR_ARCH="${BUILD_DIR}/${arch}"
    local OUTPUT_DIR_ARCH="${OUTPUT_DIR}/${arch}"
    
    mkdir -p "${BUILD_DIR_ARCH}"
    mkdir -p "${OUTPUT_DIR_ARCH}"
    
    cd "${FFMPEG_SOURCE_DIR}"
    
    # 清理之前配置
    if [ -f "config.h" ]; then
        make distclean
    fi
    
    # 配置FFmpeg
    log_info "配置FFmpeg for $arch..."
    ./configure \
        --prefix="${BUILD_DIR_ARCH}" \
        --enable-shared \
        --disable-static \
        --disable-doc \
        --disable-programs \
        --enable-avdevice \
        --enable-postproc \
        --enable-swresample \
        --enable-swscale \
        --enable-avfilter \
        --target-os=android \
        --arch="$arch" \
        --cpu="$TARGET_CPU" \
        --enable-cross-compile \
        --sysroot="${TOOLCHAIN}/sysroot" \
        --cc="${TOOLCHAIN}/bin/${TOOLCHAIN_PREFIX}${API_LEVEL}-clang" \
        --cxx="${TOOLCHAIN}/bin/${TOOLCHAIN_PREFIX}${API_LEVEL}-clang++" \
        --ar="${TOOLCHAIN}/bin/llvm-ar" \
        --strip="${TOOLCHAIN}/bin/llvm-strip" \
        --extra-cflags="-Os -fPIC $EXTRA_CFLAGS" \
        --extra-ldflags="" \
        --enable-small \
        --enable-gpl \
        --enable-neon \
        --enable-hwaccels \
        --enable-jni \
        --enable-mediacodec \
        --enable-decoder=h264 \
        --enable-decoder=hevc \
        --enable-decoder=aac \
        --enable-decoder=mp3 \
        --enable-encoder=aac \
        --enable-encoder=libx264 \
        --enable-parser=h264 \
        --enable-parser=hevc \
        --enable-parser=aac \
        --enable-muxer=mp4 \
        --enable-muxer=flv \
        --enable-demuxer=mpegts \
        --enable-demuxer=flv \
        --enable-protocol=file \
        --enable-protocol=http \
        --enable-protocol=https \
        --enable-zlib \
        --disable-iconv \
        --disable-asm
    
    if [ $? -ne 0 ]; then
        log_error "配置失败: $arch"
        return 1
    fi
    
    # 编译
    log_info "编译FFmpeg for $arch..."
    make -j$(nproc)
    
    if [ $? -ne 0 ]; then
        log_error "编译失败: $arch"
        return 1
    fi
    
    # 安装
    log_info "安装FFmpeg for $arch..."
    make install
    
    if [ $? -ne 0 ]; then
        log_error "安装失败: $arch"
        return 1
    fi
    
    # 复制.so文件
    log_info "复制.so文件..."
    find "${BUILD_DIR_ARCH}/lib" -name "*.so" -exec cp {} "${OUTPUT_DIR_ARCH}/" \;
    
    # 验证文件存在
    if [ $(ls -1 "${OUTPUT_DIR_ARCH}".so 2>/dev/null | wc -l) -eq 0 ]; then
        log_error "未生成.so文件: $arch"
        return 1
    fi
    
    # 标记编译完成
    mark_build_complete "$arch"
    log_success "架构 $arch 编译完成!"
    
    return 0
}

# 主函数
main() {
    log_info "===== FFmpeg智能编译脚本 ====="
    log_info "FFmpeg版本: $FFMPEG_VERSION"
    log_info "NDK路径: $NDK_PATH"
    log_info "输出目录: $OUTPUT_DIR"
    log_info "============================="
    
    # 检查NDK
    if [ ! -d "$NDK_PATH" ]; then
        log_error "NDK未找到: $NDK_PATH"
        log_info "请从 https://developer.android.com/ndk/downloads 下载NDK r25+"
        log_info "并解压到: C:\\Android\\android-ndk-r21b"
        exit 1
    fi
    
    # 检查并下载源码
    if ! check_ffmpeg_source; then
        download_ffmpeg
    fi
    
    # 编译所有架构
    local ARCHS=("armeabi-v7a" "arm64-v8a" "x86" "x86_64")
    local FAILED_ARCHS=()
    
    for arch in "${ARCHS[@]}"; do
        case "$arch" in
            "armeabi-v7a") cpu="armv7-a" ;;
            "arm64-v8a") cpu="armv8-a" ;;
            "x86") cpu="i686" ;;
            "x86_64") cpu="x86-64" ;;
        esac
        
        if build_for_arch "$arch" "$cpu"; then
            log_success "成功编译: $arch"
        else
            log_error "编译失败: $arch"
            FAILED_ARCHS+=("$arch")
        fi
    done
    
    # 输出总结
    log_info "===== 编译完成 ====="
    log_info "输出目录: $OUTPUT_DIR"
    
    for arch in "${ARCHS[@]}"; do
        local so_count=$(ls -1 "${OUTPUT_DIR}/${arch}".so 2>/dev/null | wc -l)
        if [ "$so_count" -gt 0 ]; then
            log_success "$arch: 生成 $so_count 个.so文件"
        else
            log_warn "$arch: 未生成.so文件"
        fi
    done
    
    if [ ${#FAILED_ARCHS[@]} -gt 0 ]; then
        log_error "以下架构编译失败: ${FAILED_ARCHS[@]}"
    fi
}

# 解析参数
if [ "$1" = "--clean" ] || [ "$1" = "-c" ]; then
    log_warn "清理所有缓存和输出文件..."
    rm -rf "$FFMPEG_SOURCE_DIR"
    rm -rf "$BUILD_DIR"
    rm -rf "$OUTPUT_DIR"
    rm -rf "$CACHE_DIR"
    log_info "清理完成"
    exit 0
elif [ "$1" = "--force" ] || [ "$1" = "-f" ]; then
    log_warn "强制重新下载和编译..."
    rm -rf "$FFMPEG_SOURCE_DIR"
    rm -rf "$BUILD_DIR"
    rm -rf "$OUTPUT_DIR".so
    rm -rf "$CACHE_DIR"_downloaded
    rm -rf "$CACHE_DIR"/build_*_success
fi

# 运行主函数
main

大约等待十来分钟,期间你会看到

下一篇文章继续讲解,如何引入到Android studio中,展示实操流程。

相关推荐
Echo_NGC22372 小时前
【FFmpeg 使用指南】Part 3:码率控制策略与质量评估体系
人工智能·ffmpeg·视频·码率
xmRao9 小时前
Qt+FFmpeg 实现 PCM 音频转 AAC 编码
qt·ffmpeg·pcm
xmRao9 小时前
Qt+FFmpeg 实现录音程序(pcm转wav)
qt·ffmpeg
阿里巴啦2 天前
python+yt-dlp开源项目,支持 YouTube, Bilibili, TikTok/抖音,快手 等多个平台的视频/音频/字幕下载/ai摘要等功能
python·ffmpeg·whisper·音视频·视频处理·ai摘要·音视频转录
来鸟 鸣间2 天前
linux下ffmpeg源码编译
linux·运维·ffmpeg
Echo_NGC22372 天前
【FFmpeg使用指南】Part 2:滤镜图架构与信号处理
架构·ffmpeg·音视频·信号处理
Echo_NGC22372 天前
【FFmpeg使用指南】Part 1:核心架构与媒体流处理
ffmpeg·音视频·媒体·视频
ssxueyi2 天前
用 Claude Code 从零开发自己的Direct3D 硬件加速播放器
ffmpeg·ai编程·directx·视频播放器·从零开始·claude code·csdn征文活动
Yan_uuu3 天前
ubuntu18.04 安装 x264、ffmpeg、nv-codec-hearers 支持GPU硬件加速
c++·图像处理·ubuntu·ffmpeg
runner365.git3 天前
做一个基于ffmpeg的AI Agent智能体
人工智能·ffmpeg·大模型