libmpv binder creation | mpv API error: invalid parameter

External libraries (GTK, GSL, SDL, Allegro, OpenGL, etc) questions.
Post Reply
Axle
Posts: 67
Joined: May 31, 2022 6:49
Location: Australia

libmpv binder creation | mpv API error: invalid parameter

Post by Axle »

Hi forum

I Have recently embarked upon creating some base examples using libmpv.
https://github.com/mpv-player/mpv/tree/master/libmpv

I have Done just a basic command line video splash screen in the native C API (will look at embedding the playback window in SDL2 or raylib/raygui at a latter date) and also plan on doing some Python examples of which 2 binders are readily available.
FreeBASIC doesn't currently have a libmpv binder, so I have rolled one up with FBFrog to have a shot at it.
I am only using the libmpv client.h and the shared libmpv-2.dll at the moment. The definitions and declarations of the output FBFrog client is only about 300 lines so not too much.

I am currently stuck on 2 issues that I can't seam to find a way past and both may be related. It's difficult to debug what is going on inside the dll so I am attempting to be pedantic with the data being sent to the dll.

Issue one is with type name conflicts between FB and C. As and example the C routines are using "char *string;" in one of the structures which conflicts with the FB String type:

Code: Select all

typedef struct mpv_node {
    union {
        char *string;   /** valid if format==MPV_FORMAT_STRING */
        int flag;       /** valid if format==MPV_FORMAT_FLAG   */
        int64_t int64;  /** valid if format==MPV_FORMAT_INT64  */
        double double_; /** valid if format==MPV_FORMAT_DOUBLE */
        struct mpv_node_list *list;
        struct mpv_byte_array *ba;
    } u;

Code: Select all

union mpv_node_u
	string as zstring ptr
	flag as long
	int64 as longint
	double_ as double
	list as mpv_node_list ptr
	ba as mpv_byte_array ptr
end union
I recall reading somewhere previously that I can create an alias for "string as zstring ptr" to get around the conflict?
I have about 6 of these type conflicts to correct.

Second issue is in sending a 2D array of zstring Ptr to the dll
I feel sure that I have constructed the array correctly but sending it to the libmpv-2.dll via the client.bi returns an error message:
mpv API error: invalid parameter
I am uncertain if I am mangling the pointers via the binder, or if it is related to the type name conflict above.
Here is the basic routines.
In C:

Code: Select all

const char *cmd1[] = {"loadfile", "donkey.mp4", NULL};  // A 2D static array **cmd1
mpv_command(ctx, cmd1)  // The function call. ctx is the handle mpv_handle *ctx = mpv_create();
int mpv_command(mpv_handle *ctx, const char **args)  // the function declaration from the dll header.
In FB

Code: Select all

Dim As zString Ptr cmd1(2) = {@"loadfile", @"donkey.mp4", NULL}
mpv_command(ctx, cmd1())
'declare function mpv_command(byval ctx as mpv_handle ptr, byval args as const zstring ptr ptr) as long  '' FBFrog Output (type error)
declare function mpv_command(byval ctx as mpv_handle ptr, args() as const zstring ptr ptr) as long  '' <- my correction args()
The above gives me the "mpv API error: invalid parameter"

Printing the zString before sending it as an argument appears correct:

Code: Select all

    dim j As Integer
    For j = 0 To 2 Step +1
        Print *cmd1(j); "|"
        'Print *cast(zstring ptr, cmd1(j))
    Next j
Any thoughts on if it's my construction of the zString pointer array? ( I have checked this with the same question I had on raylib binders and seams correct)

Some more context to how mpv_command( , args()) is used within the libmpv source:

Code: Select all

'' client.bi
declare function mpv_command(byval ctx as mpv_handle ptr, args() as const zstring ptr ptr) as long
// C Source
// wrapper
int mpv_command(mpv_handle *ctx, const char **args)
{
    return run_client_command(ctx, mp_input_parse_cmd_strv(ctx->log, args), NULL);
}

Code: Select all

// tokenise the array of 3 strings to a node structure
struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, const char **argv)
{
    int count = 0;
    while (argv[count])
        count++;
    mpv_node *items = talloc_zero_array(NULL, mpv_node, count);
    mpv_node_list list = {.values = items, .num = count};
    mpv_node node = {.format = MPV_FORMAT_NODE_ARRAY, .u = {.list = &list}};
    for (int n = 0; n < count; n++) {
        items[n] = (mpv_node){.format = MPV_FORMAT_STRING,
                              .u = {.string = (char *)argv[n]}};
    }
    struct mp_cmd *res = mp_input_parse_cmd_node(log, &node);
    talloc_free(items);
    return res;
}

Code: Select all

// The structure
typedef struct mp_cmd {
    char *name;
    struct mp_cmd_arg *args;
    int nargs;
    int flags; // mp_cmd_flags bitfield
    char *original;
    char *desc; // (usually NULL since stripped away later)
    char *input_section;
    bool is_up_down : 1;
    bool is_up : 1;
    bool emit_on_up : 1;
    bool is_mouse_button : 1;
    bool repeated : 1;
    bool mouse_move : 1;
    int mouse_x, mouse_y;
    struct mp_cmd *queue_next;
    double scale;               // for scaling numeric arguments
    int scale_units;
    const struct mp_cmd_def *def;
    char *sender; // name of the client API user which sent this
    char *key_name; // string representation of the key binding
    char *key_text; // text if key is a text key
} mp_cmd_t;

Code: Select all

// Finally runs node structure with the 3 arg()
static int run_client_command(mpv_handle *ctx, struct mp_cmd *cmd, mpv_node *res)
{
    if (!cmd)
        return MPV_ERROR_INVALID_PARAMETER;
    if (!ctx->mpctx->initialized) {
        talloc_free(cmd);
        return MPV_ERROR_UNINITIALIZED;
    }

    cmd->sender = ctx->name;

    struct cmd_request req = {
        .mpctx = ctx->mpctx,
        .cmd = cmd,
        .res = res,
        .completion = MP_WAITER_INITIALIZER,
    };

    bool async = cmd->flags & MP_ASYNC_CMD;

    lock_core(ctx);
    if (async) {
        run_command(ctx->mpctx, cmd, NULL, NULL, NULL);
    } else {
        struct mp_abort_entry *abort = NULL;
        if (cmd->def->can_abort) {
            abort = talloc_zero(NULL, struct mp_abort_entry);
            abort->client = ctx;
        }
        run_command(ctx->mpctx, cmd, abort, cmd_complete, &req);
    }
    unlock_core(ctx);

    if (!async)
        mp_waiter_wait(&req.completion);

    return req.status;
}
Summary:
1. How to fix the type conflict between C and FB?
2. Does the 2D zString array look correct? or maybe it is just because of the C/BF type conflict 1. causing 2.?

Thanks
Axle :)
fxm
Moderator
Posts: 12193
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: libmpv binder creation | mpv API error: invalid parameter

Post by fxm »

For the second issue, try this:

Code: Select all

declare function mpv_command(byval ctx as mpv_handle ptr, byval args as const zstring ptr ptr) as long

mpv_command(ctx, @cmd1(0))
fxm
Moderator
Posts: 12193
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: libmpv binder creation | mpv API error: invalid parameter

Post by fxm »

First issue:
An FB type name can be used as a member name in a Type/Union FB structure as long as this structure does not contain any member procedures.

What exactly is the error you are getting ?
Axle
Posts: 67
Joined: May 31, 2022 6:49
Location: Australia

Re: libmpv binder creation | mpv API error: invalid parameter

Post by Axle »

fxm wrote: Jun 23, 2023 13:15 For the second issue, try this:

Code: Select all

declare function mpv_command(byval ctx as mpv_handle ptr, byval args as const zstring ptr ptr) as long

mpv_command(ctx, @cmd1(0))
"declare function mpv_command(byval ctx as mpv_handle ptr, byval args as const zstring ptr ptr) as long" was the original output from BFFrog.

I had attempted multiple renditions of this with incompatible type errors including the above solution with incompatible pointer error etc. from FBC. I spent hours attempting different ways to approach it including the original FBFrog conversion without success.

Now I just switched back to the original FBFrog conversion " ... , byval args as const zstring ptr ptr) and complied and run with the MPV player window and video playing as expected lol
Missed it by "That much" as Maxwell Smart would have said.

@fmx I think you have some kind of magical touch just from mentioning the solution the FBC complied lol
It's a bit like quantum "Spooky actions at a distance" :)

Thank you for your mind control over my copy of FBC. As always I am very appreciative, even if it was a rubber duck debugger moment lol
Axle
Posts: 67
Joined: May 31, 2022 6:49
Location: Australia

Re: libmpv binder creation | mpv API error: invalid parameter

Post by Axle »

fxm wrote: Jun 23, 2023 14:15 First issue:
An FB type name can be used as a member name in a Type/Union FB structure as long as this structure does not contain any member procedures.

What exactly is the error you are getting ?
The FB keywords, Types used as variable/object names, only occur in In type definition and unions.
So, it is OK for this usage in a binder to a C library (dll/so). If did this in C or Py (use keywords as variable name) I would have a demon come out of my screen tear a rift in the fabric of space time lol

FB is a curious critter sometimes :)

The binder appears to be working as expected for the moment, but I will throw some usage at it and see if I can break it. At some point I will post the libmpv Media player library binders up on github with the original C comment and description for other FB users that may find it useful. It seams to be a somewhat simpler solution for MMedia embedding over libVLC or using ffmpeg directly.

Thank you @fmx for you help :)

[Edit] Proof of concept.
Direct link:

Image

Imgur:
https://imgur.com/a/rQRQUFx
fxm
Moderator
Posts: 12193
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: libmpv binder creation | mpv API error: invalid parameter

Post by fxm »

In FreeBASIC, an array is not a simple list of elements but a complete structure with a specific descriptor that contains all the information defining the array.
When you pass an array to an FB procedure, you are actually passing the address of its descriptor and not the array data address (the array data address is the address of the first array element).
Axle
Posts: 67
Joined: May 31, 2022 6:49
Location: Australia

Re: libmpv binder creation | mpv API error: invalid parameter

Post by Axle »

fxm wrote: Jun 24, 2023 5:17 In FreeBASIC, an array is not a simple list of elements but a complete structure with a specific descriptor that contains all the information defining the array.
When you pass an array to an FB procedure, you are actually passing the address of its descriptor and not the array data address (the array data address is the address of the first array element).
Thanks @fmx, much the same as Python. Variables are really a pointer to the object, so their is no such thing as passing a variable byref or byval as it is an object. I did make several attampts at passing the pointer reference to the data @agr(0) , @cmd1(0) but obviously managed to miss something on the previous 20 odd compiles lol
Under the FB hood I expect something like a tagVARIANT or tagged union. I recently created a naive tagVARIANT for holding the the five data affinities in SQLite when retrieving mixed data types. (P.S. I grew up on MS BASIC starting at MS BASIC V1.0 Rev 2 lol on OSI C1P and C4P)

Anyways, I have the header and a base example working fine as well as some examples for converting the FB String literals and String Array() to zString literals and zString Ptr Array() and vice versa for working with the C types in the libmpv header and shared object.

Although not officially part of my books I still have to do this exercise for for SDL_BGI (graphics.h) (Using raylib/raygui in the books)
I had a lot of fun with SDL_BGI and it's historical relevance with BASIC QBASIC etc prob makes it worth a shot :)

Thanks again @fmx. Always appreciate your words of wisdom :)
Axle

P.S. Just have to finish tidying up that binder and libmpv example and I will upload it to github and drop a link here :)
Post Reply