Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 142 additions & 17 deletions InterfacePlayerRDK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ const char * CipherTypeToString(CipherType type)
InterfacePlayerRDK::InterfacePlayerRDK(bool isRialto) :
mProtectionLock(), mPauseInjector(false), mSourceSetupMutex(), stopCallback(NULL), tearDownCb(NULL), notifyFirstFrameCallback(NULL),
mSourceSetupCV(), mScheduler(), callbackMap(), setupStreamCallbackMap(), mDrmSystem(NULL), mEncrypt(NULL), mDRMSessionManager(NULL),
trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), PipelineSetToReady(false), mSchedulerStarted(false)
trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), PipelineSetToReady(false), mSchedulerStarted(false),
mProgressCallbackContext(std::make_shared<ProgressCallbackContext>(this))
{
interfacePlayerPriv = new InterfacePlayerPriv(isRialto);
MW_LOG_MIL("InterfacePlayerRDK constructed using external library");
Expand All @@ -106,6 +107,7 @@ trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), Pipeli
/* InterfacePlayerRDK destructor*/
InterfacePlayerRDK::~InterfacePlayerRDK()
{
CancelProgressCallbackContext();
DestroyPipeline();
if (mDrmSystem)
{
Expand Down Expand Up @@ -803,17 +805,40 @@ void MonitorAV( InterfacePlayerRDK *pInterfacePlayerRDK )
*/
gboolean InterfacePlayerRDK::ProgressCallbackOnTimeout(gpointer user_data)
{
InterfacePlayerRDK *pInterfacePlayerRDK = (InterfacePlayerRDK *)user_data;
InterfacePlayerPriv* privatePlayer = nullptr;
if (pInterfacePlayerRDK)
auto *weakContext = static_cast<std::weak_ptr<ProgressCallbackContext> *>(user_data);
if (!weakContext)
{
privatePlayer = pInterfacePlayerRDK->GetPrivatePlayer();
if (pInterfacePlayerRDK->m_gstConfigParam->monitorAV)
return G_SOURCE_REMOVE;
}
auto callbackContext = weakContext->lock();
if (!callbackContext)
{
return G_SOURCE_REMOVE;
}
InterfacePlayerRDK *pInterfacePlayerRDK = nullptr;
{
std::unique_lock<std::mutex> lock(callbackContext->mutex);
if (callbackContext->cancelled || !callbackContext->player)
{
MonitorAV(pInterfacePlayerRDK);
return G_SOURCE_REMOVE;
}
callbackContext->activeCallbacks++;
pInterfacePlayerRDK = callbackContext->player;
}
if (pInterfacePlayerRDK->m_gstConfigParam->monitorAV)
{
MonitorAV(pInterfacePlayerRDK);
}
pInterfacePlayerRDK->TriggerEvent(InterfaceCB::progressCb);
InterfacePlayerPriv *privatePlayer = pInterfacePlayerRDK->GetPrivatePlayer();
MW_LOG_TRACE("current %d, stored %d ", g_source_get_id(g_main_current_source()), privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId);
{
std::lock_guard<std::mutex> lock(callbackContext->mutex);
callbackContext->activeCallbacks--;
if (callbackContext->cancelled && (0 == callbackContext->activeCallbacks))
{
callbackContext->cv.notify_all();
}
pInterfacePlayerRDK->TriggerEvent(InterfaceCB::progressCb);
MW_LOG_TRACE("current %d, stored %d ", g_source_get_id(g_main_current_source()), privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId);
}
return G_SOURCE_CONTINUE;
}
Expand All @@ -838,8 +863,10 @@ gboolean InterfacePlayerRDK::IdleCallback(gpointer user_data)
double reportProgressInterval = pInterfacePlayerRDK->m_gstConfigParam->progressTimer;
reportProgressInterval *= 1000; //convert s to ms

auto callbackContext = pInterfacePlayerRDK->GetOrCreateProgressCallbackContext();
auto *timerUserData = new std::weak_ptr<ProgressCallbackContext>(callbackContext);
GSourceFunc timerFunc = ProgressCallbackOnTimeout;
pInterfacePlayerRDK->TimerAdd(timerFunc, (int)reportProgressInterval, privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId, user_data, "periodicProgressCallbackIdleTask");
pInterfacePlayerRDK->TimerAdd(timerFunc, (int)reportProgressInterval, privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId, timerUserData, "periodicProgressCallbackIdleTask", DestroyProgressCallbackUserData);
}
else
{
Expand Down Expand Up @@ -1438,6 +1465,9 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame)

interfacePlayerPriv->gstPrivateContext->syncControl.disable();
interfacePlayerPriv->gstPrivateContext->aSyncControl.disable();
// Stop async worker before removing idle/timer tasks to prevent
// new tasks from being scheduled concurrently during teardown.
mScheduler.StopScheduler();
{
std::unique_lock<std::mutex> sourceSetupLock(mSourceSetupMutex);
mSourceSetupCV.notify_all();
Expand All @@ -1454,8 +1484,7 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame)
interfacePlayerPriv->gstPrivateContext->firstAudioFrameReceived = false ;
}
IdleTaskRemove(interfacePlayerPriv->gstPrivateContext->firstProgressCallbackIdleTask);

this->TimerRemove(interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId, "periodicProgressCallbackIdleTaskId");
CancelProgressCallbackContext();
if (interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId)
{
MW_LOG_MIL("InterfacePlayerRDK: Remove bufferingTimeoutTimerId %d", interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId);
Expand Down Expand Up @@ -1538,6 +1567,8 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame)

// Reset mp4demux playback semantic shim on pipeline event reset
interfacePlayerPriv->gstPrivateContext->isMp4DemuxPlayback = false;
// Restart scheduler so the same InterfacePlayerRDK instance can be reused.
mScheduler.StartScheduler();
}

void InterfacePlayerRDK::ResetGstEvents()
Expand Down Expand Up @@ -3754,24 +3785,118 @@ bool InterfacePlayerRDK::CheckDiscontinuity(int mediaType, int streamFormat , bo
/**
* @brief TimerAdd - add a new glib timer in thread safe manner
*/
void InterfacePlayerRDK::TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint& taskId, gpointer user_data, const char* timerName)
std::shared_ptr<ProgressCallbackContext> InterfacePlayerRDK::GetOrCreateProgressCallbackContext()
{
std::lock_guard<std::mutex> lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex);
bool createNewContext = false;
if (!mProgressCallbackContext)
{
createNewContext = true;
}
else
{
std::lock_guard<std::mutex> contextLock(mProgressCallbackContext->mutex);
if (mProgressCallbackContext->cancelled)
{
createNewContext = true;
}
}
if (createNewContext)
{
mProgressCallbackContext = std::make_shared<ProgressCallbackContext>(this);
}
return mProgressCallbackContext;
}

void InterfacePlayerRDK::CancelProgressCallbackContext()
{
std::shared_ptr<ProgressCallbackContext> callbackContext;
{
std::lock_guard<std::mutex> lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex);
callbackContext = mProgressCallbackContext;
}
if (!callbackContext)
{
return;
}
{
std::lock_guard<std::mutex> lock(callbackContext->mutex);
callbackContext->cancelled = true;
callbackContext->player = nullptr;
}
const guint progressTimerId = interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId;
this->TimerRemove(interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId, "periodicProgressCallbackIdleTaskId");

// Avoid deadlock if cancellation is triggered re-entrantly from within
// the progress timer callback itself.
GSource *currentSource = g_main_current_source();
if (currentSource && (g_source_get_id(currentSource) == progressTimerId))
{
std::lock_guard<std::mutex> taskLock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex);
if (mProgressCallbackContext == callbackContext)
{
mProgressCallbackContext.reset();
}
return;
}

{
std::unique_lock<std::mutex> lock(callbackContext->mutex);
constexpr auto kTeardownTimeout = std::chrono::seconds(5);
if (!callbackContext->cv.wait_for(lock, kTeardownTimeout, [&callbackContext]() {
return 0 == callbackContext->activeCallbacks;
})) {
MW_LOG_WARN("Teardown timeout: callback context still has active callbacks (%zu). Possible I/O blockage or deadlock detected.", callbackContext->activeCallbacks);
}
}
std::lock_guard<std::mutex> taskLock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex);
if (mProgressCallbackContext == callbackContext)
{
mProgressCallbackContext.reset();
}
}
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

void InterfacePlayerRDK::DestroyProgressCallbackUserData(gpointer user_data)
{
delete static_cast<std::weak_ptr<ProgressCallbackContext> *>(user_data);
}

void InterfacePlayerRDK::TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint& taskId, gpointer user_data, const char* timerName, GDestroyNotify destroyNotify)
{
std::lock_guard<std::mutex> lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex);
if (funcPtr && user_data)
{
if (0 == taskId)
{
/* Sets the function pointed by functPtr to be called at regular intervals of repeatTimeout, supplying user_data to the function */
taskId = g_timeout_add(repeatTimeout, funcPtr, user_data);
if (destroyNotify)
{
taskId = g_timeout_add_full(G_PRIORITY_DEFAULT, repeatTimeout, funcPtr, user_data, destroyNotify);
if (0 == taskId)
{
Comment on lines +3871 to +3875
destroyNotify(user_data);
}
}
else
{
taskId = g_timeout_add(repeatTimeout, funcPtr, user_data);
}
MW_LOG_INFO("InterfacePlayerRDK: Added timer '%.50s', %d", (nullptr!=timerName) ? timerName : "unknown" , taskId);
}
else
{
if (destroyNotify)
{
destroyNotify(user_data);
}
MW_LOG_INFO("InterfacePlayerRDK: Timer '%.50s' already added, taskId=%d", (nullptr!=timerName) ? timerName : "unknown", taskId);
}
}
else
{
if (destroyNotify && user_data)
{
destroyNotify(user_data);
}
MW_LOG_ERR("Bad pointer. funcPtr = %p, user_data=%p",funcPtr,user_data);
}
}
Expand Down Expand Up @@ -3923,7 +4048,7 @@ void InterfacePlayerRDK::NotifyFirstFrame(int mediaType)
void InterfacePlayerRDK::TriggerEvent(InterfaceCB event)
{
auto it = callbackMap.find(event);
if (it != callbackMap.end())
if ((it != callbackMap.end()) && it->second)
{
it->second();
}
Comment on lines 4050 to 4054
Expand All @@ -3935,7 +4060,7 @@ void InterfacePlayerRDK::TriggerEvent(InterfaceCB event)
void InterfacePlayerRDK::TriggerEvent(InterfaceCB event, int data)
{
auto it = setupStreamCallbackMap.find(event);
if (it != setupStreamCallbackMap.end())
if ((it != setupStreamCallbackMap.end()) && it->second)
{
it->second(data);
}
Expand Down
25 changes: 24 additions & 1 deletion InterfacePlayerRDK.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,26 @@
#include <functional>
#include <condition_variable>
#include <chrono>
#include <memory>
#include <any>
#include "SocUtils.h"

class InterfacePlayerRDK;

struct ProgressCallbackContext
{
std::mutex mutex;
std::condition_variable cv;
InterfacePlayerRDK *player;
bool cancelled;
size_t activeCallbacks;

explicit ProgressCallbackContext(InterfacePlayerRDK *playerInstance)
: player(playerInstance), cancelled(false), activeCallbacks(0)
{
}
};

#include "GstUtils.h"
#include "DemuxDataTypes.h"
#include "MediaSample.h"
Expand Down Expand Up @@ -163,6 +181,11 @@ class InterfacePlayerRDK
bool trickTeardown;
std::mutex mMutex;
std::map<std::string, int> configMap;
std::shared_ptr<ProgressCallbackContext> mProgressCallbackContext;

std::shared_ptr<ProgressCallbackContext> GetOrCreateProgressCallbackContext();
void CancelProgressCallbackContext();
static void DestroyProgressCallbackUserData(gpointer user_data);

public:
Configs *m_gstConfigParam;
Expand Down Expand Up @@ -726,7 +749,7 @@ class InterfacePlayerRDK
* @param[in] timerName name of the timer being added
* @param[out] taskId id of the timer to be returned
*/
void TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint &taskId, gpointer user_data, const char *timerName = nullptr);
void TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint &taskId, gpointer user_data, const char *timerName = nullptr, GDestroyNotify destroyNotify = nullptr);
/**
* @fn TimerIsRunning
* @param[in] taskId id of the timer to be removed
Expand Down
9 changes: 8 additions & 1 deletion test/utests/fakes/FakeGLib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,14 @@ int g_strcmp0(const char *str1, const char *str2)

guint g_timeout_add_full(gint priority, guint interval, GSourceFunc function, gpointer data, GDestroyNotify notify)
{
return 0;
guint retval = 0;

if (g_mockGLib != nullptr)
{
retval = g_mockGLib->g_timeout_add_full(priority, interval, function, data, notify);
}

return retval;
}

void g_usleep(gulong microseconds)
Expand Down
1 change: 1 addition & 0 deletions test/utests/mocks/MockGLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class MockGLib
public:
MOCK_METHOD(GParamSpec*, g_object_class_find_property, (GObjectClass* oclass, const gchar* property_name));
MOCK_METHOD(guint, g_timeout_add, (guint interval, GSourceFunc function, gpointer data));
MOCK_METHOD(guint, g_timeout_add_full, (gint priority, guint interval, GSourceFunc function, gpointer data, GDestroyNotify notify));
MOCK_METHOD(gboolean, g_source_remove, (guint tag));
MOCK_METHOD(gpointer, g_malloc, (gsize n_bytes));
MOCK_METHOD(void, g_free, (gpointer mem));
Expand Down
Loading