BASS_FFMPEG

Started by pudding,

pudding

Hello, I have been working on a new plugin: https://github.com/pudding-fox/BASS_FFMPEG
This adds support for many formats. I wrote this mainly to support .mka and atmos .m4a files.

It is mostly working, seeking seems to be very slightly off but I doubt it's noticeable in practice.

Ian @ un4seen

Nice. The BASS webpage now includes a link to it.

One issue I notice in the bass_ffmpeg.c code is that the (Can)SetPosition functions aren't ignoring flags in the "mode" parameter, which means seeking may not work sometimes. Instead of this:

    if (mode == BASS_POS_BYTE) {

Those functions should do this:

    if ((BYTE)mode == BASS_POS_BYTE) { // ignore flags

I think your BASS_DTS add-on has/had the same issue.

You also don't need to provide a GetPosition function for the BASS_POS_BYTE mode because BASS handles that itself, unless you also set the ADDON_OWNPOS flag. The ADDON_OWNPOS flag should only be used when the add-on isn't always going forwards at normal speed, eg. the BASS_FX add-on uses it on tempo and reverse streams.

QuentinC

Hello,

I'm quite interested by this FFMPEG plugin.

However, it doesn't work for me.
When trying to load the 64 bit version of the DLL, BASS_PluginLoad returns error BASS_ERROR_FILEOPEN (2). Just in case, I have also tried the 32 bit version although my program is compiled in 64 bit. The 32 bit version returns BASS_ERROR_FILEFORM (41) (this later is of course not a surprise).
I have no other error or info to give, sadly.

Do I need to do something special except copying bass_ffmpeg.dll and load it via BASS_PluginLoad ?
Normally, that's the only thing to do with other plugins and it works.

One particularity of my program is that I'm using the compiler MinGW-W64, not MSVC. Maybe it's the cause of my problem, but I don't want to switch to MSVC, since all the rest is working very well with it.
I have no problem at all with BASS otherwise.

AS an extension for the future of your plugin, it would be interesting to be able to list all, and select a different audio track to read.
One possible use is to play or extract the 2nd or 3rd audio track of a video, for those having an "original language" or "audiodescription" audio track.

You may reuse BASS_ChannelGetTag for retrieving the names, and BASS_ChannelGet/SetPosition for retrieving/selecting the audio track number to take, maybe with new BASS_TAG_XXX and BASS_POS_XXX flags, instead of exporting dedicated functions.
This encourage interoperability between plugins, if other formats have similar features. I have already done that kind of thing for some of my own plugins.


I might even help in doing this, if you can find a relatively up to date prebuilt binary version of FFMPEG for Windows 64 bit for MinGW-W64 compiler.
I have already tried to compile FFMPEG myself because I also wanted to make this kind of plugin myself, but it's too complicated and I don't have the patience, I have given up integrating FFMPEG in my own player project for that reason.
For info, my own BASS player is here: https://github.com/qtnc/sample


Thank you !


pudding

Quote from: Ian @ un4seenNice. The BASS webpage now includes a link to it.

One issue I notice in the bass_ffmpeg.c code is that the (Can)SetPosition functions aren't ignoring flags in the "mode" parameter, which means seeking may not work sometimes. Instead of this:

    if (mode == BASS_POS_BYTE) {

Those functions should do this:

    if ((BYTE)mode == BASS_POS_BYTE) { // ignore flags

I think your BASS_DTS add-on has/had the same issue.

You also don't need to provide a GetPosition function for the BASS_POS_BYTE mode because BASS handles that itself, unless you also set the ADDON_OWNPOS flag. The ADDON_OWNPOS flag should only be used when the add-on isn't always going forwards at normal speed, eg. the BASS_FX add-on uses it on tempo and reverse streams.

I'll test this again later, but when I was debugging I had a breakpoint on the last line;

QWORD WINAPI BASS_FFMPEG_GetPosition(void* inst, QWORD position, DWORD mode) {
FFMPEG_STREAM* stream = inst;
if (mode == BASS_POS_BYTE) {
noerrorn(stream->position);
}
else {
errorn(BASS_ERROR_NOTAVAIL); //This line is never hit.
}
}

It never seems to hit, so I don't *think* the (BYTE) cast is needed. But I will add it anyway.

pudding

Quote from: QuentinCHello,

I'm quite interested by this FFMPEG plugin.

However, it doesn't work for me.
When trying to load the 64 bit version of the DLL, BASS_PluginLoad returns error BASS_ERROR_FILEOPEN (2). Just in case, I have also tried the 32 bit version although my program is compiled in 64 bit. The 32 bit version returns BASS_ERROR_FILEFORM (41) (this later is of course not a surprise).
I have no other error or info to give, sadly.

Do I need to do something special except copying bass_ffmpeg.dll and load it via BASS_PluginLoad ?
Normally, that's the only thing to do with other plugins and it works.

One particularity of my program is that I'm using the compiler MinGW-W64, not MSVC. Maybe it's the cause of my problem, but I don't want to switch to MSVC, since all the rest is working very well with it.
I have no problem at all with BASS otherwise.

AS an extension for the future of your plugin, it would be interesting to be able to list all, and select a different audio track to read.
One possible use is to play or extract the 2nd or 3rd audio track of a video, for those having an "original language" or "audiodescription" audio track.

You may reuse BASS_ChannelGetTag for retrieving the names, and BASS_ChannelGet/SetPosition for retrieving/selecting the audio track number to take, maybe with new BASS_TAG_XXX and BASS_POS_XXX flags, instead of exporting dedicated functions.
This encourage interoperability between plugins, if other formats have similar features. I have already done that kind of thing for some of my own plugins.


I might even help in doing this, if you can find a relatively up to date prebuilt binary version of FFMPEG for Windows 64 bit for MinGW-W64 compiler.
I have already tried to compile FFMPEG myself because I also wanted to make this kind of plugin myself, but it's too complicated and I don't have the patience, I have given up integrating FFMPEG in my own player project for that reason.
For info, my own BASS player is here: https://github.com/qtnc/sample


Thank you !



I think the only reason it wouldn't load is you're missing the c runtime dll.
I usually build against the "windows xp driver runtime" which is always available for xp and greater operating systems but the pre-build ffmpeg lib files I used target "vcruntime140d.dll" and like you I don't have the patience to build my own ffmpeg libs.



I'm not sure about reading tags, I use taglib for this. I'll look into it.

Reading alternate audio streams should be possible. Is BASS_POS_XXX standard for this?


Ian @ un4seen

Quote from: puddingI'll test this again later, but when I was debugging I had a breakpoint on the last line;

QWORD WINAPI BASS_FFMPEG_GetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_BYTE) {
        noerrorn(stream->position);
    }
    else {
        errorn(BASS_ERROR_NOTAVAIL); //This line is never hit.
    }
}

It never seems to hit, so I don't *think* the (BYTE) cast is needed. But I will add it anyway.

As I mentioned, because you don't have ADDON_OWNPOS set (in addon_functions), this function won't actually be called with BASS_POS_BYTE because BASS handles that mode itself (it knows how many bytes have been played). So a breakpoint anywhere in it probably wouldn't be hit currently :)

It's the BASS_FFMPEG_(Can)SetPosition functions where the "(BYTE)" cast (to ignore flags) applies.

Quote from: puddingI think the only reason it wouldn't load is you're missing the c runtime dll.
I usually build against the "windows xp driver runtime" which is always available for xp and greater operating systems but the pre-build ffmpeg lib files I used target "vcruntime140d.dll" and like you I don't have the patience to build my own ffmpeg libs.

Perhaps you could link BASS_FFMPEG to the ffmpeg DLLs instead of the static libraries? That would allow users to swap out the ffmpeg DLLs, possibly for builds that use a different runtime version, and BASS_FFMPEG wouldn't need to be rebuilt whenever ffmpeg is updated.

Quote from: puddingReading alternate audio streams should be possible. Is BASS_POS_XXX standard for this?

Yes, there is a BASS_POS_TRACK mode that looks appropriate for this. You would need to add support for that in the BASS_FFMPEG_(Can)SetPosition functions. For example:

BOOL WINAPI BASS_FFMPEG_CanSetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if ((BYTE)mode == BASS_POS_BYTE)
        return ffmpeg_stream_can_seek(stream, position);
    if ((BYTE)mode == BASS_POS_TRACK)
        return ffmpeg_stream_can_seek_track(stream, position);
    error(BASS_ERROR_NOTAVAIL);
}

You would then also reintroduce the BASS_FFMPEG_GetPosition function to handle the BASS_POS_TRACK mode:

QWORD WINAPI BASS_FFMPEG_GetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_TRACK)
        return stream->track;
    errorn(BASS_ERROR_NOTAVAIL);
}


pudding

Quote from: Ian @ un4seen
Quote from: puddingI'll test this again later, but when I was debugging I had a breakpoint on the last line;

QWORD WINAPI BASS_FFMPEG_GetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_BYTE) {
        noerrorn(stream->position);
    }
    else {
        errorn(BASS_ERROR_NOTAVAIL); //This line is never hit.
    }
}

It never seems to hit, so I don't *think* the (BYTE) cast is needed. But I will add it anyway.

As I mentioned, because you don't have ADDON_OWNPOS set (in addon_functions), this function won't actually be called with BASS_POS_BYTE because BASS handles that mode itself (it knows how many bytes have been played). So a breakpoint anywhere in it probably wouldn't be hit currently :)

It's the BASS_FFMPEG_(Can)SetPosition functions where the "(BYTE)" cast (to ignore flags) applies.

Quote from: puddingI think the only reason it wouldn't load is you're missing the c runtime dll.
I usually build against the "windows xp driver runtime" which is always available for xp and greater operating systems but the pre-build ffmpeg lib files I used target "vcruntime140d.dll" and like you I don't have the patience to build my own ffmpeg libs.

Perhaps you could link BASS_FFMPEG to the ffmpeg DLLs instead of the static libraries? That would allow users to swap out the ffmpeg DLLs, possibly for builds that use a different runtime version, and BASS_FFMPEG wouldn't need to be rebuilt whenever ffmpeg is updated.

Quote from: puddingReading alternate audio streams should be possible. Is BASS_POS_XXX standard for this?

Yes, there is a BASS_POS_TRACK mode that looks appropriate for this. You would need to add support for that in the BASS_FFMPEG_(Can)SetPosition functions. For example:

BOOL WINAPI BASS_FFMPEG_CanSetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if ((BYTE)mode == BASS_POS_BYTE)
        return ffmpeg_stream_can_seek(stream, position);
    if ((BYTE)mode == BASS_POS_TRACK)
        return ffmpeg_stream_can_seek_track(stream, position);
    error(BASS_ERROR_NOTAVAIL);
}

You would then also reintroduce the BASS_FFMPEG_GetPosition function to handle the BASS_POS_TRACK mode:

QWORD WINAPI BASS_FFMPEG_GetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_TRACK)
        return stream->track;
    errorn(BASS_ERROR_NOTAVAIL);
}

Like this? https://github.com/pudding-fox/BASS_FFMPEG/commit/4fd0d7a1bee54808819443bb6e4fdc0f86b3806e

Ian @ un4seen

Yep, but also add the "(BYTE)" in BASS_FFMPEG_SetPosition:

QWORD WINAPI BASS_FFMPEG_SetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if ((BYTE)mode == BASS_POS_BYTE) {

pudding

Quote from: Ian @ un4seenYep, but also add the "(BYTE)" in BASS_FFMPEG_SetPosition:

QWORD WINAPI BASS_FFMPEG_SetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if ((BYTE)mode == BASS_POS_BYTE) {


I amended my commmit.

pudding

Tracks can be enumerated and selected: https://github.com/pudding-fox/BASS_FFMPEG/commit/e112eb41ae1e643cbf60aff3abc93823a0fe9af6

There are specific api calls. I'm not sure exactly how this can be done with the existing BASS api, there needs to be some way to get all available tracks ?

Ian @ un4seen

Instead of having separate BASS_FFMPEG_GetTracks and BASS_FFMPEG_SetTrack functions, I would recommend supporting the BASS_POS_TRACK mode in BASS_FFMPEG_GetLength and BASS_FFMPEG_SetPosition. For example:

QWORD WINAPI BASS_FFMPEG_GetLength(void* inst, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_BYTE)
        noerrorn(stream->length);
    if (mode == BASS_POS_TRACK)
        noerrorn(stream->tracks);
    errorn(BASS_ERROR_NOTAVAIL);
}

See also the BASS_FFMPEG_CanSetPosition and BASS_FFMPEG_GetPosition examples that I posted above.

It looks like ManagedBass doesn't include the BASS_POS_TRACK mode yet, but it does have BASS_POS_CD_TRACK ("CDTrack"), which is the same thing (both are mode 4).

pudding

Quote from: Ian @ un4seenInstead of having separate BASS_FFMPEG_GetTracks and BASS_FFMPEG_SetTrack functions, I would recommend supporting the BASS_POS_TRACK mode in BASS_FFMPEG_GetLength and BASS_FFMPEG_SetPosition. For example:

QWORD WINAPI BASS_FFMPEG_GetLength(void* inst, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if (mode == BASS_POS_BYTE)
        noerrorn(stream->length);
    if (mode == BASS_POS_TRACK)
        noerrorn(stream->tracks);
    errorn(BASS_ERROR_NOTAVAIL);
}

See also the BASS_FFMPEG_CanSetPosition and BASS_FFMPEG_GetPosition examples that I posted above.

It looks like ManagedBass doesn't include the BASS_POS_TRACK mode yet, but it does have BASS_POS_CD_TRACK ("CDTrack"), which is the same thing (both are mode 4).

I think this should work, I can't test right now though: https://github.com/pudding-fox/BASS_FFMPEG/commit/eae6ec36070d9fc78c997231cf8d78f1b9efe3f9

Ian @ un4seen

I forgot to mention that when using the BASS_POS_TRACK mode, BASS_FFMPEG_SetPosition should seek to the start of the track and return 0 to indicate that:

    else if ((BYTE)mode == BASS_POS_TRACK) {
        if (ffmpeg_stream_set_track(stream, (DWORD)position))
            return 0; // now at start of track
    }

Also, BASS_FFMPEG_CanSetPosition should fail with a BASS_ERROR_POSITION error when the mode is valid but the position isn't:

BOOL WINAPI BASS_FFMPEG_CanSetPosition(void* inst, QWORD position, DWORD mode) {
    FFMPEG_STREAM* stream = inst;
    if ((BYTE)mode == BASS_POS_BYTE) {
        if (ffmpeg_stream_can_seek(stream, position)) return TRUE;
        error(BASS_ERROR_POSITION);
    }
    else if ((BYTE)mode = BASS_POS_TRACK) {
        if (ffmpeg_stream_get_tracks(stream, NULL, (DWORD)-1) > position) return TRUE;
        error(BASS_ERROR_POSITION);
    }
    else {
        error(BASS_ERROR_NOTAVAIL);
    }
}

If each track has its own length and/or tags then BASS_FFMPEG_GetLength(BASS_POS_BYTE) and/or BASS_FFMEG_GetTags should return the length and tags of the current track.

QuentinC

Hello,

It indeed looks like that bass_ffmpeg.dll doesn't load because vcruntime140d.dll and ucrtbased.dll are missing on my system. Thank you for the indication.
I was able to find the first one on dllfiles.com, but not the second one (the download link doesn't work). So I'm still stuck for the moment.

This is confirmed by the dependency list of your DLL. I can obtain that list with MinGW-W64 and GNU tools, with the following command:
objdump -p bass_ffmpeg.dll | grep "DLL Name:"

However, it looks like your plugin is never supposed to require these files. These DLL are debug versions of the VSC runtime.
It's OK for your own development on your own machine,but not when distributing your plugin.
You should rather link to qcruntime140.dll, which is the normal runtime distributed by Microsoft, without the final d in the name. 
IN fact you probably don't need the debug version of the ffmpeg library, unless you want to debug ffmpeg itself.

Additionally, I agree with Ian, if you can, it would certainly be better to link dynamically to ffmpeg.dll rather than statically, so that an update of ffmpeg wouldn't necessarily require a rebuild of your plugin.
I don't think that there's an issue distributing ffmpeg.dll with bass_ffmpeg.dll in your zip.

Thank you.

pudding

Quote from: QuentinCHello,

It indeed looks like that bass_ffmpeg.dll doesn't load because vcruntime140d.dll and ucrtbased.dll are missing on my system. Thank you for the indication.
I was able to find the first one on dllfiles.com, but not the second one (the download link doesn't work). So I'm still stuck for the moment.

This is confirmed by the dependency list of your DLL. I can obtain that list with MinGW-W64 and GNU tools, with the following command:
objdump -p bass_ffmpeg.dll | grep "DLL Name:"

However, it looks like your plugin is never supposed to require these files. These DLL are debug versions of the VSC runtime.
It's OK for your own development on your own machine,but not when distributing your plugin.
You should rather link to qcruntime140.dll, which is the normal runtime distributed by Microsoft, without the final d in the name. 
IN fact you probably don't need the debug version of the ffmpeg library, unless you want to debug ffmpeg itself.

Additionally, I agree with Ian, if you can, it would certainly be better to link dynamically to ffmpeg.dll rather than statically, so that an update of ffmpeg wouldn't necessarily require a rebuild of your plugin.
I don't think that there's an issue distributing ffmpeg.dll with bass_ffmpeg.dll in your zip.

Thank you.

I'd love to use an ffmpeg.dll but building it is simply beyond my skillset. The only reason I managed to get anything working at all was the fact that I found some .lib/.h files in a random github repo that "worked". Those static libs were built against the "modern" microsoft c runtime and there really isn't much I can do about it. It's possible that a Release build will help a little but who knows...

Ian @ un4seen

Pre-built ffmpeg DLLs are available here:

   https://github.com/BtbN/FFmpeg-Builds/releases
   https://github.com/defisym/FFmpeg-Builds-Win32/releases

You would want the "lgpl-shared" packages. GPL software could use the "gpl-shared" DLLs instead.

pudding

Quote from: Ian @ un4seenPre-built ffmpeg DLLs are available here:

   https://github.com/BtbN/FFmpeg-Builds/releases
   https://github.com/defisym/FFmpeg-Builds-Win32/releases

You would want the "lgpl-shared" packages. GPL software could use the "gpl-shared" DLLs instead.

After some swearing I have managed to rebuild against those lib/dll files.
A new release was published: https://github.com/pudding-fox/BASS_FFMPEG/releases/tag/0.0.5

There were significant changes to the API.

Ian @ un4seen

You can leave out the ffmpeg LIB files (but keep BASS_FFMPEG.LIB). They're needed for building BASS_FFMPEG.DLL but not for using it. You can also leave out the BASS.DLL/LIB files, as the user should already have those.