iOS 使用 FFmpeg+libx264 构建 PJSIP

iOS Build PJSIP with FFmpeg+libx264

我已经将带有 libx264 的 FFmpeg 构建到静态库中,这是我的目录树。

./ffmpeg
├── include
│   ├── libavcodec
│   │   ├── ac3_parser.h
│   │   ├── adts_parser.h
│   │   ├── avcodec.h
│   │   ├── avdct.h
│   │   ├── avfft.h
│   │   ├── bsf.h
│   │   ├── codec.h
│   │   ├── codec_desc.h
│   │   ├── codec_id.h
│   │   ├── codec_par.h
│   │   ├── d3d11va.h
│   │   ├── dirac.h
│   │   ├── dv_profile.h
│   │   ├── dxva2.h
│   │   ├── jni.h
│   │   ├── mediacodec.h
│   │   ├── packet.h
│   │   ├── qsv.h
│   │   ├── vaapi.h
│   │   ├── vdpau.h
│   │   ├── version.h
│   │   ├── videotoolbox.h
│   │   ├── vorbis_parser.h
│   │   └── xvmc.h
│   ├── libavdevice
│   │   ├── avdevice.h
│   │   └── version.h
│   ├── libavfilter
│   │   ├── avfilter.h
│   │   ├── buffersink.h
│   │   ├── buffersrc.h
│   │   └── version.h
│   ├── libavformat
│   │   ├── avformat.h
│   │   ├── avio.h
│   │   └── version.h
│   ├── libavutil
│   │   ├── adler32.h
│   │   ├── aes.h
│   │   ├── aes_ctr.h
│   │   ├── attributes.h
│   │   ├── audio_fifo.h
│   │   ├── avassert.h
│   │   ├── avconfig.h
│   │   ├── avstring.h
│   │   ├── avutil.h
│   │   ├── base64.h
│   │   ├── blowfish.h
│   │   ├── bprint.h
│   │   ├── bswap.h
│   │   ├── buffer.h
│   │   ├── camellia.h
│   │   ├── cast5.h
│   │   ├── channel_layout.h
│   │   ├── common.h
│   │   ├── cpu.h
│   │   ├── crc.h
│   │   ├── des.h
│   │   ├── dict.h
│   │   ├── display.h
│   │   ├── dovi_meta.h
│   │   ├── downmix_info.h
│   │   ├── encryption_info.h
│   │   ├── error.h
│   │   ├── eval.h
│   │   ├── ffversion.h
│   │   ├── fifo.h
│   │   ├── file.h
│   │   ├── frame.h
│   │   ├── hash.h
│   │   ├── hdr_dynamic_metadata.h
│   │   ├── hmac.h
│   │   ├── hwcontext.h
│   │   ├── hwcontext_cuda.h
│   │   ├── hwcontext_d3d11va.h
│   │   ├── hwcontext_drm.h
│   │   ├── hwcontext_dxva2.h
│   │   ├── hwcontext_mediacodec.h
│   │   ├── hwcontext_opencl.h
│   │   ├── hwcontext_qsv.h
│   │   ├── hwcontext_vaapi.h
│   │   ├── hwcontext_vdpau.h
│   │   ├── hwcontext_videotoolbox.h
│   │   ├── hwcontext_vulkan.h
│   │   ├── imgutils.h
│   │   ├── intfloat.h
│   │   ├── intreadwrite.h
│   │   ├── lfg.h
│   │   ├── log.h
│   │   ├── lzo.h
│   │   ├── macros.h
│   │   ├── mastering_display_metadata.h
│   │   ├── mathematics.h
│   │   ├── md5.h
│   │   ├── mem.h
│   │   ├── motion_vector.h
│   │   ├── murmur3.h
│   │   ├── opt.h
│   │   ├── parseutils.h
│   │   ├── pixdesc.h
│   │   ├── pixelutils.h
│   │   ├── pixfmt.h
│   │   ├── random_seed.h
│   │   ├── rational.h
│   │   ├── rc4.h
│   │   ├── replaygain.h
│   │   ├── ripemd.h
│   │   ├── samplefmt.h
│   │   ├── sha.h
│   │   ├── sha512.h
│   │   ├── spherical.h
│   │   ├── stereo3d.h
│   │   ├── tea.h
│   │   ├── threadmessage.h
│   │   ├── time.h
│   │   ├── timecode.h
│   │   ├── timestamp.h
│   │   ├── tree.h
│   │   ├── twofish.h
│   │   ├── tx.h
│   │   ├── version.h
│   │   ├── video_enc_params.h
│   │   └── xtea.h
│   ├── libpostproc
│   │   ├── postprocess.h
│   │   └── version.h
│   ├── libswresample
│   │   ├── swresample.h
│   │   └── version.h
│   ├── libswscale
│   │   ├── swscale.h
│   │   └── version.h
│   └── libx264
│       ├── x264.h
│       └── x264_config.h
└── lib
    ├── libavcodec.a
    ├── libavdevice.a
    ├── libavfilter.a
    ├── libavformat.a
    ├── libavutil.a
    ├── libpostproc.a
    ├── libswresample.a
    ├── libswscale.a
    └── libx264.a

最终的库创建成功,日志文件中没有错误信息, 但是当我将 lib 导入我的 Xcode 项目时, linker 给了我很多错误(缺少符号,如:Undefined symbol: _pjsua_vid_codec_set_priority)。 我发现 pjsua_vid.o 与我的 pjsip+openh264 构建文件(47KB)相比非常小(200 字节),也许这就是导致 link 错误的原因。

这是我的构建日志: log_file

我正在使用的构建脚本:

#!/bin/sh

# see 
function realpath { echo $(cd $(dirname ""); pwd)/$(basename ""); }
__FILE__=`realpath "[=12=]"`
__DIR__=`dirname "${__FILE__}"`

# download
function download() {
    "${__DIR__}/download.sh" "" "" #--no-cache
}

BASE_DIR=""
PJSIP_URL="https://github.com/pjsip/pjproject/archive/2.10.zip"
#http://www.pjsip.org/release/2.8.0/pjproject-2.8.0.tar.bz2
PJSIP_DIR="/src"
LIB_PATHS=("pjlib/lib" \
           "pjlib-util/lib" \
           "pjmedia/lib" \
           "pjnath/lib" \
           "pjsip/lib" \
           "third_party/lib")

OPENSSL_PREFIX=
FFMPEG_PREFIX=
OPENH264_PREFIX=
OPUS_PREFIX=
while [ "$#" -gt 0 ]; do
    case  in
        --with-openssl)
            if [ "$#" -gt 1 ]; then
                OPENSSL_PREFIX=$(python -c "import os,sys; print os.path.realpath(sys.argv[1])" "")
                shift 2
                continue
            else
                echo 'ERROR: Must specify a non-empty "--with-openssl PREFIX" argument.' >&2
                exit 1
            fi
            ;;
        --with-openh264)
            if [ "$#" -gt 1 ]; then
                OPENH264_PREFIX=$(python -c "import os,sys; print os.path.realpath(sys.argv[1])" "")
                shift 2
                continue
            else
                echo 'ERROR: Must specify a non-empty "--with-openh264 PREFIX" argument.' >&2
                exit 1
            fi
            ;;
        --with-ffmpeg)
            if [ "$#" -gt 1 ]; then
                FFMPEG_PREFIX=$(python -c "import os,sys; print os.path.realpath(sys.argv[1])" "")
                shift 2
                continue
            else
                echo 'ERROR: Must specify a non-empty "--with-ffmpeg PREFIX" argument.' >&2
                exit 1
            fi
            ;;
        --with-opus)
            if [ "$#" -gt 1 ]; then
                OPUS_PREFIX=$(python -c "import os,sys; print os.path.realpath(sys.argv[1])" "")
                shift 2
                continue
            else
                echo 'ERROR: Must specify a non-empty "--with-opus PREFIX" argument.' >&2
                exit 1
            fi
            ;;
    esac

    shift
done

function config_site() {
    SOURCE_DIR=
    PJSIP_CONFIG_PATH="${SOURCE_DIR}/pjlib/include/pj/config_site.h"
    HAS_VIDEO=

    echo "Creating config_site.h ..."

    if [ -f "${PJSIP_CONFIG_PATH}" ]; then
        rm "${PJSIP_CONFIG_PATH}"
    fi

    echo "#define PJ_CONFIG_IPHONE 1" >> "${PJSIP_CONFIG_PATH}"
    echo "#define PJ_HAS_IPV6 1" >> "${PJSIP_CONFIG_PATH}" # Enable IPV6
    if [[ ${OPENH264_PREFIX} ]]; then
        # echo "#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        # echo "#define PJMEDIA_HAS_OPENH264_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#define PJMEDIA_HAS_FFMPEG_VID_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        HAS_VIDEO=1
    fi
    if [[ ${HAS_VIDEO} ]]; then
        echo "#define PJMEDIA_HAS_VIDEO 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#define PJMEDIA_VIDEO_DEV_HAS_OPENGL 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#define PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#define PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#include <OpenGLES/ES3/glext.h>" >> "${PJSIP_CONFIG_PATH}"
    fi
    echo "#include <pj/config_site_sample.h>" >> "${PJSIP_CONFIG_PATH}"
}

function clean_libs () {
    ARCH=
    for SRC_DIR in ${LIB_PATHS[*]}; do
        DIR="${PJSIP_DIR}/${SRC_DIR}"
        if [ -d "${DIR}" ]; then
            rm -rf "${DIR}"/*
        fi

        DIR="${PJSIP_DIR}/${SRC_DIR}-${ARCH}"
        if [ -d "${DIR}" ]; then
            rm -rf "${DIR}"
        fi
    done
}

function copy_libs () {
    ARCH=

    for SRC_DIR in ${LIB_PATHS[*]}; do
        SRC_DIR="${PJSIP_DIR}/${SRC_DIR}"
        DST_DIR="${SRC_DIR}-${ARCH}"
        if [ -d "${DST_DIR}" ]; then
            rm -rf "${DST_DIR}"
        fi
        cp -R "${SRC_DIR}" "${DST_DIR}"
        rm -rf "${SRC_DIR}"/* # delete files because this directory will be used for the final lipo output
    done
}

function _build() {
    pushd . > /dev/null
    cd ${PJSIP_DIR}

    ARCH=
    LOG=${BASE_DIR}/${ARCH}.log

    # configure
    CONFIGURE="./configure-iphone"
    if [[ ${OPENSSL_PREFIX} ]]; then
        CONFIGURE="${CONFIGURE} --with-ssl=${OPENSSL_PREFIX}"
    fi
    # if [[ ${OPENH264_PREFIX} ]]; then
    #     CONFIGURE="${CONFIGURE} --with-openh264=${OPENH264_PREFIX}"
    # fi
    if [[ ${FFMPEG_PREFIX} ]]; then
        CONFIGURE="${CONFIGURE} --with-ffmpeg=${FFMPEG_PREFIX}"
    fi
    if [[ ${OPUS_PREFIX} ]]; then
        CONFIGURE="${CONFIGURE} --with-opus=${OPUS_PREFIX}"
    fi

    # flags
    if [[ ! ${CFLAGS} ]]; then
        export CFLAGS=
    fi
    if [[ ! ${LDFLAGS} ]]; then
        export LDFLAGS=
    fi
    if [[ ${OPENSSL_PREFIX} ]]; then
        export CFLAGS="${CFLAGS} -I${OPENSSL_PREFIX}/include"
        export LDFLAGS="${LDFLAGS} -L${OPENSSL_PREFIX}/lib"
    fi
    # if [[ ${OPENH264_PREFIX} ]]; then
    #     export CFLAGS="${CFLAGS} -I${OPENH264_PREFIX}/include"
    #     export LDFLAGS="${LDFLAGS} -L${OPENH264_PREFIX}/lib"
    # fi
    if [[ ${FFMPEG_PREFIX} ]]; then
        export CFLAGS="${CFLAGS} -I${FFMPEG_PREFIX}/include"
        export LDFLAGS="${LDFLAGS} -L${FFMPEG_PREFIX}/lib"
    fi
    export LDFLAGS="${LDFLAGS} -lstdc++"

    echo "Building for ${ARCH}..."

    clean_libs ${ARCH}

    make distclean > ${LOG} 2>&1
    ARCH="-arch ${ARCH}" ${CONFIGURE} >> ${LOG} 2>&1
    make dep >> ${LOG} 2>&1
    make clean >> ${LOG}
    make lib >> ${LOG} 2>&1

    copy_libs ${ARCH}
}

# function armv7() {
#     export DEVPATH="`xcrun -sdk iphoneos --show-sdk-platform-path`/Developer"
#     export CFLAGS="-miphoneos-version-min=8.0"
#     export LDFLAGS=
#     _build "armv7"
# }
# function armv7s() {
#     export DEVPATH="`xcrun -sdk iphoneos --show-sdk-platform-path`/Developer"
#     export CFLAGS="-miphoneos-version-min=8.0"
#     export LDFLAGS=
#     _build "armv7s"
# }
function arm64() {
    export DEVPATH="`xcrun -sdk iphoneos --show-sdk-platform-path`/Developer"
    export CFLAGS="-miphoneos-version-min=8.0"
    export LDFLAGS=
    _build "arm64"
}
function i386() {
    export DEVPATH="`xcrun -sdk iphonesimulator --show-sdk-platform-path`/Developer"
    export CFLAGS="-O2 -m32 -mios-simulator-version-min=8.0"
    export LDFLAGS="-O2 -m32 -mios-simulator-version-min=8.0"
    _build "i386"
}
function x86_64() {
    export DEVPATH="`xcrun -sdk iphonesimulator --show-sdk-platform-path`/Developer"
    export CFLAGS="-O2 -m32 -mios-simulator-version-min=8.0"
    export LDFLAGS="-O2 -m32 -mios-simulator-version-min=8.0"
    _build "x86_64"
}

function lipo() {
    TMP=`mktemp -t lipo`
    echo "Lipo libs... (${TMP})"

    for LIB_DIR in ${LIB_PATHS[*]}; do # loop over libs
        DST_DIR="${PJSIP_DIR}/${LIB_DIR}"

        # use the first architecture to find all libraries
        PATTERN_DIR="${DST_DIR}-"
        for PATTERN_FILE in `ls -l1 "${PATTERN_DIR}"`; do
            OPTIONS=""

            # loop over all architectures and collect the current library
            for ARCH in "$@"; do
                FILE="${DST_DIR}-${ARCH}/${PATTERN_FILE/--/-${ARCH}-}"
                if [ -e "${FILE}" ]; then
                    OPTIONS="$OPTIONS -arch ${ARCH} ${FILE}"
                fi
            done

            if [ "$OPTIONS" != "" ]; then
                OUTPUT_PREFIX=$(dirname "${DST_DIR}")
                OUTPUT="${OUTPUT_PREFIX}/lib/${PATTERN_FILE/--/-}"

                OPTIONS="${OPTIONS} -create -output ${OUTPUT}"
                echo "$OPTIONS" >> "${TMP}"
            fi
        done
    done

    while read LINE; do
        xcrun -sdk iphoneos lipo ${LINE}
    done < "${TMP}"
}

# download "${PJSIP_URL}" "${PJSIP_DIR}"
config_site "${PJSIP_DIR}"
arm64 && i386 && x86_64
lipo arm64 i386 x86_64

感谢任何建议。

我在构建脚本中犯了一个错误:

if [[ ${OPENH264_PREFIX} ]]; then
            # echo "#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
            # echo "#define PJMEDIA_HAS_OPENH264_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
            echo "#define PJMEDIA_HAS_FFMPEG_VID_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
            HAS_VIDEO=1
    fi

应该是这样的:

if [[ ${FFMPEG_PREFIX} ]]; then
        # echo "#define PJMEDIA_HAS_VID_TOOLBOX_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        # echo "#define PJMEDIA_HAS_OPENH264_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        echo "#define PJMEDIA_HAS_FFMPEG_VID_CODEC 1" >> "${PJSIP_CONFIG_PATH}"
        HAS_VIDEO=1
    fi

ffmpeg_vid_codecs.c 中的 FF_INPUT_BUFFER_PADDING_SIZE 需要更改为 AV_INPUT_BUFFER_PADDING_SIZE