Decoder plugins must implement and export the functions defined in decoder_plug.h.
There are two interface revision levels. For developing new plugins the level 3 interface is strongly recommended. But level 0/1 plugins are still supported. The level 2 of PM123 1.40 is discontinued.
Decoder plugins are used to decode audio data as well as to examine playlists. Each item is identified by
an URI.
You should not take the term 'playlist' too literally. It only means that an URI has logically a sequence of sub URIs, i.e.
the URI has to be enumerable. This applies to a playlist as well as to a file system folder or a compact disc containing
tracks.
In theory an item could be enumerable as well as directly playable. E.g. index tracks of a CD or a CUE sheet. But this has not
been well tested in the PM123 core so far.
The level 3 interface has the following components:
The info interface functions must be independent of the current decoder status. They should always be functional and give consistent results in any conditions. The functions must be thread safe.
ULONG DLLENTRY decoder_support(const DECODER_FILETYPE** types, int* count)
types
(out) - Pointer to the first element of an array of supported file types.count
(out) - Number of entries in the above array.This is used by PM123 to suggest to the user what he can play with the decoder. Furthermore, by default PM123 will not query the decoder about files or other objects that are not listed in the above filter list to keep performance up.
The function is called again after a successful call to plugin_configure. This allows to modify the supported file types through the configuration.
ULONG DLLENTRY decoder_fileinfo(const char *url, XFILE* handle, int* what, const INFO_BUNDLE* info,
DECODER_INFO_ENUMERATION_CB cb, void* param)
cb
- Callback function.decoder_fileinfo
returns. Of course, this does not apply to songs.param
- This parameter is to be passed as first argument to each call to *cb
.bit in *what on input | bit in *what on return | corresponding info available | corresponding fields in INFO_BUNDLE |
---|---|---|---|
reset | reset | don't care | ignored |
set | reset | don't care | not allowed! |
don't care | set | no | leave unmodified |
don't care | set | yes | fill with valid content |
If a decoder knows that some information, that is not explicitly requested, is not available for the current URI or it knows it's content during processing of the request, it should always set the corresponding bit in *what and return the information if applicable. This avoids redundant calls to decoder_fileinfo.
PM123 does not need all informations for all kind of objects. The aggregate type recursive playlist information is never requested explicitly by PM123. PM123 will also never request INFO_PHYS if a handle is not NULL. However, a plugin may supply aggregate information if it is available. E.g. playlists may store cached information about their children to improve performance. If you are in doubt, do not supply this kind of information.
Legend:
X = required if requested by the PM123
Q = may be requested by the PM123
C = my be cached and returned by the decoder
O = may be overridden by a playlist item
info type | song | playlist | playlist item |
---|---|---|---|
phys |
X | X | C |
tech |
X | X | C |
obj |
X | X | C |
meta |
X | X | C O |
attr |
Q | X | C O |
children | Q | X | |
rpl |
C | C | |
drpl | C | C | |
item | |
|
O |
void (DLLENTRY* cb)(void* param, const char* url, const INFO_BUNDLE* info, int cached, int reliable)
param
- Arbitrary parameter from decoder_fileinfo
.url
- URI of the sub item. The URI may be absolute or relative. In case of relative URIs the
context of the enclosing playlist is used for URI resolution.info
- Known information about the sub item. If you already know some information about an item,
you should place it in *info
. This can make PM123 significantly faster. If you do not have any
information, you can pass NULL
as info
.cached, reliable
- Two bit vectors of INFOTYPE
corresponding to fields in *info.cached | reliable | Corresponding field in *info contains |
---|---|---|
0 | 0 | - ignored -, may be NULL |
1 | 0 | The field contain cached information that might no longer be valid. |
0 | 1 | The field contains reliable information that just had been verified. |
1 | 1 | The field contains information that is overridden by this reference only. |
int DLLENTRY decoder_init (struct DECODER_STRUCT **w)
BOOL DLLENTRY decoder_uninit(struct DECODER_STRUCT *w)
decoder_uninit
is called when another decoder than yours is needed, and should destroy the decoder's thread,
semaphores, other opened handles and free allocated memory for w. The decoder must not execute any
callback function like OutRequestBuffer from another thread when decoder_uninit
has returned.
ULONG DLLENTRY decoder_command(struct DECODER_STRUCT *w, ULONG msg, DECODER_PARAMS2 *params)
There are a lot of commands to implement for this function. Parameters needed for each of them are passed in the DECODER_PARAMS2 structure and described in detail here and in the decoder_plug.h include. The decoder necessarily should support following commands: DECODER_SETUP, DECODER_PLAY and DECODER_STOP.
In the level 3 interface the data source always passed as an URL. The URL parameter uses the following syntax:
file:///D:/Music/my favorite song.mp3
file://server/share/path/song.mp3 (UNC path)
http://... (as you would expect)
cdda:///H:/track02 (CD track)
cdda:///H:/ (CD TOC)
The DECODER_SETUP call is intended to capture the callback entries for the output interface. They do not change during decoding.
This command tell the decoder which song to decode. This should spawn a new thread that decodes the song and feeds the result to the output interface.
Tells the decoder to continue decoding at a certain location of the song. The structure member JumpTo is the location in seconds from the song's start.
Change the fast forward or rewind mode. The structure member SkipSpeed tell the decoder which playback speed is intended. The value i a delta to the normal playback speed, i.e. speed = SkipSpeed + 1. Examples:
- 0.0 => normal playback
- 1.0 => play twice as fast
- 3.0 => play 4 times faster
- -2.0 => play reverse
- -3.0 => play reverse twice as fast
- -5.0 => play revers 4 time faster
- -0.5 => play at half speed (currently unused)
There is no need to hit this value exactly. It is better to skip parts of the song instead of transforming the sample rate. Common values are marked bold. If a decoder can't support different speeds, it could simply use SkipSpeed > 0 for fast forward, and SkipSpeed < 0 for fest rewind.
Implementation hint: If you implement fast scan mode by seeking every 100 ms forward or reverse, then you need to seek SkipSpeed * 100 ms every time to get the desired average speed.
Stop decoding of the current song. This should terminate the decoder thread. After DECODER_STOP the decoder should ignore all errors and simply terminate.
The status interface has to be thread safe.
ULONG DLLENTRY decoder_status(struct DECODER_STRUCT *w)
PM123_TIME DLLENTRY decoder_length(struct DECODER_STRUCT *w)
-1
if unknown).The call to this function must be valid even if DECODER_STARTING or DECODER_STOPPED is reported (when the stream plays too fast for example). The function is used to follow increasing length of files that are written on the fly while playing.
The decoder must use this interface to pass the decoded samples to the output stage. The samples must be passed as 32 bit floating point values.
Strictly speaking this is part of the playback interface, but the interface functions have
to be called by the decoder in a separate thread. The function entry points for these callbacks are passed in DECODER_PARAMS2
at the DECODER_SETUP call.
The level 3 interface allocates the buffers by the consumer. This causes less double buffering and allows dynamic buffer sizes. In the optimal case the samples can be placed immediately in the output buffers of the audio device.
You must call OutRequestBuffer
and OutCommitBuffer
alternately to pass the samples to the next
plugin. Anything else is an error. Note that any of the two functions might block.
int (DLLENTRYP OutRequestBuffer)(void* a, const FORMAT_INFO2* format, float** buf)
DECODER_PARAMS2
.*format
need not to be
valid after this call returned.If you get a smaller buffer as you need to pass your data you should call OutRequestBuffer
and OutCommitBuffer
again until all your data is consumed. There is no guaranteed minimum size of the buffer, but you should not expect to get
very small buffers quite often.
void (DLLENTRY* OutCommitBuffer)(void* a, int len, PM123_TIME posmarker)
a
- pointer from DECODER_PARAMS2
.len
- used number of samples placed in the buffer.posmarker
- stream position in seconds of the first sample that will be stored in this buffer
relative to the beginning of the stream.The length must not be higher than the the return value from the previous OutRequestBuffer
call. But it might
be less than the requested length. This causes no significant performance impact as long as you do not always pass very few
samples.
void (DLLENTRY* DecEvent)(void* a, DECEVENTTYPE event, void* param)
a
- pointer from DECODER_PARAMS2
.event
- type of the event to send.param
- event specific parameter. See below.The decoder plugin MUST call the above function on the following conditions:
DECEVENT_PLAYSTOP
when the stream has finished decoding and the last sample has been passed to output_commit_buffer
or when the decoder received a DECODER_STOP command.DECEVENT_PLAYERROR
when a playback error occurred so that PM123 should know to stop immediately.DECEVENT_SEEKSTOP
when a DECODER_JUMPTO
operation is completed (i.e. when no buffers
from before the seek is sent to the output plugin anymore). The message must also be sent, when playback starts in the
middle of a song. DECEVENT_CHANGETECH
is sent whenever you want PM123 to change the display of the current technical
information of the stream (like samplerate). param must point to a TECH_INFO
structure.DECEVENT_CHANGEOBJ
is sent whenever you want PM123 to change the display of the current object
information of the stream (like song length). param must point to a OBJ_INFO
structure. Note that
you should not sent the bitrate of individual MPEG frames this way like the level 1 interface supported by WM_CHANGEBR.
The object information keeps the bitrate of the entire stream. DECEVENT_METADATA
is sent whenever streaming metadata is updated. param must point to a META_INFO
structure.void DLLENTRY decoder_event(void* w, OUTEVENTTYPE event)
w
- pointer from decoder_init
OUTEVENT_LOW_WATER
- the output plugin detected that it will run out of data soon.OUTEVENT_HIGH_WATER
- the output plugin detected a clear condition.The events may be used to speed up the data source. If you get OUTEVENT_HIGH_WATER
you should return to normal
behavior. There is one task that the core engine does for you: PM123 automatically changes the priority of the decoder
thread. So if there is nothing more the decoder can do (e.g. QoS settings) it could safely ignore the events.
The event handler may be called from any thread in any context. It might be called when the decoder is in stopped state if
the end of the stream has been reached recently. This should be ignored. You will usually get a OUTEVENT_LOW_WATER
call immediately after the decoding started or after a seek command because the buffers are not yet filled.
The export of decoder_event is optional.
ULONG DLLENTRY decoder_saveinfo(char* url, const META_INFO* info, int haveinfo, xstring* errortext)
url
- URI to the desired object.info
- new meta information to write.haveinfo
- Components of info to modify.PLUGIN_OK
(0) = everything is perfect, meta info is saved to url
.The function modifies the meta information of a certain song. Calling the function should succeed even if the file is currently playing.
ULONG DLLENTRY decoder_savefile(const char* url, const char* format, int* what, const INFO_BUNDLE* info,
DECODER_SAVE_ENUMERATION_CB cb, void* param, xstring* errortext)
url
- URI to the desired object.
what
(in/out) - Bit vector of INFOTYPE
flags with the information to save. Any information that corresponds to bits that are not set in *what
should be preserved by this call. And if INFO_META is specified, any meta information that does not have the
corresponding bit in info->meta->haveinfo set, should be left unchanged too.info
- Information of the playlist object itself. Usually playlists do not contain additional
object information other than their content. So the info may be mostly ignored. However, it could speed up PM123 if decoder_fileinfo
can restore some infos in a cheap way.cb
- Enumeration callback. The decoder should call this function
to retrieve the
information about the playlist content from PM123, but only if *what contains INFO_CHILD.param
- Parameter to be passed as first argument to *cb
.PLUGIN_OK
(0) = everything is perfect, meta info is saved to url
.The function is used to modify a file (or other object) in place. It is currently only used to save playlists.
int (DLLENTRY* cb)(void* param, xstring* url, const INFO_BUNDLE** info, int* cached, int* reliable)
param
- Arbitrary parameter from decoder_savelist
.url
(out) - Return URI of the sub item. The URI may be absolute or relative. In case of
relative URIs the context of the enclosing playlist is used for URI resolution. Note that the xstring
structure must be initialized when the function is called.info
(out) - Return known information about the sub item. The decoder can store this information
in the playlist and return it when the list is read with decoder_fileinfo to speed
up large playlists.cached, reliable
(out) - Return INFOTYPE
bit
vectors. See DECODER_INFO_ENUMERATION_CB for details.**info
is overridden by this reference only. The decoder should store this
information together with the URI reference when possible.PLUGIN_OK
(0) = everything is fine, anything else = error, e.g. no
more items.typedef struct
{ const char* category;
const char* eatype;
const char* extension;
int flags;
} DECODER_FILETYPE;
Field | Meaning |
---|---|
category | File type category, e.g. "Playlist file" This field is only used for reading files. |
eatype | File type, e.g. "PM123 playlist file" |
extension | File extension, e.g. "*.lst;*.m3u" |
flags | Bit vector of DECODER_TYPE. DECODER_FILENAME (1) - Decoder can play files of this type, should always be set for EA types. DECODER_URL (2) - Decoder can play URIs (http, ftp, etc.), should be set for MIME types. DECODER_SONG (0x0100) - Decoder can play songs with this file type. DECODER_PLAYLIST (0x0200) - Decoder can play playlists with this file type. DECODER_WRITABLE (0x1000) - Decoder can save items of this type. DECODER_METAINFO (0x2000) - Decoder can save a meta info. |
TODO
The following functions are used to improve the GUI of PM123 with plugin specific content.
All functions in this section are optional.
ULONG DLLENTRY decoder_editmeta(HWND owner, const char* url)
owner
- Parent window handle.url
- URL to the desired stream. See URI samples. Keep in mind that
this URL may be the same as the one currently played. The plugin must handle this concurrent write access.If decoder_editmeta
is not implemented, the edit tag entry is always disabled.
const DECODER_WIZARD* DLLENTRY decoder_getwizard(void)
DECODER_WIZARD
structures (linked list).DECODER_WIZARD
objects set the link pointer to NULL
. The returned storage must be valid
until the next call to decoder_getwizard
or the plugin gets unloaded.If the function is not implemented or returns NULL
the context menu is not extended with entries specific to
this plugin.
The field prompt
is the menu text. It should not contain information about the accelerator key, because this is
generated automatically.
The fields accel_key
[2
] and accel_opt
[2
] can be used to extend the
accelerator table of PM123. They correspond to the fields key
and fs
of the ACCEL
structure. The first set of entries is used for the PM123 main window and the playlist windows. The second set is for the
Playlist Manager to append to the currently selected playlist.
You should set accel_key
to 0
if you do not want an accelerator key for your plugin's wizard
dialog. Be careful with the choice of the accelerator keys because there may be clashes. Using Alt for the first entry and
Shift-Alt for the second entry as meta keys is recommended.
When the menu entry is selected the corresponding wizard function is called by PM123.
ULONG (DLLENTRY* wizard)(HWND owner, const char* title,
DECODER_INFO_ENUMERATION_CB callback, void* param)
owner
- Parent window handle.title
- Window title.callback
- Function that will be called for each selected item.param
- Parameter to pass to the callback function as the first argument.callback
has been called at
least once.