Android音视频开发实战01-环境搭建

这篇具有很好参考价值的文章主要介绍了Android音视频开发实战01-环境搭建。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一,FFmpeg介绍

FFmpeg 是一款流行的开源多媒体处理工具,它可以用于转换、编辑、录制和流式传输音视频文件。FFmpeg 具有广泛的应用场景,包括视频编解码、格式转换、裁剪、合并、滤镜等等。官网:https://ffmpeg.org/

FFmpeg 支持各种常见的音视频格式,例如 MP4、AVI、FLV、MOV、AAC、MP3、M4A 等等,并且可以通过添加插件支持更多的格式。与其他视频处理软件相比,FFmpeg 优势在于它的跨平台性能好,可以在 Windows、macOS 和 Linux IOS Android等平台上运行。

FFmpeg 提供了一个命令行界面(CLI),可以使用它来执行各种操作。以下是一些常用的 FFmpeg 命令:

  • 裁剪:从视频中截取指定时间段的视频片段。

    ffmpeg -i input.mp4 -ss 00:01:00 -t 00:00:30 -c copy output.mp4
    
  • 视频旋转:将视频顺时针或逆时针旋转指定角度。

    ffmpeg -i input.mp4 -vf "rotate=PI/2" output.mp4
    
  • 视频拼接:将多个视频文件拼接成一个文件。

    ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mp4
    
  • 视频缩放:按比例缩小或放大视频尺寸。

    ffmpeg -i input.mp4 -vf scale=640:-1 output.mp4
    
  • 音频提取:将视频文件中的音频提取出来。

    ffmpeg -i input.mp4 -vn -acodec copy output.aac
    
  • 音频合并:将多个音频文件合并为一个文件。

    ffmpeg -i "concat:input1.mp3|input2.mp3" -acodec copy output.mp3
    
  • 视频转码:将一个视频文件转换为另一种格式。

    ffmpeg -i input.avi -c:v libx264 -preset slow -crf 22 -c:a libmp3lame -b:a 192k output.mp4
    
  • 视频加减速:将视频的播放速度加快或减慢。

    ffmpeg -i input.mp4 -filter:v "setpts=0.5*PTS" output.mp4
    
  • 音频加减速:将视频中的音频的播放速度加快或减慢。

    ffmpeg -i input.mp4 -vn -af atempo=%.3f output.mp4
    
  • 音频重采样

    ffmpeg -y -i input.mp4 -vn -ar 44100 -ac 2  output.mp4
    
  • 视频转Gif并等比例缩放

    ffmpeg -y -i input.mp4 -vf scale=320:-2 -r 5 output.gif
    
  • 统计I帧数量

    ffprobe -show_frames input.mp4 > frames.txt
    cat frames.txt | grep "pict_type=I" |wc -l
    
  • 视频信息查看 开发过程中最常用的命令,没有之一

    ffprobe -v error -show_format -show_streams input.mp4
    

结果如下:

    [STREAM]
	index=0
	codec_name=h264
	codec_long_name=H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10
	profile=Baseline
	codec_type=video
	codec_tag_string=avc1
	codec_tag=0x31637661
	width=1920
	height=1080
	coded_width=1920
	coded_height=1080
	closed_captions=0
	film_grain=0
	has_b_frames=0
	sample_aspect_ratio=1:1
	display_aspect_ratio=16:9
	pix_fmt=yuvj420p
	level=40
	color_range=pc
	color_space=smpte170m
	color_transfer=smpte170m
	color_primaries=smpte170m
	chroma_location=left
	field_order=progressive
	refs=1
	is_avc=true
	nal_length_size=4
	id=0x1
	r_frame_rate=30/1
	avg_frame_rate=40230000/1339297
	time_base=1/90000
	start_pts=0
	start_time=0.000000
	duration_ts=1339297
	duration=14.881078
	bit_rate=20028287
	max_bit_rate=N/A
	bits_per_raw_sample=8
	nb_frames=447
	nb_read_frames=N/A
	nb_read_packets=N/A
	extradata_size=33
	DISPOSITION:default=1
	DISPOSITION:dub=0
	DISPOSITION:original=0
	DISPOSITION:comment=0
	DISPOSITION:lyrics=0
	DISPOSITION:karaoke=0
	DISPOSITION:forced=0
	DISPOSITION:hearing_impaired=0
	DISPOSITION:visual_impaired=0
	DISPOSITION:clean_effects=0
	DISPOSITION:attached_pic=0
	DISPOSITION:timed_thumbnails=0
	DISPOSITION:captions=0
	DISPOSITION:descriptions=0
	DISPOSITION:metadata=0
	DISPOSITION:dependent=0
	DISPOSITION:still_image=0
	TAG:creation_time=2021-02-27T07:35:19.000000Z
	TAG:language=eng
	TAG:handler_name=VideoHandle
	TAG:vendor_id=[0][0][0][0]
	[SIDE_DATA]
	side_data_type=Display Matrix
	displaymatrix=
	00000000:            0       65536           0
	00000001:       -65536           0           0
	00000002:            0           0  1073741824
	rotation=-90
	[/SIDE_DATA]
	[/STREAM]
	[STREAM]
	index=1
	codec_name=aac
	codec_long_name=AAC (Advanced Audio Coding)
	profile=LC
	codec_type=audio
	codec_tag_string=mp4a
	codec_tag=0x6134706d
	sample_fmt=fltp
	sample_rate=48000
	channels=2
	channel_layout=stereo
	bits_per_sample=0
	initial_padding=0
	id=0x2
	r_frame_rate=0/0
	avg_frame_rate=0/0
	time_base=1/48000
	start_pts=0
	start_time=0.000000
	duration_ts=715765
	duration=14.911771
	bit_rate=96041
	max_bit_rate=N/A
	bits_per_raw_sample=N/A
	nb_frames=699
	nb_read_frames=N/A
	nb_read_packets=N/A
	extradata_size=2
	DISPOSITION:default=1
	DISPOSITION:dub=0
	DISPOSITION:original=0
	DISPOSITION:comment=0
	DISPOSITION:lyrics=0
	DISPOSITION:karaoke=0
	DISPOSITION:forced=0
	DISPOSITION:hearing_impaired=0
	DISPOSITION:visual_impaired=0
	DISPOSITION:clean_effects=0
	DISPOSITION:attached_pic=0
	DISPOSITION:timed_thumbnails=0
	DISPOSITION:captions=0
	DISPOSITION:descriptions=0
	DISPOSITION:metadata=0
	DISPOSITION:dependent=0
	DISPOSITION:still_image=0
	TAG:creation_time=2021-02-27T07:35:19.000000Z
	TAG:language=eng
	TAG:handler_name=SoundHandle
	TAG:vendor_id=[0][0][0][0]
	[/STREAM]
	[FORMAT]
	filename=input.mp4
	nb_streams=2
	nb_programs=0
	format_name=mov,mp4,m4a,3gp,3g2,mj2
	format_long_name=QuickTime / MOV
	start_time=0.000000
	duration=14.911771
	size=37839486
	bit_rate=20300465
	probe_score=100
	TAG:major_brand=mp42
	TAG:minor_version=0
	TAG:compatible_brands=isommp42
	TAG:creation_time=2021-02-27T07:35:19.000000Z
	TAG:com.android.version=8.1.0
	[/FORMAT]

在FFmpeg工具中,ffprobe是一种用于分析媒体文件的命令行工具,参数解释如下:

profile
在H.264/AVC (Advanced Video Coding)视频编码标准中,有以下四种预定义的profile:

  1. Baseline Profile:这是最基本的profile,它定义了一组基本功能,包括单个参考帧、CAVLC (Context-adaptive Variable Length Coding)、8x8变换等。Baseline Profile可用于低比特率、低延迟和低复杂度的应用。

  2. Main Profile:Main Profile扩展了Baseline Profile并增加了一些高级功能,例如B帧、8x8和4x4变换以及熵编码模式自适应技术。Main Profile可以提供更好的视频质量和压缩率,适合于中等比特率的应用。

  3. Extended Profile:Extended Profile扩展了Main Profile,并添加了更多的高级功能,例如8x8和4x4变换、支持更高分辨率、更高的比特率和更丰富的颜色空间。Extended Profile适用于高清视频和广电级别的视频应用。

  4. High Profile:High Profile包含所有的H.264/AVC高级功能,例如无损编码、多层编码、亮度调整等。High Profile通常用于专业级别的视频应用,例如数字电视广播、蓝光光盘和高清视频流媒体服务。
    除了上述四个预定义的profile之外,H.264/AVC还支持用户定义的profile,以根据具体应用需求自定义编解码器的参数设置。

编码level
在H.264/AVC视频编码标准中,Level指的是视频编码器的限制条件,例如最大分辨率、最高比特率以及其他一些技术参数。这些限制条件与profile不同,因为它们通常受到实时处理硬件设备的限制。以下是H.264/AVC定义的level:

  1. Level 1:支持最大352x288像素分辨率和1.5 Mbps的解码速度。

  2. Level 1b:类似于Level 1,但要求使用Baseline Profile。

  3. Level 1.1:支持最大352x480像素分辨率和12 Mbps的解码速度。

  4. Level 1.2:支持最大720x480像素分辨率和30 Mbps的解码速度。

  5. Level 1.3:支持最大1280x720像素分辨率和60 Mbps的解码速度。

  6. Level 2:支持最大1920x1080像素分辨率和60 Mbps的解码速度。

  7. Level 2.1:支持最大1920x1080像素分辨率和120 Mbps的解码速度。

  8. Level 2.2:支持最大1920x1080像素分辨率和120 Mbps的解码速度,并要求使用High Profile。

  9. Level 3:支持最大1920x1080像素分辨率和240 Mbps的解码速度。

  10. Level 3.1:支持最大1920x1080像素分辨率和240 Mbps的解码速度,并要求使用High Profile。

  11. Level 3.2:支持最大1920x1080像素分辨率和240 Mbps的解码速度,并要求使用High Profile和4:2:2色度采样。

  12. Level 4:支持最大2048x2048像素分辨率和480 Mbps的解码速度。

  13. Level 4.1:支持最大2048x2048像素分辨率和1 Gbps的解码速度。

  14. Level 4.2:支持最大2048x2048像素分辨率和1 Gbps的解码速度,并要求使用High Profile和10位色深。

  15. Level 5:支持最大4096x2304像素分辨率和1 Gbps的解码速度。

  16. Level 5.1:支持最大4096x2304像素分辨率和2 Gbps的解码速度。

  17. Level 5.2:支持最大4096x2304像素分辨率和4 Gbps的解码速度。

需要注意的是,level越高,支持的分辨率和比特率就越高,因此所需的处理能力和存储空间也越大。同时,实际可用的level还受到编码器和解码器的硬件限制。

DISPOSITION
DISPOSITION是指多媒体文件流(例如音频、视频)在其容器中的位置以及是否被默认启用的标志。它告诉我们这个特定的流的角色是什么,并且是否应该被自动启用。
以下是一些常见的DISPOSITION标志:
default:表示该流是默认启用的。
dub:表示这个流是一个双语版本的语言流。
original:表示这个流是原始的无损版本。
comment:表示这个流是注释版本的。
lyrics:表示这个流包含歌词信息。
通过查看DISPOSITION标志,我们可以确定每个音频或视频流的作用和启用状态,这对于解析和编辑多媒体文件非常有用。

除了命令行界面之外,FFmpeg 还提供了许多开发库,例如 libavcodec 和 libavformat 等,这些库可以帮助您将 FFmpeg 集成到自己的应用程序中。

总之,FFmpeg 是一款强大的多媒体处理工具,可以让您对音视频文件进行各种操作。如果您需要在自己的应用程序中使用 FFmpeg,可以使用其提供的开发库

二,FFmpeg编译

要想把FFmpeg运行在移动设备上,就需要使用到交叉编译,交叉编译是指在一台计算机上,使用一个编译器将程序或库编译成可以在不同架构的计算机上运行的二进制文件。通常情况下,交叉编译是在开发人员的计算机上完成的,然后将生成的可执行文件或库文件拷贝到目标设备上运行。

2.1 常见的错误

FFmpeg 交叉编译一直依赖都是老大难的问题,新手往往要折腾好几天才能成功编译
为 Android 编译 FFmpeg 时,常见的错误如下:

C compiler test failed.

出现这个问题一般有两种情况

  • NDK版本不对,NDK 的版本非常重要,因为不同版本的 NDK 可能与不同版本的 FFmpeg 不兼容。在下载 NDK 时,请确保使用与您要构建的 FFmpeg 版本相匹配的 NDK 版本。
    -NDK路径写错了
ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol
  1. 在–extra-ldflags 加入-fPIC
  2. 在链接的时候加入-Bsymbolic,慎重使用
  3. 注意链接的时候的顺序 比如libavutil是每个库都需要的,那么就要放在第一个链接
duplicate symbol

–enable-shared --enable-static 同时打开,在合并so的时候会出现重复的方法

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol “cos”

在编译以及在把.a文件链接到SO的时候没有把m,z,android库链接上

2.2 编译步骤

2.2.1 下载Android SDK

我们一般通过Android Studio下载SDK,官网地址:https://developer.android.com/?hl=zh-cn
运行之后打开设置,找到Android SDK
SDK Platforms我们一般下最新正式版本就好了,这里最新正式版本为33(Android 13),如图所示:
Android音视频开发实战01-环境搭建

SDK Tools一般都需要选择上,如图所示:
Android音视频开发实战01-环境搭建

2.2.2下载Android NDK

NDK 我们一般去官网下载对应的版本,地址如下:https://developer.android.com/ndk/downloads?hl=zh-cn
这个地方我们使用r25c就好了

2.2.3 安装必备的软件

MAC需要安装Xcode Command Line Tools 地址:https://developer.apple.com/download/all/?q=xcode
Linux需要安装curl git yasm

2.2.4 Start build

A 基础build

需要下载一个成品脚本,地址:https://github.com/Javernaut/ffmpeg-android-maker 这个脚本我亲测在Mac Linux都可以成功Build但是在实际开发中还需要做修改(修改完的build脚本工程详见这里:https://github.com/bookzhan/ffmpeg-android-build)
cd 到脚本目录,指定SDK目录,如下:

export ANDROID_SDK_HOME='/Users/guaishou/Library/Android/sdk'
export ANDROID_NDK_HOME='/Users/guaishou/Library/Android/sdk/ndk/AndroidNDK9519653.app/Contents/NDK'

执行脚本:
./ffmpeg-android-maker.sh
脚本的执行时间比较长,安静的等待脚本执行完成,脚本执行完成之后,生成的SO在output目录下,如图所示:
Android音视频开发实战01-环境搭建
Android音视频开发实战01-环境搭建

可以看到,这个脚本构建是不能直接用于开发,主要有以下问题:

  1. 可以看到这个脚本构建的结构有多个SO,这在开发中显得不是很优雅,我们需要做的是把这些SO合并成一个SO
  2. 构建出来的SO很大,包大小遭不住
  3. 在构建的时候我们常用的库libmp3lame,libx264(libx264是GPL协议的,要注意安全合规)没有编译进去
B 多个SO合并成一个SO

要想把多个SO合并成一个SO,那么就需要把FFmpeg编译成静态库,有关静态库与动态库的解释如下:

静态库是编译时链接到应用程序中的,它将库文件的全部内容复制到应用程序中。这意味着在运行时,所有的库函数都已经存在于应用程序中,不需要再进行加载。由于静态库已经被完全链接到应用程序中,因此它们的大小通常比动态库更大,但执行速度更快,因为没有额外的加载和解析操作。同时,使用静态库可以避免版本冲突和依赖问题,因为每个应用程序都会使用自己的一份库文件副本。

相反,动态库是在运行时动态加载的,只有当需要使用库函数时才会被加载到内存中。由于动态库仅在需要时才加载,因此它们的大小通常比静态库小,但执行速度可能稍慢,因为需要进行额外的加载和解析操作。另外,由于多个应用程序可以共享相同的库文件,因此动态库提供了更好的资源利用率。

总之,静态库是在编译时链接到应用程序中的,而动态库是在运行时动态加载的。静态库的优点是执行速度更快,依赖和版本控制更容易,而动态库的优点是更小的库文件大小和更好的资源利用率。可以用文件后缀做简单的区分:静态库文件后缀为.a, 动态库文件后缀为.so

步骤如下:

  1. 首选我们需要修改ffmpeg-android-maker-master/scripts/ffmpeg/build.sh 文件把–disable-static 改成–enable-static 再次build 的时候就可以在ffmpeg-android-maker-master/build目录下看到.a文件已经生成了(在调试脚本的时候可以在后面加-abis=arm64-v8a 参数,只是构建64位的库,这个构建时间就会短很多)
  2. 合并.a文件
${FAM_CC} -shared -o ${STATIC_LIB_DIR}/${OUTPUT_SO_NAME} 
  -Wl,--whole-archive,-Bsymbolic 
  ${STATIC_LIB_DIR}/libavutil.a 
  ${STATIC_LIB_DIR}/libavcodec.a 
  ${STATIC_LIB_DIR}/libavfilter.a 
  ${STATIC_LIB_DIR}/libswresample.a 
  ${STATIC_LIB_DIR}/libavformat.a 
  ${STATIC_LIB_DIR}/libswscale.a 
  -Wl,--no-whole-archive
${FAM_STRIP} --strip-unneeded ${STATIC_LIB_DIR}/${OUTPUT_SO_NAME}
#FAM_CC STATIC_LIB_DIR FAM_STRIP 都是export-build-variables.sh生成的环境变量,FAM_CC其实就是clang(aarch64-linux-android21-clang系列)  --whole-archive指的是把全部代码都写入到so中,不管是否使用到了
#FAM_STRIP --strip-unneeded 标识剔除debug信息,减少so大小,其实Android在打包的时候会对所有so执行这个操作
#-Bsymbolic是解决ld: error: relocation R_AARCH64_ADD_ABS_LO12_NC cannot be used against symbol 'ff_tx_tab_32_float'; recompile with -fPIC错误的关键,但是不要轻易使用,尽量去解决编译问题,否则在运行的时候会出问题
#在后面写入-Wl,--no-whole-archive是为了解决重复符号表的问题,表示只对接下来的目标文件执行局部符号绑定。这样,就可以将不必要的符号从最终的符号表中删除,减小输出文件的大小,并避免一些潜在的问题。
#需要主要这个合并的顺序!!!
C FFmpeg裁剪

so合并的文件的问题解决了,但是合并的so会显得很大,这是因为默认是把FFmpeg的代码全部编译了,我们需要做一些裁剪,去掉我们不用的代码
在不知道有那些配置可以选择的时候,可以在FFmpeg源码下执行./configure 这样默认会把所有支持的配置都列出来,然后依次去掉不必要的配置就好
最终scripts/ffmpeg/build.sh文件的样式如下:

#!/usr/bin/env bash

case $ANDROID_ABI in
  x86)
    # Disabling assembler optimizations, because they have text relocations
    EXTRA_BUILD_CONFIGURATION_FLAGS="$EXTRA_BUILD_CONFIGURATION_FLAGS --disable-neon --disable-asm"
    ;;
  x86_64)
    EXTRA_BUILD_CONFIGURATION_FLAGS="$EXTRA_BUILD_CONFIGURATION_FLAGS --disable-neon --disable-asm"
    ;;
  armeabi-v7a)
    EXTRA_BUILD_CONFIGURATION_FLAGS="$EXTRA_BUILD_CONFIGURATION_FLAGS --enable-neon --disable-asm --enable-inline-asm"
    ;;
  arm64-v8a)
    EXTRA_BUILD_CONFIGURATION_FLAGS="$EXTRA_BUILD_CONFIGURATION_FLAGS --enable-neon --disable-asm --enable-inline-asm"
    ;;
esac

if [ "$FFMPEG_GPL_ENABLED" = true ] ; then
    EXTRA_BUILD_CONFIGURATION_FLAGS="$EXTRA_BUILD_CONFIGURATION_FLAGS --enable-gpl"
fi

# Preparing flags for enabling requested libraries
ADDITIONAL_COMPONENTS=
for LIBARY_NAME in ${FFMPEG_EXTERNAL_LIBRARIES[@]}
do
  ADDITIONAL_COMPONENTS+=" --enable-$LIBARY_NAME"
  case $LIBARY_NAME in
        libx264)
          ADDITIONAL_COMPONENTS+=" --enable-encoder=libx264"
          ;;
        libmp3lame)
          ADDITIONAL_COMPONENTS+=" --enable-decoder=mp3"
          ;;
        *)
          echo "Unknown ADDITIONAL_COMPONENTS LIBARY_NAME: $LIBARY_NAME"
          ;;
  esac
done

echo ADDITIONAL_COMPONENTS=${ADDITIONAL_COMPONENTS}

# Referencing dependencies without pkgconfig
DEP_CFLAGS="-I${BUILD_DIR_EXTERNAL}/${ANDROID_ABI}/include"
DEP_LD_FLAGS="-L${BUILD_DIR_EXTERNAL}/${ANDROID_ABI}/lib $FFMPEG_EXTRA_LD_FLAGS"

./configure 
  --prefix=${BUILD_DIR_FFMPEG}/${ANDROID_ABI} 
  --enable-cross-compile 
  --enable-small 
  --target-os=android 
  --arch=${TARGET_TRIPLE_MACHINE_ARCH} 
  --sysroot=${SYSROOT_PATH} 
  --cc=${FAM_CC} 
  --cxx=${FAM_CXX} 
  --ld=${FAM_LD} 
  --ar=${FAM_AR} 
  --as=${FAM_CC} 
  --nm=${FAM_NM} 
  --ranlib=${FAM_RANLIB} 
  --strip=${FAM_STRIP} 
  --extra-cflags="-O3 -fPIC -lm -lz -landroid $DEP_CFLAGS" 
  --extra-ldflags="$DEP_LD_FLAGS" 
  --disable-shared 
  --enable-static 
  --disable-vulkan 
  --disable-symver 
  --disable-doc 
  --disable-htmlpages 
  --disable-manpages 
  --disable-podpages 
  --disable-txtpages 
  --disable-ffplay 
  --disable-ffmpeg 
  --disable-ffprobe 
  --disable-avdevice 
  --disable-bsfs 
  --disable-devices 
  --disable-protocols 
  --disable-postproc 
  --enable-protocol=file 
  --enable-protocol=concat 
  --disable-parsers 
  --disable-demuxers 
  --enable-demuxer=mov 
  --enable-demuxer=mp3 
  --enable-demuxer=image2 
  --enable-demuxer=gif 
  --enable-demuxer=wav 
  --enable-demuxer=asf 
  --enable-demuxer=flv 
  --enable-demuxer=avi 
  --enable-demuxer=webm_dash_manifest 
  --enable-demuxer=matroska 
  --enable-demuxer=mpegts 
  --disable-decoders 
  --enable-decoder=aac 
  --enable-decoder=png 
  --enable-decoder=h264 
  --enable-decoder=mp3 
  --enable-decoder=mjpeg 
  --enable-decoder=mpeg4 
  --enable-decoder=gif 
  --enable-decoder=pcm_s16le 
  --enable-decoder=hevc 
  --enable-decoder=msmpeg4v1 
  --enable-decoder=msmpeg4v2 
  --enable-decoder=msmpeg4v3 
  --enable-decoder=wmav1 
  --enable-decoder=wmav2 
  --enable-decoder=flv 
  --enable-decoder=adpcm_swf 
  --enable-decoder=ac3 
  --enable-decoder=vp8 
  --enable-decoder=vorbis 
  --enable-decoder=mpeg2video 
  --enable-decoder=mp2 
  --enable-decoder=indeo4 
  --enable-decoder=amrnb 
  --disable-muxers 
  --enable-muxer=mov 
  --enable-muxer=mp4 
  --enable-muxer=image2 
  --enable-muxer=mp3 
  --enable-muxer=ipod 
  --enable-muxer=gif 
  --disable-encoders 
  --enable-encoder=aac 
  --enable-encoder=png 
  --enable-encoder=mjpeg 
  --enable-encoder=gif 
  --enable-swscale 
  --disable-filters 
  --enable-filter=crop 
  --enable-filter=scale 
  --enable-filter=afade 
  --enable-filter=atempo 
  --enable-filter=copy 
  --enable-filter=aformat 
  --enable-filter=overlay 
  --enable-filter=vflip 
  --enable-filter=hflip 
  --enable-filter=transpose 
  --enable-filter=volume 
  --enable-filter=rotate 
  --enable-filter=apad 
  --enable-filter=amerge 
  --enable-filter=aresample 
  --enable-filter=setpts 
  --enable-filter=fps 
  --enable-filter=palettegen 
  --enable-filter=paletteuse 
  --enable-filter=trim 
  --enable-filter=null 
  --enable-filter=overlay 
  --enable-filter=format 
  --enable-filter=atrim 
  --enable-filter=split 
  --enable-filter=amix 
  --enable-filter=anull 
  --enable-filter=adelay 
  --enable-zlib 
  --enable-jni 
  --enable-nonfree 
  --enable-mediacodec 
  --enable-version3 
  --pkg-config=${PKG_CONFIG_EXECUTABLE} 
  ${EXTRA_BUILD_CONFIGURATION_FLAGS} 
  ${ADDITIONAL_COMPONENTS} || exit 1

${MAKE_EXECUTABLE} clean
${MAKE_EXECUTABLE} -j${HOST_NPROC}
${MAKE_EXECUTABLE} install


export STATIC_LIB_DIR=${BUILD_DIR_FFMPEG}/${ANDROID_ABI}/lib
export EXTERNAL_LIB_DIR=${INSTALL_DIR}/lib
echo STATIC_LIB_DIR=${STATIC_LIB_DIR}
echo EXTERNAL_LIB_DIR=${EXTERNAL_LIB_DIR}
echo FAM_CC=${FAM_CC}

EXTERNAL_STATIC_LIB_PATH=""
for LIBARY_NAME in ${FFMPEG_EXTERNAL_LIBRARIES[@]}
do
  EXTERNAL_STATIC_LIB_PATH+="${EXTERNAL_LIB_DIR}/${LIBARY_NAME}.a "
done
echo EXTERNAL_STATIC_LIB_PATH=${EXTERNAL_STATIC_LIB_PATH}

${FAM_CC} -shared -o ${STATIC_LIB_DIR}/${OUTPUT_SO_NAME} 
  -Wl,--whole-archive 
  ${EXTERNAL_STATIC_LIB_PATH}
  ${STATIC_LIB_DIR}/libavutil.a 
  ${STATIC_LIB_DIR}/libavcodec.a 
  ${STATIC_LIB_DIR}/libavfilter.a 
  ${STATIC_LIB_DIR}/libswresample.a 
  ${STATIC_LIB_DIR}/libavformat.a 
  ${STATIC_LIB_DIR}/libswscale.a 
  -Wl,--no-whole-archive -lm -lz -landroid

OUTPUT_CONFIG_HEADERS_DIR=${OUTPUT_DIR}/include/${ANDROID_ABI}
mkdir -p ${OUTPUT_CONFIG_HEADERS_DIR}
cp config.h ${OUTPUT_CONFIG_HEADERS_DIR}/config.h

${FAM_STRIP} --strip-unneeded ${STATIC_LIB_DIR}/${OUTPUT_SO_NAME}

脚本说明

  1. 从Android 5.0开始,默认情况下所有设备都支持NEON指令集,在编译的时候尽量打开NEON,这样性能会更好
  2. asm指的是汇编优化,这样代码执行性能会更好,但是在FFmpeg上编译一般使用inline-asm(内联汇编),因为而普通汇编需要单独编写成汇编代码文件,增加了额外的编译链接过程,很容易链接失败,而且内联汇编执行效率更高
D 添加常用的第三方库

在音视频开发中,我们一般会使用一些优秀的第三方库,如对Mp3支持的库libmp3lame,H264软编库libx264(在移动端我们一般不使用H265进行软编,主要原因在于H265软编对性能消耗要比H264大得多,
在执行构建命令的时候启用./ffmpeg-android-maker.sh --enable-libmp3lame --enable-libx264就好了,最后在链接的时候把对应的.a文件链接在一起就好了,如上完整的scripts/ffmpeg/build.sh文件

3.Android 开发环境搭建

3.1 新建工程

新建工程,选择Native c++,然后一直点Next就好了
Android音视频开发实战01-环境搭建

Android音视频开发实战01-环境搭建

指定ndk版本
ndk版本一般要在各个开发者直接统一,写法为,在build.gradle->android下面写,如下
ndkVersion ‘25.2.9519653’

3.2 cmake简介

其中cpp目录下的CMakeLists.txt文件是构建so的关键文件,俗称cmake文件,如下:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.18.1)

# Declares and names the project.

project("ffmpegtest")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        ffmpegtest

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        common/BZLogUtil.cpp
        native-lib.cpp)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
        ffmpegtest

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

3.2.1 cmake基本语法

cmake的基本语法和C类似,都是通过方法名()的方式来使用的如:

project('ffmpegtest') #指定了工程的名称,非必选

条件语句

if(condition)
  # do something
elseif(condition2)
  # do something else
else()
  # do another thing
endif()

循环语句

foreach(loop_var RANGE start end [step])
  # do something
endforeach()

while(condition)
  # do something
endwhile()

函数

函数由命令组成,通过 function(func_name [args...]) 定义,可以在任意位置调用。

function(hello_world)
  message("Hello World!")
endfunction()

hello_world()

3.2.2 cmake变量的使用与定义

变量在 CMake 中以 $ 开头,有以下几种类型:

  • 内置变量:如 ${CMAKE_CURRENT_LIST_DIR} 表示当前 CMake 文件所在的目录。
  • 环境变量:如 $ENV{PATH} 表示系统环境变量 PATH 的值。
  • 用户定义变量:如 set(VAR_NAME value),通过 ${VAR_NAME} 引用。

3.2.3 常见的用法

其实上面的很少用,我们来看一个稍微复杂一点的,工作中掌握这么多就够用了,如下:

	# For more information about using CMake with Android Studio, read the
	# documentation: https://d.android.com/studio/projects/add-native-code.html
	
	# Sets the minimum version of CMake required to build the native library.
	
	#这个是必选的
	cmake_minimum_required(VERSION 3.4.1)
	
	# Creates and names a library, sets it as either STATIC
	# or SHARED, and provides the relative paths to its source code.
	# You can define multiple libraries, and CMake builds them for you.
	# Gradle automatically packages shared libraries with your APK.
	#添加宏
	if (${CMAKE_BUILD_TYPE} STREQUAL Debug)
	    add_definitions(-DDEBUG_TYPE)
	endif()
	
	#添加整个目录的源码作为编译对象
	aux_source_directory(./glprogram DIR_GLPROGRAM_SRCS)
	aux_source_directory(./mediaedit DIR_MEDIAEDIT_SRCS)
	aux_source_directory(./player DIR_PLAYER_SRCS)
	aux_source_directory(./soundtouch DIR_SOUNDTOUCH_SRCS)
	
	add_library( # Sets the name of the library.
	        bzmedia
	
	        # Sets the library as a shared library.
	        SHARED
	
	        # Provides a relative path to your source file(s).
	        ./jni/android_bzmedia.cpp
	        ./jni/ffmpeg_base_info.cpp
	        ./jni/OnActionListener.cpp
	        ./jni/ffmpeg_audio_player.cpp
	        ./jni/video_edit_sdk.cpp
	        ./jni/jni_VideoFrameGetter.cpp
	        ./jni/video_recorder_jni.cpp
	        ./jni/android_gl_program.cpp
	        ./jni/jni_BZRenderEngine.cpp;
	        ./jni/video_player_jni.cpp;
	        ./jni/soundtouch_jni.cpp;
	
	
	        ./common/BZLogUtil.cpp
	        ./common/bz_time.cpp
	        ./common/JvmManager.cpp
	        ./common/GLUtil.cpp
	        ./common/PngReader.cpp
	
	        ${DIR_MEDIAEDIT_SRCS}
	
	        ./glutils/FrameBufferUtils.cpp;
	        ./glutils/GLImageTextureUtil.cpp;
	        ./glutils/GLMatrixUtils.cpp;
	        ./glutils/MatrixVaryTools.cpp;
	        ./glutils/CropTextureUtil.cpp;
	        ./glutils/BZRenderEngine.cpp;
	        ./glutils/VideoTextureManger.cpp;
	        ./glutils/EGLContextUtil.cpp;
	        ./glutils/TextureConvertYUVUtil.cpp;
	        ./glutils/TextureUtil.cpp;
	
	        ${DIR_GLPROGRAM_SRCS}
	        ${DIR_PLAYER_SRCS}
	        ${DIR_SOUNDTOUCH_SRCS}
	
	        ./recorder/VideoRecorder.cpp
	
	        ./utils/AdjustConfigUtil.cpp
	        ./utils/PcmDeque.cpp
	
	        ./permission/base64.c
	        ./permission/PermissionUtil.cpp
	        )
	
	# Searches for a specified prebuilt library and stores the path as a
	# variable. Because CMake includes system libraries in the search path by
	# default, you only need to specify the name of the public NDK library
	# you want to add. CMake verifies that the library exists before
	# completing its build.
	configure_file(${Project_SOURCE_DIR}/lib/${ANDROID_ABI}/libbzffmpeg.so ${Project_BINARY_DIR}/libbzffmpeg.so COPYONLY)
	configure_file(${Project_SOURCE_DIR}/lib/${ANDROID_ABI}/libbzffmpegcmd.so ${Project_BINARY_DIR}/libbzffmpegcmd.so COPYONLY)
	
	#ffmpeg
	add_library(bzffmpeg-lib
	        SHARED
	        IMPORTED)
	set_target_properties(bzffmpeg-lib
	        PROPERTIES IMPORTED_LOCATION
	        libbzffmpeg.so)
	#ffmpegcmd
	add_library(bzffmpegcmd-lib
	        SHARED
	        IMPORTED)
	set_target_properties(bzffmpegcmd-lib
	        PROPERTIES IMPORTED_LOCATION
	        libbzffmpegcmd.so)
	add_library(yuv-lib
	        STATIC
	        IMPORTED)
	set_target_properties(yuv-lib
	        PROPERTIES IMPORTED_LOCATION
	        ${Project_SOURCE_DIR}/lib/${ANDROID_ABI}/libyuv_static.a)
	
	#添加系统库
	find_library(log-lib log)
	find_library(m-lib m)
	find_library(z-lib z)
	find_library(android-lib android)
	find_library(EGL-lib EGL)
	find_library(jnigraphics-lib jnigraphics)
	find_library(GLES-lib GLESv2)
	find_library(OpenSLES-lib OpenSLES)
	
	
	include_directories(
	        ./
	        ./include/
	        ./common
	        ./bean
	        ./cmdutilt
	        ./include
	        ./utils
	        ./permission
	        ./include/libavcodec
	        ./include/libavdevice
	        ./include/libavfilter
	        ./include/libavformat
	        ./include/libavutil
	        ./include/libpostproc
	        ./include/libswresample
	        ./include/libswscale
	        ./include/libyuv
	        ./include/soundtouch
	)
	# Specifies libraries CMake should link to your target library. You
	# can link multiple libraries, such as libraries you define in this
	# build script, prebuilt third-party libraries, or system libraries.
	
	#把所有库连接到最终生成的库中
	target_link_libraries( # Specifies the target library.
	        bzmedia
	
	        # Links the target library to the log library
	        # included in the NDK.
	        bzffmpeg-lib bzffmpegcmd-lib yuv-lib ${log-lib} ${m-lib} ${z-lib} ${android-lib} ${EGL-lib} ${GLES-lib} ${jnigraphics-lib} ${OpenSLES-lib})

3.2.4 配置支持的CPU架构

默认情况下SO会生成armeabi-v7a,arm64-v8a,x86,x86_64,如果我们只想生成指定的SO,那么在build.gradle文件android->defaultConfig下配置ndk来过滤就好了,如下:

	apply plugin: 'com.android.library'
	apply from: '../config.gradle'
	
	android {
	    compileSdkVersion project.ext.compileSdkVersion
	    defaultConfig {
	        minSdkVersion project.ext.minSdkVersion
	        targetSdkVersion project.ext.targetSdkVersion
	
	        ndk {
	            abiFilters 'armeabi-v7a', 'arm64-v8a'
	        }
	    }
	}

3.3验证FFmpeg编译是否成功

测试工程:https://github.com/bookzhan/ffmpegtest
成功标准:文章来源地址https://www.toymoban.com/news/detail-498827.html

  1. 执行System.loadLibrary没有任何报错
  2. 能够成功获取到编码器,解码器,avformat信息,验证代码如下:
	#include <common/BZLogUtil.h>
	#include <cstring>
	#include "ffmpeg_base_info.h"
	
	
	void printFFmpegBaseInfo() {
	    char buffer[4096];
	    getFFmpegConfigure(buffer);
	    BZLogUtil::logD(buffer);
	
	    memset(buffer, 0, 4096);
	    getFFmpegSupportProtocol(buffer);
	    BZLogUtil::logD(buffer);
	
	    memset(buffer, 0, 4096);
	    getFFmpegSupportAVFormat(buffer);
	    BZLogUtil::logD(buffer);
	
	    memset(buffer, 0, 4096);
	    getFFmpegSupportAVCodec(buffer);
	    BZLogUtil::logD(buffer);
	
	    memset(buffer, 0, 4096);
	    getFFmpegSupportAVFilter(buffer);
	    BZLogUtil::logD(buffer);
	
	    testLib();
	}
	
	int getFFmpegConfigure(char *info) {
	    return sprintf(info, "%sn", avcodec_configuration());;
	}
	
	int getFFmpegSupportProtocol(char *info) {
	    struct URLProtocol *pup = nullptr;
	    int ret = 0;
	    //Input
	    struct URLProtocol **p_temp = &pup;
	    avio_enum_protocols((void **) p_temp, 0);
	    while ((*p_temp) != nullptr) {
	        ret = sprintf(info, "%s[In ][%10s]n", info, avio_enum_protocols((void **) p_temp, 0));
	    }
	    pup = nullptr;
	    //Output
	    avio_enum_protocols((void **) p_temp, 1);
	    while ((*p_temp) != nullptr) {
	        ret = sprintf(info, "%s[Out][%10s]n", info, avio_enum_protocols((void **) p_temp, 1));
	    }
	    return ret;
	}
	
	int getFFmpegSupportAVFormat(char *info) {
	    int ret = 0;
	    void *opaque_in = nullptr;
	    void *opaque_out = nullptr;
	    const AVInputFormat *if_temp = av_demuxer_iterate(&opaque_in);
	    const AVOutputFormat *of_temp = av_muxer_iterate(&opaque_out);
	    //Input
	    while (if_temp != nullptr) {
	        ret = sprintf(info, "%s[In ][%10s]n", info, if_temp->name);
	        if_temp = av_demuxer_iterate(&opaque_in);;
	    }
	    //Output
	    while (of_temp != nullptr) {
	        ret = sprintf(info, "%s[Out][%10s]n", info, of_temp->name);
	        of_temp = av_muxer_iterate(&opaque_out);
	    }
	    return ret;
	}
	
	int getFFmpegSupportAVCodec(char *info) {
	    int ret = 0;
	    void *opaque = nullptr;
	    const AVCodec *c_temp = av_codec_iterate(&opaque);
	
	    while (c_temp != nullptr) {
	        if (av_codec_is_decoder(c_temp) != 0) {
	            ret = sprintf(info, "%s[Dec]", info);
	        } else {
	            ret = sprintf(info, "%s[Enc]", info);
	        }
	        switch (c_temp->type) {
	            case AVMEDIA_TYPE_VIDEO:
	                sprintf(info, "%s[Video]", info);
	                break;
	            case AVMEDIA_TYPE_AUDIO:
	                sprintf(info, "%s[Audio]", info);
	                break;
	            default:
	                sprintf(info, "%s[Other]", info);
	                break;
	        }
	        ret = sprintf(info, "%s[%10s]n", info, c_temp->name);
	        c_temp = av_codec_iterate(&opaque);
	    }
	    return ret;
	}
	
	int getFFmpegSupportAVFilter(char *info) {
	    int ret = 0;
	    void *opaque = nullptr;
	    const AVFilter *f_temp = av_filter_iterate(&opaque);
	    while (nullptr != f_temp) {
	        ret = sprintf(info, "%s[%10s]n", info, f_temp->name);
	        f_temp = av_filter_iterate(&opaque);
	    }
	    return ret;
	}
	
	int testLib() {
	    const AVCodec *avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
	    if (nullptr == avCodec) {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_H264 未发现");
	    } else {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_H264 正常");
	    }
	    const AVCodec *avCodecAAC = avcodec_find_decoder(AV_CODEC_ID_AAC);
	    if (nullptr == avCodecAAC) {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_AAC 未发现");
	    } else {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_AAC 正常");
	    }
	
	
	    const AVCodec *avCodecEncoder = avcodec_find_encoder(AV_CODEC_ID_H264);
	    if (nullptr == avCodecEncoder) {
	        BZLogUtil::logD("avcodec_find_encoder AV_CODEC_ID_H264 未发现");
	    } else {
	        BZLogUtil::logD("avcodec_find_encoder AV_CODEC_ID_H264 正常");
	    }
	
	
	    const AVCodec *avCodecDecoderMp3 = avcodec_find_decoder(AV_CODEC_ID_MP3);
	    if (nullptr == avCodecDecoderMp3) {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_MP3 未发现");
	    } else {
	        BZLogUtil::logD("avcodec_find_decoder AV_CODEC_ID_MP3 正常");
	    }
	
	
	    const AVCodec *avCodecAACEncoder = avcodec_find_encoder(AV_CODEC_ID_AAC);
	    if (nullptr == avCodecAACEncoder) {
	        BZLogUtil::logD("avcodec_find_encoder AV_CODEC_ID_AAC 未发现");
	    } else {
	        BZLogUtil::logD("avcodec_find_encoder AV_CODEC_ID_AAC 正常");
	    }
	    return 0;
	}

到了这里,关于Android音视频开发实战01-环境搭建的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 音视频开发:音频编码原理+采集+编码实战

    消除冗余信息,压缩量最大,也叫有损压缩 剔除人耳听觉范围外的音频信号20Hz以下和20000Hz以上; 去除被掩蔽的音频信号,信号的遮蔽可以分为频域遮蔽和时域遮蔽; 频域遮蔽效应 屏蔽70分贝以下,20HZ以下,20000HZ以上 屏蔽分贝小,频率小的声音 两个频率相近发出的声音,

    2024年02月05日
    浏览(60)
  • Android 音视频开发—MediaPlayer音频与视频的播放介绍

    Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。 它支持三种不同的媒体来源: 本地资源 内部的URI,比如你可以通过ContentResolver来获取 外部URL(流

    2024年02月10日
    浏览(60)
  • 【原理+实战+视频+源码】抖音,快手大热背后——Android 贴心的音视频学习指南来咯

    (三)C 与 C++之预处理命令与用 typedef 命名已有类型 JNI 模块 JNI 开发之 静态注册与动态注册(一) JNI 开发之方法签名与 Java 通信(二) JNI 开发之局部引用、全局引用和弱全局引用(三) 二、中级进阶篇 学习 Android 平台 OpenGL ES API,了解 OpenGL 开发的基本流程,使用 OpenG

    2024年04月22日
    浏览(74)
  • 【原理+实战+视频+源码】抖音,快手大热背后——Android-贴心的音视频学习指南来咯

    下面将这份文档的内容以图片的形式展现出来,但篇幅有限只能展示部分,如果你需要“高清完整的pdf版”,可以直接点击我的【GitHub】即可免费领取。 一、初级入门篇 初级入门篇主要是接触Android多媒体展示相关的API, 通过单独的列举和使用这些API,对Android音视频处理有

    2024年04月17日
    浏览(46)
  • 【秒懂音视频开发】15_AAC编码实战

    本文将分别通过命令行、编程2种方式进行AAC编码实战,使用的编码库是libfdk_aac。 fdk-aac对输入的PCM数据是有参数要求的,如果参数不对,就会出现以下错误: 采样格式 必须是16位整数PCM。 采样率 支持的采样率有(Hz): 8000、11025、12000、16000、22050、24000、32000 44100、48000、

    2024年02月03日
    浏览(87)
  • 音视频开发实战03-FFmpeg命令行工具移植

    作为一个音视频开发者,在日常工作中经常会使用ffmpeg 命令来做很多事比如转码 ffmpeg -y -i test.mov -g 150 -s 1280x720 -codec libx265 -r 25 test_h265.mp4 ,水平翻转视频: ffmpeg -i src.mp4 -vf hflip -acodec copy -vcodec h264 -b 22000000 out.mp4 ,视频截取: ffmpeg -i input.wmv -ss 00:00:30.0 -c copy -t 00:00:10.0 ou

    2024年02月16日
    浏览(78)
  • 音视频开发系列(6)——全面了解Android MediaFormat

    MediaFormat 是 Android 平台中用于描述音视频格式的类,它提供了许多 API 用于设置和获取音视频的格式信息。以下是 MediaFormat 类的主要 API: 用于创建音频和视频格式的 MediaFormat 对象。需要指定媒体类型(例如 audio/mp4a-latm 或 video/avc)、媒体的采样率、通道数、码率、帧率等信

    2024年02月01日
    浏览(40)
  • Android开源计划-一周开发app,webrtc音视频开发

    题目 – 一周开发app计划 首批参与成员 -小巫 -墨香 -梦痕 -边城刀客 -徐cc 要求 – -每位认领者按照开源规范来做,代码规范和Android开发规范 -每位认领者必须拥有github账号,熟练使用git对代码进来管理 -每个人认领一个功能点或模块 -提出完善的解决方案并提供封装良好的库

    2024年04月08日
    浏览(58)
  • 音视频开发三:Windows环境下FFmpeg编译安装

    FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的 开源计算机程序 。采用LGPL或GPL许可证 。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec。 FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环

    2024年02月04日
    浏览(70)
  • 音视频开发十七:Windows VS 2022 环境配置SDL2开发环境

    SDL(Simple DirectMedia Layer)是 一套开放源代码的跨平台多媒体开发库 , 使用C语言写成 。能够提供视频渲染,音频播放,鼠标/键盘控制等操作。 跨平台是说它对外接供了一套统一的接口,但在内部, 它会根据不同平台调用不同的底层 API库 。如在 Linux 系统下,它会使用 ope

    2024年02月10日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包