Commit 215a6a51 authored by Hugo Beauzée-Luyssen's avatar Hugo Beauzée-Luyssen

MediaPlayer: Simplify callbacks handling

This also fix user callbacks perfect forwarding
parent 378faa57
......@@ -701,11 +701,11 @@ public:
static_assert(signature_match_or_nullptr<DrainCb, void()>::value, "Mismatched drain callback prototype");
libvlc_audio_set_callbacks( *this,
CallbackWrapper<(int)EventIdx::AudioPlay, PlayCb, libvlc_audio_play_cb>::wrap( this, std::forward<PlayCb>( play ) ),
CallbackWrapper<(int)EventIdx::AudioPause, PauseCb, libvlc_audio_pause_cb>::wrap( this, std::forward<PauseCb>( pause ) ),
CallbackWrapper<(int)EventIdx::AudioResume, ResumeCb,libvlc_audio_resume_cb>::wrap( this, std::forward<ResumeCb>( resume ) ),
CallbackWrapper<(int)EventIdx::AudioFlush, FlushCb, libvlc_audio_flush_cb>::wrap( this, std::forward<FlushCb>( flush ) ),
CallbackWrapper<(int)EventIdx::AudioDrain, DrainCb, libvlc_audio_drain_cb>::wrap( this, std::forward<DrainCb>( drain ) ),
CallbackWrapper<(int)EventIdx::AudioPlay, libvlc_audio_play_cb>::wrap( this, std::forward<PlayCb>( play ) ),
CallbackWrapper<(int)EventIdx::AudioPause, libvlc_audio_pause_cb>::wrap( this, std::forward<PauseCb>( pause ) ),
CallbackWrapper<(int)EventIdx::AudioResume, libvlc_audio_resume_cb>::wrap( this, std::forward<ResumeCb>( resume ) ),
CallbackWrapper<(int)EventIdx::AudioFlush, libvlc_audio_flush_cb>::wrap( this, std::forward<FlushCb>( flush ) ),
CallbackWrapper<(int)EventIdx::AudioDrain, libvlc_audio_drain_cb>::wrap( this, std::forward<DrainCb>( drain ) ),
// We will receive the pointer as a void*, we need to offset the value *now*, otherwise we'd get
// a shifted value, resulting in an empty callback array.
static_cast<EventOwner<13>*>( this ) );
......@@ -727,7 +727,7 @@ public:
{
static_assert(signature_match_or_nullptr<VolumeCb, void(float, bool)>::value, "Mismatched set volume callback");
libvlc_audio_set_volume_callback(*this,
CallbackWrapper<(int)EventIdx::AudioVolume, VolumeCb, libvlc_audio_set_volume_cb>::wrap( this, std::forward<VolumeCb>( func ) ) );
CallbackWrapper<(int)EventIdx::AudioVolume, libvlc_audio_set_volume_cb>::wrap( this, std::forward<VolumeCb>( func ) ) );
}
/**
......@@ -750,8 +750,8 @@ public:
static_assert(signature_match_or_nullptr<CleanupCb, void()>::value, "Mismatched cleanup callback");
libvlc_audio_set_format_callbacks(*this,
CallbackWrapper<(int)EventIdx::AudioSetup, SetupCb, libvlc_audio_setup_cb>::wrap( this, std::forward<SetupCb>( setup ) ),
CallbackWrapper<(int)EventIdx::AudioCleanup, CleanupCb, libvlc_audio_cleanup_cb>::wrap( this, std::forward<CleanupCb>( cleanup ) ) );
CallbackWrapper<(int)EventIdx::AudioSetup, libvlc_audio_setup_cb>::wrap( this, std::forward<SetupCb>( setup ) ),
CallbackWrapper<(int)EventIdx::AudioCleanup, libvlc_audio_cleanup_cb>::wrap( this, std::forward<CleanupCb>( cleanup ) ) );
}
/**
......@@ -1048,9 +1048,9 @@ public:
static_assert(signature_match_or_nullptr<DisplayCb, void(void*)>::value, "Mismatched lock callback signature");
libvlc_video_set_callbacks(*this,
CallbackWrapper<(int)EventIdx::VideoLock, LockCb, libvlc_video_lock_cb>::wrap( this, std::forward<LockCb>( lock ) ),
CallbackWrapper<(int)EventIdx::VideoUnlock, UnlockCb, libvlc_video_unlock_cb>::wrap( this, std::forward<UnlockCb>( unlock ) ),
CallbackWrapper<(int)EventIdx::VideoDisplay, DisplayCb, libvlc_video_display_cb>::wrap( this, std::forward<DisplayCb>( display ) ),
CallbackWrapper<(int)EventIdx::VideoLock, libvlc_video_lock_cb>::wrap( this, std::forward<LockCb>( lock ) ),
CallbackWrapper<(int)EventIdx::VideoUnlock, libvlc_video_unlock_cb>::wrap( this, std::forward<UnlockCb>( unlock ) ),
CallbackWrapper<(int)EventIdx::VideoDisplay, libvlc_video_display_cb>::wrap( this, std::forward<DisplayCb>( display ) ),
// We will receive the pointer as a void*, we need to offset the value *now*, otherwise we'd get
// a shifted value, resulting in an empty callback array.
static_cast<EventOwner<13>*>( this ) );
......@@ -1102,8 +1102,8 @@ public:
static_assert(signature_match_or_nullptr<CleanupCb, void()>::value, "Unmatched prototype for cleanup callback");
libvlc_video_set_format_callbacks(*this,
CallbackWrapper<(int)EventIdx::VideoFormat, FormatCb, libvlc_video_format_cb>::wrap( static_cast<EventOwner<13>*>( this ), std::forward<FormatCb>( setup ) ),
CallbackWrapper<(int)EventIdx::VideoCleanup, CleanupCb, libvlc_video_cleanup_cb>::wrap( this, std::forward<CleanupCb>( cleanup ) ) );
CallbackWrapper<(int)EventIdx::VideoFormat, libvlc_video_format_cb>::wrap( static_cast<EventOwner<13>*>( this ), std::forward<FormatCb>( setup ) ),
CallbackWrapper<(int)EventIdx::VideoCleanup, libvlc_video_cleanup_cb>::wrap( this, std::forward<CleanupCb>( cleanup ) ) );
}
/**
......
......@@ -108,60 +108,58 @@ namespace VLC
EventOwner() = default;
};
template <int Idx, typename Func, typename... Args>
template <int, typename>
struct FromOpaque;
template <int NbEvents>
struct FromOpaque<NbEvents, void*>
{
static EventOwner<NbEvents>* get(void* opaque)
{
return reinterpret_cast<EventOwner<NbEvents>*>( opaque );
}
};
template <int NbEvents>
struct FromOpaque<NbEvents, void**>
{
static EventOwner<NbEvents>* get(void** opaque)
{
return reinterpret_cast<EventOwner<NbEvents>*>( *opaque );
}
};
template <int Idx, typename... Args>
struct CallbackWrapper;
// We assume that any callback will take a void* opaque as its first parameter.
// We assume that any callback will take a void*/void** opaque as its first parameter.
// We intercept this parameter, and use it to fetch the list of user provided
// functions. Once we know what function to call, we forward the rest of the
// parameters.
// Using partial specialization also allows us to get the list of the expected
// callback parameters automatically, rather than having to specify them.
template <int Idx, typename Func, typename Ret, typename... Args>
struct CallbackWrapper<Idx, Func, Ret(*)(void*, Args...)>
template <int Idx, typename Ret, typename Opaque, typename... Args>
struct CallbackWrapper<Idx, Ret(*)(Opaque, Args...)>
{
using Wrapped = Ret(void*, Args...);
template <int NbEvents>
static Wrapped* wrap(EventOwner<NbEvents>* owner, Func&& func)
{
owner->callbacks[Idx] = std::shared_ptr<CallbackHandlerBase>( new CallbackHandler<Func>( std::forward<Func>( func ) ) );
return [](void* opaque, Args... args) -> Ret {
auto self = reinterpret_cast<EventOwner<NbEvents>*>(opaque);
assert(self->callbacks[Idx].get());
auto cbHandler = static_cast<CallbackHandler<Func>*>( self->callbacks[Idx].get() );
return cbHandler->func( std::forward<Args>(args)... );
};
}
};
using Wrapped = Ret(Opaque, Args...);
// Special handling for events with a void** opaque first parameter.
// We fetch it and do our business with it, then forward the parameters
// to the user callback, without this opaque param.
template <int Idx, typename Func, typename Ret, typename... Args>
struct CallbackWrapper<Idx, Func, Ret(*)(void**, Args...)>
{
using Wrapped = Ret(void**, Args...);
template <int NbEvents>
template <int NbEvents, typename Func>
static Wrapped* wrap(EventOwner<NbEvents>* owner, Func&& func)
{
owner->callbacks[Idx] = std::shared_ptr<CallbackHandlerBase>( new CallbackHandler<Func>( std::forward<Func>( func ) ) );
return [](void** opaque, Args... args) -> Ret {
auto self = reinterpret_cast<EventOwner<NbEvents>*>(*opaque);
return [](Opaque opaque, Args... args) -> Ret {
auto self = FromOpaque<NbEvents, Opaque>::get( opaque );
assert(self->callbacks[Idx].get());
auto cbHandler = static_cast<CallbackHandler<Func>*>( self->callbacks[Idx].get() );
return cbHandler->func( std::forward<Args>(args)... );
};
}
};
// Specialization to handle null callbacks at build time.
// We could try to compare any "Func" against nullptr at runtime, though
// since Func is a template type, which roughly has to satisfy the "Callable" concept,
// it could be an instance of a function object, which doesn't compare nicely against nullptr.
// Using the specialization at build time is easier and performs better.
template <int Idx, typename... Args>
struct CallbackWrapper<Idx, std::nullptr_t, void(*)(void*, Args...)>
{
// Overload to handle null callbacks at build time.
// We could try to compare any "Func" against nullptr at runtime, though
// since Func is a template type, which roughly has to satisfy the "Callable" concept,
// it could be an instance of a function object, which doesn't compare nicely against nullptr.
// Using the specialization at build time is easier and performs better.
template <int NbEvents>
static std::nullptr_t wrap(EventOwner<NbEvents>*, std::nullptr_t)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment