sdcb / Sdcb.FFmpeg

FFmpeg basic .NET API generated by CppSharp
GNU Lesser General Public License v3.0
334 stars 53 forks source link

Access Violation (av_opt_set_int_list / av_opt_set_bin) #23

Closed SuRGeoNix closed 1 month ago

SuRGeoNix commented 2 months ago

Invalid marshalling from C# array to C array which can be reproduced by using multiple entries (not just one, i think for one would work fine) :-

sinkCtx.Options.Set("pix_fmts", [ (int)AVPixelFormat.None, (int)AVPixelFormat.Yuv444p ], AV_OPT_SEARCH.Children);

Currently using the following helpers to resolve this (that might help) :-

public static int av_opt_set_int_list<T>(void* obj, string name, List<T>? value, OptSearchFlags flags) where T : Enum
{
    if (value == null || value.Count == 0)
        return 0;

    Span<int> arr = stackalloc int[value.Count];
    for (int i = 0; i < value.Count; i++)
        arr[i] =(int) Enum.Parse(typeof(T), value[i].ToString());

    int ret;
    fixed(int* ptr = arr)
        ret = av_opt_set_bin(obj, name, (byte*)ptr, sizeof(int) * value.Count, flags);

    return ret;
}

public static int av_opt_set_int_list(void* obj, string name, List<int>? value, OptSearchFlags flags)
{
    if (value == null || value.Count == 0)
        return 0;

    Span<int> arr = stackalloc int[value.Count];
    for (int i = 0; i < value.Count; i++)
        arr[i] = value[i];

    int ret;
    fixed(int* ptr = arr)
        ret = av_opt_set_bin(obj, name, (byte*)ptr, sizeof(int) * value.Count, flags);

    return ret;

}
SuRGeoNix commented 2 months ago

Here an update for easy copy/paste in FFmpegOptions.cs

public void Set<T>(string name, T[] value, AV_OPT_SEARCH searchFlags = AV_OPT_SEARCH.Children) where T : Enum
{
    Span<int> arr = stackalloc int[value.Length];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = Convert.ToInt32(value[i]);

    fixed(int* ptr = arr)
        av_opt_set_bin(_obj, name, (byte*)ptr, sizeof(int) * arr.Length, (int)searchFlags).ThrowIfError();
}

public void Set<T>(string name, List<T> value, AV_OPT_SEARCH searchFlags = AV_OPT_SEARCH.Children) where T : Enum
{
    Span<int> arr = stackalloc int[value.Count];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = Convert.ToInt32(value[i]);

    fixed(int* ptr = arr)
        av_opt_set_bin(_obj, name, (byte*)ptr, sizeof(int) * arr.Length, (int)searchFlags).ThrowIfError();
}

public void Set(string name, int[] value, AV_OPT_SEARCH searchFlags = AV_OPT_SEARCH.Children)
{
    Span<int> arr = stackalloc int[value.Length];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = value[i];

    fixed(int* ptr = arr)
        av_opt_set_bin(_obj, name, (byte*)ptr, sizeof(int) * arr.Length, (int)searchFlags).ThrowIfError();
}

public void Set(string name, List<int> value, AV_OPT_SEARCH searchFlags = AV_OPT_SEARCH.Children)
{
    Span<int> arr = stackalloc int[value.Count];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = value[i];

    fixed(int* ptr = arr)
        av_opt_set_bin(_obj, name, (byte*)ptr, sizeof(int) * arr.Length, (int)searchFlags).ThrowIfError();
}

public void Set(string name, ulong[] value, AV_OPT_SEARCH searchFlags = AV_OPT_SEARCH.Children)
{
    Span<ulong> arr = stackalloc ulong[value.Length];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = value[i];

    fixed(ulong* ptr = arr)
        av_opt_set_bin(_obj, name, (byte*)ptr, sizeof(ulong) * arr.Length, (int)searchFlags).ThrowIfError();
}
SuRGeoNix commented 1 month ago

That was another issue, this is happening when passing AVPixelFormat.None (which is -1 = FF FF FF FF) not sure why (probably FFmpeg assert down to set pixel formats in options) but that's not because what I thought initially. The marshalling works fine, even if the C# array has an overhead the fixed pointer will point directly to the data and not at the start of the actual array (pointing to overhead).