undefined reference to `av_close_input_file' 原因分析

本文详细分析了在使用libdlna和ffmpeg时遇到的编译错误,指出原因是ffmpeg2.4.2版本默认不支持av_close_input_file函数。通过将ffmpeg版本更改为2.2.9或修改版本号,可以解决此问题。文章还提供了深入的分析过程,包括如何定位问题、检查函数定义位置及版本兼容性等。

这是使用libdlna0.2.4ffmpeg2.4.2搭配遇到的问题

 

以下是本人自己分析出错原因的分析,内容比较长,写得也比较随意。如果只是想找解决方法,我先在这总结一下:

ffmpeg2.4.2默认是不支持av_close_input_file函数的了,换成了avformat_close_input函数


解决方法是使用ffmpeg2.2.9版本(更久的版本没测试过),

或者坚持要使用ffmpeg2.4.2版本的话,只需将

…/ffmpeg-2.4.2/libavformat/version.h文件中一点点内容进行修改:

-------------------------------------------

 32 #defineLIBAVFORMAT_VERSION_MAJOR 56

 33 #define LIBAVFORMAT_VERSION_MINOR  4

 34 #define LIBAVFORMAT_VERSION_MICRO 101

-------------------------------------------

修改成:

 32 #defineLIBAVFORMAT_VERSION_MAJOR 55


以下开始我的分析过程:

--------------------------------------------------------------------------------------------------------------------


编译libdlna时候遇到的问题

 

执行make的出错信息:

--------------------------------------------------------------

gcc test-libdlna.c-W -Wall -Wno-unused-but-set-variable -D_LARGEFILE_SOURCE-D_FILE_OFFSET_BITS=64 -D_REENTRANT -O3 -Isrc -Lsrc -ldlna -lavformat -pthread -lavcodec -lswresample -lavutil -lrt-lm -o test-libdlna

src/libdlna.so:undefined reference to `av_close_input_file'

src/libdlna.so:undefined reference to `av_find_stream_info'

collect2: ldreturned 1 exit status

make: ***[test-libdlna] Error 1

--------------------------------------------------------------

 

先看下libdlna.so是如何编译出来的:

分析src/Makefile文件:

----------------------------------------------------

LIBNAME = libdlna

LIBNAME_SHARED =${LIBNAME}.so

 

lib_shared:lib_shared_info_pre $(LOBJS) lib_shared_info_post

        $(CC) -shared-Wl,-soname,$(LIBNAME_MAJOR) $(LOBJS) \

          $(LDFLAGS) $(EXTRALIBS) -o$(LIBNAME_VERSION)

        $(LN) -sf $(LIBNAME_VERSION)$(LIBNAME_MAJOR)

        $(LN) -sf $(LIBNAME_MAJOR)$(LIBNAME_SHARED)

----------------------------------------------------

 

对应输出信息中的:

---------------------------------------------------------------------------------------------------------------------------------------------

gcc -shared-Wl,-soname,libdlna.so.0 profiles.lo containers.lo image_jpeg.lo image_png.loaudio_aac.lo audio_ac3.lo audio_amr.lo audio_atrac3.lo audio_g726.loaudio_lpcm.lo audio_mp2.lo audio_mp3.lo audio_wma.lo av_mpeg1.lo av_mpeg2.loav_mpeg4_part2.lo av_mpeg4_part10.lo av_wmv9.lo upnp_dms.lo \

           -pthread -lavformat -lavcodec-lswresample -lavutil -lrt -lm -o libdlna.so.0.2.4

ln -sflibdlna.so.0.2.4 libdlna.so.0

ln -sf libdlna.so.0libdlna.so

---------------------------------------------------------------------------------------------------------------------------------------------

 

想知道av_close_input_filelibdlna.so出现之前是在哪个库中定义的,

那么先查看下函数av_close_input_file在哪里有被使用:

------------------------------------------------------------

$ grep -rn"av_close_input_file" ./src/

Binary file./src/libdlna.so matches

Binary file./src/profiles.lo matches

Binary file./src/profiles.o matches

Binary file./src/libdlna.so.0.2.4 matches

./src/profiles.c:337:  av_close_input_file (ctx);

Binary file./src/libdlna.a matches

Binary file./src/libdlna.so.0 matches

------------------------------------------------------------

还不能直接看出在哪里,但是可以看出被profiles.c文件调用了

 

./src/profiles.c

------------------------------------------------------------

 22 #include <stdlib.h>

 23 #include <string.h>

 24

 25 #include "dlna_internals.h"

 26 #include"profiles.h"

 27 #include "containers.h"

337   av_close_input_file(ctx);

338   free (codecs);

339   return profile;

340 }

------------------------------------------------------------

 

看下头文件:

./src/profiles.h

------------------------------------------------------------

 25 #include <libavcodec/avcodec.h>

 26 #include <libavformat/avformat.h>

 27

 28 #include "dlna_internals.h"

 29 #include "containers.h"

------------------------------------------------------------

看到这里,应该可以猜测出av_close_input_file是在avcodec.h或者avformat.h(编译安装ffmpeg时生成的)中定义的

 

grep可以发现是在avformat.h

/usr/local/include/libavformat/avformat.h

----------------------------------------------------------------------

2173 #ifFF_API_CLOSE_INPUT_FILE

2174 /**

2175  * @deprecated use avformat_close_input()

2176  * Close a media file (but not its codecs).

2177  *

2178  * @param s media file handle

2179  */

2180attribute_deprecated

2181 void av_close_input_file(AVFormatContext *s);

2182 #endif

---------------------------------------------------------------------

也只是函数av_close_input_file的声明,而不是定义

 

百度了一下才知道,一般函数的定义是在库中:

nm命令查看库avformat调用的函数:

-----------------------------------------------------

$ nm libavformat.a |grep "close_input"

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

         U avformat_close_input

0000c710 Tavformat_close_input

----------------------------------------------------

可以发现,并没有av_close_input_file,只有avformat_close_input。

 

难道编译ffmpeg的时候没有将函数av_close_input_file编译进库里面吗?

 


 

好吧

我们要回到ffmpeg源码的目录(ffmpeg-2.4.2)找一下了

---------------------------------------------------------------------------------------------------------------------

$ grep -rn"av_close_input_file" ./

./libavformat/avformat.h:2181:voidav_close_input_file(AVFormatContext *s);

./libavformat/utils.c:3534:voidav_close_input_file(AVFormatContext *s)

./doc/APIchanges:1108:  Deprecate av_close_input_file() andav_close_input_stream().

---------------------------------------------------------------------------------------------------------------------

 

看来av_close_input_file的函数声明应该是在./libavformat/utils.c文件里了。

------------------------------------------------------------------------------

3533 #ifFF_API_CLOSE_INPUT_FILE

3534 voidav_close_input_file(AVFormatContext *s)

3535 {

3536     avformat_close_input(&s);

3537 }

3538 #endif

------------------------------------------------------------------------------

 

发现还有一个文件中包含av_close_input_file

…/ffmpeg-2.4.2/doc/APIchanges

----------------------------------------------------------

1106 2011-12-12 -8bc7fe4 / 5266045 - lavf 53.25.0 / 53.17.0

1107   Add avformat_close_input().

1108   Deprecate av_close_input_file() and av_close_input_stream().

----------------------------------------------------------

难道av_close_input_file() and av_close_input_stream()已经被抛弃了???

被换成avformat_close_input()了????????????

 


看下libavformat.a库是由哪些库组成的:

…/ffmpeg-2.4.2/libavformat/Makefile

----------------------------------------------------------

  3 NAME = avformat

  4

  5 HEADERS = avformat.h                                                   \

  6          avio.h                                                       \

  7          version.h                                                    \

  8

  9 OBJS = allformats.o         \

 10       avio.o               \

 11       aviobuf.o            \

 12       cutils.o             \

 13       dump.o               \

 14       format.o             \

 15       id3v1.o              \

 16       id3v2.o              \

 17       metadata.o           \

 18       mux.o                \

 19       options.o            \

 20       os_support.o         \

 21       riff.o               \

 22       sdp.o                \

 23       seek.o               \

 24       url.o                \

 25       utils.o              \

----------------------------------------------------------

 

看下utils.o库中有没有av_close_input_file的定义:

------------------------------------

$nm utils.o |grep "close_input"

0000c710 Tavformat_close_input

-----------------------------------

没有发现该函数的定义,但之前我们看到其实av_close_input_file()就是在utils.c中定义的:

------------------------------------------------------------------

3533 #if FF_API_CLOSE_INPUT_FILE

3534 voidav_close_input_file(AVFormatContext *s)

3535 {

3536     avformat_close_input(&s);

3537 }

3538 #endif

-----------------------------------------------------------------

难道是因为FF_API_CLOSE_INPUT_FILE的值不为1

 

再研究一下哪些文件中有FF_API_CLOSE_INPUT_FILE值的定义:

--------------------------------------------------------------------------------------------------------------------

$ grep -rn"FF_API_CLOSE_INPUT_FILE" ./

./avformat.h:2173:#ifFF_API_CLOSE_INPUT_FILE

./version.h:76:#ifndefFF_API_CLOSE_INPUT_FILE

./version.h:77:#define FF_API_CLOSE_INPUT_FILE        (LIBAVFORMAT_VERSION_MAJOR < 56)

./utils.c:3533:#ifFF_API_CLOSE_INPUT_FILE

--------------------------------------------------------------------------------------------------------------------

 

…/ffmpeg-2.4.2/libavformat/version.h

-------------------------------------------

 32 #defineLIBAVFORMAT_VERSION_MAJOR 56

 33 #define LIBAVFORMAT_VERSION_MINOR  4

 34 #define LIBAVFORMAT_VERSION_MICRO 101

------------------------------------------

原来如此,ffmpeg版本在56以上就不支持av_close_input_file函数了!!!



#defineLIBAVFORMAT_VERSION_MAJOR 56

中的56改成54

 

再重新make就行了,问题解决。

 

如果还是不行的话,先执行make clean,再make应该就好了。

std::string yuv_to_h264_string(const std::string& yuv_path, int width, int height, int frame_count) { // 1. 初始化FFmpeg av_register_all(); avcodec_register_all(); // 2. 查找H264编码器 const AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_H264); if (!codec) { throw std::runtime_error("H264 encoder not found"); } // 3. 创建编码器上下文 AVCodecContext* codec_ctx = avcodec_alloc_context3(codec); if (!codec_ctx) { throw std::runtime_error("Could not allocate video codec context"); } // 4. 设置编码参数 codec_ctx->bit_rate = 400000; codec_ctx->width = width; codec_ctx->height = height; codec_ctx->time_base = (AVRational){1, 25}; codec_ctx->framerate = (AVRational){25, 1}; codec_ctx->gop_size = 10; codec_ctx->max_b_frames = 1; codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; // 5. 打开编码器 if (avcodec_open2(codec_ctx, codec, nullptr) < 0) { throw std::runtime_error("Could not open codec"); } // 6. 创建帧缓冲区 AVFrame* frame = av_frame_alloc(); if (!frame) { throw std::runtime_error("Could not allocate video frame"); } frame->format = codec_ctx->pix_fmt; frame->width = codec_ctx->width; frame->height = codec_ctx->height; if (av_frame_get_buffer(frame, 0) < 0) { throw std::runtime_error("Could not allocate frame buffer"); } // 7. 打开YUV输入文件 std::ifstream yuv_file(yuv_path, std::ios::binary); if (!yuv_file.is_open()) { throw std::runtime_error("Could not open YUV file"); } // 8. 初始化数据包 AVPacket* pkt = av_packet_alloc(); if (!pkt) { throw std::runtime_error("Could not allocate packet"); } // 9. 存储H264数据的缓冲区 std::string h264_data; // 10. 逐帧编码循环 for (int i = 0; i < frame_count; ++i) { // 读取YUV数据 int frame_size = width * height * 3 / 2; // YUV420P格式 std::vector<uint8_t> yuv_buffer(frame_size); yuv_file.read(reinterpret_cast<char*>(yuv_buffer.data()), frame_size); // 准备帧数据 if (av_frame_make_writable(frame) < 0) { throw std::runtime_error("Frame not writable"); } // 填充YUV平面数据 frame->data[0] = yuv_buffer.data(); // Y分量 frame->data[1] = yuv_buffer.data() + width * height; // U分量 frame->data[2] = yuv_buffer.data() + width * height + width * height / 4; // V分量 frame->linesize[0] = width; frame->linesize[1] = width / 2; frame->linesize[2] = width / 2; frame->pts = i; // 编码帧 int ret = avcodec_send_frame(codec_ctx, frame); if (ret < 0) { throw std::runtime_error("Error sending frame to encoder"); } // 接收编码后的数据包 while (ret >= 0) { ret = avcodec_receive_packet(codec_ctx, pkt); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { break; } else if (ret < 0) { throw std::runtime_error("Error during encoding"); } // 将H264数据添加到字符串 h264_data.append(reinterpret_cast<char*>(pkt->data), pkt->size); av_packet_unref(pkt); } } // 11. 冲刷编码器 avcodec_send_frame(codec_ctx, nullptr); while (true) { int ret = avcodec_receive_packet(codec_ctx, pkt); if (ret == AVERROR_EOF) break; if (ret < 0) throw std::runtime_error("Error flushing encoder"); // 添加剩余数据 h264_data.append(reinterpret_cast<char*>(pkt->data), pkt->size); av_packet_unref(pkt); } // 12. 清理资源 av_frame_free(&frame); av_packet_free(&pkt); avcodec_free_context(&codec_ctx); yuv_file.close(); return h264_data; }这段代码添加了这些库之后, /usr/lib/aarch64-linux-gnu/libavcodec.so.58 /usr/lib/aarch64-linux-gnu/libavcodec.so.58.134.100 /usr/lib/aarch64-linux-gnu/libavcodec.so /usr/lib/aarch64-linux-gnu/libavcodec.a,还是报错如下,如何解决,[main] 正在生成文件夹: /home/aiec/Templates/test/build all [build] 正在启动生成 [proc] 正在执行命令: /usr/bin/cmake --build /home/aiec/Templates/test/build --config Debug --target all -j 8 -- [build] Consolidate compiler generated dependencies of target CrossCompileProject [build] [ 50%] Linking CXX executable bin/CrossCompileProject [build] /usr/bin/ld: CMakeFiles/CrossCompileProject.dir/TrainControl.cpp.o: in function `yuv_to_h264_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int, int, int)': [build] /home/aiec/Templates/test/TrainControl.cpp:471: undefined reference to `av_register_all()' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:472: undefined reference to `avcodec_register_all()' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:475: undefined reference to `avcodec_find_encoder(AVCodecID)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:481: undefined reference to `avcodec_alloc_context3(AVCodec const*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:497: undefined reference to `avcodec_open2(AVCodecContext*, AVCodec const*, AVDictionary**)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:502: undefined reference to `av_frame_alloc()' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:510: undefined reference to `av_frame_get_buffer(AVFrame*, int)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:521: undefined reference to `av_packet_alloc()' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:537: undefined reference to `av_frame_make_writable(AVFrame*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:552: undefined reference to `avcodec_send_frame(AVCodecContext*, AVFrame const*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:559: undefined reference to `avcodec_receive_packet(AVCodecContext*, AVPacket*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:568: undefined reference to `av_packet_unref(AVPacket*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:573: undefined reference to `avcodec_send_frame(AVCodecContext*, AVFrame const*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:575: undefined reference to `avcodec_receive_packet(AVCodecContext*, AVPacket*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:581: undefined reference to `av_packet_unref(AVPacket*)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:585: undefined reference to `av_frame_free(AVFrame**)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:586: undefined reference to `av_packet_free(AVPacket**)' [build] /usr/bin/ld: /home/aiec/Templates/test/TrainControl.cpp:587: undefined reference to `avcodec_free_context(AVCodecContext**)' [build] collect2: error: ld returned 1 exit status [build] gmake[2]: *** [CMakeFiles/CrossCompileProject.dir/build.make:160: bin/CrossCompileProject] Error 1 [build] gmake[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/CrossCompileProject.dir/all] Error 2 [build] gmake: *** [Makefile:91: all] Error 2 [proc] 命令“/usr/bin/cmake --build /home/aiec/Templates/test/build --config Debug --target all -j 8 --”已退出,代码为 2 [driver] 生成完毕: 00:00:01.544 [build] 生成已完成,退出代码为 2
最新发布
11-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值