[vlc-commits] [Git][videolan/libvlcpp][master] Parser: adapt to the libvlc_parser split
Alaric Senat (@asenat)
gitlab at videolan.org
Thu Sep 24 08:26:41 UTC 2026
Alaric Senat pushed to branch master at VideoLAN / libvlcpp
Commits:
3095992b by Ayush Dey at 2026-09-23T18:18:44+05:30
Parser: adapt to the libvlc_parser split
Refs vlc!10169
Replace queue() and queueThumbnailing() with createParseTask(),
createThumbnailTask() and submit().
The create methods return a move-only Task object, which submit()
consumes on success. Submitting it twice or to another parser throws
to avoid undefined behavior. Task::id() returns a TaskIdentifier,
fetched before submission to identify it in callbacks, which now
exposes the same TaskIdentifier for that task.
Bump the libvlc wrap to 778ec071ff216602f2a6e4ddd522dfdb6b791472.
- - - - -
5 changed files:
- meson.build
- subprojects/libvlc.wrap
- test/Parser/parse.cpp
- test/Parser/thumbnail.cpp
- vlcpp/Parser.hpp
Changes:
=====================================
meson.build
=====================================
@@ -5,7 +5,7 @@ project(
['cpp'],
license: 'LGPL-2.1-or-later',
license_files: ['COPYING'],
- version: '4.0.0+vlc.266f1fe08a',
+ version: '4.0.0+vlc.778ec071ff',
meson_version: '>=1.1.0',
default_options: ['cpp_std=c++11'],
)
=====================================
subprojects/libvlc.wrap
=====================================
@@ -2,8 +2,8 @@
url = https://code.videolan.org/videolan/vlc.git
depth = 1
-# Jun 28 2026
-revision = 266f1fe08a8bce490c51d830262f0b965b12c698
+# Sep 16 2026
+revision = 778ec071ff216602f2a6e4ddd522dfdb6b791472
[provide]
libvlc = libvlc_dep
=====================================
test/Parser/parse.cpp
=====================================
@@ -1,5 +1,5 @@
/*****************************************************************************
- * parse.cpp: Parser::queue regression test
+ * parse.cpp: Parser parse task regression test
*****************************************************************************
* Copyright © 2026 libvlcpp authors & VideoLAN
*
@@ -27,6 +27,7 @@
#include <memory>
#include <mutex>
#include <iostream>
+#include <utility>
int main(int ac, char** av)
{
@@ -51,25 +52,28 @@ int main(int ac, char** av)
std::atomic<int64_t> reportedDuration{-1};
std::mutex stateMutex;
std::condition_variable stateCv;
- std::unique_ptr<VLC::Parser::TaskIdentifier> queuedTaskId;
+ VLC::Parser::TaskIdentifier expectedId;
- VLC::Parser::Callbacks cbs([&](VLC::Parser::Task&& task, VLC::Parser::Status status) {
+ VLC::Parser::Callbacks cbs([&](VLC::Parser::TaskIdentifier task, VLC::Parser::Status status) {
std::lock_guard<std::mutex> lk(stateMutex);
- assert(*queuedTaskId == task);
- auto media = task.getMedia();
- assert(media.isValid());
+ assert(task == expectedId);
reportedDuration.store(media.duration().count());
parserStatus = status;
parsingFinished = true;
stateCv.notify_all();
});
- /* block until the parsing is done. Hold stateMutex across queue() so the
- callback (which also locks stateMutex) cannot observe queuedTaskId
- before we assign it */
+ /* the task identifier is fetched before submission, so the callback can
+ safely compare against it even if it fires before submit() returns */
+ auto task = parser.createParseTask(req, cbs);
+ expectedId = task.id();
+ auto submitted = parser.submit(std::move(task));
+ assert(submitted.id() == expectedId);
+ assert(submitted.getMedia() == media);
+
+ /* block until the parsing is done */
{
std::unique_lock<std::mutex> lk(stateMutex);
- queuedTaskId.reset(new VLC::Parser::TaskIdentifier(parser.queue(req, cbs)));
assert(stateCv.wait_for(lk, std::chrono::seconds(5),
[&] { return parsingFinished; }));
}
=====================================
test/Parser/thumbnail.cpp
=====================================
@@ -1,5 +1,5 @@
/*****************************************************************************
- * thumbnail.cpp: Parser::queueThumbnailing regression test
+ * thumbnail.cpp: Parser thumbnail task regression test
*****************************************************************************
* Copyright © 2026 libvlcpp authors & VideoLAN
*
@@ -53,7 +53,7 @@ int main(int ac, char** av)
bool pictureReceived = false;
VLC::Parser::ThumbnailerCallbacks thumbCbs(
- [&](VLC::Parser::Task&& task, const VLC::Picture& picture) {
+ [&](VLC::Parser::TaskIdentifier, const VLC::Picture& picture) {
assert(picture.isValid());
std::lock_guard<std::mutex> lk(mtx);
captured = picture;
@@ -62,7 +62,7 @@ int main(int ac, char** av)
}
);
- parser.queueThumbnailing(thumbReq, thumbCbs);
+ parser.submit(parser.createThumbnailTask(thumbReq, thumbCbs));
/* block until the thumbnail is received and verify some of its properties,
then save it to a file and verify the file is non-empty before cleaning up */
=====================================
vlcpp/Parser.hpp
=====================================
@@ -42,79 +42,150 @@ public:
Done = libvlc_parser_status_done,
};
- class Task : public Internal<libvlc_parser_task>
+ class Task;
+ class SubmittedTask;
+
+ /**
+ * Identifier of a parsing/thumbnailing task.
+ *
+ * Returned by Task::id() and SubmittedTask::id(), and passed to the parser
+ * and thumbnailer callbacks, to identify which task a callback refers to.
+ * It is a plain value that can be copied and compared with other identifiers.
+ *
+ * \warning An identifier is only unique while the task it identifies is
+ * alive, i.e. while a Task or SubmittedTask referring to it exists, or while
+ * one of its callbacks runs. Once the task is released, a new task may get
+ * the same identifier.
+ *
+ * \note A default constructed identifier doesn't identify any task. It is also
+ * returned by a Task that was submitted or moved from.
+ */
+ class TaskIdentifier
{
private:
- explicit Task( libvlc_parser_task *task ) : Internal( task, libvlc_parser_task_release )
+ std::uintptr_t m_id = 0;
+
+ explicit TaskIdentifier( libvlc_parser_task *task )
+ : m_id( reinterpret_cast<std::uintptr_t>( task ) )
{
}
public:
- /**
- * Fetch the media associated with the task handle.
- *
- * \return the media associated with the task
- */
- Media getMedia() const
+ TaskIdentifier() = default;
+
+ bool operator==( const TaskIdentifier& another ) const
{
- auto media = libvlc_parser_task_get_media( *this );
- return Media( media, true );
+ return m_id == another.m_id;
}
+ friend class Task;
+ friend class SubmittedTask;
template <size_t, typename ...>
friend struct CallbackWrapper;
};
- class TaskIdentifier
+ /**
+ * Owning handle of a submitted parsing/thumbnailing task.
+ *
+ * Returned by Parser::submit(). It can be used to cancel the task and to
+ * fetch its media. The underlying task is released when the last copy of
+ * this object is destroyed. Releasing it while the task is running is
+ * safe, libVLC keeps the task alive until its completion callback returns.
+ */
+ class SubmittedTask : public Internal<libvlc_parser_task>
{
private:
- libvlc_parser_task *m_task;
-
- explicit TaskIdentifier( libvlc_parser_task *task )
- : m_task( task )
+ explicit SubmittedTask( Pointer task )
{
+ m_obj = std::move( task );
}
public:
- friend bool operator==( const TaskIdentifier& id, const Task& task )
+ /**
+ * Get the identifier of the task.
+ *
+ * \return the identifier of the task, the same one returned by the
+ * Task it was submitted from and passed to its callbacks, or a
+ * default constructed identifier if this handle is empty
+ */
+ TaskIdentifier id() const
{
- return id.m_task == task.get();
+ return TaskIdentifier( get() );
}
- friend bool operator==( const Task& task, const TaskIdentifier& id )
+ /**
+ * Fetch the media associated with the task handle.
+ *
+ * \return the media associated with the task, or an empty Media if
+ * this handle is empty
+ */
+ Media getMedia() const
{
- return id == task;
+ if ( !isValid() )
+ return Media();
+ auto media = libvlc_parser_task_get_media( *this );
+ return Media( media, true );
}
- bool operator==(const TaskIdentifier& another) const
+ friend class Parser;
+ };
+
+ /**
+ * A created task that has not been submitted yet.
+ *
+ * Returned by Parser::createParseTask() and Parser::createThumbnailTask(),
+ * and started by passing it to Parser::submit(), which consumes it on
+ * success. It can't be copied, so a task that was submitted successfully
+ * can't be submitted again. Destroying it without submitting it releases
+ * the task, and no callback is invoked for it.
+ */
+ class Task
+ {
+ private:
+ /* Identifies the parser that created the task without owning it.
+ It doesn't keep the parser that created it alive. If that parser is
+ destroyed first, the task can no longer be submitted, only released. */
+ std::weak_ptr<libvlc_parser_t> m_parser;
+ std::shared_ptr<libvlc_parser_task> m_task;
+
+ Task( const std::shared_ptr<libvlc_parser_t>& parser, libvlc_parser_task *task )
+ : m_parser( parser )
+ , m_task( task, libvlc_parser_task_release )
{
- return m_task == another.m_task;
}
+ public:
+ Task( Task&& ) = default;
+ Task& operator=( Task&& ) = default;
+ Task( const Task& ) = delete;
+ Task& operator=( const Task& ) = delete;
+
/**
- * Fetch the media associated with the task handle.
+ * Get the identifier of the task.
*
- * \return the media associated with the task
+ * The completion callback may run before Parser::submit() returns,
+ * so fetch the identifier before submitting the task to identify it
+ * in its callbacks.
+ *
+ * \return the identifier of the task, or a default constructed
+ * identifier if this object was submitted or moved from
*/
- Media getMedia() const
+ TaskIdentifier id() const
{
- auto media = libvlc_parser_task_get_media( m_task );
- return Media( media, true );
+ return TaskIdentifier( m_task.get() );
}
friend class Parser;
- template <size_t, typename ...>
- friend struct CallbackWrapper;
};
/**
* Callback prototype that notifies when a parser request finishes
*
- * \param task opaque handle of the task that finished, can be compared to the TaskIdentifier
- * returned by Parser::queue() using operator==, to identify which task finished
+ * \param task identifier of the task that finished, can be compared with the one returned by
+ * Task::id() and SubmittedTask::id(), to identify which task finished
* \param status terminal parse outcome, \ref Parser::Status
*/
- using ExpectedOnParsedCb = void(Parser::Task&& task, Parser::Status status);
+ using ExpectedOnParsedCb = void(Parser::TaskIdentifier task, Parser::Status status);
/**
* Callback prototype that notify when the parser add new attachments to
@@ -122,8 +193,8 @@ public:
*
* Called before onParsed, if there are valid attachments.
*
- * \param task opaque handle of the task, can be compared to the TaskIdentifier
- * returned by Parser::queue() using operator==, to identify which task added attachments
+ * \param task identifier of the task, can be compared with the one returned by
+ * Task::id() and SubmittedTask::id(), to identify which task added attachments
* \param list list of pictures, the list is only valid from this
* callback, each pictures can be held separately with list.at(index) method
*/
@@ -132,14 +203,14 @@ public:
/**
* Callback prototype that notify when a thumbnailer request finishes
*
- * \param task opaque handle of the task that finished, can be compared to the TaskIdentifier
- * returned by Parser::queueThumbnailing() using operator==, to identify which task finished
+ * \param task identifier of the task that finished, can be compared with the one returned by
+ * Task::id() and SubmittedTask::id(), to identify which task finished
* \param picture generated thumbnail, the thumbnail is only valid for the duration
* of the callback, but can be safely copied if needed. It is an empty Picture object in case
* of an error, timeout or request was cancelled. User should check if the Picture is valid by
* calling picture.isValid()
*/
- using ExpectedOnThumbnailerEndedCb = void(Parser::Task&& task, const Picture& picture);
+ using ExpectedOnThumbnailerEndedCb = void(Parser::TaskIdentifier task, const Picture& picture);
enum class ParseFlags
{
@@ -239,7 +310,7 @@ public:
*
* \param media the media to parse
*
- * \warning The media object must remain valid until the parser request is queued.
+ * \warning The media object must remain valid until the parser task is created.
*/
Request( Media& media )
{
@@ -294,7 +365,8 @@ public:
m_cbs = {};
m_cbs.version = 0;
m_cbs.on_parsed = CallbackWrapper<(unsigned int)CallbackIdx::OnParsed,
- decltype(libvlc_parser_cbs::on_parsed)>::wrap<Parser::Task, Parser::Status>(
+ decltype(libvlc_parser_cbs::on_parsed)>::wrap<
+ Parser::TaskIdentifier, Parser::Status>(
*m_callbacks, std::forward<OnParsedCb>( onParsedCb ) );
}
@@ -329,7 +401,7 @@ public:
*
* \param media the media for which to generate thumbnails
*
- * \warning The media object must remain valid until the thumnailer request is queued.
+ * \warning The media object must remain valid until the thumbnailer task is created.
*/
ThumbnailerRequest( Media& media )
{
@@ -455,7 +527,8 @@ public:
m_cbs = {};
m_cbs.version = 0;
m_cbs.on_ended = CallbackWrapper<(unsigned int)CallbackIdx::OnThumbnailerEnded,
- decltype(libvlc_thumbnailer_cbs::on_ended)>::wrap<Parser::Task, Picture>(
+ decltype(libvlc_thumbnailer_cbs::on_ended)>::wrap<
+ Parser::TaskIdentifier, Picture>(
*m_callbacks, std::forward<OnThumbnailerEnded>( onThumbnailerEnded ) );
}
};
@@ -476,74 +549,122 @@ public:
}
/**
- * Queue a parsing request.
+ * Create a media parsing task.
+ *
+ * Nothing runs and no callback can fire until the returned Task is
+ * passed to submit().
*
* \param request the parsing request
* \param cbs pre-built \ref Parser::Callbacks object
- * \return the queued task \ref TaskIdentifier that can be used to identify the
- * task in callbacks and to cancel the task if needed
+ * \return the created \ref Task, to pass to submit(). Its
+ * Task::id() identifies the task in callbacks.
*
* \warning The application must ensure that the Callbacks object supplied
- * remains valid and unmodified until the parser request terminates and the onParsedCb callback is called
- * on that Task (the returned TaskIdentifier can be used to identify the Task in the onParsedCb
- * using ==).
- *
- * \warning The returned TaskIdentifier is only valid till the parser request finishes
- * and the onParsedCb callback is called, after that it should not be used anymore
- * as the underlying task object is released by libVLC and the TaskIdentifier will hold a
- * dangling pointer.
+ * remains valid and unmodified until the onParsedCb callback is called for
+ * the submitted task.
*/
- TaskIdentifier queue( const Request& request, const Callbacks& cbs )
+ Task createParseTask( const Request& request, const Callbacks& cbs )
{
- auto task = libvlc_parser_queue( *this, &request.m_req, &cbs.m_cbs, cbs.m_callbacks.get() );
+ auto task = libvlc_parser_task_new_parse( *this, &request.m_req, &cbs.m_cbs, cbs.m_callbacks.get() );
if ( task == nullptr )
- throw std::runtime_error( "Failed to queue parser task" );
- return TaskIdentifier( task );
+ throw std::runtime_error( "Failed to create parser task" );
+ return Task( m_obj, task );
}
/**
- * Queue a thumbnail generation request.
+ * Create a thumbnail generation task.
+ *
+ * Nothing runs and no callback can fire until the returned Task is
+ * passed to submit().
*
* \param request the thumbnail generation request
* \param cbs pre-built \ref Parser::ThumbnailerCallbacks object
- * \return the queued task \ref TaskIdentifier that can be used to identify the
- * task in callbacks and to cancel the task if needed
+ * \return the created \ref Task, to pass to submit(). Its
+ * Task::id() identifies the task in callbacks.
*
* \warning The application must ensure that the ThumbnailerCallbacks object supplied
- * remains valid and unmodified until the request terminates and the onThumbnailerEnded callback is called
- * on that Task (the returned TaskIdentifier can be used to identify the Task in the onThumbnailerEnded
- * callback using ==).
- *
- * \warning The returned TaskIdentifier is only valid till the thumbnailer request finishes
- * and the onThumbnailerEnded callback is called, after that it should not be used anymore
- * as the underlying task object is released by libVLC and the TaskIdentifier will hold a dangling pointer.
+ * remains valid and unmodified until the onThumbnailerEnded callback is called for
+ * the submitted task.
*/
- TaskIdentifier queueThumbnailing( const ThumbnailerRequest& request, const ThumbnailerCallbacks& cbs )
+ Task createThumbnailTask( const ThumbnailerRequest& request,
+ const ThumbnailerCallbacks& cbs )
{
- auto task = libvlc_parser_queue_thumbnailing( *this, &request.m_req, &cbs.m_cbs, cbs.m_callbacks.get() );
+ auto task = libvlc_parser_task_new_thumbnail( *this, &request.m_req, &cbs.m_cbs,
+ cbs.m_callbacks.get() );
if ( task == nullptr )
- throw std::runtime_error( "Failed to queue thumbnailer task" );
- return TaskIdentifier( task );
+ throw std::runtime_error( "Failed to create thumbnailer task" );
+ return Task( m_obj, task );
+ }
+
+ /**
+ * Start a task created by createParseTask() or createThumbnailTask().
+ *
+ * On success the task is scheduled and its completion callback is guaranteed
+ * to be called exactly once, including when the task is cancelled. That
+ * callback may run, on another thread, even before this function returns:
+ * fetch Task::id() before submitting to identify the task in its
+ * callbacks.
+ *
+ * \param task a task created by this parser that has not been submitted yet
+ * \return the \ref SubmittedTask, which can be used to cancel the task
+ *
+ * \throws std::logic_error if the task was already submitted
+ * \throws std::invalid_argument if the task was created by another parser
+ * \throws std::runtime_error if the task could not be submitted
+ *
+ * \note On success \p task is consumed and left empty. On error no
+ * callback is invoked, \p task is left untouched and may be submitted again.
+ */
+ SubmittedTask submit( Task&& task )
+ {
+ if ( task.m_task == nullptr )
+ throw std::logic_error( "Parser task was already submitted" );
+
+ /* Ensures whether the task was created by this parser.
+ Comparing libvlc_parser_t addresses isn't enough. Once the task's parser
+ is destroyed, a new parser can reuse the same address, and the stale task
+ would be submitted to a parser that didn't create it (UB in libVLC).
+ owner_before() compares shared_ptr control blocks instead. It ensures that
+ the task was created by this parser and rejects every other parser,
+ even if it reuses the same address. */
+ if ( m_obj.owner_before( task.m_parser ) || task.m_parser.owner_before( m_obj ) )
+ throw std::invalid_argument( "Parser task was created by another parser" );
+ if ( libvlc_parser_submit( *this, task.m_task.get() ) != 0 )
+ throw std::runtime_error( "Failed to submit parser task" );
+
+ return SubmittedTask( std::move( task.m_task ) );
}
/**
* Cancel a parser request.
*
- * \param task the task to cancel \ref TaskIdentifier
+ * If the task already terminated, this is a no-op and no callback is
+ * invoked. An empty Task cancels nothing.
+ *
+ * \param task the task to cancel \ref SubmittedTask
* \return the number of cancelled tasks
*
- * \warning The TaskIdentifier supplied must be valid and not expired,
- * otherwise the behavior is undefined.
+ * \warning The completion callback of the cancelled task may be invoked
+ * synchronously from this call, on the calling thread. Don't hold a lock
+ * that the callback also takes while calling this function.
*/
- size_t cancelRequest( TaskIdentifier& task )
+ size_t cancelRequest( const SubmittedTask& task )
{
- return libvlc_parser_cancel_request( *this, task.m_task );
+ /* libvlc cancels all tasks when given a NULL, so check
+ it early to avoid passing nullptr from an empty Task */
+ if ( !task.isValid() )
+ return 0;
+ return libvlc_parser_cancel_request( *this, task );
}
/**
* Cancel all parser requests.
*
* \return the number of cancelled tasks
+ *
+ * \warning The completion callbacks of the cancelled tasks may be invoked
+ * synchronously from this call, on the calling thread. Don't hold a lock
+ * that the callbacks also take while calling this function.
*/
size_t cancelAll()
{
View it on GitLab: https://code.videolan.org/videolan/libvlcpp/-/commit/3095992b779ef7ee4487f39f936f8ab3750cb23a
--
View it on GitLab: https://code.videolan.org/videolan/libvlcpp/-/commit/3095992b779ef7ee4487f39f936f8ab3750cb23a
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help
More information about the vlc-commits
mailing list