FFMPEG Android移植进阶

1.文章介绍

在应用开发中,你是否面临着第三方库的FFMPEG不是最新版本,或者需要使用扩展自己的业务的FFMPEG版本等问题,一旦遇到这些问题,你会怎么处理?


本篇文章是把FFMPEGlibavcodec,libavformat,libavutil,libavfilter等移植到Android设备上,并且使用这些API完成音视频转码功能,作为FFMPEG进阶使用。

2.干货

1.准备编译工具
Android NDK编译环境:
关于NDK的下载我就不多说了,基本百度就能搞定。

android-ndk-r14b

为了方便配置NDK编译环境,可以添加如下脚本:

#! /bin/sh
set -e
############
# Configuring Android NDK #
############
Env_Dir=$(pwd)
echo "#########config android ndk############"
export ANDROID_NDK="$Env_Dir/android-ndk-r14b"
echo ""
echo "ANDROID_NDK:$ANDROID_NDK"
echo ""

2.git源码到linux编译环境下:

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

3.配置Android编译脚本:
  搞音视频开发,相信对avcaac的编解码库再熟悉不过了,那么这篇文章该涉及到X264libfdk-aac的交叉编译了。
  参考了某某开源项目的编译脚本配置为合适自己的编译脚本,基于最新的FFMPEG源码,编译出可在Android终端上使用的FFMPEG库

#!/bin/bash

#Detect ANDROID_NDK
export ANDROID_NDK=/home3/yangwu/build_tools/android-ndk-r14b

NDK_TOOLCHAIN_VERSION=4.9
ANDROID_PLATFROM_VERSION=android-19

if [ -z "$ANDROID_NDK" ]; then
 echo "You must define ANDROID_NDK before starting."
 echo "You must point to your NDK directories.\n"
 exit 1
fi

#Detect OS
OS=`uname`
HOST_ARCH=`uname -m`
export CCACHE=; type ccache >/dev/null 2>&1 && export CCACHE=ccache
if [ $OS == 'Linux' ]; then
 export HOST_SYSTEM=linux-$HOST_ARCH
elif [ $OS == 'Darwin' ]; then
 export HOST_SYSTEM=darwin-$HOST_ARCH
fi

platform="$1"
version_type="$2"

function arm_toolchain()
{
 export CROSS_PREFIX=arm-linux-androideabi-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=${CROSS_PREFIX}${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=arm --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}

function x86_toolchain()
{
 export CROSS_PREFIX=i686-linux-android-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=x86-${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=x86 --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}

function mips_toolchain()
{
 export CROSS_PREFIX=mipsel-linux-android-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=${CROSS_PREFIX}${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=mips --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}


SOURCE=`pwd`
DEST=$SOURCE/build/android
TOOLCHAIN=$SOURCE/ffmpeg_toolchain
SYSROOT=$TOOLCHAIN/sysroot/

function download {
  mkdir -p "$SOURCE/downloads"
  if [[ ! -e "$SOURCE/downloads/$2" ]]; then
    echo "Downloading $1"
    curl -L "$1" -o "$SOURCE/downloads/$2"
  fi
}

if [ "$platform" = "x86" ];then
 echo "Build Android x86 ffmpeg\n"
 x86_toolchain
 TARGET="x86"
 TARGET_HOST="x86-linux-android"
 PLATFORM="arch-x86"
elif [ "$platform" = "mips" ];then
 echo "Build Android mips ffmpeg\n"
 mips_toolchain
 TARGET="mips"
 TARGET_HOST="mipsel-linux-android"
 PLATFORM="arch-mips"
elif [ "$platform" = "armv7" ];then
 echo "Build Android armv7 ffmpeg\n"
 arm_toolchain
 TARGET="armv7"
 TARGET_HOST="arm-linux-android"
 PLATFORM="arch-arm"
else
 echo "Build Android arm ffmpeg\n"
 arm_toolchain
 TARGET="neon armv7 vfp armv6"
 TARGET_HOST="arm-linux-android"
 PLATFORM="arch-arm"
fi
export PATH=$TOOLCHAIN/bin:$PATH
export CC="$CCACHE ${CROSS_PREFIX}gcc"
export CXX=${CROSS_PREFIX}g++
export LD=${CROSS_PREFIX}ld
export AR=${CROSS_PREFIX}ar
export STRIP=${CROSS_PREFIX}strip

#set ffmpeg dep libs here
echo "Decompressing archives..."
OPENH264_VERSION=1.6.0
FDKACC_VERSION=0.1.6
OPENSSL_VERSION=openssl-1.0.2j

download https://github.com/cisco/openh264/archive/v$OPENH264_VERSION.tar.gz openh264-$OPENH264_VERSION.tar.gz
download ftp://ftp.videolan.org/pub/videolan/x264/snapshots/last_stable_x264.tar.bz2 last_stable_x264.tar.bz2
#download https://downloads.sourceforge.net/opencore-amr/fdk-aac-$FDKACC_VERSION.tar.gz
download https://www.openssl.org/source/$OPENSSL_VERSION.tar.gz $OPENSSL_VERSION.tar.gz

tar --totals -xzf $SOURCE/downloads/openh264-$OPENH264_VERSION.tar.gz -C $SOURCE/downloads/
tar --totals -xjf $SOURCE/downloads/last_stable_x264.tar.bz2 -C $SOURCE/downloads/
tar --totals -xzf $SOURCE/downloads/fdk-aac-$FDKACC_VERSION.tar.gz -C $SOURCE/downloads/
tar --totals -xzf $SOURCE/downloads/$OPENSSL_VERSION.tar.gz -C $SOURCE/downloads/

X264=`echo $SOURCE/downloads/x264-snapshot-*`
FDKACC=`echo $SOURCE/downloads/fdk-aac-*`
OpenSSL=`echo $SOURCE/downloads/openssl-*`

CFLAGS="-std=c99 -O3 -Wall -pipe -fpic -fasm -finline-limit=300 -ffast-math -fstrict-aliasing -Wno-psabi -Wa,--noexecstack -fdiagnostics-color=always -DANDROID -DNDEBUG"
LDFLAGS="-lm -lz -Wl,--no-undefined -Wl,-z,noexecstack"

case $CROSS_PREFIX in
 arm-*)
  CFLAGS="-mthumb $CFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__"
  ;;
 x86-*)
  ;;
 mipsel-*)
  CFLAGS="-std=c99 -O3 -Wall -pipe -fpic -fasm  -ftree-vectorize -ffunction-sections -funwind-tables -fomit-frame-pointer -funswitch-loops  -finline-limit=300 -finline-functions -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone  -Wno-psabi -Wa,--noexecstack  -DANDROID -DNDEBUG"
  ;;
esac

if [ "$version_type" = "online" ]; then
 FFMPEG_FLAGS_COMMON="--target-os=android --cross-prefix=$CROSS_PREFIX --enable-cross-compile --enable-version3 --enable-shared --disable-static --disable-symver --disable-programs --disable-doc --disable-avdevice --disable-encoders  --disable-muxers --disable-devices --disable-everything --disable-protocols  --disable-demuxers --disable-decoders --disable-bsfs --disable-debug --enable-optimizations --enable-filters --enable-parsers --disable-parser=hevc --enable-swscale  --enable-network --enable-protocol=file --enable-protocol=http --enable-protocol=rtmp --enable-protocol=rtp --enable-protocol=mmst --enable-protocol=mmsh --enable-protocol=crypto --enable-protocol=hls --enable-demuxer=hls --enable-demuxer=mpegts --enable-demuxer=mpegtsraw --enable-demuxer=mpegvideo --enable-demuxer=concat --enable-demuxer=mov --enable-demuxer=flv --enable-demuxer=rtsp --enable-demuxer=mp3 --enable-demuxer=matroska --enable-decoder=mpeg4 --enable-decoder=mpegvideo --enable-decoder=mpeg1video --enable-decoder=mpeg2video --enable-decoder=h264 --enable-decoder=h263 --enable-decoder=flv --enable-decoder=vp8 --enable-decoder=wmv3 --enable-decoder=aac --enable-decoder=ac3 --enable-decoder=mp3 --enable-decoder=nellymoser --enable-muxer=mp4 --enable-asm --enable-pic"
else
 FFMPEG_FLAGS_COMMON="--target-os=android --cross-prefix=$CROSS_PREFIX --enable-cross-compile --enable-version3 --enable-shared --disable-static --disable-symver --disable-programs --disable-doc --disable-avdevice --disable-encoders --enable-libx264 --enable-gpl --enable-libfdk_aac --enable-nonfree --enable-encoder=libx264 --enable-encoder=libfdk_aac --disable-muxers --enable-muxer=mp4 --enable-muxer=mpegts --disable-devices --disable-demuxer=sbg --disable-demuxer=dts --disable-parser=dca --disable-decoder=dca --disable-decoder=svq3 --enable-optimizations --disable-fast-unaligned --disable-postproc --enable-network --enable-asm --enable-openssl"
fi

 for version in $TARGET; do

  cd $SOURCE

  FFMPEG_FLAGS="$FFMPEG_FLAGS_COMMON"

  case $version in
   neon)
    FFMPEG_FLAGS="--arch=armv7-a --cpu=cortex-a8 --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=softfp -mvectorize-with-neon-quad"
    EXTRA_LDFLAGS="-Wl,--fix-cortex-a8"
    SSL_OBJS=""
    ;;
   armv7)
    FFMPEG_FLAGS="--arch=armv7-a --cpu=cortex-a8 --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp"
    EXTRA_LDFLAGS="-Wl,--fix-cortex-a8"
    SSL_OBJS=""
    ;;
   vfp)
    FFMPEG_FLAGS="--arch=arm --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv6 -mfpu=vfp -mfloat-abi=softfp"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   armv6)
    FFMPEG_FLAGS="--arch=arm --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv6 -msoft-float"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   x86)
    FFMPEG_FLAGS="--arch=x86 --cpu=i686 --enable-runtime-cpudetect --enable-yasm --disable-amd3dnow --disable-amd3dnowext $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=atom -msse3 -ffast-math -mfpmath=sse"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   mips)
    FFMPEG_FLAGS="--arch=mips --cpu=mips32r2 --enable-runtime-cpudetect --enable-yasm --disable-mipsfpu --disable-mipsdspr1 --disable-mipsdspr2 $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-fno-strict-aliasing -fmessage-length=0 -fno-inline-functions-called-once -frerun-cse-after-loop -frename-registers"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   *)
    FFMPEG_FLAGS=""
    EXTRA_CFLAGS=""
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
  esac

  PREFIX="$DEST/$version" && rm -rf $PREFIX && mkdir -p $PREFIX
  FFMPEG_FLAGS="$FFMPEG_FLAGS --prefix=$PREFIX"

# build OpenSSL  
    cd $OpenSSL
    ./Configure --prefix=$PREFIX android-$TARGET $CFLAGS $EXTRA_CFLAGS no-shared 
    [ $PIPESTATUS == 0 ] || exit 1
    make -j12 || exit 1
    make install
    
# build X264    
    cd $X264
    ./configure --prefix=$PREFIX --enable-static --enable-pic --disable-cli --cross-prefix=$CROSS_PREFIX --sysroot=$SYSROOT --host=$TARGET_HOST --extra-cflags="$CFLAGS $EXTRA_CFLAGS" --extra-ldflags="$LDFLAGS $EXTRA_LDFLAGS"
    [ $PIPESTATUS == 0 ] || exit 1
    make -j12 || exit 1
    make install
    
# build FDKACC
    cd $FDKACC
    ./configure --prefix=$PREFIX --with-sysroot=$ANDROID_NDK/platforms/$ANDROID_PLATFROM_VERSION/$PLATFORM --host=$TARGET_HOST
    [ $PIPESTATUS == 0 ] || exit 1
    make -j12 || exit 1
    make install    
    
# build ffmpeg  
    cd $SOURCE
    ./configure $FFMPEG_FLAGS --extra-cflags="$CFLAGS $EXTRA_CFLAGS -I$DEST/$TARGET/include" --extra-ldflags="$LDFLAGS $EXTRA_LDFLAGS -L$DEST/$TARGET/lib" | tee $PREFIX/configuration.txt
    cp config.* $PREFIX
    [ $PIPESTATUS == 0 ] || exit 1

    make clean
    find . -path $TOOLCHAIN -prune -name "*.o" -type f -delete
    make -j12 || exit 1
        
    make examples

    make install
  echo "----------------------$version -----------------------------"
    
 done 

以上脚本参考了其他开源项目中的思路,并且额外添加了自己的业务需求。


注意:在FFMPEG的configure配置时,添加你业务上需要的encoder:

    --enable-libx264 \
    --enable-gpl
    --enable-encoder=libx264 \

    --enable-libfdk-aac \
    --enable-nonfree
    --enable-encoder=libfdk-aac \

编译时比较重要的配置,连接对应的x264和fdk_aac:

-I$DEST/$TARGET/include
-L$DEST/$TARGET/lib

可以看到demux,decode,filter,encode,mux成功输出:


FFMPEG Android移植进阶_第1张图片

FFMPEG Android移植进阶_第2张图片

关键问题:

  • aac编码时由于编码器的采样率和输入数据采样率不一致,需要等缓存输入数据到编码器的size
  • 转码后的数据如何通过封装成网络传输流的格式进行实时测试

/*
 *  Copyright (c) 20180510 yangwu
 *  基本思路:解复用->视频+音频流->解码->YUV/PCM等->音/视频编码->重新生成的音视频流->复用->合成流
 */

#include 

#include 
#include 

#include 
#include 
#include 
#include 

#define ENCODE_TYPE_VIDEO AV_CODEC_ID_H264 
#define ENCODE_TYPE_AUDIO AV_CODEC_ID_MP2 

#define DECODE_THREADS 1
#define ENCODE_THREADS 1

static AVFormatContext *ifmt_ctx;
static AVFormatContext *ofmt_ctx;

typedef struct StreamContext {
  AVCodecContext *dec_ctx;
  AVCodecContext *enc_ctx;
} StreamContext;
static StreamContext *stream_ctx;

static int disable_audio = 0;
static int disable_video = 0;

static int open_input_file(const char *filename)
{
  int ret;
  unsigned int i;

  ifmt_ctx = NULL;
  if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
    return ret;
  }
  
  if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
    av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
    return ret;
  }

  stream_ctx = av_mallocz_array(ifmt_ctx->nb_streams, sizeof(*stream_ctx));
  if (!stream_ctx){
    return AVERROR(ENOMEM);  
  }
    
  for (i = 0; i < ifmt_ctx->nb_streams; i++) {
      
    //add for single stream mode
    if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && disable_audio){
        continue;
    }else if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && disable_video){
        continue;
    }
    
    AVStream *stream = ifmt_ctx->streams[i];    
    AVCodec *dec = avcodec_find_decoder(stream->codecpar->codec_id);
    AVCodecContext *codec_ctx;
    if (!dec) {
      av_log(NULL, AV_LOG_ERROR, "Failed to find decoder for stream #%u\n", i);
      return AVERROR_DECODER_NOT_FOUND;
    }
    codec_ctx = avcodec_alloc_context3(dec);
    if (!codec_ctx) {
      av_log(NULL, AV_LOG_ERROR, "Failed to allocate the decoder context for stream #%u\n", i);
      return AVERROR(ENOMEM);
    }
    ret = avcodec_parameters_to_context(codec_ctx, stream->codecpar);
    if (ret < 0) {
      av_log(NULL, AV_LOG_ERROR, "Failed to copy decoder parameters to input decoder context "
          "for stream #%u\n", i);
      return ret;
    }
    
    /* Reencode video & audio and remux subtitles etc. */
    if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || codec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {   
    
      if (codec_ctx->codec_type == AVMEDIA_TYPE_VIDEO){
        codec_ctx->framerate = av_guess_frame_rate(ifmt_ctx, stream, NULL);
      }
      
      codec_ctx->thread_count = DECODE_THREADS;
      codec_ctx->thread_type = FF_THREAD_FRAME;//FF_THREAD_SLICE;//FF_THREAD_FRAME
      
      /* Open decoder */
      ret = avcodec_open2(codec_ctx, dec, NULL);
      
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Failed to open decoder for stream #%u\n", i);
        return ret;
      }
    }
    stream_ctx[i].dec_ctx = codec_ctx;
  }

  av_dump_format(ifmt_ctx, 0, filename, 0);
  return 0;
}

static int open_output_file(const char *filename)
{
  AVStream *out_stream;
  AVStream *in_stream;
  AVCodecContext *dec_ctx, *enc_ctx;
  AVCodec *encoder;
  int ret;
  unsigned int i;

  ofmt_ctx = NULL;
  avformat_alloc_output_context2(&ofmt_ctx, NULL, "rtp_mpegts", NULL);
  if (!ofmt_ctx) {
    av_log(NULL, AV_LOG_ERROR, "Could not create output context\n");
    return AVERROR_UNKNOWN;
  }
  
  for (i = 0; i < ifmt_ctx->nb_streams; i++) {
    
    //for mux just creat a stream here
    out_stream = avformat_new_stream(ofmt_ctx, NULL);
    if (!out_stream) {
      av_log(NULL, AV_LOG_ERROR, "Failed allocating output stream\n");
      return AVERROR_UNKNOWN;
    }
    
    //add for single stream mode
    if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && disable_audio){
        continue;
    }else if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && disable_video){
        continue;
    }

    in_stream = ifmt_ctx->streams[i];
    dec_ctx = stream_ctx[i].dec_ctx;

    if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO || dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
        
      if(dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO){
        encoder = avcodec_find_encoder(ENCODE_TYPE_AUDIO);
      }else if(dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO){
        encoder = avcodec_find_encoder(ENCODE_TYPE_VIDEO);
      }
      if (!encoder) {
        av_log(NULL, AV_LOG_FATAL, "Necessary %s encoder not found\n",(dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO)?"audio":"video");
        return AVERROR_INVALIDDATA;
      }
      enc_ctx = avcodec_alloc_context3(encoder);
      if (!enc_ctx) {
        av_log(NULL, AV_LOG_FATAL, "Failed to allocate the encoder context\n");
        return AVERROR(ENOMEM);
      }

      if (dec_ctx->codec_type == AVMEDIA_TYPE_VIDEO) {
        enc_ctx->height = dec_ctx->height;
        enc_ctx->width = dec_ctx->width;
        enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;
        /* take first format from list of supported formats */
        if (encoder->pix_fmts){
            enc_ctx->pix_fmt = encoder->pix_fmts[0];
        }else{
            enc_ctx->pix_fmt = dec_ctx->pix_fmt;
        }
        /* video time_base can be set to whatever is handy and supported by encoder */
        enc_ctx->time_base = av_inv_q(dec_ctx->framerate);
        
        //Constrained Baseline
        enc_ctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
      } else if(dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO){
        enc_ctx->sample_rate = dec_ctx->sample_rate;
        enc_ctx->channel_layout = dec_ctx->channel_layout;
        enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
        /* take first format from list of supported formats */
        enc_ctx->sample_fmt = encoder->sample_fmts[0];
        enc_ctx->time_base = (AVRational){1, enc_ctx->sample_rate};
      }
      
      enc_ctx->thread_count = ENCODE_THREADS;
      enc_ctx->thread_type = FF_THREAD_FRAME;//FF_THREAD_SLICE;//FF_THREAD_FRAME
      
      /* Third parameter can be used to pass settings to encoder */
      ret = avcodec_open2(enc_ctx, encoder, NULL);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open video encoder for stream #%u\n", i);
        return ret;
      }
      ret = avcodec_parameters_from_context(out_stream->codecpar, enc_ctx);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Failed to copy encoder parameters to output stream #%u\n", i);
        return ret;
      }
      if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER){
        enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;  
      }
      out_stream->time_base = enc_ctx->time_base;
      stream_ctx[i].enc_ctx = enc_ctx;
    } else if (dec_ctx->codec_type == AVMEDIA_TYPE_UNKNOWN) {
      av_log(NULL, AV_LOG_FATAL, "Elementary stream #%d is of unknown type, cannot proceed\n", i);
      return AVERROR_INVALIDDATA;
    } else {
      /* if this stream must be remuxed */
      ret = avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar);
      if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Copying parameters for stream #%u failed\n", i);
        return ret;
      }
      out_stream->time_base = in_stream->time_base;
    }
  }
  av_dump_format(ofmt_ctx, 0, filename, 1);

  if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE)) {
    ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
    if (ret < 0) {
      av_log(NULL, AV_LOG_ERROR, "Could not open output file '%s'", filename);
      return ret;
    }
  }

  /* init muxer, write output file header */
  ret = avformat_write_header(ofmt_ctx, NULL);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "Error occurred when opening output file\n");
    return ret;
  }

  return 0;
}

static int encode_write_frame(AVFrame *filt_frame, unsigned int stream_index, int *got_frame) {
  int ret;
  int got_frame_local;
  AVPacket enc_pkt;
  
  //add for single stream mode
  if(ifmt_ctx->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && disable_audio){
    return 0;
  }else if(ifmt_ctx->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && disable_video){
    return 0;
  }
    
  int (*enc_func)(AVCodecContext *, AVPacket *, const AVFrame *, int *) =
    (ifmt_ctx->streams[stream_index]->codecpar->codec_type ==
     AVMEDIA_TYPE_VIDEO) ? avcodec_encode_video2 : avcodec_encode_audio2;

  if (!got_frame){
    got_frame = &got_frame_local;
  }
  //av_log(NULL, AV_LOG_INFO, "Encoding frame\n");
  /* encode filtered frame */
  enc_pkt.data = NULL;
  enc_pkt.size = 0;
  av_init_packet(&enc_pkt);
    
  ret = enc_func(stream_ctx[stream_index].enc_ctx, &enc_pkt,
      filt_frame, got_frame);
  if (ret < 0){
    return ret;
  }
  if (!(*got_frame)){
    return 0;
  }
  /* prepare packet for muxing */
  enc_pkt.stream_index = stream_index;
  av_packet_rescale_ts(&enc_pkt,
             stream_ctx[stream_index].enc_ctx->time_base,
             ofmt_ctx->streams[stream_index]->time_base);

  //av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n");
  /* mux encoded frame */
  ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
  return ret;
}

static int flush_encoder(unsigned int stream_index)
{
  int ret;
  int got_frame;

  if (!(stream_ctx[stream_index].enc_ctx->codec->capabilities &
        AV_CODEC_CAP_DELAY)){
    return 0;
  }

  while (1) {
    av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index);
    ret = encode_write_frame(NULL, stream_index, &got_frame);
    if (ret < 0){
      break;
    }
    if (!got_frame){
      return 0;
    }
    //av_usleep(50);
  }
  return ret;
}

int main(int argc, char **argv)
{
  int ret;
  AVPacket packet = { .data = NULL, .size = 0 };
  AVFrame *frame = NULL;
  enum AVMediaType type;
  unsigned int stream_index;
  unsigned int i;
  int got_frame;
  int (*dec_func)(AVCodecContext *, AVFrame *, int *, const AVPacket *);
  
  //for network
  avformat_network_init();
  
  if (argc < 3) {
    av_log(NULL, AV_LOG_ERROR, "Usage: %s   [-noa,nov]\n", argv[0]);
    return 1;
  } 
  
  if(argc == 3){
    disable_audio = 0;
    disable_video = 0;  
  }else if(argc == 4 && !strcmp(argv[3], "-noa")){
    disable_audio = 1;
    disable_video = 0;  
  }else if(argc == 4 && !strcmp(argv[3], "-nov")){
    disable_audio = 0;
    disable_video = 1; 
  }
  
  if ((ret = open_input_file(argv[1])) < 0){
    goto end;   
  }
  
  if ((ret = open_output_file(argv[2])) < 0){
    goto end;  
  }
      
  /* read all packets */  
  while (1) {
    if ((ret = av_read_frame(ifmt_ctx, &packet)) < 0){
        break;
    }
    stream_index = packet.stream_index;
    type = ifmt_ctx->streams[packet.stream_index]->codecpar->codec_type;
    //av_log(NULL, AV_LOG_DEBUG, "Demuxer gave frame of stream_index %u\n",stream_index);
    
    //add for single stream mode
    if(type == AVMEDIA_TYPE_AUDIO && disable_audio){

    }else if(type == AVMEDIA_TYPE_VIDEO && disable_video){

    }else{
            frame = av_frame_alloc();
            if (!frame) {
                ret = AVERROR(ENOMEM);
                break;
            }
            av_packet_rescale_ts(&packet,ifmt_ctx->streams[stream_index]->time_base,stream_ctx[stream_index].dec_ctx->time_base);
            dec_func = (type == AVMEDIA_TYPE_VIDEO) ? avcodec_decode_video2 : avcodec_decode_audio4;
            ret = dec_func(stream_ctx[stream_index].dec_ctx,frame,&got_frame, &packet);
            if (ret < 0) {
                av_frame_free(&frame);
                av_log(NULL, AV_LOG_ERROR, "Decoding failed\n");
                break;
            }
            if (got_frame) {
                frame->pts = frame->best_effort_timestamp;
                //just do for iframe
                if(frame->pict_type == AV_PICTURE_TYPE_I || type == AVMEDIA_TYPE_AUDIO){
                    ret = encode_write_frame(frame, stream_index,NULL);
                    av_frame_free(&frame);
                    if (ret < 0){
                        goto end;
                    }
                }   
            } else {
                av_frame_free(&frame);
            }
    }
    
    av_packet_unref(&packet);
  }

/* flush encoders */
  for (i = 0; i < ifmt_ctx->nb_streams; i++) {  
    /* flush encoder */
    ret = flush_encoder(i);
      
    if (ret < 0) {
      av_log(NULL, AV_LOG_ERROR, "Flushing encoder failed\n");
      goto end;
    }
  }

  av_write_trailer(ofmt_ctx);
  
end:

  av_packet_unref(&packet);
  av_frame_free(&frame);
  
  for (i = 0; i < ifmt_ctx->nb_streams; i++) {
      
    //add for single stream mode
    if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && disable_audio){
        continue;
    }else if(ifmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && disable_video){
        continue;
    }
    
    avcodec_free_context(&stream_ctx[i].dec_ctx);
    if (ofmt_ctx && ofmt_ctx->nb_streams > i && ofmt_ctx->streams[i] && stream_ctx[i].enc_ctx){
        avcodec_free_context(&stream_ctx[i].enc_ctx);   
    }
  }
  
  av_free(stream_ctx);
  avformat_close_input(&ifmt_ctx);
  if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE)){
    avio_closep(&ofmt_ctx->pb);  
  }
  avformat_free_context(ofmt_ctx);

  if (ret < 0){
    av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", av_err2str(ret));  
  }
  return ret ? 1 : 0;    
}

使用如下指令 完成了在两台Android设备上(我使用的是机顶盒,一台完成转码,另一台播放转码封装后的音视频)

codec /mnt/sda/sda1/demo.mpg rtp://192.168.30.102:6666 &

针对之前说提到的两个关键问题:

  • aac编码size问题
    此阶段转换为mp2音频先验证功能问题,其实解决思路也就是等输入的size满足编码器的size后再传入给编码器
  • 转码后数据封装成网络实时传输流
    这里就得说说ffmpeg的框架设计了,我的理解是一种组件热插拔模式,先看看以下几种muxer:
--enable-muxer=mp4 
--enable-muxer=mpegts

拿以上两种muxer来说,是针对文件的合成,比如mpegts:

/**libavformat/mpegtsenc.c */
AVOutputFormat ff_mpegts_muxer = {
    .name           = "mpegts",
    .long_name      = NULL_IF_CONFIG_SMALL("MPEG-TS (MPEG-2 Transport Stream)"),
    .mime_type      = "video/MP2T",
    .extensions     = "ts,m2t,m2ts,mts",
    .priv_data_size = sizeof(MpegTSWrite),
    .audio_codec    = AV_CODEC_ID_MP2,
    .video_codec    = AV_CODEC_ID_MPEG2VIDEO,
    .init           = mpegts_init,
    .write_packet   = mpegts_write_packet,
    .write_trailer  = mpegts_write_end,
    .deinit         = mpegts_deinit,
    .check_bitstream = mpegts_check_bitstream,
    .flags          = AVFMT_ALLOW_FLUSH | AVFMT_VARIABLE_FPS | AVFMT_NODIMENSIONS,
    .priv_class     = &mpegts_muxer_class,
};

如果在设置编码器参数时如下配置:

avformat_alloc_output_context2(&ofmt_ctx, NULL, "mpegts", NULL);

编码时就会使用mpegtsenc来合成文件。


--enable-muxer=rtp 
--enable-muxer=rtp_mpegts

而这两种是针对网络实时流的合成处理,比如rtp_mpegts:

/**libavformat/rtpenc_mpegts.c */
AVOutputFormat ff_rtp_mpegts_muxer = {
    .name              = "rtp_mpegts",
    .long_name         = NULL_IF_CONFIG_SMALL("RTP/mpegts output format"),
    .priv_data_size    = sizeof(struct MuxChain),
    .audio_codec       = AV_CODEC_ID_AAC,
    .video_codec       = AV_CODEC_ID_MPEG4,
    .write_header      = rtp_mpegts_write_header,
    .write_packet      = rtp_mpegts_write_packet,
    .write_trailer     = rtp_mpegts_write_close,
};

当设置编码器参数时按如下配置:

avformat_alloc_output_context2(&ofmt_ctx, NULL, "rtp_mpegts", NULL);

编码时就会使用rtpenc_mpegts来合成音视频并且封装成rtp的ts流实时发送。

小结:

以上就是使用X264来编码视频,mp2编码音频,并封装成ts包,然后通过rtp转发的处理。
在实际测试时发现X264编码很耗CPU,于是萌生了使用openh264来编码视频的想法:

FFMPEG Android移植进阶_第3张图片

于是取消了 x264的编译,使用 openh264的编译( openh264的编译参考 openh264目录下的README):

For Android Builds
------------------
To build for android platform, You need to install android sdk and ndk. You also need to export `**ANDROID_SDK**/tools` to PATH. On Linux, this can be done by

    export PATH=**ANDROID_SDK**/tools:$PATH

The codec and demo can be built by

    make OS=android NDKROOT=**ANDROID_NDK** TARGET=**ANDROID_TARGET**

Valid `**ANDROID_TARGET**` can be found in `**ANDROID_SDK**/platforms`, such as `android-12`.
You can also set `ARCH`, `NDKLEVEL` according to your device and NDK version.
`ARCH` specifies the architecture of android device. Currently `arm`, `arm64`, `x86` and `x86_64` are supported, the default is `arm`. (`mips` and `mips64` can also be used, but there's no specific optimization for those architectures.)
`NDKLEVEL` specifies android api level, the default is 12. Available possibilities can be found in `**ANDROID_NDK**/platforms`, such as `android-21` (strip away the `android-` prefix).

By default these commands build for the `armeabi-v7a` ABI. To build for the other android
ABIs, add `ARCH=arm64`, `ARCH=x86`, `ARCH=x86_64`, `ARCH=mips` or `ARCH=mips64`.
To build for the older `armeabi` ABI (which has armv5te as baseline), add `APP_ABI=armeabi` (`ARCH=arm` is implicit).
To build for 64-bit ABI, such as `arm64`, explicitly set `NDKLEVEL` to 21 or higher.

编译时又发现在FFMPEGconfigure中,需要使用pkg-config校验openh264

enabled libopenh264       && require_pkg_config libopenh264 openh264 wels/codec_api.h WelsGetCodecVersion

编译FFMPEG Android版本时由于使用的是交叉编译工具链,在NDKarm-linux-androideabi下是没有arm-linux-androideabi-pkg-config的,但是可以替换成编译环境下的pkg-config,否则会提示:

ERROR: openh264 not found using pkg-config

可以修改FFMPEG的编译脚本如下:

#!/bin/bash

#Detect ANDROID_NDK
export ANDROID_NDK=/home3/yangwu/build_tools/android-ndk-r14b

NDK_TOOLCHAIN_VERSION=4.9
ANDROID_PLATFROM_VERSION=android-19

if [ -z "$ANDROID_NDK" ]; then
 echo "You must define ANDROID_NDK before starting."
 echo "You must point to your NDK directories.\n"
 exit 1
fi

#Detect OS
OS=`uname`
HOST_ARCH=`uname -m`
export CCACHE=; type ccache >/dev/null 2>&1 && export CCACHE=ccache
if [ $OS == 'Linux' ]; then
 export HOST_SYSTEM=linux-$HOST_ARCH
elif [ $OS == 'Darwin' ]; then
 export HOST_SYSTEM=darwin-$HOST_ARCH
fi

platform="$1"
version_type="$2"

function arm_toolchain()
{
 export CROSS_PREFIX=arm-linux-androideabi-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=${CROSS_PREFIX}${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=arm --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}

function x86_toolchain()
{
 export CROSS_PREFIX=i686-linux-android-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=x86-${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=x86 --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}

function mips_toolchain()
{
 export CROSS_PREFIX=mipsel-linux-android-
 $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --toolchain=${CROSS_PREFIX}${NDK_TOOLCHAIN_VERSION} --platform=${ANDROID_PLATFROM_VERSION} --install-dir=$TOOLCHAIN --arch=mips --force
  #--system=$HOST_SYSTEM #ndk4.9 do not support --system
}


SOURCE=`pwd`
DEST=$SOURCE/build/android
TOOLCHAIN=$SOURCE/ffmpeg_toolchain
SYSROOT=$TOOLCHAIN/sysroot/

function download {
  mkdir -p "$SOURCE/downloads"
  if [[ ! -e "$SOURCE/downloads/$2" ]]; then
    echo "Downloading $1"
    curl -L "$1" -o "$SOURCE/downloads/$2"
  fi
}

if [ "$platform" = "x86" ];then
 echo "Build Android x86 ffmpeg\n"
 x86_toolchain
 TARGET="x86"
 TARGET_HOST="x86-linux-android"
 PLATFORM="arch-x86"
elif [ "$platform" = "mips" ];then
 echo "Build Android mips ffmpeg\n"
 mips_toolchain
 TARGET="mips"
 TARGET_HOST="mipsel-linux-android"
 PLATFORM="arch-mips"
elif [ "$platform" = "armv7" ];then
 echo "Build Android armv7 ffmpeg\n"
 arm_toolchain
 TARGET="armv7"
 TARGET_HOST="arm-linux-android"
 PLATFORM="arch-arm"
else
 echo "Build Android arm ffmpeg\n"
 arm_toolchain
 TARGET="neon armv7 vfp armv6"
 TARGET_HOST="arm-linux-android"
 PLATFORM="arch-arm"
fi
export PATH=$TOOLCHAIN/bin:$PATH
export CC="$CCACHE ${CROSS_PREFIX}gcc"
export CXX=${CROSS_PREFIX}g++
export LD=${CROSS_PREFIX}ld
export AR=${CROSS_PREFIX}ar
export STRIP=${CROSS_PREFIX}strip

#set ffmpeg dep libs here
echo "Decompressing archives..."
OPENH264_VERSION=1.7.0
FDKACC_VERSION=0.1.6
OPENSSL_VERSION=openssl-1.0.2j

download https://github.com/cisco/openh264/archive/v$OPENH264_VERSION.tar.gz openh264-$OPENH264_VERSION.tar.gz
#download ftp://ftp.videolan.org/pub/videolan/x264/snapshots/last_stable_x264.tar.bz2 last_stable_x264.tar.bz2
#download https://downloads.sourceforge.net/opencore-amr/fdk-aac-$FDKACC_VERSION.tar.gz
download https://www.openssl.org/source/$OPENSSL_VERSION.tar.gz $OPENSSL_VERSION.tar.gz

tar --totals -xzf $SOURCE/downloads/openh264-$OPENH264_VERSION.tar.gz -C $SOURCE/downloads/
#tar --totals -xjf $SOURCE/downloads/last_stable_x264.tar.bz2 -C $SOURCE/downloads/
#tar --totals -xzf $SOURCE/downloads/fdk-aac-$FDKACC_VERSION.tar.gz -C $SOURCE/downloads/
tar --totals -xzf $SOURCE/downloads/$OPENSSL_VERSION.tar.gz -C $SOURCE/downloads/

OPENH264=`echo $SOURCE/downloads/openh264-*`
X264=`echo $SOURCE/downloads/x264-snapshot-*`
FDKACC=`echo $SOURCE/downloads/fdk-aac-*`
OpenSSL=`echo $SOURCE/downloads/openssl-*`

CFLAGS="-std=c99 -O3 -Wall -pipe -fpic -fasm -finline-limit=300 -ffast-math -fstrict-aliasing -Wno-psabi -Wa,--noexecstack -fdiagnostics-color=always -DANDROID -DNDEBUG"
LDFLAGS="-lm -lz -Wl,--no-undefined -Wl,-z,noexecstack"

case $CROSS_PREFIX in
 arm-*)
  CFLAGS="-mthumb $CFLAGS -D__ARM_ARCH_5__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5TE__"
  ;;
 x86-*)
  ;;
 mipsel-*)
  CFLAGS="-std=c99 -O3 -Wall -pipe -fpic -fasm  -ftree-vectorize -ffunction-sections -funwind-tables -fomit-frame-pointer -funswitch-loops  -finline-limit=300 -finline-functions -fpredictive-commoning -fgcse-after-reload -fipa-cp-clone  -Wno-psabi -Wa,--noexecstack  -DANDROID -DNDEBUG"
  ;;
esac

if [ "$version_type" = "online" ]; then
 FFMPEG_FLAGS_COMMON="--target-os=android --cross-prefix=$CROSS_PREFIX --enable-cross-compile --enable-version3 --enable-shared --disable-static --disable-symver --disable-programs --disable-doc --disable-avdevice --disable-encoders  --disable-muxers --disable-devices --disable-everything --disable-protocols  --disable-demuxers --disable-decoders --disable-bsfs --disable-debug --enable-optimizations --enable-filters --enable-parsers --disable-parser=hevc --enable-swscale  --enable-network --enable-protocol=file --enable-protocol=http --enable-protocol=rtmp --enable-protocol=rtp --enable-protocol=mmst --enable-protocol=mmsh --enable-protocol=crypto --enable-protocol=hls --enable-demuxer=hls --enable-demuxer=mpegts --enable-demuxer=mpegtsraw --enable-demuxer=mpegvideo --enable-demuxer=concat --enable-demuxer=mov --enable-demuxer=flv --enable-demuxer=rtsp --enable-demuxer=mp3 --enable-demuxer=matroska --enable-decoder=mpeg4 --enable-decoder=mpegvideo --enable-decoder=mpeg1video --enable-decoder=mpeg2video --enable-decoder=h264 --enable-decoder=h263 --enable-decoder=flv --enable-decoder=vp8 --enable-decoder=wmv3 --enable-decoder=aac --enable-decoder=ac3 --enable-decoder=mp3 --enable-decoder=nellymoser --enable-muxer=mp4 --enable-asm --enable-pic"
else
 FFMPEG_FLAGS_COMMON="--target-os=android --cross-prefix=$CROSS_PREFIX --enable-cross-compile --enable-version3 --enable-shared --disable-static --disable-symver --disable-programs --disable-doc --disable-avdevice --disable-encoders --enable-gpl --enable-nonfree --enable-libopenh264 --enable-encoder=libopenh264 --enable-encoder=aac --enable-encoder=mp2 --disable-muxers --enable-muxer=mp4 --enable-muxer=mpegts --enable-muxer=rtp --enable-muxer=rtp_mpegts --disable-devices --disable-demuxer=sbg --disable-demuxer=dts --disable-parser=dca --disable-decoder=dca --disable-decoder=svq3 --enable-optimizations --disable-fast-unaligned --disable-postproc --enable-network --enable-asm --enable-openssl --disable-debug --enable-pthreads"
fi

for version in $TARGET; do

  cd $SOURCE

  FFMPEG_FLAGS="$FFMPEG_FLAGS_COMMON"

  case $version in
   neon)
    FFMPEG_FLAGS="--arch=armv7-a --cpu=cortex-a8 --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv7-a -mfpu=neon -mfloat-abi=softfp -mvectorize-with-neon-quad"
    EXTRA_LDFLAGS="-Wl,--fix-cortex-a8"
    SSL_OBJS=""
    ;;
   armv7)
    FFMPEG_FLAGS="--arch=armv7-a --cpu=cortex-a8 --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp"
    EXTRA_LDFLAGS="-Wl,--fix-cortex-a8"
    SSL_OBJS=""
    ;;
   vfp)
    FFMPEG_FLAGS="--arch=arm --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv6 -mfpu=vfp -mfloat-abi=softfp"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   armv6)
    FFMPEG_FLAGS="--arch=arm --disable-runtime-cpudetect $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=armv6 -msoft-float"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   x86)
    FFMPEG_FLAGS="--arch=x86 --cpu=i686 --enable-runtime-cpudetect --enable-yasm --disable-amd3dnow --disable-amd3dnowext $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-march=atom -msse3 -ffast-math -mfpmath=sse"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   mips)
    FFMPEG_FLAGS="--arch=mips --cpu=mips32r2 --enable-runtime-cpudetect --enable-yasm --disable-mipsfpu --disable-mipsdspr1 --disable-mipsdspr2 $FFMPEG_FLAGS"
    EXTRA_CFLAGS="-fno-strict-aliasing -fmessage-length=0 -fno-inline-functions-called-once -frerun-cse-after-loop -frename-registers"
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
   *)
    FFMPEG_FLAGS=""
    EXTRA_CFLAGS=""
    EXTRA_LDFLAGS=""
    SSL_OBJS=""
    ;;
esac

  PREFIX="$DEST/$version"
  rm -rf $PREFIX && mkdir -p $PREFIX
  FFMPEG_FLAGS="$FFMPEG_FLAGS --prefix=$PREFIX"

 #build OpenSSL  
    cd $OpenSSL
    ./Configure --prefix=$PREFIX android-$TARGET $CFLAGS $EXTRA_CFLAGS no-shared 
    [ $PIPESTATUS == 0 ] || exit 1
    make -j12 || exit 1
    make install
    
# build OPENH264    
    cd $OPENH264
    make -j12 PREFIX=$PREFIX OS=android NDKROOT="$ANDROID_NDK" TARGET=$ANDROID_PLATFROM_VERSION libraries install-static

# build X264    
#   cd $X264
#   ./configure --prefix=$PREFIX --enable-static --enable-pic --disable-cli --cross-prefix=$CROSS_PREFIX --sysroot=$SYSROOT --host=$TARGET_HOST --extra-cflags="$CFLAGS $EXTRA_CFLAGS" --extra-ldflags="$LDFLAGS $EXTRA_LDFLAGS"
#   [ $PIPESTATUS == 0 ] || exit 1
#   make -j12 || exit 1
#   make install
    
# build FDKACC
#   cd $FDKACC
#   ./configure --prefix=$PREFIX --with-sysroot=$ANDROID_NDK/platforms/$ANDROID_PLATFROM_VERSION/$PLATFORM --host=$TARGET_HOST
#   [ $PIPESTATUS == 0 ] || exit 1
#   make -j12 || exit 1
#   make install    

# build ffmpeg  
    export PKG_CONFIG_PATH=$DEST/$TARGET/lib/pkgconfig:$PKG_CONFIG_PATH
    cd $SOURCE
    ./configure $FFMPEG_FLAGS --extra-cflags="$CFLAGS $EXTRA_CFLAGS -I$DEST/$TARGET/include" --extra-ldflags="$LDFLAGS $EXTRA_LDFLAGS -L$DEST/$TARGET/lib" --pkg-config="pkg-config" | tee $PREFIX/configuration.txt
    cp config.* $PREFIX
    [ $PIPESTATUS == 0 ] || exit 1

    make clean
    find . -path $TOOLCHAIN -prune -name "*.o" -type f -delete
    make -j12 || exit 1
        
    make examples

    make install
  echo "----------------------$version -----------------------------"
    
 done 

把configure的pkg_config_default指向编译本机中的pkg_config:

#pkg_config_default="${cross_prefix}${pkg_config_default}"
pkg_config_default="/usr/bin/pkg_config"

x264转码效率:

FFMPEG Android移植进阶_第4张图片

openh264转码效率:

FFMPEG Android移植进阶_第5张图片


实测openh264的cpu占用确实由x264的90%降到了25%,可画质效果确实也是差了很多,实际情况还需要根据自身业务做调整。
  x264的在使用多线程调优的情况下也可以从90%优化降到60%左右,但是效果还是不理想,其实就是修改多线程编解码DECODE_THREADSENCODE_THREADS的线程数,修改多线程工作方式为FF_THREAD_SLICEFF_THREAD_FRAME

3.结束语

本篇文章旨在帮助同学们自己完成FFMPEG源码移植的编译,并且在次基础上通过音视频转码来让大家熟悉FFMPEG API的使用,FFMPEG是个很优秀的框架,学习的路还很长,我会把自己的理解和成果尽可能的记录下来,便于大家一起学习。
  文章更新的进度越来越慢了,因为有些还没有写完,好几篇文章在日记本中躺着。感谢各位读者的支持!

你可能感兴趣的:(FFMPEG Android移植进阶)