Accessing the media library
The medialibrary is an optional component of the Qt interface, if you’re
developing a feature that depends on medialibrary component you should ensure
that it won’t break the application if you start the application with
--no-media-library
or if you compile VLC without medialibrary support.
From the QML side you can check that MainCtx.mediaLibraryAvailable
is true
From C++ side you can check that MainCtx::hasMediaLibrary()
returns true.
Modeling medialibray views
If you need to model a new view from the medialibrary, the usual way is to
Create a class that inherit from MLBaseModel, this class is a QAbstractListModel for the interaction on the QML side.
Loading the data done by implementing a subclass BaseLoader
which performs the
raw queries on the medialibrary (counting elements, loading a range of elements,
loading a single element). These functions are called from a background thread
you should not access the model or the GUI from there.
From the qml side, the different properties of the items are access using a role
name, the data associated to a role is access thought the itemRoleData
, and a
mapping between roles ID and their name (string) through the roleNames
function
A model can react to events from the medialibrary by overriding the
onVlcMlEvent
function, this function is called from the Qt thread, so it is
safe to acts on the model/UI from there
a simple model might be
class MLStuff : public MLItem {
//store item data here
};
class MLStuffModel : public MLBaseModel
{
Q_OBJECT
public:
enum Roles {
STUFF_ID = Qt::UserRole + 1,
STUFF_ROLE_FOO,
STUFF_ROLE_BAR
};
Q_ENUM(Roles);
//map Roles to their string representation
QHash<int, QByteArray> roleNames() const override;
protected:
//get data for a given Role on the object item
QVariant itemRoleData(MLItem *item, int role) const override;
//create an Loader instance to load the data
std::unique_ptr<MLBaseModel::BaseLoader> createLoader() const override;
private:
// allows to sort using a given role
vlc_ml_sorting_criteria_t roleToCriteria(int role) const override;
// react to events from the medialibrary
virtual void onVlcMlEvent( const MLEvent &event ) override;
struct Loader : public BaseLoader
{
Loader(const MLStuffModel &model) : BaseLoader(model) {}
// count the number of items
size_t count(vlc_medialibrary_t* ml) const override;
// load a range of items
std::vector<std::unique_ptr<MLItem>> load(vlc_medialibrary_t* ml, size_t index, size_t count) const override;
// load a single item
std::unique_ptr<MLItem> loadItemById(vlc_medialibrary_t* ml, MLItemId itemId) const override;
};
};
the model should be registered in the QML engine using qmlRegisterType
in
MainUI.cpp
qmlRegisterType<MLStuffModel>( uri, versionMajor, versionMinor, "MLStuffModel" );
On the QML side a model is instantiated locally and is given the medialibrary as
import org.videolan.medialib 0.1
MLStuffModel {
id: stuffModel
ctx: MediaLib
}
Querying the medialibrary
Queries should not be made directly on the Qt thread and you should not block the Qt thread while the medialibrary is performing a query.
If you need to query on the medialibrary, you need to use the runOnMLThread
endpoint on the MediaLib
instance. These queries will be executed
asynchronous, and the result will be provided to you through a callback.
A sample call:
//this structure is used to exchange data between
struct Ctx {
bool success;
};
uint64_t taskid = m_mediaLib->runOnMLThread<Ctx>(
//reference on the QObject making the call
this,
//this callback is executed in the medialibray thread
[url, indexed](vlc_medialibrary_t* ml, Ctx& ctx)
{
//making whatever calls on the medialibrary
int res;
if ( indexed )
res = vlc_ml_add_folder( ml, qtu( url ) );
else
res = vlc_ml_remove_folder( ml, qtu( url ) );
ctx.success = (res == VLC_SUCCESS);
},
//callback executed on the GUI thread, we waranty that
//"this" still exists
[this, indexed](quint64 taskid, Ctx& ctx){
if (ctx.success)
{
m_indexed = indexed;
emit isIndexedChanged();
}
},
//Optionnal named queue if queries needs to be executed sequentially
"ML_FOLDER_ADD_QUEUE");
The first callback will be called in a background thread, you can perform queries on the medialibrary from there, the second callback will be called on the GUI thread.
The ML or UI callback may not be called if the caller is destroyed before the callback is executed. Note that it may be destroyed during the execution of the ML callback, so you should not try to access the caller from this callback, the UI callback warranties that the caller still exists on the main thread.
A structure type can be specified to pass data between the two callbacks. Data put inside the structure should adhere to the RAII principle, the structure will be freed by broker, you have no warranty that the UI callback will actually be called.
If you need to pass data from the call site, you should pass it though lambda captures. Beware that the object making the call may be destroyed during the execution of the ML callback, so be wary of the life cycle of what you give (usually pass arguments by copy, move or using shared pointers)