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.