Decoder plug-ins must implement and export the functions defined in decoder_plug.h.
There are two interface revision levels. For developing new plug-ins the level 3 interface is strongly recommended. But level 0/1 plug-ins are still supported. The level 2 of PM123 1.40 is discontinued.
Decoder plug-ins 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 plug-in 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 plug-in. 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 plug-in 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
plug-in 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 plug-in
detected that it will run out of data soon.OUTEVENT_HIGH_WATER
- the output plug-in
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 plug-in 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 plug-in 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 plug-in gets unloaded.If the function is not implemented or returns NULL
the context menu is not extended with entries specific to this plug-in.
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 plug-in'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.