Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong PictureID rolling over in VP9 and VP8 #984

Merged
merged 6 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion worker/include/RTC/Codecs/H264_SVC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ namespace RTC
}

public:
RTC::SeqManager<uint16_t> pictureIdManager;
RTC::SeqManager<uint16_t, 15> pictureIdManager;
bool syncRequired{ false };
};

Expand Down
2 changes: 1 addition & 1 deletion worker/include/RTC/Codecs/VP8.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ namespace RTC
}

public:
RTC::SeqManager<uint16_t> pictureIdManager;
RTC::SeqManager<uint16_t, 15> pictureIdManager;
RTC::SeqManager<uint8_t> tl0PictureIndexManager;
bool syncRequired{ false };
};
Expand Down
2 changes: 1 addition & 1 deletion worker/include/RTC/Codecs/VP9.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ namespace RTC
}

public:
RTC::SeqManager<uint16_t> pictureIdManager;
RTC::SeqManager<uint16_t, 15> pictureIdManager;
bool syncRequired{ false };
};

Expand Down
4 changes: 2 additions & 2 deletions worker/include/RTC/SeqManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

namespace RTC
{
template<typename T>
template<typename T, unsigned int N = 0>
ibc marked this conversation as resolved.
Show resolved Hide resolved
class SeqManager
{
public:
static constexpr T MaxValue = std::numeric_limits<T>::max();
static constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);

public:
struct SeqLowerThan
Expand Down
1 change: 1 addition & 0 deletions worker/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ mediasoup_worker_test = executable(
'test/src/RTC/TestTrendCalculator.cpp',
'test/src/RTC/TestRtpEncodingParameters.cpp',
'test/src/RTC/Codecs/TestVP8.cpp',
'test/src/RTC/Codecs/TestVP9.cpp',
'test/src/RTC/Codecs/TestH264.cpp',
'test/src/RTC/Codecs/TestH264_SVC.cpp',
'test/src/RTC/RTCP/TestFeedbackPsAfb.cpp',
Expand Down
2 changes: 1 addition & 1 deletion worker/src/RTC/Codecs/VP8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ namespace RTC
this->payloadDescriptor->hasPictureId &&
this->payloadDescriptor->hasTlIndex &&
this->payloadDescriptor->hasTl0PictureIndex &&
!RTC::SeqManager<uint16_t>::IsSeqLowerThan(
!RTC::SeqManager<uint16_t, 15>::IsSeqLowerThan(
this->payloadDescriptor->pictureId,
context->pictureIdManager.GetMaxInput())
)
Expand Down
2 changes: 1 addition & 1 deletion worker/src/RTC/Codecs/VP9.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ namespace RTC
// clang-format off
bool isOldPacket = (
this->payloadDescriptor->hasPictureId &&
RTC::SeqManager<uint16_t>::IsSeqLowerThan(
RTC::SeqManager<uint16_t, 15>::IsSeqLowerThan(
this->payloadDescriptor->pictureId,
context->pictureIdManager.GetMaxInput())
);
Expand Down
65 changes: 33 additions & 32 deletions worker/src/RTC/SeqManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,43 @@

namespace RTC
{
template<typename T>
bool SeqManager<T>::SeqLowerThan::operator()(const T lhs, const T rhs) const
template<typename T, unsigned int N>
bool SeqManager<T, N>::SeqLowerThan::operator()(const T lhs, const T rhs) const
{
return ((rhs > lhs) && (rhs - lhs <= MaxValue / 2)) ||
((lhs > rhs) && (lhs - rhs > MaxValue / 2));
}

template<typename T>
bool SeqManager<T>::SeqHigherThan::operator()(const T lhs, const T rhs) const
template<typename T, unsigned int N>
bool SeqManager<T, N>::SeqHigherThan::operator()(const T lhs, const T rhs) const
{
return ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||
((rhs > lhs) && (rhs - lhs > MaxValue / 2));
}

template<typename T>
const typename SeqManager<T>::SeqLowerThan SeqManager<T>::isSeqLowerThan{};
template<typename T, unsigned int N>
const typename SeqManager<T, N>::SeqLowerThan SeqManager<T, N>::isSeqLowerThan{};

template<typename T>
const typename SeqManager<T>::SeqHigherThan SeqManager<T>::isSeqHigherThan{};
template<typename T, unsigned int N>
const typename SeqManager<T, N>::SeqHigherThan SeqManager<T, N>::isSeqHigherThan{};

template<typename T>
bool SeqManager<T>::IsSeqLowerThan(const T lhs, const T rhs)
template<typename T, unsigned int N>
bool SeqManager<T, N>::IsSeqLowerThan(const T lhs, const T rhs)
{
return isSeqLowerThan(lhs, rhs);
}

template<typename T>
bool SeqManager<T>::IsSeqHigherThan(const T lhs, const T rhs)
template<typename T, unsigned int N>
bool SeqManager<T, N>::IsSeqHigherThan(const T lhs, const T rhs)
{
return isSeqHigherThan(lhs, rhs);
}

template<typename T>
void SeqManager<T>::Sync(T input)
template<typename T, unsigned int N>
void SeqManager<T, N>::Sync(T input)
{
// Update base.
this->base = this->maxOutput - input;
this->base = (this->maxOutput - input) & MaxValue;

// Update maxInput.
this->maxInput = input;
Expand All @@ -52,24 +52,24 @@ namespace RTC
this->dropped.clear();
}

template<typename T>
void SeqManager<T>::Drop(T input)
template<typename T, unsigned int N>
void SeqManager<T, N>::Drop(T input)
{
// Mark as dropped if 'input' is higher than anyone already processed.
if (SeqManager<T>::IsSeqHigherThan(input, this->maxInput))
if (SeqManager<T, N>::IsSeqHigherThan(input, this->maxInput))
{
this->dropped.insert(input);
}
}

template<typename T>
void SeqManager<T>::Offset(T offset)
template<typename T, unsigned int N>
void SeqManager<T, N>::Offset(T offset)
{
this->base += offset;
this->base = (this->base + offset) & MaxValue;
}

template<typename T>
bool SeqManager<T>::Input(const T input, T& output)
template<typename T, unsigned int N>
bool SeqManager<T, N>::Input(const T input, T& output)
{
auto base = this->base;

Expand All @@ -78,10 +78,10 @@ namespace RTC
{
// Delete dropped inputs older than input - MaxValue/2.
size_t droppedCount = this->dropped.size();
auto it = this->dropped.lower_bound(input - MaxValue / 2);

size_t threshold = (input - MaxValue / 2) & MaxValue;
auto it = this->dropped.lower_bound(threshold);
this->dropped.erase(this->dropped.begin(), it);
this->base -= (droppedCount - this->dropped.size());
this->base = (this->base - (droppedCount - this->dropped.size())) & MaxValue;

// Count dropped entries before 'input' in order to adapt the base.
droppedCount = this->dropped.size();
Expand All @@ -100,10 +100,10 @@ namespace RTC
droppedCount -= std::distance(it, this->dropped.end());
}

base = this->base - droppedCount;
base = (this->base - droppedCount) & MaxValue;
}

output = input + base;
output = (input + base) & MaxValue;

T idelta = input - this->maxInput;
T odelta = output - this->maxOutput;
Expand All @@ -121,21 +121,22 @@ namespace RTC
return true;
}

template<typename T>
T SeqManager<T>::GetMaxInput() const
template<typename T, unsigned int N>
T SeqManager<T, N>::GetMaxInput() const
{
return this->maxInput;
}

template<typename T>
T SeqManager<T>::GetMaxOutput() const
template<typename T, unsigned int N>
T SeqManager<T, N>::GetMaxOutput() const
{
return this->maxOutput;
}

// Explicit instantiation to have all SeqManager definitions in this file.
template class SeqManager<uint8_t>;
template class SeqManager<uint16_t>;
template class SeqManager<uint16_t, 15>; // For PictureID (15bits).
ibc marked this conversation as resolved.
Show resolved Hide resolved
template class SeqManager<uint32_t>;

} // namespace RTC
1 change: 1 addition & 0 deletions worker/src/RTC/SvcConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ namespace RTC
MS_DEBUG_TAG(rtp, "sync key frame received");

this->rtpSeqManager.Sync(packet->GetSequenceNumber() - 1);
this->encodingContext->SyncRequired();
jmillan marked this conversation as resolved.
Show resolved Hide resolved

this->syncRequired = false;
}
Expand Down
28 changes: 28 additions & 0 deletions worker/test/src/RTC/Codecs/TestVP8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,32 @@ SCENARIO("process VP8 payload descriptor", "[codecs][vp8]")
REQUIRE(forwarded->pictureId == 1);
REQUIRE(forwarded->tl0PictureIndex == 1);
}

SECTION("drop packets that belong to other temporal layers after rolling over pictureID")
{
RTC::Codecs::EncodingContext::Params params;
params.spatialLayers = 0;
params.temporalLayers = 2;
Codecs::VP8::EncodingContext context(params);
context.SyncRequired();

context.SetCurrentTemporalLayer(0);
context.SetTargetTemporalLayer(0);

// Frame 1
ibc marked this conversation as resolved.
Show resolved Hide resolved
auto forwarded = ProcessPacket(context, 32767, 0, 0);
REQUIRE(forwarded);
REQUIRE(forwarded->pictureId == 1);
REQUIRE(forwarded->tl0PictureIndex == 1);

// Frame 2
ibc marked this conversation as resolved.
Show resolved Hide resolved
forwarded = ProcessPacket(context, 0, 0, 0);
REQUIRE(forwarded);
REQUIRE(forwarded->pictureId == 2);
REQUIRE(forwarded->tl0PictureIndex == 1);

// Frame 3
ibc marked this conversation as resolved.
Show resolved Hide resolved
forwarded = ProcessPacket(context, 1, 0, 1);
REQUIRE_FALSE(forwarded);
}
}
76 changes: 76 additions & 0 deletions worker/test/src/RTC/Codecs/TestVP9.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "common.hpp"
#include "RTC/Codecs/VP9.hpp"
#include <catch2/catch.hpp>
#include <cstring> // std::memcmp()

using namespace RTC;

Codecs::VP9::PayloadDescriptor* CreateVP9Packet(
uint8_t* buffer, size_t bufferLen, uint16_t pictureId, uint8_t tlIndex)
{
buffer[0] = 0xAD; // I and L bits
uint16_t netPictureId = htons(pictureId);
std::memcpy(buffer + 1, &netPictureId, 2);
buffer[1] |= 0x80;
buffer[3] = tlIndex << 6;

auto* payloadDescriptor = Codecs::VP9::Parse(buffer, bufferLen);

REQUIRE(payloadDescriptor);

return payloadDescriptor;
}

std::unique_ptr<Codecs::VP9::PayloadDescriptor> ProcessVP9Packet(
Codecs::VP9::EncodingContext& context, uint16_t pictureId, uint8_t tlIndex)
{
// clang-format off
uint8_t buffer[] =
{
0xAD, 0x80, 0x00, 0x00, 0x00, 0x00
};
// clang-format on
bool marker;
auto* payloadDescriptor = CreateVP9Packet(buffer, sizeof(buffer), pictureId, tlIndex);
std::unique_ptr<Codecs::VP9::PayloadDescriptorHandler> payloadDescriptorHandler(
new Codecs::VP9::PayloadDescriptorHandler(payloadDescriptor));

if (payloadDescriptorHandler->Process(&context, buffer, marker))
{
return std::unique_ptr<Codecs::VP9::PayloadDescriptor>(Codecs::VP9::Parse(buffer, sizeof(buffer)));
}

return nullptr;
}

SCENARIO("process VP9 payload descriptor", "[codecs][vp9]")
{
SECTION("drop packets that belong to other temporal layers after rolling over pictureID")
{
RTC::Codecs::EncodingContext::Params params;
params.spatialLayers = 1;
params.temporalLayers = 3;

Codecs::VP9::EncodingContext context(params);
context.SyncRequired();
context.SetCurrentTemporalLayer(0);
context.SetTargetTemporalLayer(0);

context.SetCurrentSpatialLayer(0);
context.SetTargetSpatialLayer(0);

// Frame 1
ibc marked this conversation as resolved.
Show resolved Hide resolved
auto forwarded = ProcessVP9Packet(context, 32767, 0);
ibc marked this conversation as resolved.
Show resolved Hide resolved
REQUIRE(forwarded);
REQUIRE(forwarded->pictureId == 32767);

// Frame 2
ibc marked this conversation as resolved.
Show resolved Hide resolved
forwarded = ProcessVP9Packet(context, 0, 0);
REQUIRE(forwarded);
REQUIRE(forwarded->pictureId == 0);

// Frame 3
ibc marked this conversation as resolved.
Show resolved Hide resolved
forwarded = ProcessVP9Packet(context, 1, 1);
REQUIRE_FALSE(forwarded);
}
}
Loading