前言
之前在Windows下编译过FFmpeg库,但是编译出来的DLL库不仅个数多达7个,而且库总大小高达几十兆。现经过研究,使用shell脚本根据自己的需要编译自己的FFmpeg库,不仅可以编译出来一个自己命名的DLL,而且大小可以减小到10MB以下。
1、下载
1.1、下载ffmpeg
在https://github.com/FFmpeg/FFmpeg/tree/master下载zip压缩包即可,我下载的是n6.1.1版本
1.2、下载Visual Studio
在Microsoft官网下载VS,我下载的是Visual Studio Professional 2022
1.3、下载msys2
在https://www.msys2.org/下载msys2工具,我下载的是msys2-x86_64-20240113版本
2、安装
2.1、安装ffmpeg
将zip解压至D:\workspace\build\FFmpeg-n6.1.1
2.2、安装VS2022
自行选择安装,我不习惯到C盘,选择安装到D盘
2.3、安装msys2
安装到C:\msys64;
将msys2_shell.cmd中rem set MSYS2_PATH_TYPE=inherit改为set MSYS2_PATH_TYPE=inherit,这样可以从中环境%PATH%读取变量;
再把usr\bin中link.exe改为link.exe.bak,这样可以使用VS自带link.exe进行链接。
使用x86 Native Tools Command Prompt for VS 2022输入C:\msys64\msys2_shell.cmd,打开msys2之后输入下面命令更新并安装工具:
pacman -Syu
pacman -Su
pacman -S make
pacman -S diffutils
pacman -S yasm
pacman -S nasm
3、编译
3.1、设置全局环境,包括cl.exe以及ffmpeg头文件
/* 在Path中添加cl.exe所在文件夹路径。若未找到,可以在VS的安装目录下搜索cl.exe */
D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.35.32215\bin\Hostx86\x86;
/* 在系统变量中新建变量INCLUDE,添加ffmpeg、cl.exe包含头文件目录 */
D:\workspace\build\FFmpeg-n6.1.1;
D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.35.32215\include;
D:\Windows Kits\10\Include\10.0.22000.0\shared;
D:\Windows Kits\10\Include\10.0.22000.0\ucrt;
D:\Windows Kits\10\Include\10.0.22000.0\um;
D:\Windows Kits\10\Include\10.0.22000.0\winrt;
/* 在系统变量中新建变量LIB,添加cl.exe库文件目录 */
D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.35.32215\lib\x86;
D:\Windows Kits\10\Lib\10.0.22000.0\ucrt\x86;
D:\Windows Kits\10\Lib\10.0.22000.0\um\x86;
3.2、编写source桥接文件.h和.c
编写桥接头文件myffmpeg.h
#ifndef __MY_FFMPEG_H__
#define __MY_FFMPEG_H__
#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/mathematics.h"
#include "libavutil/time.h"
#include "libavcodec/avcodec.h"
#include "libavcodec/packet.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavdevice/avdevice.h"
#ifdef _WIN32
#define MYFFMPEG_EXPORT __declspec(dllexport)
#else
#define MYFFMPEG_EXPORT __attribute__((visibility("default")))
#endif // _WIN32
/* 自定义ffmpeg结构体 */
typedef struct MyFfmpeg {
const AVCodec* (*avcodec_find_encoder)(enum AVCodecID id);
AVCodecContext* (*avcodec_alloc_context3)(const AVCodec* codec);
int (*avcodec_open2)(AVCodecContext* avctx, const AVCodec* codec, AVDictionary** options);
/***省略***/
int (*avformat_network_init)(void);
AVFormatContext* (*avformat_alloc_context)(void);
AVStream* (*avformat_new_stream)(AVFormatContext* s, const struct AVCodec* c);
/***省略***/
int (*avio_open)(AVIOContext** s, const char* url, int flags);
int (*avio_open2)(AVIOContext** s, const char* url, int flags,
const AVIOInterruptCB* int_cb, AVDictionary** options);
int (*avio_closep)(AVIOContext** s);
/***省略***/
AVFilterInOut* (*avfilter_inout_alloc)(void);
AVFilterGraph* (*avfilter_graph_alloc)(void);
int (*avfilter_graph_create_filter)(AVFilterContext** filt_ctx, const AVFilter* filt,
const char* name, const char* args, void* opaque,
AVFilterGraph* graph_ctx);
/***省略***/
struct SwsContext* (*sws_getContext)(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter* srcFilter,
SwsFilter* dstFilter, const double* param);
int (*sws_scale)(struct SwsContext* c, const uint8_t* const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t* const dst[], const int dstStride[]);
void (*sws_freeContext)(struct SwsContext* swsContext);
/***省略***/
int64_t (*av_get_default_channel_layout)(int nb_channels);
AVFrame* (*av_frame_alloc)(void);
void (*av_frame_free)(AVFrame** frame);
/***省略***/
} MyFfmpeg;
MYFFMPEG_EXPORT MyFfmpeg* GetMyFfmpeg();
#endif
编写桥接实现myffmpeg.c
#include "myffmpeg.h"
/* 全局ffmpeg对象 */
static MyFfmpeg g_myffmpeg = {
.avcodec_find_encoder = avcodec_find_encoder,
.avcodec_alloc_context3 = avcodec_alloc_context3,
.avcodec_open2 = avcodec_open2,
/***省略***/
.avformat_network_init = avformat_network_init,
.avformat_alloc_context = avformat_alloc_context,
.avformat_new_stream = avformat_new_stream,
/***省略***/
.avio_open = avio_open,
.avio_open2 = avio_open2,
.avio_closep = avio_closep,
/***省略***/
.avfilter_inout_alloc = avfilter_inout_alloc,
.avfilter_graph_alloc = avfilter_graph_alloc,
.avfilter_graph_create_filter = avfilter_graph_create_filter,
/***省略***/
.sws_getContext = sws_getContext,
.sws_scale = sws_scale,
.sws_freeContext = sws_freeContext,
/***省略***/
.av_get_default_channel_layout = av_get_default_channel_layout,
.av_frame_alloc = av_frame_alloc,
.av_frame_free = av_frame_free,
/***省略***/
};
MyFfmpeg* GetMyFfmpeg()
{
return &g_myffmpeg;
}
3.3、编写shell脚本
编写脚本build_myffmpeg.sh
cur_path=$(cd `dirname $0`;pwd)
echo ${cur_path}
rm -f -r ffmpeg_build_win32
mkdir ffmpeg_build_win32
make clean
make distclean
./configure --prefix=./ffmpeg_build_win32 --target-os=win32 --arch=i386 --toolchain=msvc --enable-small --enable-static --disable-gpl --disable-ffmpeg --disable-ffprobe --disable-ffplay --disable-programs --disable-doc --disable-encoders --disable-decoders --disable-muxers --disable-demuxers --disable-parsers --disable-protocols --enable-filters --enable-bsfs --enable-encoder=aac,bmp,flv,gif,h264,h264_mf,hevc,hevc_mf,mjpeg,mp3_mf,mpeg4,opus,png,tiff --enable-decoder=aac,bmp,flv,gif,h264,hevc,mjpeg,mp3,mpeg4,opus,png,tiff --enable-muxer=avi,flv,gif,h264,hevc,hls,m4v,mjpeg,mov,mp3,mp4,ogg,opus,wav --enable-demuxer=aac,avi,flv,gif,h264,hevc,hls,m4v,mjpeg,mov,mp3,mp4,ogg,wav --enable-parser=aac,bmp,flac,gif,h264,hevc,mjpeg,opus,png --enable-protocol=data,fd,file,ftp,hls,http,https,pipe,rtmp,rtp,srtp,tcp,udp
make -j10
make install
cl.exe -c -GS -nologo -Fo:ffmpeg_build_win32/myffmpeg.obj source/myffmpeg.c -Iffmpeg_build_win32/include
link.exe -DLL -DEBUG -nologo -opt:ref -opt:icf -out:ffmpeg_build_win32/myffmpeg.dll ffmpeg_build_win32/myffmpeg.obj -LIBPATH:ffmpeg_build_win32/lib libavutil.a libavcodec.a libavformat.a libavfilter.a libswscale.a libswresample.a libavdevice.a kernel32.lib user32.lib ole32.lib shell32.lib Bcrypt.lib ws2_32.lib Secur32.lib Strmiids.lib Mfuuid.lib Mfplat.lib
3.4、用msys2编译
拷贝.h和.c到D:\workspace\build\FFmpeg-n6.1.1\source中,再拷贝.sh到D:\workspace\build\FFmpeg-n6.1.1中。
在x86 Native Tools Command Prompt for VS 2022输入C:\msys64\msys2_shell.cmd,打开msys2之后输入D:\workspace\build\FFmpeg-n6.1.1,再输入sh build_myffmpeg.sh运行脚本开始编译……
最终生成了对应的include、lib文件夹以及myffmpeg.dll、myffmpeg.lib文件,即编译成功
4、使用
写个简单的test工程使用下新编译出来的myffmpeg库:
4.1、拷贝库文件
将编译出来的myffmpeg头文件、lib、dll库文件拷贝到工程对应的inc、lib、bin目录下
需要注意的是除了拷贝myffmpeg.h外,需要将编译产生的ffmpeg头文件include目录拷贝过来
4.2、配置工程属性
在工程中添加头文件目录,找到C/C++,然后按图所示
在工程中添加LIB库目录和文件信息,找到链接器,接着按图所示
4.3、编译TEST工程
简单写了几句,加载myffmpeg成功并成功运行(需要将myffmpeg.dll拷贝到test.exe目录下),说明编译出来的库可用,大功告成!
#include <iostream>
extern "C" {
#include "myffmpeg.h"
}
using namespace std;
#define MY_FFMPEG() (*GetMyFfmpeg())
int main()
{
int result = MY_FFMPEG().avformat_network_init();
cout << "init network ret=" << result << endl;
return 0;
}