Skip to content
26 changes: 23 additions & 3 deletions AAMP-UVE-API.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,34 @@ Note: starting in RDK 6.9, we support ability to start video paused on first fra

---

### stop()
### stop( forceCleanup )
### stop( sendStateChangeEvent, forceCleanup )
- Supported UVE version 0.7 and above.
- Stop playback and free resources associated with playback.
Usage example:

**Single Parameter Form:**
| Name | Type | Description |
| ---- | ---- | ----------- |
| forceCleanup | Boolean | Optional parameter. If True, forces DRM handle cleanup for Deep Sleep scenarios. Default is false. Prevents playback failures after device wake-up from Deep Sleep by clearing stale DRM sessions and failed key IDs. |

**Two Parameter Form (Advanced Usage):**
| Name | Type | Description |
| ---- | ---- | ----------- |
| sendStateChangeEvent | Boolean | If True, sends state change events during stop operation. Default is true. |
| forceCleanup | Boolean | If True, forces DRM handle cleanup for Deep Sleep scenarios. Default is false. |

Usage examples:
```js
{
.....
// for immediate stop of playback
// Standard stop - sends state change events
player.stop();

// Stop with DRM cleanup before Deep Sleep
player.stop(true);

// Advanced: Stop without state events, with DRM cleanup
player.stop(false, true);
}
```
---
Expand Down Expand Up @@ -2527,6 +2546,7 @@ A subset of UVE APIs and Events are available when using UVE JS APIs for ATSC pl

##### stop
- Stop playback and free resources
- Optional forceCleanup parameter for DRM cleanup in Deep Sleep scenarios

##### getAudioTrack
- Get the index of the currently selected Audio track
Expand Down
4 changes: 2 additions & 2 deletions jsbindings/jsbindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4439,7 +4439,7 @@ static void AAMP_finalize(JSObjectRef thisObject)
{
//when finalizing JS object, don't generate state change events
LOG_WARN(pAAMP," aamp->Stop(false)");
_allocated_aamp->Stop(false);
_allocated_aamp->Stop(false, true); // sendStateChangeEvent=false, forceCleanup=true
LOG_WARN(pAAMP,"delete aamp %p",_allocated_aamp);
SAFE_DELETE(_allocated_aamp);
}
Expand Down Expand Up @@ -4831,7 +4831,7 @@ void __attribute__ ((destructor(101))) _aamp_term()
{
LOG_WARN_EX("stopping aamp");
//when finalizing JS object, don't generate state change events
_allocated_aamp->Stop(false);
_allocated_aamp->Stop(false, true); // sendStateChangeEvent=false, forceCleanup=true
LOG_WARN_EX("stopped aamp");
delete _allocated_aamp;
_allocated_aamp = NULL;
Expand Down
41 changes: 37 additions & 4 deletions jsbindings/jsmediaplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ static void releaseNativeResources(AAMPMediaPlayer_JS *privObj)
{
//when finalizing JS object, don't generate state change events
LOG_WARN(privObj," aamp->Stop(false)");
privObj->_aamp->Stop(false);
privObj->_aamp->Stop(false, true); // sendStateChangeEvent=false, forceCleanup=true
privObj->clearCallbackForAllAdIds();
if (privObj->_listeners.size() > 0)
{
Expand Down Expand Up @@ -839,7 +839,7 @@ JSValueRef AAMPMediaPlayerJS_pause (JSContextRef ctx, JSObjectRef function, JSOb
* @param[in] function JSObject that is the function being called
* @param[in] thisObject JSObject that is the 'this' variable in the function's scope
* @param[in] argumentCount number of args
* @param[in] arguments[] JSValue array of args
* @param[in] arguments[] JSValue array of args - Optional forceCleanup boolean parameter
* @param[out] exception pointer to a JSValueRef in which to return an exception, if any
* @retval JSValue that is the function's return value
*/
Expand All @@ -853,8 +853,30 @@ JSValueRef AAMPMediaPlayerJS_stop (JSContextRef ctx, JSObjectRef function, JSObj
*exception = aamp_GetException(ctx, AAMPJS_MISSING_OBJECT, "Can only call stop() on instances of AAMPPlayer");
return JSValueMakeUndefined(ctx);
}
LOG_WARN(privObj," _aamp->Stop()");
privObj->_aamp->Stop();

bool sendStateChangeEvent = true; // Default: send state change events
bool forceCleanup = false; // Default: no DRM cleanup

if (argumentCount >= 1)
{
// For backward compatibility and UVE API design:
// - If 1 argument: treat as forceCleanup (boolean)
// - If 2 arguments: treat as (sendStateChangeEvent, forceCleanup)
if (argumentCount == 1)
{
// stop(forceCleanup) - send events by default, cleanup based on argument
forceCleanup = JSValueToBoolean(ctx, arguments[0]);
}
else if (argumentCount >= 2)
{
// stop(sendStateChangeEvent, forceCleanup) - both explicit
sendStateChangeEvent = JSValueToBoolean(ctx, arguments[0]);
forceCleanup = JSValueToBoolean(ctx, arguments[1]);
}
}

LOG_WARN(privObj," _aamp->Stop() sendStateChangeEvent=%d forceCleanup=%d", sendStateChangeEvent, forceCleanup);
privObj->_aamp->Stop(sendStateChangeEvent, forceCleanup);
LOG_TRACE("Exit");
return JSValueMakeUndefined(ctx);
}
Expand Down Expand Up @@ -1965,6 +1987,17 @@ JSValueRef AAMPMediaPlayerJS_setPlaybackRate (JSContextRef ctx, JSObjectRef func
{
overshootCorrection = (int) JSValueToNumber(ctx, arguments[1], exception);
}
// special magic playback rate triggers a forced DRM cleanup stop
// App can call: Stop(); SetRate(); SetRate(magic)
// When magic number is passed, invoke Stop without state events and force DRM cleanup
const float kForceCleanupMagicRate = 76234.0; // Magic number for triggering forced cleanup stop
if (rate == kForceCleanupMagicRate)
{
LOG_WARN(privObj,"Magic rate %.0f received - invoking Stop(sendStateChangeEvent=false, forceCleanup=true)", kForceCleanupMagicRate);
privObj->_aamp->Stop(false /*sendStateChangeEvent*/, true /*forceCleanup*/);
bRet = true;
}
else
{
LOG_WARN(privObj,"_aamp->SetRate(%f, %d)", rate, overshootCorrection);
privObj->_aamp->SetRate(rate, overshootCorrection);
Expand Down
28 changes: 20 additions & 8 deletions main_aamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "PlayerLogManager.h"
#include "PlayerMetadata.hpp"
#include "PlayerLogManager.h"
#include "AampDRMLicManager.h"

#include <dlfcn.h>
#include <termios.h>
Expand Down Expand Up @@ -187,7 +188,7 @@ PlayerInstanceAAMP::~PlayerInstanceAAMP()
mScheduler.RemoveAllTasks();
if (state != eSTATE_IDLE && state != eSTATE_RELEASED)
{
aamp->Stop( true );
aamp->Stop(false); // Don't send state change events during destruction
}
std::lock_guard<std::mutex> lock (mPrvAampMtx);
aamp = NULL;
Expand Down Expand Up @@ -239,7 +240,7 @@ void PlayerInstanceAAMP::ResetConfiguration()
/**
* @brief Stop playback and release resources.
*/
void PlayerInstanceAAMP::Stop(bool sendStateChangeEvent)
void PlayerInstanceAAMP::Stop(bool sendStateChangeEvent, bool forceCleanup)
{
if (aamp)
{
Expand All @@ -256,9 +257,17 @@ void PlayerInstanceAAMP::Stop(bool sendStateChangeEvent)
//state will be eSTATE_IDLE or eSTATE_RELEASED, right after an init or post-processing of a Stop call
if (state != eSTATE_IDLE && state != eSTATE_RELEASED)
{
StopInternal(sendStateChangeEvent);
StopInternal(sendStateChangeEvent, forceCleanup);
}
// Enhanced DRM cleanup for Deep Sleep scenarios
// Must be done AFTER Stop() to ensure GStreamer pipeline is torn down
// and all encrypted buffers are flushed before destroying DRM sessions
if (forceCleanup && aamp->mDRMLicenseManager)
{
AAMPLOG_WARN("Force cleanup: Clearing DRM sessions and failed key IDs for Deep Sleep");
aamp->mDRMLicenseManager->clearDrmSession(true);
aamp->mDRMLicenseManager->clearFailedKeyIds();
}

//Release lock
mScheduler.ResumeScheduler();
}
Expand Down Expand Up @@ -353,7 +362,7 @@ void PlayerInstanceAAMP::TuneInternal(const char *mainManifestUrl,
if ((state != eSTATE_IDLE) && (state != eSTATE_RELEASED) && (!IsOTAtoOTA))
{
//Calling tune without closing previous tune
StopInternal(false);
StopInternal(true, false);
}
aamp->getAampCacheHandler()->StartPlaylistCache();
aamp->Tune(mainManifestUrl, autoPlay, contentType, bFirstAttempt, bFinalAttempt, traceUUID, audioDecoderStreamSync, refreshManifestUrl, mpdStitchingMode, std::move(sid),manifestData);
Expand Down Expand Up @@ -3055,16 +3064,19 @@ void PlayerInstanceAAMP::PersistBitRateOverSeek(bool bValue)
/**
* @brief Stop playback and release resources.
*/
void PlayerInstanceAAMP::StopInternal(bool sendStateChangeEvent)
void PlayerInstanceAAMP::StopInternal(bool sendStateChangeEvent, bool forceCleanup)
{
aamp->StopPausePositionMonitoring("Stop() called");
AAMPPlayerState state = aamp->GetState();
if(!aamp->IsTuneCompleted())
{
aamp->TuneFail(true);
}
AAMPLOG_MIL("aamp_stop PlayerState=%d",state);
aamp->Stop();
AAMPLOG_MIL("aamp_stop PlayerState=%d forceCleanup=%d", state, forceCleanup);

// Negate sendStateChangeEvent since no need to send state change event on destructor call
aamp->Stop(!sendStateChangeEvent);

// Revert all custom specific setting, tune specific setting and stream specific setting , back to App/default setting
mConfig.RestoreConfiguration(AAMP_CUSTOM_DEV_CFG_SETTING);
mConfig.RestoreConfiguration(AAMP_TUNE_SETTING);
Expand Down
6 changes: 4 additions & 2 deletions main_aamp.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,10 @@ class PlayerInstanceAAMP
/**
* @brief Stop playback and release resources.
* @param[in] sendStateChangeEvent - true if state change events need to be sent for Stop operation
* @param[in] forceCleanup - true to force DRM handle cleanup for Deep Sleep scenarios (default false)
* @return void
*/
void Stop(bool sendStateChangeEvent = true);
void Stop(bool sendStateChangeEvent = true, bool forceCleanup = false);

/**
* @fn ResetConfiguration
Expand Down Expand Up @@ -1472,9 +1473,10 @@ class PlayerInstanceAAMP
* @fn StopInternal
*
* @param[in] sendStateChangeEvent - true if state change events need to be sent for Stop operation
* @param[in] forceCleanup - true to force DRM handle cleanup for Deep Sleep scenarios
* @return void
*/
void StopInternal(bool sendStateChangeEvent);
void StopInternal(bool sendStateChangeEvent, bool forceCleanup);

void* mJSBinding_DL; /**< Handle to AAMP plugin dynamic lib. */
static std::mutex mPrvAampMtx; /**< Mutex to protect aamp instance in GetState() */
Expand Down
2 changes: 1 addition & 1 deletion test/utests/fakes/FakePlayerInstanceAamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ const std::vector<TimedMetadata> & PlayerInstanceAAMP::GetTimedMetadata( void )
std::string session_id,
const char *preprocessedManifest
) { }
void PlayerInstanceAAMP::Stop(bool sendStateChangeEvent) { }
void PlayerInstanceAAMP::Stop(bool sendStateChangeEvent, bool forceCleanup) { }
void PlayerInstanceAAMP::ResetConfiguration() { }
void PlayerInstanceAAMP::SetRate(float rate, int overshootcorrection) { }
void PlayerInstanceAAMP::PauseAt(double position) { }
Expand Down
2 changes: 1 addition & 1 deletion test/utests/mocks/MockPrivateInstanceAAMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MockPrivateInstanceAAMP
public:
MOCK_METHOD(double, RecalculatePTS, (AampMediaType mediaType, const void *ptr, size_t len));

MOCK_METHOD(void, Stop, (bool sendStateChangeEvent));
MOCK_METHOD(void, Stop, (bool sendStateChangeEvent, bool forceCleanup));

MOCK_METHOD(void, StartPausePositionMonitoring, (long long pausePositionMilliseconds));

Expand Down
Loading