FFMPEG Android移植進(jìn)階

1.文章介紹

在應(yīng)用開發(fā)中疚膊,你是否面臨著第三方庫的FFMPEG不是最新版本蛮原,或者需要使用擴展自己的業(yè)務(wù)的FFMPEG版本等問題揍异,一旦遇到這些問題备徐,你會怎么處理萄传?


本篇文章是把FFMPEGlibavcodec,libavformat蜜猾,libavutil秀菱,libavfilter等移植到Android設(shè)備上,并且使用這些API完成音視頻轉(zhuǎn)碼功能蹭睡,作為FFMPEG進(jìn)階使用衍菱。

2.干貨

1.準(zhǔn)備編譯工具
Android NDK編譯環(huán)境:
關(guān)于NDK的下載我就不多說了,基本百度就能搞定棠笑。

android-ndk-r14b

為了方便配置NDK編譯環(huán)境梦碗,可以添加如下腳本:

#! /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編譯環(huán)境下:

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

3.配置Android編譯腳本:
  搞音視頻開發(fā),相信對avcaac的編解碼庫再熟悉不過了蓖救,那么這篇文章該涉及到X264洪规,libfdk-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 

以上腳本參考了其他開源項目中的思路,并且額外添加了自己的業(yè)務(wù)需求从橘。


注意:在FFMPEG的configure配置時念赶,添加你業(yè)務(wù)上需要的encoder:

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

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

編譯時比較重要的配置础钠,連接對應(yīng)的x264和fdk_aac:

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

可以看到demux,decode叉谜,filter旗吁,encode,mux成功輸出:



關(guān)鍵問題:

  • aac編碼時由于編碼器的采樣率和輸入數(shù)據(jù)采樣率不一致停局,需要等緩存輸入數(shù)據(jù)到編碼器的size
  • 轉(zhuǎn)碼后的數(shù)據(jù)如何通過封裝成網(wǎng)絡(luò)傳輸流的格式進(jìn)行實時測試

/*
 *  Copyright (c) 20180510 yangwu
 *  基本思路:解復(fù)用->視頻+音頻流->解碼->YUV/PCM等->音/視頻編碼->重新生成的音視頻流->復(fù)用->合成流
 */

#include <libavcodec/avcodec.h>

#include <libavformat/avformat.h>
#include <libavformat/avio.h>

#include <libavutil/audio_fifo.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/time.h>

#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 <input file> <output file> [-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設(shè)備上(我使用的是機頂盒很钓,一臺完成轉(zhuǎn)碼,另一臺播放轉(zhuǎn)碼封裝后的音視頻)

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

針對之前說提到的兩個關(guān)鍵問題:

  • aac編碼size問題
    此階段轉(zhuǎn)換為mp2音頻先驗證功能問題董栽,其實解決思路也就是等輸入的size滿足編碼器的size后再傳入給編碼器
  • 轉(zhuǎn)碼后數(shù)據(jù)封裝成網(wǎng)絡(luò)實時傳輸流
    這里就得說說ffmpeg的框架設(shè)計了码倦,我的理解是一種組件熱插拔模式,先看看以下幾種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,
};

如果在設(shè)置編碼器參數(shù)時如下配置:

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

編碼時就會使用mpegtsenc來合成文件。


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

而這兩種是針對網(wǎng)絡(luò)實時流的合成處理擒抛,比如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,
};

當(dāng)設(shè)置編碼器參數(shù)時按如下配置:

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

編碼時就會使用rtpenc_mpegts來合成音視頻并且封裝成rtp的ts流實時發(fā)送推汽。

小結(jié):

以上就是使用X264來編碼視頻,mp2編碼音頻闻葵,并封裝成ts包民泵,然后通過rtp轉(zhuǎn)發(fā)的處理。
在實際測試時發(fā)現(xiàn)X264編碼很耗CPU槽畔,于是萌生了使用openh264來編碼視頻的想法:


于是取消了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.

編譯時又發(fā)現(xiàn)在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的鳞尔,但是可以替換成編譯環(huán)境下的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轉(zhuǎn)碼效率:

openh264轉(zhuǎn)碼效率:


實測openh264的cpu占用確實由x264的90%降到了25%早直,可畫質(zhì)效果確實也是差了很多寥假,實際情況還需要根據(jù)自身業(yè)務(wù)做調(diào)整。
  x264的在使用多線程調(diào)優(yōu)的情況下也可以從90%優(yōu)化降到60%左右霞扬,但是效果還是不理想糕韧,其實就是修改多線程編解碼DECODE_THREADSENCODE_THREADS的線程數(shù),修改多線程工作方式為FF_THREAD_SLICEFF_THREAD_FRAME喻圃。

3.結(jié)束語

本篇文章旨在幫助同學(xué)們自己完成FFMPEG源碼移植的編譯萤彩,并且在次基礎(chǔ)上通過音視頻轉(zhuǎn)碼來讓大家熟悉FFMPEG API的使用,FFMPEG是個很優(yōu)秀的框架斧拍,學(xué)習(xí)的路還很長雀扶,我會把自己的理解和成果盡可能的記錄下來,便于大家一起學(xué)習(xí)肆汹。
  文章更新的進(jìn)度越來越慢了愚墓,因為有些還沒有寫完予权,好幾篇文章在日記本中躺著。感謝各位讀者的支持浪册!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扫腺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子议经,更是在濱河造成了極大的恐慌斧账,老刑警劉巖谴返,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件煞肾,死亡現(xiàn)場離奇詭異,居然都是意外死亡嗓袱,警方通過查閱死者的電腦和手機籍救,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渠抹,“玉大人蝙昙,你說我怎么就攤上這事∥嗳矗” “怎么了奇颠?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長放航。 經(jīng)常有香客問我烈拒,道長,這世上最難降的妖魔是什么广鳍? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任荆几,我火速辦了婚禮,結(jié)果婚禮上赊时,老公的妹妹穿的比我還像新娘吨铸。我一直安慰自己,他們只是感情好祖秒,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布诞吱。 她就那樣靜靜地躺著,像睡著了一般竭缝。 火紅的嫁衣襯著肌膚如雪房维。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天歌馍,我揣著相機與錄音握巢,去河邊找鬼。 笑死松却,一個胖子當(dāng)著我的面吹牛暴浦,可吹牛的內(nèi)容都是我干的溅话。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼歌焦,長吁一口氣:“原來是場噩夢啊……” “哼飞几!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起独撇,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屑墨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后纷铣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體卵史,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年搜立,在試婚紗的時候發(fā)現(xiàn)自己被綠了以躯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡啄踊,死狀恐怖忧设,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颠通,我是刑警寧澤址晕,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站顿锰,受9級特大地震影響谨垃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜撵儿,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一乘客、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淀歇,春花似錦易核、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纳决,卻和暖如春碰逸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阔加。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工饵史, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓胳喷,卻偏偏與公主長得像湃番,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吭露,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355