Skip to content

Commit

Permalink
DIOS-5950 Use weak_ptr to capture this pointer in SimulcastMediaFrame…
Browse files Browse the repository at this point in the history
…Listener and do proper detach to avoid crash (#303)
  • Loading branch information
harryz2000 committed Jul 3, 2024
1 parent 9f6a448 commit f78898d
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 32 deletions.
2 changes: 1 addition & 1 deletion include/SimulcastMediaFrameListener.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
class SimulcastMediaFrameListener :
public MediaFrame::Listener,
public MediaFrame::Producer,
public std::enable_shared_from_this<MediaFrame::Listener>
public std::enable_shared_from_this<SimulcastMediaFrameListener>

{
public:
Expand Down
67 changes: 36 additions & 31 deletions src/SimulcastMediaFrameListener.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ void SimulcastMediaFrameListener::Detach(const MediaFrame::Producer::shared& pro
{
Debug("-SimulcastMediaFrameListener::Detach() [this:%p,producer:%p]\n", this, producer.get());
timeService.Sync([=](std::chrono::milliseconds) {
producer->RemoveMediaListener(shared_from_this());
producers.erase(producer);
});
}
Expand Down Expand Up @@ -153,46 +154,50 @@ void SimulcastMediaFrameListener::ForwardFrame(VideoFrame& frame)

void SimulcastMediaFrameListener::Push(std::shared_ptr<VideoFrame>&& frame)
{
timeService.Async([this, frame = std::move(frame)](std::chrono::milliseconds) mutable {
timeService.Async([selfWeak = weak_from_this(), frame = std::move(frame)](std::chrono::milliseconds) mutable {

auto self = selfWeak.lock();
if (!self) return;

DWORD ssrc = frame->GetSSRC();

if (!referenceFrameTime)
if (!self->referenceFrameTime)
{
if (!frame->IsIntra()) return;
referenceFrameTime = frame->GetTime();
self->referenceFrameTime = frame->GetTime();
}

if (initialTimestamps.find(ssrc) == initialTimestamps.end())
if (self->initialTimestamps.find(ssrc) == self->initialTimestamps.end())
{
if (!frame->IsIntra()) return;
auto offset = (int64_t(frame->GetTime()) - int64_t(*referenceFrameTime)) * frame->GetClockRate() / 1000;
initialTimestamps[ssrc] = frame->GetTimeStamp() - offset;
auto offset = (int64_t(frame->GetTime()) - int64_t(*self->referenceFrameTime)) * frame->GetClockRate() / 1000;
self->initialTimestamps[ssrc] = frame->GetTimeStamp() - offset;
}

if (layerDimensions.find(ssrc) == layerDimensions.end())
if (self->layerDimensions.find(ssrc) == self->layerDimensions.end())
{
if (!frame->IsIntra()) return;
layerDimensions[ssrc] = frame->GetWidth() * frame->GetHeight();
self->layerDimensions[ssrc] = frame->GetWidth() * frame->GetHeight();
}

// Convert to relative timestamp
auto tm = frame->GetTimeStamp() > initialTimestamps[ssrc] ? frame->GetTimeStamp() - initialTimestamps[ssrc] : 0;
auto tm = frame->GetTimeStamp() > self->initialTimestamps[ssrc] ? frame->GetTimeStamp() - self->initialTimestamps[ssrc] : 0;
frame->SetTimestamp(tm);

// Update the layer latest timestamp
layerTimestamps[ssrc] = tm;
self->layerTimestamps[ssrc] = tm;

// Initially, select the first intra frame. Will later to update to higher
// quality one if exists
if (selectedSsrc == 0 && frame->IsIntra())
if (self->selectedSsrc == 0 && frame->IsIntra())
{
selectedSsrc = ssrc;
self->selectedSsrc = ssrc;
}

if (maxQueueSize == 0)
if (self->maxQueueSize == 0)
{
assert(numLayers == 1);
initialised = true;
assert(self->numLayers == 1);
self->initialised = true;
}

// Enqueue the selected layer frame.
Expand All @@ -202,38 +207,38 @@ void SimulcastMediaFrameListener::Push(std::shared_ptr<VideoFrame>&& frame)
// 2. The frame is at least later than the last forwarded frame.
// 3. The frame has higher dimension or it has been too long since the current
// layer frame was queued.
if (ssrc == selectedSsrc)
if (ssrc == self->selectedSsrc)
{
Enqueue(std::move(frame));
self->Enqueue(std::move(frame));
}
else if (frame->IsIntra() && (!lastForwardedTimestamp || frame->GetTimeStamp() > *lastForwardedTimestamp))
else if (frame->IsIntra() && (!self->lastForwardedTimestamp || frame->GetTimeStamp() > *self->lastForwardedTimestamp))
{
if (layerDimensions[ssrc] > layerDimensions[selectedSsrc] ||
frame->GetTime() > (lastEnqueueTimeMs + MaxWaitingTimeBeforeSwitchingLayerMs))
if (self->layerDimensions[ssrc] > self->layerDimensions[self->selectedSsrc] ||
frame->GetTime() > (self->lastEnqueueTimeMs + MaxWaitingTimeBeforeSwitchingLayerMs))
{
//UltraDebug("layer switch: 0x%x -> 0x%x, time: %lld, timestamp: %lld\n", ssrc, selectedSsrc, frame->GetTime(), tm);

selectedSsrc = ssrc;
Enqueue(std::move(frame));
self->selectedSsrc = ssrc;
self->Enqueue(std::move(frame));
}
}

// Select the best available frame in the queue during initialising
if (!initialised && (queue.size() == maxQueueSize || layerDimensions.size() == numLayers))
if (!self->initialised && (self->queue.size() == self->maxQueueSize || self->layerDimensions.size() == self->numLayers))
{
assert(maxQueueSize > 0);
auto bestLayerFrame = std::max_element(queue.begin(), queue.end(),
[this](const auto& elementA, const auto& elementB) {
return layerDimensions[elementA->GetSSRC()] < layerDimensions[elementB->GetSSRC()];
assert(self->maxQueueSize > 0);
auto bestLayerFrame = std::max_element(self->queue.begin(), self->queue.end(),
[self](const auto& elementA, const auto& elementB) {
return self->layerDimensions[elementA->GetSSRC()] < self->layerDimensions[elementB->GetSSRC()];
});

selectedSsrc = (*bestLayerFrame)->GetSSRC();
while(!queue.empty() && queue.front()->GetSSRC() != selectedSsrc)
self->selectedSsrc = (*bestLayerFrame)->GetSSRC();
while(!self->queue.empty() && self->queue.front()->GetSSRC() != self->selectedSsrc)
{
queue.pop_front();
self->queue.pop_front();
}

initialised = true;
self->initialised = true;
}
});
}
Expand Down

0 comments on commit f78898d

Please sign in to comment.