I have a small C program that uses 3 DLLs. It uses 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL.
Problem 1) When I open the outputed executable with Dependency Walker, the executable only tries to import 2 functions from each DLL. That can't be right.
Problem 2) What's worse, it tries to import the same 2 functions from each DLL. But only one of the DLLs has these 2 functions it's even trying to import. So the executable throws an error saying it can't find a function. But of course it can't, because it's looking inside DLLs that clearly don't have the function.
Question) How do I fix this? I want to open the executable with Dependency Walker and see that it tries to import 8 functions from the first DLL. 5 functions from the second DLL. And 2 functions from the 3rd DLL. And I want to see that it tries to import the right functions from the right DLLs.
Hopefully someone can help me based on just the information above. But if not, hopefully I can edit this question, and I will include all the details including the source code of the program (it's one file and short). Screenshots of Dependency Walker. Links to the .h, .lib, and .dll files (they are a part of libx264). And screenshots of all the settings in Visual Studio that are needed to get the program to build without any errors.
Aditional Information...
Sorry I'm new to C programming but I will try to provide enough information.
The following is a C program that uses libx264 (or maybe an ffmpeg API that uses libx264 behind the scenes) that manually draws raw RGB images (as uint8_t arrays), feeds them to the libx264 encoder, receives individual x264 frames from the encoder, and writes the x264 frames to a file (as uint8_t arrays). These x264 frames can then be fed to a decoder. But this program just encodes.
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#pragma warning(disable : 4996)
static AVCodecContext *c = NULL;
static AVFrame *frame;
static AVPacket pkt;
static FILE *file;
struct SwsContext *sws_context = NULL;
static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) {
const int in_linesize[1] = { 3 * c->width };
sws_context = sws_getCachedContext(sws_context,
c->width, c->height, AV_PIX_FMT_RGB24,
c->width, c->height, AV_PIX_FMT_YUV420P,
0, 0, 0, 0);
sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0,
c->height, frame->data, frame->linesize);
}
uint8_t* generate_rgb(int width, int height, int pts, uint8_t *rgb) {
int x, y, cur;
rgb = (uint8_t*)realloc(rgb, 3 * sizeof(uint8_t) * height * width);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
cur = 3 * (y * width + x);
rgb[cur + 0] = 0;
rgb[cur + 1] = 0;
rgb[cur + 2] = 0;
if ((frame->pts / 25) % 2 == 0) {
if (y < height / 2) {
if (x < width / 2) {
/* Black. */
}
else {
rgb[cur + 0] = 255;
}
}
else {
if (x < width / 2) {
rgb[cur + 1] = 255;
}
else {
rgb[cur + 2] = 255;
}
}
}
else {
if (y < height / 2) {
rgb[cur + 0] = 255;
if (x < width / 2) {
rgb[cur + 1] = 255;
}
else {
rgb[cur + 2] = 255;
}
}
else {
if (x < width / 2) {
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
}
else {
rgb[cur + 0] = 255;
rgb[cur + 1] = 255;
rgb[cur + 2] = 255;
}
}
}
}
}
return rgb;
}
/* Allocate resources and write header data to the output file. */
void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) {
AVCodec *codec;
int ret;
codec = avcodec_find_encoder(codec_id);
if (!codec) {
fprintf(stderr, "Codec not found\n");
exit(1);
}
c = avcodec_alloc_context3(codec);
if (!c) {
fprintf(stderr, "Could not allocate video codec context\n");
exit(1);
}
c->bit_rate = 400000;
c->width = width;
c->height = height;
c->time_base.num = 1;
c->time_base.den = fps;
c->keyint_min = 600;
c->pix_fmt = AV_PIX_FMT_YUV420P;
if (codec_id == AV_CODEC_ID_H264)
av_opt_set(c->priv_data, "preset", "slow", 0);
if (avcodec_open2(c, codec, NULL) < 0) {
fprintf(stderr, "Could not open codec\n");
exit(1);
}
file = fopen(filename, "wb");
if (!file) {
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);
}
frame->format = c->pix_fmt;
frame->width = c->width;
frame->height = c->height;
ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32);
if (ret < 0) {
fprintf(stderr, "Could not allocate raw picture buffer\n");
exit(1);
}
}
/*
Write trailing data to the output file
and free resources allocated by ffmpeg_encoder_start.
*/
void ffmpeg_encoder_finish(void) {
uint8_t endcode[] = { 0, 0, 1, 0xb7 };
int got_output, ret;
do {
fflush(stdout);
ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
} while (got_output);
fwrite(endcode, 1, sizeof(endcode), file);
fclose(file);
avcodec_close(c);
av_free(c);
av_freep(&frame->data[0]);
av_frame_free(&frame);
}
/*
Encode one frame from an RGB24 input and save it to the output file.
Must be called after ffmpeg_encoder_start, and ffmpeg_encoder_finish
must be called after the last call to this function.
*/
void ffmpeg_encoder_encode_frame(uint8_t *rgb) {
int ret, got_output;
ffmpeg_encoder_set_frame_yuv_from_rgb(rgb);
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
if (frame->pts == 1) {
frame->key_frame = 1;
frame->pict_type = AV_PICTURE_TYPE_I;
}
else {
frame->key_frame = 0;
frame->pict_type = AV_PICTURE_TYPE_P;
}
ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
if (ret < 0) {
fprintf(stderr, "Error encoding frame\n");
exit(1);
}
if (got_output) {
fwrite(pkt.data, 1, pkt.size, file);
av_packet_unref(&pkt);
}
}
/* Represents the main loop of an application which generates one frame per loop. */
static void encode_example(const char *filename, int codec_id) {
int pts;
int width = 320;
int height = 240;
uint8_t *rgb = NULL;
ffmpeg_encoder_start(filename, codec_id, 25, width, height);
for (pts = 0; pts < 100; pts++) {
frame->pts = pts;
rgb = generate_rgb(width, height, pts, rgb);
ffmpeg_encoder_encode_frame(rgb);
}
ffmpeg_encoder_finish();
}
int main(void) {
avcodec_register_all();
encode_example("tmp.h264", AV_CODEC_ID_H264);
encode_example("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO);
return 0;
}
The line #pragma warning(disable : 4996)
ignores depreciation warnings that are treated as errors. You can see that some of the functions I'm using are marked as depreciated in the header files. I'll worry about that later, right now I just need something that runs.
The .h, .lib, and .dll files can be found here: https://ffmpeg.zeranoe.com/builds/
There are some download options on that site: Screenshot of Download Options
I'm using that 64-bit beta version of the .h, .lib, and .dll files. You also have to select if you want to download the "Static", "Shared", or "Dev" version of the files. The "Shared" version has the .dll files. The "Dev" version has the .h and .lib files. So I needed both of those.
I was advised to not post screenshots of settings in Visual Studio but I'll summary of what I did in Visual Studio to get the program to build. I had to make sure the project was set to 64-bit output (it was 32-bit by default). I had to tell Visual Studio to compile and link C not C++ code (maybe I did this by renaming the source code file extension to .c instead of .cpp, I can't remember). I put the folders with the .lib files and .h files in a logical place inside my project folder and added the folder paths to the places Visual Studio will look for .lib files and .h files. I also had to specifically name the .lib files I was using. After building I put the .dll files in the same folder as the compiled executable. There might have been something else but I can't remember.
I was also advised not to make screenshots of Dependency Walker, and instead paste output from the command:
dumpbin /imports LibLinkingTest.exe
LibLinkingTest.exe is the name of the executable that was compiled from the C source code above, based on the name I gave to my project. Here is the output from that command above:
Microsoft (R) COFF/PE Dumper Version 14.12.25835.0 Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file LibLinkingTest.exe
File Type: EXECUTABLE IMAGE
Section contains the following imports:
avcodec-58.dll 1400221D8 Import Address Table 1400226D8 Import Name Table 0 time date stamp 0 Index of first forwarder reference C sws_getCachedContext 1B sws_scale avutil-56.dll 1400221D8 Import Address Table 1400226D8 Import Name Table 0 time date stamp 0 Index of first forwarder reference C sws_getCachedContext 1B sws_scale swscale-5.dll 1400221D8 Import Address Table 1400226D8 Import Name Table 0 time date stamp 0 Index of first forwarder reference C sws_getCachedContext 1B sws_scale VCRUNTIME140D.dll 140022150 Import Address Table 140022650 Import Name Table 0 time date stamp 0 Index of first forwarder reference 31 __vcrt_LoadLibraryExW 2F __vcrt_GetModuleHandleW 25 __std_type_info_destroy_list 8 __C_specific_handler 2E __vcrt_GetModuleFileNameW ucrtbased.dll 140022270 Import Address Table 140022770 Import Name Table 0 time date stamp 0 Index of first forwarder reference 4D __p__commode 52B strcpy_s 527 strcat_s 68 __stdio_common_vsprintf_s 2C1 _seh_filter_dll 172 _initialize_onexit_table 2B4 _register_onexit_function E5 _execute_onexit_table C2 _crt_atexit C1 _crt_at_quick_exit 54A terminate 39B _wmakepath_s 3B7 _wsplitpath_s 563 wcscpy_s B5 _configthreadlocale 2CA _set_fmode 2B5 _register_thread_local_exe_atexit_callback 175 _initterm_e 174 _initterm 13D _get_initial_narrow_environment 171 _initialize_narrow_environment B6 _configure_narrow_argv 5B __setusermatherr 2C5 _set_app_type 2C2 _seh_filter_exe 5 _CrtDbgReportW 4 _CrtDbgReport 44F exit 504 realloc 5C __stdio_common_vfprintf 48A fwrite 47B fopen 468 fflush 459 fclose 35 __acrt_iob_func 2CD _set_new_mode A4 _cexit 9F _c_exit EA _exit 4A __p___argv 49 __p___argc KERNEL32.dll 140022000 Import Address Table 140022500 Import Name Table 0 time date stamp 0 Index of first forwarder reference 37D IsDebuggerPresent 45F RaiseException 3EB MultiByteToWideChar 605 WideCharToMultiByte 4CB RtlCaptureContext 4D2 RtlLookupFunctionEntry 4D9 RtlVirtualUnwind 5B4 UnhandledExceptionFilter 573 SetUnhandledExceptionFilter 2B1 GetProcAddress 1AF FreeLibrary 5D5 VirtualQuery 21B GetCurrentProcess 592 TerminateProcess 2B7 GetProcessHeap 34E HeapFree 384 IsProcessorFeaturePresent 34A HeapAlloc 449 QueryPerformanceCounter 21C GetCurrentProcessId 220 GetCurrentThreadId 2EC GetSystemTimeAsFileTime 367 InitializeSListHead 2D3 GetStartupInfoW 27A GetModuleHandleW 263 GetLastError
Summary
1000 .00cfg 1000 .data 2000 .idata 3000 .pdata 3000 .rdata 1000 .reloc 1000 .rsrc A000 .text 10000 .textbss
It's associating the functions sws_getCachedContext
and sws_scale
which are used in the C source code above with 3 DLLs: avcodec-58.dll, avutil-56.dll, and swscale-5.dll
But the functions sws_getCachedContext
and sws_scale
are only present in swscale-5.dll.
And there is no mention of the 13 other functions in the C source code above that should be loaded from these DLLs.
The functions avcodec_find_encoder
, avcodec_alloc_context3
, avcodec_encode_video2
, avcodec_open2
, avcodec_close
, avcodec_register_all
, av_packet_unref
, and av_init_packet
from the C source code above should be loaded from avcodec-58.dll.
The functions av_opt_set
, av_image_alloc
, av_free
, av_freep
, and av_frame_free
from the C source code above should be loaded from avutil-56.dll.
The functions sws_getCachedContext
and sws_scale
from the C source code above should be loaded from swscale-5.dll (so it looks like it actually got that right).
These function are definitely present in these DLLs because I can see them inside the DLLs using Dependency Walker. I'm pretty sure that none of these functions are statically embedded inside the final executable. All of these functions should be loaded from the DLLs.
And I was advised to post the build log. Here is the build log:
Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25835 for x64
Copyright (C) Microsoft Corporation. All rights reserved.cl /c /ZI /W3 /WX- /diagnostics:classic /sdl /Od /D _DEBUG /D _CONSOLE /D _UNICODE /D UNICODE /Gm /EHsc /RTC1 /MDd /GS /fp:precise /permissive- /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"x64\Debug\" /Fd"x64\Debug\vc141.pdb" /Gd /TC /errorReport:prompt LibLinkingTest.c
LibLinkingTest.c
Microsoft (R) Incremental Linker Version 14.12.25835.0
Copyright (C) Microsoft Corporation. All rights reserved."/OUT:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe" /INCREMENTAL avcodec.lib avdevice.lib avfilter.lib avformat.lib avutil.lib postproc.lib swresample.lib swscale.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /MANIFEST "/MANIFESTUAC:level='asInvoker' uiAccess='false'" /manifest:embed /DEBUG:FASTLINK "/PDB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT "/IMPLIB:C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.lib" /MACHINE:X64 x64\Debug\LibLinkingTest.obj x64\Debug\stdafx.obj
LibLinkingTest.vcxproj -> C:\Users\me\Desktop\Programming\Windows\VS2017\LibLinkingTest\x64\Debug\LibLinkingTest.exe