zergon321/reisen

Support for Windows

rookiesH opened this issue · 23 comments

Hello,
I have just discovered this repo. Is there any plan to support shared library(by calling .dll, .so, .dylib) and support for windows ? There was a WIP project that has not been updated over a year. I tried it but it has some audio skipping issues.
Thank you for this work

Happy Halloween 🎃 btw.

Hi. Thank you for the issue.

I've just taken a look at the project you referenced. It appears that the person who created it used some DLLs taken from the Internet to work with ffmpeg on Windows. He calls their functions using the syscall standard library. In my mind, that's a pretty unreliable approach. What if the user found corrupt or malicious DLLs from an unauthorized source? Also the author had to manually export symbols one by one which is pretty tiresome and might be hard to maintain.

So let me tell you about the method I use to launch CGO programs on Windows. First you have to install MSYS2. To do that, just follow the instruction. Don't forget to install the libraries mentioned there. After it you should install the mingw-w64-x86_64-ffmpeg library.

pacman -S mingw-w64-x86_64-ffmpeg

The above command will download libav components (libavformat, libavcodec, etc.) and create DLLs for them. After that you need to go to the directory with the player source code and build it. The process should look like this:

Q99ItUWuwPI

-s -w option strips unneeded debug symbols from the assembly and -H=windowsgui option makes the application run without opening the console leaving only GUI on the screen.

The hardest part is to make the player run outside of MSYS2 context. If you launched the built application by double-clicking on the executable file, it would show you an error message about a DLL that is missing. Unfortunately, you'd have to copy all the required DLLs from C:\msys64\mingw64\bin (if you didn't change the original installation path). As the StackOverflow topic states, you can obtain the list of all the DLLs required by an executable using dumpbin from the Visual Studio command prompt toolset. But I haven't checked it yet because Visual Studio installation process takes a huge amount of time and I don't don't need that IDE im my usual development stack at all.

As for the shared library support, I don't think it's necessary. If someone would need to use Reisen as a Go plugin, he could just create a wrapper by himself just as described in this article. And there is more than one way to implement some things in the plugin. Also all the symbols are loaded separately and you need to know the name of the symbol to load it which is pretty tiresome. And Go's dependency system is source-based, not assembly-based as in C# or Java. I guess that's why nobody ships their libraries as DLLs. Moreover, if you wanted to create a C-shared DLL out of Reisen, it's senseless because you have the original libav components that are much easier to use in C/C++.

Happy Halloween. =3

I was asking for shared library because issues similar to this I want a way to user compile their own version of ffmpeg if they need and I assume it allows me to be independent of dependency licenses.

Reisen is licensed under MIT and can be statically linked to a Go apllication without any circumstances. ffmpeg is mostly licensed under LGPL and linked dynamically with CGO. If you use Reisen in your commercial application, you only have to supply separate binaries for ffmpeg, not Reisen itself because Reisen is neither a fork of ffmpeg nor uses its code snippets directly in its own codebase.

I followed your direction but unfortunately import path is not being detected by go. Do I need to set any go env variable?
$ pacman -S mingw-w64-x86_64-ffmpeg
$ go build -ldflags "-s -w -H=windowsgui" -o player.exe


# github.com/zergon321/reisen
..\..\audio_windows.go:4:11: fatal error: libavcodec/avcodec.h: No such file or directory
    4 | // #include <libavcodec/avcodec.h>
      |           ^~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

$ go env


set GO111MODULE=
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\User\AppData\Local\go-build
set GOENV=C:\Users\User\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\User\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\User\go
set GOPRIVATE=
set GOPROXY=direct,https://proxy.golang.org
set GOROOT=C:\msys64\mingw64\lib\go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\msys64\mingw64\lib\go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.17
set GCCGO=gccgo
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=C:\users\User\go\src\reisen\go.mod
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\msys64\tmp\go-build3223021300=/tmp/go-build -gno-record-gcc-switches

Reisen is neither a fork of ffmpeg nor uses its code snippets directly in its own codebase.

I don't have a good understanding of cgo. I will understand it with time, hopefully.

I followed your direction but unfortunately import path is not being detected by go.

Which option did you use to build the application? The required one is MSYS2 MinGW 64 bit.
fff

Yes that one
image

Please show me the output of these 2 commands:

pacman -Ql mingw-w64-x86_64-ffmpeg | grep "include"
pacman -Ql mingw-w64-x86_64-ffmpeg | grep ".dll"

$ pacman -Ql mingw-w64-x86_64-ffmpeg | grep "include"
mingw-w64-x86_64-ffmpeg /mingw64/include/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/ac3_parser.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/adts_parser.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/avcodec.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/avdct.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/avfft.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/bsf.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/codec.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/codec_desc.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/codec_id.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/codec_par.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/d3d11va.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/dirac.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/dv_profile.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/dxva2.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/jni.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/mediacodec.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/packet.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/qsv.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/vaapi.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/vdpau.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/videotoolbox.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/vorbis_parser.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavcodec/xvmc.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavdevice/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavdevice/avdevice.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavdevice/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavfilter/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavfilter/avfilter.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavfilter/buffersink.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavfilter/buffersrc.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavfilter/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavformat/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavformat/avformat.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavformat/avio.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavformat/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/adler32.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/aes.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/aes_ctr.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/attributes.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/audio_fifo.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/avassert.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/avconfig.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/avstring.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/avutil.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/base64.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/blowfish.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/bprint.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/bswap.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/buffer.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/camellia.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/cast5.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/channel_layout.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/common.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/cpu.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/crc.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/des.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/dict.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/display.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/dovi_meta.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/downmix_info.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/encryption_info.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/error.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/eval.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/ffversion.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/fifo.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/file.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/film_grain_params.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/frame.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hash.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hdr_dynamic_metadata.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hmac.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_cuda.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_d3d11va.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_drm.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_dxva2.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_mediacodec.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_opencl.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_qsv.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_vaapi.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_vdpau.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_videotoolbox.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/hwcontext_vulkan.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/imgutils.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/intfloat.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/intreadwrite.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/lfg.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/log.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/lzo.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/macros.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/mastering_display_metadata.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/mathematics.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/md5.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/mem.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/motion_vector.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/murmur3.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/opt.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/parseutils.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/pixdesc.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/pixelutils.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/pixfmt.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/random_seed.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/rational.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/rc4.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/replaygain.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/ripemd.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/samplefmt.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/sha.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/sha512.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/spherical.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/stereo3d.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/tea.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/threadmessage.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/time.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/timecode.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/timestamp.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/tree.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/twofish.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/tx.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/video_enc_params.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libavutil/xtea.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libpostproc/
mingw-w64-x86_64-ffmpeg /mingw64/include/libpostproc/postprocess.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libpostproc/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libswresample/
mingw-w64-x86_64-ffmpeg /mingw64/include/libswresample/swresample.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libswresample/version.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libswscale/
mingw-w64-x86_64-ffmpeg /mingw64/include/libswscale/swscale.h
mingw-w64-x86_64-ffmpeg /mingw64/include/libswscale/version.h

$ pacman -Ql mingw-w64-x86_64-ffmpeg | grep ".dll"
mingw-w64-x86_64-ffmpeg /mingw64/bin/avcodec-58.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/avdevice-58.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/avfilter-7.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/avformat-58.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/avutil-56.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/postproc-55.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/swresample-3.dll
mingw-w64-x86_64-ffmpeg /mingw64/bin/swscale-5.dll
mingw-w64-x86_64-ffmpeg /mingw64/lib/libavcodec.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libavdevice.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libavfilter.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libavformat.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libavutil.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libpostproc.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libswresample.dll.a
mingw-w64-x86_64-ffmpeg /mingw64/lib/libswscale.dll.a

Well, for some reason MinGW doesn't see the header files for ffmpeg. You see, if it was a C program, you could just compile it like that:

gcc -I./include -o main ...etc.

Where -I is a linker flag to specify the directory with header files (./include in this case). So you need to somehow pass the include path to CGO. I don't really know the best way to do that because I can't reproduce your issue (I've never seen MSYS2 returning errors like that one when everything is set up properly), but I suppose you could use it like

go build -ldflags "-s -w -H=windowsgui -I/mingw64/include" -o player.exe

or

go build -ldflags "-s -w -H=windowsgui -I=/mingw64/include" -o player.exe

I'm not sure which one is correct for CGO. Also there are LDFLAGS sections in the start of many Reisen source code files, for example:

package reisen

// #cgo LDFLAGS: -lavutil -lavformat -lavcodec -lswresample

I think the other way may be to append -I/mingw64/include or -I=/mingw64/include in the beginning of each CGO section of Reisen source code like this:

package reisen

// #cgo LDFLAGS: -I/mingw64/include  -lavutil -lavformat -lavcodec -lswresample

But I'm not gonna do that myself because those paths might be machine-specific.

I tried that before asking about it. But none of them works. I might just reset and let you know if it solves the problem.

Okay I had finally was able to build after full windows reset. I don't know what caused it but it's working fine now.

As the StackOverflow topic states, you can obtain the list of all the DLLs required by an executable using dumpbin from the Visual Studio command prompt toolset. But I haven't checked it yet because Visual Studio installation process takes a huge amount of time and I don't don't need that IDE im my usual development stack at all.
Dumpbin didn't list any dependencies.

For that I have an easy solution. Just deleting all files in the bin folder while the exe is running filters out all unused binaries.

After running the file I have now found out it causes memory usage upto 4gb with the demo files.

Yeah, 4 GB is the frame buffer size in the constants section (the number of frames stored). At the time of writing the example player I didn't really care about optimizing the capacity so I just put something big. You can decrease the size and the player will still work.

I'm glad you managed to build the application. Your issue was quite a contribution. Thank you again.

Please close the issue if there's no problems anymore.

Commenting on a closed issue because I think this stuff deserves a place in the documentation rather than a handful of notes in an issue. I like the idea of using reisen along with ebiten but getting the dependancies all in a line seems somewhat tricky. Then I'll actually want CI/CD which inevitably means cross-compiling and that sounds like a huge undertaking.

Commenting on a closed issue because I think this stuff deserves a place in the documentation rather than a handful of notes in an issue. I like the idea of using reisen along with ebiten but getting the dependancies all in a line seems somewhat tricky. Then I'll actually want CI/CD which inevitably means cross-compiling and that sounds like a huge undertaking.

True . Ci is needed

it’s not super hard thanks to GitHub actions supporting all operating systems .

a makefile that works locally can also be called from CI

The thing lacking for now is managing the libav binaries and packing for all operating systems ( win, max, Linux)

i have no idea if libav works on iOS or android . Anyone know ?

@iMartyn @gedw99

Ok, then I'll soon add a comprehensive guide about building Reisen on Windows with a full list of required DLLs on the project wiki.

After it I will set up the CI/CD via GitHub Actions. I'll try to do that as soon as I can. But if someone can propose a solution via a PR, I'll check and accept it.

Thanks

I use this to use a makefile in GitHub actions. Builds on all operating systems.

the Fyne and Gio and other golang gui projects that import this code can also use it if they want.

I am using this golang package with gio.

https://github.com/amplify-edge/sys/blob/master/.github/workflows/build.yml

Just copy it exactly

The make target is a variable of the GitHub action file . See line 17 . It’s called “all”

So “all” should be designed to pull the pull any deps, build and test the code.

Makes sense ? Just ask if it does not.

I am on Mac , not windows. Am currently using brew for pulling deps. If someone works out a better way where we pull directly from wherever libav binaries live and are cross platform it would help .

https://github.com/amplify-edge/booty/tree/master/internal/downloader Shows an agnostic way to download the binaries dependencies .

Suggest that this approach / code is used .

it will help for CI and non CI because it’s a cross platform way to grab the binaries needed .

So with the CI and the Downloader we can achieve cross platform builds in CI and single platform locally .

If I did not explain this well please feel free to ask.

@zergon321
I missed the fact that this issue is closed . Let me know if the two items above are useful to you .

i can help with them if you like their approach .

I used them on other projects and it worked well.

i don’t know where to download the binary dependencies for every Operating system btw. Would be useful to work that out first I think ?

Not sure about it yet. I'll look into all the available options of CI/CD, yours too.

But first I will create a list of all the DLLs required to build the applications that use Reisen.

The whole process of building and bundling the examples on Windows has been summarized in this tutorial. It uses exedep command line utility to find all the DLL dependencies of the application. The pipeline described there is easy to reproduce for any other Go application that uses CGO dependencies on Windows.

@zergon321 wow you wrote a tool just for this .

i Wonder if I can do the same on Mac and Linux , although pkgconfig is the standard way for Mac and Linux.

i will read the tutorial and post back here .

I am hoping we can unify this for all OS’s