1.文章介紹
在應(yīng)用開發(fā)中疚膊,你是否面臨著第三方庫的FFMPEG不是最新版本蛮原,或者需要使用擴展自己的業(yè)務(wù)的FFMPEG版本等問題揍异,一旦遇到這些問題备徐,你會怎么處理萄传?
本篇文章是把FFMPEG的libavcodec,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ā),相信對avc和aac的編解碼庫再熟悉不過了蓖救,那么這篇文章該涉及到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)在FFMPEG的configure中,需要使用pkg-config校驗openh264:
enabled libopenh264 && require_pkg_config libopenh264 openh264 wels/codec_api.h WelsGetCodecVersion
編譯FFMPEG Android版本時由于使用的是交叉編譯工具鏈厢钧,在NDK的arm-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_THREADS和ENCODE_THREADS的線程數(shù),修改多線程工作方式為FF_THREAD_SLICE或FF_THREAD_FRAME喻圃。
3.結(jié)束語
本篇文章旨在幫助同學(xué)們自己完成FFMPEG源碼移植的編譯萤彩,并且在次基礎(chǔ)上通過音視頻轉(zhuǎn)碼來讓大家熟悉FFMPEG API的使用,FFMPEG是個很優(yōu)秀的框架斧拍,學(xué)習(xí)的路還很長雀扶,我會把自己的理解和成果盡可能的記錄下來,便于大家一起學(xué)習(xí)肆汹。
文章更新的進(jìn)度越來越慢了愚墓,因為有些還沒有寫完予权,好幾篇文章在日記本中躺著。感謝各位讀者的支持浪册!