Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,67 @@ jobs:
path: ${{ env.CCACHE_DIR }}
key: ${{ env.TARGET }}-${{ steps.get_time.outputs.timestamp }}

mingw64-fat:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
variant: [gpl, lgpl]
arch: [win64, winarm64]
container:
image: ghcr.io/btbn/ffmpeg-builds/${{ matrix.arch }}-${{ matrix.variant }}:latest
env:
WINEPREFIX: /tmp/wine
steps:
- uses: actions/checkout@v6

- name: Build
id: build
run: |
./ci/build-mingw64-fat.sh
env:
GPL: ${{ matrix.variant == 'gpl' }}

- name: Print meson log
if: ${{ failure() && steps.build.outcome == 'failure' }}
run: |
cat ./build/meson-logs/meson-log.txt

- name: Functional test
if: ${{ matrix.variant == 'gpl' && matrix.arch == 'win64' }}
run: |
cd artifact && wine ./mpv.com -v --no-config

- name: Run meson tests
if: ${{ matrix.arch == 'win64' }}
id: tests
run: |
meson test -C build

- name: Print meson test log
if: ${{ failure() && steps.tests.outcome == 'failure' }}
run: |
cat ./build/meson-logs/testlog.txt

- name: Artifact name
id: artifact
run: |
VERSION_FILE=$(find . -path "*/common/version.h" -type f | head -n 1)
VER=$(grep VERSION "$VERSION_FILE" | cut -d'"' -f2)
PR=${{ github.event.pull_request.number && format('-pr{0}', github.event.pull_request.number) || '' }}
echo "name=mpv-$VER$PR-${{ github.run_id }}-${{ matrix.arch }}-${{ matrix.variant }}" >> "$GITHUB_OUTPUT"

- uses: actions/upload-artifact@v5
if: ${{ matrix.variant == 'gpl' }}
with:
name: ${{ steps.artifact.outputs.name }}
path: artifact/*

- uses: actions/upload-artifact@v5
with:
name: ${{ steps.artifact.outputs.name }}-dev
path: artifact-dev/*

win32:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/comment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
def link: "https://nightly.link/${{ github.repository }}/actions/artifacts/\(.id).zip";
def entry: "* [\(.name)](\(link))";
"Download the artifacts for this pull request:\n<details><summary>Windows</summary>\n\n" +
(sort_by(.name) | map(select(.name | test("w64|msvc")) | entry) | join("\n")) +
(sort_by(.name) | map(select(.name | test("w64|win64|winarm64|msvc")) | entry) | join("\n")) +
"\n</details>\n<details><summary>macOS</summary>\n\n" +
(sort_by(.name) | map(select(.name | test("macos")) | entry) | join("\n")) +
"\n</details>"
Expand Down
10 changes: 5 additions & 5 deletions audio/out/ao_lavc.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static int init(struct ao *ao)
{
struct priv *ac = ao->priv;

ac->enc = encoder_context_alloc(ao->encode_lavc_ctx, STREAM_AUDIO, ao->log);
ac->enc = mp_encoder_context_alloc(ao->encode_lavc_ctx, STREAM_AUDIO, ao->log);
if (!ac->enc)
return -1;
talloc_steal(ac, ac->enc);
Expand Down Expand Up @@ -137,10 +137,10 @@ static int init(struct ao *ao)
encoder->sample_fmt = af_to_avformat(ao->format);
encoder->bits_per_raw_sample = ac->sample_size * 8;

if (!encoder_init_codec_and_muxer(ac->enc))
if (!mp_encoder_init_codec_and_muxer(ac->enc))
goto fail;

ac->worst_time_base = encoder_get_mux_timebase_unlocked(ac->enc);
ac->worst_time_base = mp_encoder_get_mux_timebase_unlocked(ac->enc);
ac->pcmhack = 0;
if (encoder->frame_size <= 1)
ac->pcmhack = av_get_bits_per_sample(encoder->codec_id) / 8;
Expand Down Expand Up @@ -183,7 +183,7 @@ static void uninit(struct ao *ao)
if (!ac->shutdown) {
if (!write_frame(ao, MP_EOF_FRAME))
MP_WARN(ao, "could not flush last frame\n");
encoder_encode(ac->enc, NULL);
mp_encoder_encode(ac->enc, NULL);
}

talloc_free(ac->filter_root);
Expand Down Expand Up @@ -217,7 +217,7 @@ static void encode(struct ao *ao, struct mp_aframe *af)
ac->lastpts = frame_pts;

frame->quality = encoder->global_quality;
encoder_encode(ac->enc, frame);
mp_encoder_encode(ac->enc, frame);
av_frame_free(&frame);
}

Expand Down
105 changes: 105 additions & 0 deletions ci/build-mingw64-fat.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
set -euo pipefail

. ./ci/build-common.sh

CFLAGS="${CFLAGS/-I${FFBUILD_PREFIX}\/include/-isystem${FFBUILD_PREFIX}/include}"
CXXFLAGS="${CXXFLAGS/-I${FFBUILD_PREFIX}\/include/-isystem${FFBUILD_PREFIX}/include}"

gitclone="git clone --depth=1 --recursive --shallow-submodules"
group_start() { echo "::group::$1"; }
group_end() { echo "::endgroup::"; }


group_start "Building FFmpeg"
$gitclone https://github.com/FFmpeg/FFmpeg.git ffmpeg
pushd ffmpeg
./configure --prefix="$FFBUILD_PREFIX" --pkg-config-flags="--static" \
$FFBUILD_TARGET_FLAGS $FF_CONFIGURE \
--extra-cflags="$FF_CFLAGS" \
--extra-cxxflags="$FF_CXXFLAGS" \
--extra-libs="$FF_LIBS" \
--extra-ldflags="$FF_LDFLAGS" \
--extra-ldexeflags="$FF_LDEXEFLAGS" \
--cc="$CC" --cxx="$CXX" \
--ar="$AR" --ranlib="$RANLIB" --nm="$NM" \
--disable-librav1e --disable-openal \
--enable-static --disable-shared \
--disable-debug --disable-doc --disable-programs
make -j`nproc` && make install
popd
group_end

group_start "Building LuaJIT"
$gitclone -b v2.1 https://github.com/LuaJIT/LuaJIT.git
pushd LuaJIT
# LuaJIT's install target doesn't really support cross-compilation, apply minor patches.
sed -i "s|^prefix=/usr/local|prefix=${FFBUILD_PREFIX}|" etc/luajit.pc
# Strip -ldl, not needed for Windows.
sed -i "/^Libs\.private/d" etc/luajit.pc
# FILE_T and INSTALL_DEP are only needed to glue install target.
make TARGET_SYS=Windows PREFIX="$FFBUILD_PREFIX" HOST_CC="$HOST_CC" \
CFLAGS="$HOST_CFLAGS" \CROSS="${FFBUILD_TOOLCHAIN}-" TARGET_CFLAGS="$CFLAGS" \
BUILDMODE=static XCFLAGS=-DLUAJIT_ENABLE_LUA52COMPAT FILE_T=luajit.exe \
INSTALL_DEP=src/luajit.exe amalg install
popd
group_end

group_start "Building subrandr"
build_subrandr "$FFBUILD_PREFIX" --target "$FFBUILD_RUST_TARGET" \
--static-library true --shared-library false
group_end

group_start "Building cppwinrt"
cppwinrt_ver=2.0.250303.1
windows_rs_ver=73
wget -q "https://github.com/microsoft/cppwinrt/archive/${cppwinrt_ver}.tar.gz" -O cppwinrt.tar.gz
wget -q "https://github.com/microsoft/windows-rs/archive/${windows_rs_ver}.tar.gz" -O windows-rs.tar.gz
tar -xzf cppwinrt.tar.gz
tar -xzf windows-rs.tar.gz
mkdir -p "cppwinrt-$cppwinrt_ver/build"
pushd "cppwinrt-$cppwinrt_ver/build"
CFLAGS="$HOST_CFLAGS" CXXFLAGS="$HOST_CXXFLAGS" \
cmake -GNinja -DCMAKE_CXX_COMPILER="$HOST_CXX" \
-DCMAKE_BUILD_TYPE=Release \
-DCPPWINRT_BUILD_VERSION="$cppwinrt_ver" ..
ninja cppwinrt
./cppwinrt -input "../../windows-rs-$windows_rs_ver/crates/libs/bindgen/default" \
-output "$FFBUILD_PREFIX/include"
popd
group_end

group_start "Setting up Meson wraps"
mkdir -p subprojects
meson wrap install mujs
meson subprojects download
group_end

group_start "Building mpv"
meson setup build --cross-file /cross.meson \
$common_args \
--buildtype=release \
--prefer-static \
--default-library=shared \
--prefix="$FFBUILD_PREFIX" \
-Dc_link_args="$FF_LIBS" \
-Dcpp_link_args="$FF_LIBS" \
-Dgpl=${GPL:-false} \
--force-fallback-for=mujs \
-Dmujs:werror=false \
-Dmujs:default_library=static \
-Dlua=luajit \
-D{amf,d3d11,javascript,lua,shaderc,spirv-cross,subrandr,vulkan,win32-smtc}=enabled
meson compile -C build
group_end

group_start "Packaging artifacts"
mkdir -p artifact artifact-dev
# mpv
cp -pv etc/mpv-*.bat build/mpv.{exe,com} artifact/

# libmpv
cp -pv build/libmpv*.dll{,.a} artifact-dev/
mkdir -p artifact-dev/include/mpv
cp -pv include/mpv/*.h artifact-dev/include/mpv/
group_end
10 changes: 5 additions & 5 deletions common/encode_lavc.c
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ static void encode_lavc_add_packet(struct mux_stream *dst, AVPacket *pkt)
av_packet_unref(pkt);
}

AVRational encoder_get_mux_timebase_unlocked(struct encoder_context *p)
AVRational mp_encoder_get_mux_timebase_unlocked(struct encoder_context *p)
{
return p->mux_stream->st->time_base;
}
Expand Down Expand Up @@ -746,7 +746,7 @@ bool encode_lavc_stream_type_ok(struct encode_lavc_context *ctx,
return !!find_codec_for(ctx, type, &auto_codec) || !auto_codec;
}

struct encoder_context *encoder_context_alloc(struct encode_lavc_context *ctx,
struct encoder_context *mp_encoder_context_alloc(struct encode_lavc_context *ctx,
enum stream_type type,
struct mp_log *log)
{
Expand Down Expand Up @@ -826,7 +826,7 @@ static void encoder_2pass_prepare(struct encoder_context *p)
talloc_free(filename);
}

bool encoder_init_codec_and_muxer(struct encoder_context *p)
bool mp_encoder_init_codec_and_muxer(struct encoder_context *p)
{
mp_assert(!avcodec_is_open(p->encoder));

Expand Down Expand Up @@ -893,7 +893,7 @@ bool encoder_init_codec_and_muxer(struct encoder_context *p)
return false;
}

bool encoder_encode(struct encoder_context *p, AVFrame *frame)
bool mp_encoder_encode(struct encoder_context *p, AVFrame *frame)
{
int status = avcodec_send_frame(p->encoder, frame);
if (status < 0) {
Expand Down Expand Up @@ -929,7 +929,7 @@ bool encoder_encode(struct encoder_context *p, AVFrame *frame)
return false;
}

void encoder_update_log(struct mpv_global *global)
void mp_encoder_update_log(struct mpv_global *global)
{
struct encode_opts *options = mp_get_config_group(NULL, global, &encode_config);
if (options->file && (!strcmp(options->file, "-") ||
Expand Down
12 changes: 6 additions & 6 deletions common/encode_lavc.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct encode_lavc_context {
// Static information after encoder init. This never changes (even if there are
// dynamic runtime changes, they have to work over AVPacket side data).
// For use in encoder_context, most fields are copied from encoder_context.encoder
// by encoder_init_codec_and_muxer().
// by mp_encoder_init_codec_and_muxer().
struct encoder_stream_info {
AVRational timebase; // timebase used by the encoder (in frames/out packets)
AVCodecParameters *codecpar;
Expand Down Expand Up @@ -91,20 +91,20 @@ struct encoder_context {
// Free with talloc_free(). (Keep in mind actual deinitialization requires
// sending a flush packet.)
// This can fail and return NULL.
struct encoder_context *encoder_context_alloc(struct encode_lavc_context *ctx,
struct encoder_context *mp_encoder_context_alloc(struct encode_lavc_context *ctx,
enum stream_type type,
struct mp_log *log);

// After setting your codec parameters on p->encoder, you call this to "open"
// the encoder. This also initializes p->mux_stream. Returns false on failure.
bool encoder_init_codec_and_muxer(struct encoder_context *p);
bool mp_encoder_init_codec_and_muxer(struct encoder_context *p);

// Encode the frame and write the packet. frame is ref'ed as need.
bool encoder_encode(struct encoder_context *p, AVFrame *frame);
bool mp_encoder_encode(struct encoder_context *p, AVFrame *frame);

// Return muxer timebase (only available if p->mux_stream is initialized).
AVRational encoder_get_mux_timebase_unlocked(struct encoder_context *p);
AVRational mp_encoder_get_mux_timebase_unlocked(struct encoder_context *p);

void encoder_update_log(struct mpv_global *global);
void mp_encoder_update_log(struct mpv_global *global);

#endif
2 changes: 1 addition & 1 deletion player/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ void mp_update_logging(struct MPContext *mpctx, bool preinit)
terminal_setup_getch(mpctx->input);

if (enabled)
encoder_update_log(mpctx->global);
mp_encoder_update_log(mpctx->global);
}

void mp_print_version(struct mp_log *log, int always)
Expand Down
8 changes: 4 additions & 4 deletions video/out/vo_lavc.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ struct priv {
static int preinit(struct vo *vo)
{
struct priv *vc = vo->priv;
vc->enc = encoder_context_alloc(vo->encode_lavc_ctx, STREAM_VIDEO, vo->log);
vc->enc = mp_encoder_context_alloc(vo->encode_lavc_ctx, STREAM_VIDEO, vo->log);
if (!vc->enc)
return -1;
talloc_steal(vc, vc->enc);
Expand All @@ -59,7 +59,7 @@ static void uninit(struct vo *vo)
struct encoder_context *enc = vc->enc;

if (!vc->shutdown)
encoder_encode(enc, NULL); // finish encoding
mp_encoder_encode(enc, NULL); // finish encoding
}

static int reconfig2(struct vo *vo, struct mp_image *img)
Expand Down Expand Up @@ -143,7 +143,7 @@ static int reconfig2(struct vo *vo, struct mp_image *img)
else
encoder->framerate = (AVRational){ 240, 1 };

if (!encoder_init_codec_and_muxer(vc->enc))
if (!mp_encoder_init_codec_and_muxer(vc->enc))
goto error;

return 0;
Expand Down Expand Up @@ -232,7 +232,7 @@ static bool draw_frame(struct vo *vo, struct vo_frame *voframe)
frame->pts = rint(outpts * av_q2d(av_inv_q(avc->time_base)));
frame->pict_type = 0; // keep this at unknown/undefined
frame->quality = avc->global_quality;
encoder_encode(enc, frame);
mp_encoder_encode(enc, frame);
av_frame_free(&frame);

done:
Expand Down
10 changes: 9 additions & 1 deletion video/out/vulkan/context_win.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,15 @@ static bool win_init(struct ra_ctx *ctx)
};

VkInstance inst = vk->vkinst->instance;
VkResult res = vkCreateWin32SurfaceKHR(inst, &wininfo, NULL, &vk->surface);
mp_assert(vk->vkinst->get_proc_addr);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is depending on the upstream vulkan loader that bad? 🤔

Copy link
Copy Markdown
Member Author

@kasper93 kasper93 Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstream Vulkan loader doesn't support static linking. That's why all our (other) packages have vulkan-1.dll in them. Apparently https://github.com/BtbN/Vulkan-Shim-Loader exists to workaround that. But it doesn't load functions from platform specific xmls. While, it could be fixed there, I don't mind loading it through get_proc_addr.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this is a static build? dynamic is nicer IMO

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't it just link to vulkan dll? Windows now includes vulkan loader.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this is a static build? dynamic is nicer IMO

This build has probably over 100 libraries linked (I didn't even count, it's everything), linking it statically just makes the output cleaner, we don't need anything else to interact with those dlls.

Why can't it just link to vulkan dll? Windows now includes vulkan loader.

It's on graphic driver, and on some systems in might not be available. Either way, it's up to docker image that I use. I think it's cleaner to dynamically load vulkan dll. We should be doing it in fact and not directly link to loader.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this is a static build? dynamic is nicer IMO

What makes dynamic mpv Windows compiles nicer for you? Legit question.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dynamic causes less weird build issues, that's all.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic linking on MinGW only introduces more problems, like auto import and runtime pseudo relocation.

PFN_vkCreateWin32SurfaceKHR pCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR)
vk->vkinst->get_proc_addr(inst, "vkCreateWin32SurfaceKHR");
if (!pCreateWin32SurfaceKHR) {
MP_MSG(ctx, msgl, "Failed to load vkCreateWin32SurfaceKHR\n");
goto error;
}

VkResult res = pCreateWin32SurfaceKHR(inst, &wininfo, NULL, &vk->surface);
if (res != VK_SUCCESS) {
MP_MSG(ctx, msgl, "Failed creating Windows surface\n");
goto error;
Expand Down
6 changes: 5 additions & 1 deletion video/out/vulkan/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ void mpvk_uninit(struct mpvk_ctx *vk)
{
if (vk->surface) {
mp_assert(vk->vkinst);
vkDestroySurfaceKHR(vk->vkinst->instance, vk->surface, NULL);
mp_assert(vk->vkinst->get_proc_addr);
PFN_vkDestroySurfaceKHR pDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)
vk->vkinst->get_proc_addr(vk->vkinst->instance, "vkDestroySurfaceKHR");
if (pDestroySurfaceKHR)
pDestroySurfaceKHR(vk->vkinst->instance, vk->surface, NULL);
vk->surface = VK_NULL_HANDLE;
}

Expand Down