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

news2024/12/23 5:09:22

一,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),如图所示:
在这里插入图片描述

SDK Tools一般都需要选择上,如图所示:
在这里插入图片描述

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目录下,如图所示:
在这里插入图片描述
在这里插入图片描述

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

  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就好了
在这里插入图片描述

在这里插入图片描述

指定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
成功标准:

  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;
	}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/654346.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设置论文中的图、表的题注(小记)

参考b站&#xff1a;毕业论文图表如何自动编号/word图表自动编号/图表编号自动更新 其中&#xff0c;更新图表序号 视频使用ctrlp进入打印再退出&#xff0c;也可以使用altf9进行更新 设置论文中的图、表的题注 step1:设置章节1.1 章节设置字体样式&#xff0c;选择标题11.2 章…

中国人民大学与加拿大女王大学金融硕士项目就像一束光,照亮你的春夏秋冬

不要因为看到别人发光&#xff0c;就默认自己的暗淡。每个人都有自己的闪光时刻&#xff0c;或早或晚。只要努力奋进&#xff0c;你也会拥有。针对金融行业计划在职的人员来说&#xff0c;中国人民大学与加拿大女王大学金融硕士项目就像一束光&#xff0c;照亮了我们的春夏秋冬…

赋能全栈软件,开源软件协调,我对英特尔有了全新认知

文章目录 一、前言二、培养开源文化三、现场展区体验四、软硬协同分论坛&#xff0c;和社区开发者共建生态五、快来加入开源社区吧 一、前言 “开源赋能 普惠未来”&#xff0c;2023 年 6 月 11 日到 13 日&#xff0c;我有幸参加了 2023 开放原子全球开源峰会。 “赋能中国软…

数据结构算法刷题(28)回溯组合型和全排列

剪枝技巧&#xff1a; 思路&#xff1a;剪枝的特点是找特定长度的子集。首先确定大框架&#xff0c;当path的长度等于k的时候&#xff0c;就要更新答案并且return。然后在进行path的元素选择&#xff0c;这里采用倒叙&#xff0c;从i到d&#xff08;dk-len(path)&#xff09;倒…

MySQL——视图(VIEW)详解

今天我们一起来学起视图(VIEW)&#xff0c;那么视图是什么呢&#xff1f;视图有什么作用呢&#xff1f;视图一方面可以帮我们使用表的一部分而不是所有的表&#xff0c;另一方面也可以针对不同的用户制定不同的查询视图&#xff01;带着问题一起来寻找答案吧~~~ 1. 常见的数据库…

【实验】语音识别

为学校数字信号处理实验总结和归纳&#xff1b; 语音识别 题目及相关要求在here. 数据预处理 大致步骤&#xff1a; 获取原始音频 检测 分帧 加窗 特征提取 端点检测 端点检测参数指标相对值初始短时能量高门限50初始短时能量低门限10初始短时过零率高门限10初始短时过零率低…

Web网页制作期末复习(1)——HTML5介绍、HTML5的DOCTYPE声明、HTML基本骨架、标题标签、段落 换行、水平线图片图片路径、超链接

目录 HTML5介绍 HTML5的DOCTYPE声明 HTML基本骨架 标题标签 段落、换行、水平线 图片 图片路径* 超链接 HTML5介绍 HTML5是用来描述网页的一种语言&#xff0c;被称为超文本标记语言。用HTML5编写的文件&#xff0c;后缀以.html结尾 HTML是一种标记语言&#xff0c;标…

在提交代码时有哪些注意事项

分享 10 种适合初学者的技术&#xff0c;这些技术将帮助您立即编写更好的代码。因此&#xff0c;如果您准备好将您的编码技能提升到一个新的水平&#xff0c;请继续阅读&#xff01; 1. 从计划开始 编写更好代码的最佳方法之一是从计划开始。在开始编码之前&#xff0c;请花几…

SQL详细处理流程.md

连接器&#xff1a;管理连接&#xff0c;权限验证解析器&#xff1a;词法以及语法分析优化器&#xff1a;生成执行计划&#xff0c;选择合适索引执行器&#xff1a;操作引擎获取结果存储引擎&#xff1a;存储数据&#xff0c;提供读写接口

iterm2 ssh免密码登录

不需要下载其他插件&#xff0c;使用脚本 目录 操作步骤如下&#xff1a; 实际举例如下&#xff1a; 1.编写sh文件 2.编辑sh文件 3.进入iterm2&#xff0c;打开profiles&#xff0c;edit profiles 4.验证 扩展 expect脚本 操作步骤如下&#xff1a; 在你电脑你想编辑的…

阿里云国际站:阿里云服务器安全性如何?有哪些安全措施和防护机制?

阿里云国际站&#xff1a;阿里云服务器安全性如何&#xff1f;有哪些安全措施和防护机制&#xff1f;   阿里云服务器安全性简介   作为全球领先的云计算服务提供商&#xff0c;阿里云始终注重保障用户数据安全。在面对各种网络攻击和安全威胁时&#xff0c;阿里云积极构建…

mysqldump 数据备份

使用实例 使用方法 Usage: mysqldump [OPTIONS] database [tables] OR mysqldump [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3…] OR mysqldump [OPTIONS] --all-databases [OPTIONS] For more options, use mysqldump --help OPTION通常是&#xff1a;-u 用户名 -p …

【计算机网络自顶向下】计算机网络从0到1全篇总结-2023电子科技大学期末考试

相关术语 URI&#xff1a;Uniform Resource Identifier 统一资源标识符&#xff0c;指的是一个资源 URL&#xff1a;Uniform Resource Location 统一资源定位符&#xff0c;URI的子集&#xff0c;用地址定为的方式指定一个资源 URN&#xff1a;Uniform Resource Name 统一资…

基于微信小程序新疆特色产品团购系统设计与实现+第四稿+文档

博主介绍&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 项目名称 基于微信小程序新疆特色产品团购系统设计与实现第四稿文档 视频演示 视频去哪了呢&#xff1f;_哔哩哔哩_bilibili 系统介绍 2.3.1 主要功能描述 在…

常见技术场景

常见技术场景 1.单点登录这块怎么实现的 1.1 概述 单点登录的英文名叫做&#xff1a;Single Sign On&#xff08;简称SSO&#xff09;,只需要登录一次&#xff0c;就可以访问所有信任的应用系统 在以前的时候&#xff0c;一般我们就单系统&#xff0c;所有的功能都在同一个…

我准备蓝桥杯的这一年

我准备蓝桥杯的这一年 文章目录 我准备蓝桥杯的这一年起步和目标确定渐入佳境焦虑疲惫&#xff0c;一天又一天国赛我来力总结 我将我这段 流水账分为四个阶段。谨以此文&#xff0c;祭奠我这一年来的焦虑、白发~ &#xff0c;最终也取得了预期的成绩。不知未来再看此章会作何感…

hadoop基础

FileSystem使用 核心类 org.apache.hadoop.fs.FileSystem 文件系统类 抽象类 //静态方法创建对象 public static FileSystem newInstance(URI uri,Configuration conf,String user) /*参数一 URI 分布式文件系统 HDFS的资源地址 NN地址 hdfs://linux01:8020参数二 Configu…

Unity常见框架探索-ET框架探索

简介 ET框架是类ECS的一个Unity前后端框架 论坛地址为&#xff1a;https://et-framework.cn Git地址为&#xff1a;https://github.com/egametang/ET 预备知识 Unity程序集的使用 接入流程 本文将会以7.2版本进行分析。所以直接clone github上的仓库&#xff0c;将工程导…

1743_MATLAB 2-D绘图小结

全部学习汇总&#xff1a; GreyZhang/g_matlab: MATLAB once used to be my daily tool. After many years when I go back and read my old learning notes I felt maybe I still need it in the future. So, start this repo to keep some of my old learning notes servral …

【网络协议详解】——知识点复习(期末不挂科版)

课本&#xff1a; 目录 &#x1f552; 1. 概述&#x1f558; 1.1 GNS3&#x1f558; 1.2 Wireshark &#x1f552; 2. PPP协议&#x1f552; 3. VLAN技术&#x1f552; 4. STP技术&#x1f552; 5. IPV6&#x1f552; 6. 路由表&#x1f552; 7. RIP协议&#x1f552; 8. OSPF…