Reputation: 377
I'm trying to create a C++ program that embeds FFMPEG functionality. To do that I took one FFMPEG example and recompiled it as is using g++ (only adding extern "C" where needed). Compilation is successful, but linking fails for undefined symbols.
cc -I/usr/include/x86_64-linux-gnu -Wall -g -fPIC -c -o bin/decode_video.o decode_video.cpp
cc -Wl,--export-dynamic,--no-undefined -L /usr/lib/x86_64-linux-gnu/ -shared -lavdevice -lavutil -lavcodec bin/decode_video.o -o decode_video.so
bin/decode_video.o: In function `decode(AVCodecContext*, AVFrame*, AVPacket*, char const*)':
/home/admin1/ffmpeg_examples/decode_video.cpp:60: undefined reference to `avcodec_send_packet'
/home/admin1/ffmpeg_examples/decode_video.cpp:67: undefined reference to `avcodec_receive_frame'
bin/decode_video.o: In function `main':
/home/admin1/ffmpeg_examples/decode_video.cpp:108: undefined reference to `av_packet_alloc'
/home/admin1/ffmpeg_examples/decode_video.cpp:116: undefined reference to `avcodec_find_decoder'
/home/admin1/ffmpeg_examples/decode_video.cpp:122: undefined reference to `av_parser_init'
/home/admin1/ffmpeg_examples/decode_video.cpp:128: undefined reference to `avcodec_alloc_context3'
/home/admin1/ffmpeg_examples/decode_video.cpp:139: undefined reference to `avcodec_open2'
/home/admin1/ffmpeg_examples/decode_video.cpp:150: undefined reference to `av_frame_alloc'
/home/admin1/ffmpeg_examples/decode_video.cpp:165: undefined reference to `av_parser_parse2'
/home/admin1/ffmpeg_examples/decode_video.cpp:184: undefined reference to `av_parser_close'
/home/admin1/ffmpeg_examples/decode_video.cpp:185: undefined reference to `avcodec_free_context'
/home/admin1/ffmpeg_examples/decode_video.cpp:186: undefined reference to `av_frame_free'
/home/admin1/ffmpeg_examples/decode_video.cpp:187: undefined reference to `av_packet_free'
checking nm I see that there's no name mangling in action so extern C seems to work well:
nm bin/decode_video.o
U avcodec_alloc_context3
U avcodec_find_decoder
U avcodec_free_context
U avcodec_open2
U avcodec_receive_frame
U avcodec_send_packet
U av_frame_alloc
U av_frame_free
U av_packet_alloc
U av_packet_free
U av_parser_close
U av_parser_init
U av_parser_parse2
U exit
U fclose
U feof
U fflush
U fopen
U fprintf
U fread
U fwrite
U _GLOBAL_OFFSET_TABLE_
000000000000026f T main
U memset
U printf
U snprintf
U __stack_chk_fail
U stderr
U stdout
00000000000000a3 t _ZL6decodeP14AVCodecContextP7AVFrameP8AVPacketPKc
0000000000000000 t _ZL8pgm_savePhiiiPc
checking the library, for one of the 'missing' symbols, I can see it is implemented
nm /usr/lib/x86_64-linux-gnu/libavcodec.a | grep avcodec_send_packet
00000000000033e0 T avcodec_send_packet
U avcodec_send_packet
When compiling in C it works perfectly (without extern C of course) which makes me wonder what am I missing?
Below find FFMPEG example code for reference (decode_video.cpp):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern "C"
{
#include <libavcodec/avcodec.h>
}
#define INBUF_SIZE 4096
static void pgm_save(unsigned char *buf, int wrap, int xsize, int ysize,
char *filename)
{
FILE *f;
int i;
f = fopen(filename,"w");
fprintf(f, "P5\n%d %d\n%d\n", xsize, ysize, 255);
for (i = 0; i < ysize; i++)
fwrite(buf + i * wrap, 1, xsize, f);
fclose(f);
}
static void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
const char *filename)
{
char buf[1024];
int ret;
ret = avcodec_send_packet(dec_ctx, pkt);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decoding\n");
exit(1);
}
while (ret >= 0) {
ret = avcodec_receive_frame(dec_ctx, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
return;
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
exit(1);
}
printf("saving frame %3d\n", dec_ctx->frame_number);
fflush(stdout);
/* the picture is allocated by the decoder. no need to
free it */
snprintf(buf, sizeof(buf), "%s-%d", filename, dec_ctx->frame_number);
pgm_save(frame->data[0], frame->linesize[0],
frame->width, frame->height, buf);
}
}
int main(int argc, char **argv)
{
const char *filename, *outfilename;
const AVCodec *codec;
AVCodecParserContext *parser;
AVCodecContext *c= NULL;
FILE *f;
AVFrame *frame;
uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
uint8_t *data;
size_t data_size;
int ret;
AVPacket *pkt;
if (argc <= 2) {
fprintf(stderr, "Usage: %s <input file> <output file>\n"
"And check your input file is encoded by mpeg1video please.\n", argv[0]);
exit(0);
}
filename = argv[1];
outfilename = argv[2];
pkt = av_packet_alloc();
if (!pkt)
exit(1);
/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);
/* find the MPEG-1 video decoder */
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
parser = av_parser_init(codec->id);
if (!parser) {
fprintf(stderr, "parser not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
/* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */
/* open it */
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Could not open %s\n", filename);
exit(1);
}
frame = av_frame_alloc();
if (!frame) {
fprintf(stderr, "Could not allocate video frame\n");
exit(1);
}
while (!feof(f)) {
/* read raw data from the input file */
data_size = fread(inbuf, 1, INBUF_SIZE, f);
if (!data_size)
break;
/* use the parser to split the data into frames */
data = inbuf;
while (data_size > 0) {
ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,
data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
if (ret < 0) {
fprintf(stderr, "Error while parsing\n");
exit(1);
}
data += ret;
data_size -= ret;
if (pkt->size)
decode(c, frame, pkt, outfilename);
}
}
/* flush the decoder */
decode(c, frame, NULL, outfilename);
fclose(f);
av_parser_close(parser);
avcodec_free_context(&c);
av_frame_free(&frame);
av_packet_free(&pkt);
return 0;
}
Upvotes: 1
Views: 1108
Reputation: 377
Thanks to @WhozCraig for helping me figure it out. I used the wrong flag when linking thinking I'm preserving the full library symbols, however it was the wrong flag. So linker did one pass and the objects order was wrong (since I naturally thought it didn't matter) To preserve the full library symbols pass --whole-archive to linker:
-Wl,--whole-archive
Upvotes: 1