Skip to content

Commit

Permalink
Fix issues with video mute in trackMuteHandler (#1525)
Browse files Browse the repository at this point in the history
  • Loading branch information
lodoyun authored Dec 16, 2019
1 parent 7f72f03 commit afb7c76
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 65 deletions.
106 changes: 56 additions & 50 deletions erizo/src/erizo/rtp/RtpTrackMuteHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ namespace erizo {

DEFINE_LOGGER(RtpTrackMuteHandler, "rtp.RtpTrackMuteHandler");

RtpTrackMuteHandler::RtpTrackMuteHandler() : audio_info_{"audio"}, video_info_{"video"}, stream_{nullptr} {}
RtpTrackMuteHandler::RtpTrackMuteHandler(std::shared_ptr<erizo::Clock> the_clock) : audio_info_{"audio"},
video_info_{"video"}, clock_{the_clock}, stream_{nullptr} {}

void RtpTrackMuteHandler::enable() {
}
Expand Down Expand Up @@ -37,35 +38,34 @@ void RtpTrackMuteHandler::read(Context *ctx, std::shared_ptr<DataPacket> packet)
}

void RtpTrackMuteHandler::handleFeedback(const TrackMuteInfo &info, const std::shared_ptr<DataPacket> &packet) {
RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(packet->data);
uint16_t offset = info.seq_num_offset;
if (offset > 0) {
char* buf = packet->data;
char* report_pointer = buf;
int rtcp_length = 0;
int total_length = 0;
do {
report_pointer += rtcp_length;
chead = reinterpret_cast<RtcpHeader*>(report_pointer);
rtcp_length = (ntohs(chead->length) + 1) * 4;
total_length += rtcp_length;
switch (chead->packettype) {
case RTCP_Receiver_PT:
if ((chead->getHighestSeqnum() + offset) < chead->getHighestSeqnum()) {
// The seqNo adjustment causes a wraparound, add to cycles
chead->setSeqnumCycles(chead->getSeqnumCycles() + 1);
RtpUtils::forEachRtcpBlock(packet, [info](RtcpHeader *chead) {
switch (chead->packettype) {
case RTCP_Receiver_PT:
{
uint16_t incoming_seq_num = chead->getHighestSeqnum();
SequenceNumber input_seq_num = info.translator.reverse(incoming_seq_num);
if (input_seq_num.type != SequenceNumberType::Valid) {
break;
}
if (RtpUtils::sequenceNumberLessThan(input_seq_num.input, incoming_seq_num)) {
chead->setSeqnumCycles(chead->getSeqnumCycles() - 1);
}
chead->setHighestSeqnum(chead->getHighestSeqnum() + offset);

chead->setHighestSeqnum(input_seq_num.input);
break;
case RTCP_RTP_Feedback_PT:
chead->setNackPid(chead->getNackPid() + offset);
break;
default:
}
case RTCP_RTP_Feedback_PT:
{
SequenceNumber input_seq_num = info.translator.reverse(chead->getNackPid());
if (input_seq_num.type == SequenceNumberType::Valid) {
chead->setNackPid(input_seq_num.input);
}
break;
}
} while (total_length < packet->length);
}
}
default:
break;
}
});
}

void RtpTrackMuteHandler::write(Context *ctx, std::shared_ptr<DataPacket> packet) {
Expand All @@ -83,37 +83,48 @@ void RtpTrackMuteHandler::write(Context *ctx, std::shared_ptr<DataPacket> packet

void RtpTrackMuteHandler::handlePacket(Context *ctx, TrackMuteInfo *info, std::shared_ptr<DataPacket> packet) {
RtpHeader *rtp_header = reinterpret_cast<RtpHeader*>(packet->data);
info->last_original_seq_num = rtp_header->getSeqNumber();
if (info->last_sent_seq_num == -1) {
info->last_sent_seq_num = info->last_original_seq_num;
}
time_point now = clock_->now();
bool should_skip_packet = false;
if (info->mute_is_active) {
if (packet->is_keyframe) {
if (info->unmute_requested) {
if (info->label == "video") {
if (packet->is_keyframe && info->unmute_requested) {
ELOG_DEBUG("%s message: Keyframe arrived - unmuting video", stream_->toLog());
info->mute_is_active = false;
info->unmute_requested = false;
} else {
ELOG_DEBUG("%s message: video muted - maybe transforming into black keyframe", stream_->toLog());
last_keyframe_sent_time_ = now;
} else if (packet->is_keyframe ||
(now - last_keyframe_sent_time_) > kMuteVideoKeyframeTimeout) {
ELOG_DEBUG("message: Will create Keyframe last_keyframe, time: %u, is_keyframe: %u",
now - last_keyframe_sent_time_, packet->is_keyframe);
if (packet->codec == "VP8") {
packet = transformIntoBlackKeyframePacket(packet);
last_keyframe_sent_time_ = now;
} else {
ELOG_WARN("%s cannot generate keyframe packet is not available for codec %s",
ELOG_INFO("%s message: cannot generate keyframe packet is not available for codec %s",
stream_->toLog(), packet->codec);
return;
should_skip_packet = true;
}
} else {
should_skip_packet = true;
}
updateOffset(info);
} else {
return;
should_skip_packet = true;
}
}
uint16_t offset = info->seq_num_offset;
info->last_sent_seq_num = info->last_original_seq_num - offset;
if (offset > 0) {
setPacketSeqNumber(packet, info->last_sent_seq_num);
uint16_t packet_seq_num = rtp_header->getSeqNumber();
maybeUpdateHighestSeqNum(info, packet_seq_num);
SequenceNumber sequence_number_info = info->translator.get(packet_seq_num, should_skip_packet);
if (!should_skip_packet) {
setPacketSeqNumber(packet, sequence_number_info.output);
ctx->fireWrite(std::move(packet));
}
}

void RtpTrackMuteHandler::maybeUpdateHighestSeqNum(TrackMuteInfo *info, uint16_t seq_num) {
if (RtpUtils::sequenceNumberLessThan(info->highest_seq_num, seq_num) || !info->highest_seq_num_initialized) {
info->highest_seq_num = seq_num;
info->highest_seq_num_initialized = true;
}
ctx->fireWrite(std::move(packet));
}

void RtpTrackMuteHandler::muteAudio(bool active) {
Expand All @@ -133,11 +144,10 @@ void RtpTrackMuteHandler::muteTrack(TrackMuteInfo *info, bool active) {
if (info->label == "video") {
info->unmute_requested = true;
getContext()->fireRead(RtpUtils::createPLI(stream_->getVideoSinkSSRC(), stream_->getVideoSourceSSRC()));
ELOG_DEBUG("%s message: Unmute requested for video, original_seq_num: %u, last_sent_seq_num: %u, offset: %u",
stream_->toLog(), info->last_original_seq_num, info->last_sent_seq_num, info->seq_num_offset);
ELOG_DEBUG("%s message: Unmute requested for video",
stream_->toLog());
} else {
info->mute_is_active = active;
updateOffset(info);
}
} else {
info->mute_is_active = active;
Expand All @@ -153,10 +163,6 @@ inline void RtpTrackMuteHandler::setPacketSeqNumber(std::shared_ptr<DataPacket>
head->setSeqNumber(seq_number);
}

void RtpTrackMuteHandler::updateOffset(TrackMuteInfo *info) {
info->seq_num_offset = info->last_original_seq_num - info->last_sent_seq_num;
}


std::shared_ptr<DataPacket> RtpTrackMuteHandler::transformIntoBlackKeyframePacket
(std::shared_ptr<DataPacket> packet) {
Expand Down
18 changes: 12 additions & 6 deletions erizo/src/erizo/rtp/RtpTrackMuteHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,25 @@

#include "./logger.h"
#include "pipeline/Handler.h"
#include "rtp/SequenceNumberTranslator.h"

#include <mutex> // NOLINT

static constexpr erizo::duration kMuteVideoKeyframeTimeout = std::chrono::seconds(5);

namespace erizo {

class MediaStream;

class TrackMuteInfo {
public:
explicit TrackMuteInfo(std::string label_)
: label{label_}, last_original_seq_num{-1}, seq_num_offset{0},
last_sent_seq_num{-1}, mute_is_active{false}, unmute_requested{false} {}
: label{label_}, highest_seq_num{0}, highest_seq_num_initialized{false},
mute_is_active{false}, unmute_requested{false} {}
std::string label;
int32_t last_original_seq_num;
uint16_t seq_num_offset;
int32_t last_sent_seq_num;
SequenceNumberTranslator translator;
uint16_t highest_seq_num;
uint16_t highest_seq_num_initialized;
bool mute_is_active;
bool unmute_requested;
};
Expand All @@ -27,7 +30,7 @@ class RtpTrackMuteHandler: public Handler {
DECLARE_LOGGER();

public:
RtpTrackMuteHandler();
explicit RtpTrackMuteHandler(std::shared_ptr<erizo::Clock> the_clock = std::make_shared<SteadyClock>());
void muteAudio(bool active);
void muteVideo(bool active);

Expand All @@ -46,13 +49,16 @@ class RtpTrackMuteHandler: public Handler {
void muteTrack(TrackMuteInfo *info, bool active);
void handleFeedback(const TrackMuteInfo &info, const std::shared_ptr<DataPacket> &packet);
void handlePacket(Context *ctx, TrackMuteInfo *info, std::shared_ptr<DataPacket> packet);
void maybeUpdateHighestSeqNum(TrackMuteInfo *info, uint16_t seq_num);
inline void setPacketSeqNumber(std::shared_ptr<DataPacket> packet, uint16_t seq_number);
std::shared_ptr<DataPacket> transformIntoBlackKeyframePacket(std::shared_ptr<DataPacket> packet);
void updateOffset(TrackMuteInfo *info);

private:
TrackMuteInfo audio_info_;
TrackMuteInfo video_info_;
time_point last_keyframe_sent_time_;
std::shared_ptr<erizo::Clock> clock_;

MediaStream* stream_;
};
Expand Down
35 changes: 34 additions & 1 deletion erizo/src/test/rtp/RtpTrackMuteHandlerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ class RtpTrackMuteHandlerTest : public erizo::HandlerTest {

protected:
void setHandler() {
track_mute_handler = std::make_shared<RtpTrackMuteHandler>();
track_mute_handler = std::make_shared<RtpTrackMuteHandler>(simulated_clock);
pipeline->addBack(track_mute_handler);
}

void advanceClock(erizo::duration time) {
simulated_clock->advanceTime(time);
}

std::shared_ptr<RtpTrackMuteHandler> track_mute_handler;
};

Expand Down Expand Up @@ -87,6 +91,35 @@ TEST_F(RtpTrackMuteHandlerTest, shouldNotWriteVideoPacketsIfActive) {
pipeline->write(video_packet);
}

TEST_F(RtpTrackMuteHandlerTest, shouldTransformKeyframes) {
auto keyframe = erizo::PacketTools::createVP8Packet(erizo::kArbitrarySeqNumber, true, true);
auto video_packet = erizo::PacketTools::createDataPacket(erizo::kArbitrarySeqNumber + 1, VIDEO_PACKET);
track_mute_handler->muteVideo(true);
EXPECT_CALL(*writer.get(), write(_, _)).
With(Args<1>(erizo::RtpHasSequenceNumber(erizo::kArbitrarySeqNumber))).Times(1);
EXPECT_CALL(*writer.get(), write(_, _)).
With(Args<1>(erizo::RtpHasSequenceNumber(erizo::kArbitrarySeqNumber + 1))).Times(0);
pipeline->write(keyframe);
pipeline->write(video_packet);
}

TEST_F(RtpTrackMuteHandlerTest, shouldTransformNormalPacketsEverykMuteVideoKeyframeTimeout) {
auto video_packet = erizo::PacketTools::createVP8Packet(erizo::kArbitrarySeqNumber, false, false);
auto video_packet2 = erizo::PacketTools::createVP8Packet(erizo::kArbitrarySeqNumber + 1, false, false);
auto video_packet3 = erizo::PacketTools::createVP8Packet(erizo::kArbitrarySeqNumber + 2, false, false);
track_mute_handler->muteVideo(true);
EXPECT_CALL(*writer.get(), write(_, _)).
With(Args<1>(erizo::RtpHasSequenceNumber(erizo::kArbitrarySeqNumber))).Times(1);
EXPECT_CALL(*writer.get(), write(_, _)).
With(Args<1>(erizo::RtpHasSequenceNumber(erizo::kArbitrarySeqNumber + 1))).Times(1);
EXPECT_CALL(*writer.get(), write(_, _)).
With(Args<1>(erizo::RtpHasSequenceNumber(erizo::kArbitrarySeqNumber + 2))).Times(0);
pipeline->write(video_packet);
pipeline->write(video_packet2);
advanceClock(kMuteVideoKeyframeTimeout + std::chrono::milliseconds(1));
pipeline->write(video_packet3);
}

TEST_F(RtpTrackMuteHandlerTest, shouldNotWriteAnyPacketsIfAllIsActive) {
auto audio_packet = erizo::PacketTools::createDataPacket(erizo::kArbitrarySeqNumber, AUDIO_PACKET);
auto video_packet = erizo::PacketTools::createDataPacket(erizo::kArbitrarySeqNumber+1, VIDEO_PACKET);
Expand Down
16 changes: 8 additions & 8 deletions erizo_controller/erizoClient/src/Stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -379,17 +379,17 @@ const Stream = (altConnectionHelpers, specInput) => {
callback('error');
return;
}
if (that.stream) {
for (let index = 0; index < that.stream.getVideoTracks().length; index += 1) {
const track = that.stream.getVideoTracks()[index];
track.enabled = !that.videoMuted;
}
if (!that.stream || !that.pc) {
Logger.warning('muteAudio/muteVideo cannot be called until a stream is published or subscribed');
callback('error');
}
for (let index = 0; index < that.stream.getVideoTracks().length; index += 1) {
const track = that.stream.getVideoTracks()[index];
track.enabled = !that.videoMuted;
}
const config = { muteStream: { audio: that.audioMuted, video: that.videoMuted } };
that.checkOptions(config, true);
if (that.pc) {
that.pc.updateSpec(config, that.getID(), callback);
}
that.pc.updateSpec(config, that.getID(), callback);
};

that.muteAudio = (isMuted, callback = () => {}) => {
Expand Down

0 comments on commit afb7c76

Please sign in to comment.