Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin support in tinycompress #6

Merged
merged 3 commits into from
Oct 16, 2020

Conversation

rohkkumar
Copy link
Contributor

These are initial set of patches to add the plugins support to tinycompress.

This pull request corresponds to #5

@@ -0,0 +1,140 @@
/* snd_utils.c
**
** Copyright (c) 2019, The Linux Foundation. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we switch to SPDX identifiers?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure .. we can do that .. only thing is current files has not moved to SPDX identifiers. So followed the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

Cc: @perexg. I would say we could start using SPDX identifiers and add a mental note to change existing files later.

@dbaluta
Copy link
Contributor

dbaluta commented Aug 27, 2020

@rohkkumar do you have any dummy decoding .so library to better understand the interface?

For example it is not very clear to me how do you decode an mp3 frame.

So, with the current interface would be possible to input an mp3 file and get the corresponding uncompressed pcm file resulted after applying the decoding algorithm?

We have this legacy interface: https://github.com/FreeSrk/codec/blob/master/ghdr/common/fsl_unia.h

It offers the following interface:

typedef int32 (*UniACodec_decode_frame) (UniACodec_Handle pua_handle,
                                         uint8 * InputBuf,
                                         uint32 InputSize,
                                         uint32 * offset,
                                         uint8 ** OutputBuf,
                                         uint32 *OutputSize);

#include "compress_ops.h"
#ifdef TINYCOMPRESS_USES_PLUGINS
#include "utils/snd_utils.h"
#endif
Copy link
Contributor

Choose a reason for hiding this comment

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

So, do you need to recompile the library to use plugins? Isn't possible to determine at runtime if one should use plugins or real hw?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, our initial implementation had the same design where virtual(plugin) device was decided if the hw node is present or not and if there is virtual node in configuration file. But as discussed in PR for tinyalsa tinyalsa/tinyalsa#137 , we got a feedback to handle it using compile time flag too so that it can be disabled for those who don't want plugin support.

Copy link
Contributor

Choose a reason for hiding this comment

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

@rohkkumar
Copy link
Contributor Author

@rohkkumar do you have any dummy decoding .so library to better understand the interface?

For example it is not very clear to me how do you decode an mp3 frame.

So, with the current interface would be possible to input an mp3 file and get the corresponding uncompressed pcm file resulted after applying the decoding algorithm?

We have this legacy interface: https://github.com/FreeSrk/codec/blob/master/ghdr/common/fsl_unia.h

It offers the following interface:

typedef int32 (*UniACodec_decode_frame) (UniACodec_Handle pua_handle,
                                         uint8 * InputBuf,
                                         uint32 InputSize,
                                         uint32 * offset,
                                         uint8 ** OutputBuf,
                                         uint32 *OutputSize);

Yes, I have implemented a ffmpeg based example plugin to decode FLAC data to PCM and do tinyalsa calls. I will plan to post that out.

Thanks

@dbaluta
Copy link
Contributor

dbaluta commented Aug 27, 2020

@rohkkumar Great, looking forward to read the code for that example.

@rohkkumar
Copy link
Contributor Author

Updated patches with SPDX identifiers and added ffmpeg based plugin example.


/* initialize the resampling context */
if ((ret = swr_init(swr_ctx)) < 0) {
fprintf(stderr, "Failed to initialize the resampling context\n");
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@vinodkoul
Copy link
Contributor

@perexg can you please review as well, we need to make sure this aligns with overall way we want alsa plugins to behave...

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: GPL-2.0-only
Copy link
Contributor

Choose a reason for hiding this comment

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

So this needs to be modified. The Tinycompress project is dual licensed as LGPL and BSD. This license is incompatible to this project, so I can not consider this change. Please reconsider licensing as LGPL or BSD or both.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure @vinodkoul , I will update the license.

Copy link
Contributor

@vinodkoul vinodkoul left a comment

Choose a reason for hiding this comment

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

License needs to be clarified before we forward on these patches. Right now it is incompatible

@perexg
Copy link
Member

perexg commented Sep 9, 2020

I have problem with 'libsndcardparser.so' references. It's not a standard library. It's a bit pity that compress_open() does not accept the device name (string) as argument - it's better for applications. I see the possibility to use a configuration file with card/device number to shared library mapping or special .so lookups in compress_open(). Also, the environment variable may be used to override the hardware.

Also, the ops functions may copy the exported functions to let implementation on the plugin itself. The exported API for tinycompress is simple and straight, so if I understand the problem correctly, the functionality should be only redirected for given card/device to another plugin/library if configured. Why use internals like ops->ioctl(), ops->poll() ?

@rohkkumar
Copy link
Contributor Author

I have problem with 'libsndcardparser.so' references. It's not a standard library. It's a bit pity that compress_open() does not accept the device name (string) as argument - it's better for applications. I see the possibility to use a configuration file with card/device number to shared library mapping or special .so lookups in compress_open(). Also, the environment variable may be used to override the hardware.

@perexg , we are planning to open libsndcardparser and will be hosted soon.
Regarding compress_open(), we have not changed the interface and applications will still be able to use the same API. tinycompress clients are not supposed to call compress_hw.c/compress_plugin.c APIs directly. Instead clients needs to pass virtual compress device node id. We can change compress->ops->open to pass string but with that compress_plugin.c will have to again parse the string and get the card and device number. Please suggest.

Also, the ops functions may copy the exported functions to let implementation on the plugin itself. The exported API for tinycompress is simple and straight, so if I understand the problem correctly, the functionality should be only redirected for given card/device to another plugin/library if configured. Why use internals like ops->ioctl(), ops->poll() ?

The problem with calling the plugin library ops directly from compress.c is that for all the ops we would need to check if it is a virtual node or a real hw node and call either the ioctl on hw device node or ops->ioctl to plugin library. This will lead to too many if/else statements in compress.c . Currently, we set the ops to either compress_hw_ops or compress_plugin_ops at compress_open() and then use it at all other places. This way, changes in compress.c is cleaner and all plugin related implementation is handled in compress_plugin.c . It will be more inline with tinyalsa plugins where plugin related common implementation is done in mixer_plugin.c (https://github.com/tinyalsa/tinyalsa/blob/master/src/mixer_plugin.c). Please let us know your opinion.

@rohkkumar
Copy link
Contributor Author

License needs to be clarified before we forward on these patches. Right now it is incompatible

Fixed License in all files.

@perexg
Copy link
Member

perexg commented Sep 10, 2020

I have problem with 'libsndcardparser.so' references. It's not a standard library. It's a bit pity that compress_open() does not accept the device name (string) as argument - it's better for applications. I see the possibility to use a configuration file with card/device number to shared library mapping or special .so lookups in compress_open(). Also, the environment variable may be used to override the hardware.

@perexg , we are planning to open libsndcardparser and will be hosted soon.

I'm sorry, but this scheme and libtinyalsa scheme looks like a big hack to create emulated cards / devices.

My problem is the dependency on another library at the time. You're proposing:

libtinycompress -> libsndcardparser -> some redirection

I'm proposing:

libtinycompress -> redirection via open -> plugin system based on your libsndcardparser (but it should not be part of tinycompress)

I don't think that libtinyalsa nor libtinycompress should be dependent on third abstraction. Just create a straight way in open functions (compress_open) to redirect the functionality to somewhere else. And then implement your plugin system based on some special libsndcardparser code out-of-tree, if you like. My proposal allows other implementations (not only libsndcardparser one), too.

The much cleaner solution is to implement the counterpart to pcm_open_by_name() for example (compress_open_by_name()) and handle virtual names like 'mp3decoder:0,0' where mp3decoder specifies the plugin and the extra arguments specifies the output PCM card and device numbers. In this case, you may try to dynamically load libtinycompress_mp3decoder.so which will implement this decoder.

Regarding compress_open(), we have not changed the interface and applications will still be able to use the same API. tinycompress clients are not supposed to call compress_hw.c/compress_plugin.c APIs directly. Instead clients needs to pass virtual compress device node id. We can change compress->ops->open to pass string but with that compress_plugin.c will have to again parse the string and get the card and device number. Please suggest.

Also, the ops functions may copy the exported functions to let implementation on the plugin itself. The exported API for tinycompress is simple and straight, so if I understand the problem correctly, the functionality should be only redirected for given card/device to another plugin/library if configured. Why use internals like ops->ioctl(), ops->poll() ?

The problem with calling the plugin library ops directly from compress.c is that for all the ops we would need to check if it is a virtual node or a real hw node and call either the ioctl on hw device node or ops->ioctl to plugin library. This will lead to too many if/else statements in compress.c .

The virtual devices may be identified by special device names. I just say, that for this case, you can just do (for all exported functions in tinycompress.h which touches som):

int compress_resume(struct compress *compress)
{
      if (!compress->ops->resume)
              return -ENXIO;
      return compress->ops->resume(compress);
}

... and don't deal with kernel APIs like ioctl/poll etc. Keep this only in compress_hw.c . Use the upper level abstraction also for the plugin interface.

@rohkkumar
Copy link
Contributor Author

rohkkumar commented Sep 11, 2020

I'm sorry, but this scheme and libtinyalsa scheme looks like a big hack to create emulated cards / devices.

My problem is the dependency on another library at the time. You're proposing:

libtinycompress -> libsndcardparser -> some redirection

I'm proposing:

libtinycompress -> redirection via open -> plugin system based on your libsndcardparser (but it should not be part of tinycompress)

I don't think that libtinyalsa nor libtinycompress should be dependent on third abstraction. Just create a straight way in open functions (compress_open) to redirect the functionality to somewhere else. And then implement your plugin system based on some special libsndcardparser code out-of-tree, if you like. My proposal allows other implementations (not only libsndcardparser one), too.

The much cleaner solution is to implement the counterpart to pcm_open_by_name() for example (compress_open_by_name()) and handle virtual names like 'mp3decoder:0,0' where mp3decoder specifies the plugin and the extra arguments specifies the output PCM card and device numbers. In this case, you may try to dynamically load libtinycompress_mp3decoder.so which will implement this decoder.

Thanks @perexg for the suggestion. It does solve the problem and reduces a lot of code. But I think there are certain limitation with this approach like:

  1. With current approach, we can use compress_open() for plugin which is not possible if we use compress_open_by_name() to denote the library name.
  2. This would need existing tinycompress clients to change the code if they want to use plugin as they won't be able to use compress_open().

Another reference for configuration file is alsa-lib which parses alsa.conf and uses the information.
I understand that this dependency on libsndcardparser should be avoided but we had kept it as a separate library as it was supposed to be common for tinyalsa and tinycompress.
Please provide your inputs.

The virtual devices may be identified by special device names. I just say, that for this case, you can just do (for all exported functions in tinycompress.h which touches som):

int compress_resume(struct compress *compress)
{
      if (!compress->ops->resume)
              return -ENXIO;
      return compress->ops->resume(compress);
}

... and don't deal with kernel APIs like ioctl/poll etc. Keep this only in compress_hw.c . Use the upper level abstraction also for the plugin interface.

Sure. Actually, our intention was :

  1. To make plugin behave as userspace driver.
  2. To reuse common code in compress.c and avoid code duplication. e.g. - compress_write/compress_get_hpointer/etc has some handling in compress.c which now needs to be implemented in the plugin library again if we use upper level abstraction.
    Please let me know your opinion.
    I can start making the changes based on your suggestion.

@rohkkumar
Copy link
Contributor Author

rohkkumar commented Sep 18, 2020

@perexg , I was evaluating your proposal and changes needed to handle all ops.
I found that with new design, existing is_codec_supported() API will not work for plugins. One solution to that is to expose another API similar to compress_open_by_name(). Another solution can be to get the codec_support after compress_open_by_name(). In that case, we need to pass struct compress * to get the info. Can you please provide your suggestion.

Other issue which I see with new approach is that:
Currently struct compress is not exposed to external components (https://github.com/alsa-project/tinycompress/blob/master/src/lib/compress.c#L87) . So, if we make Ops abstraction at upper layer, the common code and variables defined in struct compress cannot be used for compress_hw.c. In that case, we will have to move these to compress_hw.c. Does this sounds ok ?
This can be avoided if he have ops abstraction at same layer which is currently implemented.

@perexg
Copy link
Member

perexg commented Sep 18, 2020

@perexg , I was evaluating your proposal and changes needed to handle all ops.
I found that with new design, existing is_codec_supported() API will not work for plugins. One solution to that is to expose another API similar to compress_open_by_name(). Another solution can be to get the codec_support after compress_open_by_name(). In that case, we need to pass struct compress * to get the info. Can you please provide your suggestion.

I would add a new function is_codec_supported_by_name() for this case.

The other way may be separate open with the params setup and capabilities check (open -> codec supported? -> configure -> I/O). But it will make the current API incompatible. It's much cleaner solution in my eyes.

Perhaps, we can add compress_open_by_name() without 'struct compr_config' config argument and add new functions compress_set_params() and compress_is_codec_supported() and obsolete the card/device based functions.

@vinodkoul : Do you think that we can change the current compress API? I would prefer to separate the parameter setup from open.

Other issue which I see with new approach is that:
Currently struct compress is not exposed to external components (https://github.com/alsa-project/tinycompress/blob/master/src/lib/compress.c#L87) . So, if we make Ops abstraction at upper layer, the common code and variables defined in struct compress cannot be used for compress_hw.c. In that case, we will have to move these to compress_hw.c. Does this sounds ok ?
This can be avoided if he have ops abstraction at same layer which is currently implemented.

Yes, the 'struct compress' should be private to implementation. I don't see a problem to move it to compress_hw.c completely.

@vinodkoul
Copy link
Contributor

@perexg , I was evaluating your proposal and changes needed to handle all ops.
I found that with new design, existing is_codec_supported() API will not work for plugins. One solution to that is to expose another API similar to compress_open_by_name(). Another solution can be to get the codec_support after compress_open_by_name(). In that case, we need to pass struct compress * to get the info. Can you please provide your suggestion.

I would add a new function is_codec_supported_by_name() for this case.

The other way may be separate open with the params setup and capabilities check (open -> codec supported? -> configure -> I/O). But it will make the current API incompatible. It's much cleaner solution in my eyes.

Perhaps, we can add compress_open_by_name() without 'struct compr_config' config argument and add new functions compress_set_params() and compress_is_codec_supported() and obsolete the card/device based functions.

@vinodkoul : Do you think that we can change the current compress API? I would prefer to separate the parameter setup from open.

@perexg yes we can add new APIs for that, I would not change the old API as we have users for these.
So adding news one should be fine.

I think we should have compress_open_by_name()

Other issue which I see with new approach is that:
Currently struct compress is not exposed to external components (https://github.com/alsa-project/tinycompress/blob/master/src/lib/compress.c#L87) . So, if we make Ops abstraction at upper layer, the common code and variables defined in struct compress cannot be used for compress_hw.c. In that case, we will have to move these to compress_hw.c. Does this sounds ok ?
This can be avoided if he have ops abstraction at same layer which is currently implemented.

Yes, the 'struct compress' should be private to implementation. I don't see a problem to move it to compress_hw.c completely.
Agree, we dont want to expose that to users

Add compress_ops.h needed to support plugins. Compress_ops
can be used to jump to either hw compress node ops or to
virtual compress node ops.

Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
@rohkkumar
Copy link
Contributor Author

I would add a new function is_codec_supported_by_name() for this case.
The other way may be separate open with the params setup and capabilities check (open -> codec supported? -> configure -> I/O). But it will make the current API incompatible. It's much cleaner solution in my eyes.
Perhaps, we can add compress_open_by_name() without 'struct compr_config' config argument and add new functions compress_set_params() and compress_is_codec_supported() and obsolete the card/device based functions.
@vinodkoul : Do you think that we can change the current compress API? I would prefer to separate the parameter setup from open.

@perexg yes we can add new APIs for that, I would not change the old API as we have users for these.
So adding news one should be fine.

I think we should have compress_open_by_name()

Thanks @vinodkoul and @perexg for the suggestions. I have updated the PR accordingly.

@rohkkumar rohkkumar force-pushed the plugin_support branch 2 times, most recently from 01f0e60 to d89c51e Compare September 23, 2020 07:28
@rohkkumar
Copy link
Contributor Author

Moved parsing of compress_name to an API as it is used by compress_open_by_name() and compress_is_codec_supported_by_name()

@rohkkumar
Copy link
Contributor Author

@perexg , Can you please check if updated PR has proper changes.

@perexg
Copy link
Member

perexg commented Sep 30, 2020

The overall change looks good. Some little comment: I won't insist to pass card number and device numbers to plugins, they should just receive the abstract compress device name and do own parsing. For example, we may be prepared for compress devices like: net:192.168.1.10:9999.

Add compress_hw.c to handle all operations associated
with HW compress nodes. Move handling to compress_hw
using compress_hw_ops.

Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
@rohkkumar
Copy link
Contributor Author

The overall change looks good. Some little comment: I won't insist to pass card number and device numbers to plugins, they should just receive the abstract compress device name and do own parsing. For example, we may be prepared for compress devices like: net:192.168.1.10:9999.

Thanks @perexg for the suggestion. Updated PR accordingly. Please review.

src/lib/compress_hw.c Outdated Show resolved Hide resolved
src/lib/compress_hw.c Outdated Show resolved Hide resolved
src/lib/compress.c Outdated Show resolved Hide resolved
@vinodkoul
Copy link
Contributor

@charleskeepax I think you folks also have some stuff on top of this lib, can you guys test and confirm this is okay

@rohkkumar
Copy link
Contributor Author

Addressed comments. Please review.

@rohkkumar
Copy link
Contributor Author

@vinodkoul / @perexg / @charleskeepax , can you please check if there is any concern with latest patchset.

@vinodkoul
Copy link
Contributor

@perexg any comments?

src/lib/compress.c Outdated Show resolved Hide resolved
src/lib/compress.c Outdated Show resolved Hide resolved
src/lib/compress.c Show resolved Hide resolved
Add compress_open_by_name() and is_codec_supported_by_name()
to support plugins. Format of name is 'hw:<card>,<device>'
for hw compress nodes and '<plugin_name>:<custom_data>'
for virtual compress nodes. It dynamically loads the plugin
library whose name is libtinycompress_module_<plugin_name>.so.
Plugin library needs to expose compress_plugin_ops.
Default path of plugin lib is /usr/lib/tinycompress-lib/ and it
can be updated by defining TINYCOMPRESS_PLUGIN_DIR in makefile.

Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
@vinodkoul vinodkoul merged commit 22f1152 into alsa-project:master Oct 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants