Skip to content

Commit

Permalink
Fix ExternalInput for RTSP sources (#1453)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcague authored Sep 16, 2019
1 parent b789309 commit 69f1a97
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 66 deletions.
65 changes: 62 additions & 3 deletions erizo/src/erizo/media/ExternalInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,15 @@ int ExternalInput::init() {
needTranscoding_ = true;
inCodec_.initDecoder(st->codec);

bufflen_ = st->codec->width*st->codec->height*3/2;
bufflen_ = st->codec->width * st->codec->height * 3 / 2;
decodedBuffer_.reset((unsigned char*) malloc(bufflen_));


om.processorType = RTP_ONLY;
om.videoCodec.codec = VIDEO_CODEC_VP8;
om.videoCodec.bitRate = 1000000;
om.videoCodec.width = 640;
om.videoCodec.height = 480;
om.videoCodec.width = st->codec->width;
om.videoCodec.height = st->codec->height;
om.videoCodec.frameRate = 20;
om.hasVideo = true;

Expand All @@ -161,14 +161,73 @@ int ExternalInput::init() {
}

int ExternalInput::sendPLI() {
if (op_) {
op_->requestKeyframe();
}
return 0;
}

int ExternalInput::deliverFeedback_(std::shared_ptr<DataPacket> fb_packet) {
RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(fb_packet->data);
if (chead->isFeedback()) {
if (chead->getBlockCount() == 0 && (chead->getLength()+1) * 4 == fb_packet->length) {
return 0;
}
char* moving_buf = fb_packet->data;
int rtcp_length = 0;
int total_length = 0;
do {
moving_buf += rtcp_length;
chead = reinterpret_cast<RtcpHeader*>(moving_buf);
rtcp_length = (ntohs(chead->length) + 1) * 4;
total_length += rtcp_length;
switch (chead->packettype) {
case RTCP_RTP_Feedback_PT:
// NACKs are already handled by MediaStream from the subscribers
// sendPLI();
break;
case RTCP_PS_Feedback_PT:
switch (chead->getBlockCount()) {
case RTCP_PLI_FMT:
case RTCP_FIR_FMT:
sendPLI();
break;
case RTCP_AFB:
char *unique_id = reinterpret_cast<char*>(&chead->report.rembPacket.uniqueid);
if (!strncmp(unique_id, "REMB", 4)) {
uint64_t bitrate = chead->getBrMantis() << chead->getBrExp();
if (op_) {
op_->setTargetBitrate(bitrate);
}
}
break;
}
}
} while (total_length < fb_packet->length);
}
return 0;
}

void ExternalInput::receiveRtpData(unsigned char* rtpdata, int len) {
if (video_sink_ != nullptr) {
RtcpHeader* head = reinterpret_cast<RtcpHeader*>(rtpdata);
if (!head->isRtcp()) {
if (getVideoSourceSSRC() == 0) {
setVideoSourceSSRC(55543);
}
}
std::shared_ptr<DataPacket> packet = std::make_shared<DataPacket>(0, reinterpret_cast<char*>(rtpdata),
len, VIDEO_PACKET);
RtpHeader *rtp_header = reinterpret_cast<RtpHeader*>(packet->data);
unsigned char* start_buffer = reinterpret_cast<unsigned char*> (packet->data);
start_buffer = start_buffer + rtp_header->getHeaderLength();
RTPPayloadVP8* payload = vp8_parser_.parseVP8(
start_buffer, packet->length - rtp_header->getHeaderLength());
if (!payload->frameType) {
packet->is_keyframe = true;
} else {
packet->is_keyframe = false;
}
video_sink_->deliverVideoData(packet);
}
}
Expand Down
5 changes: 4 additions & 1 deletion erizo/src/erizo/media/ExternalInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ extern "C" {
#include "codecs/VideoCodec.h"
#include "media/MediaProcessor.h"
#include "./logger.h"
#include "rtp/RtpVP8Parser.h"

namespace erizo {

class ExternalInput : public MediaSource, public RTPDataReceiver {
class ExternalInput : public MediaSource, public FeedbackSink, public RTPDataReceiver {
DECLARE_LOGGER();

public:
Expand All @@ -36,6 +37,7 @@ class ExternalInput : public MediaSource, public RTPDataReceiver {
int init();
void receiveRtpData(unsigned char* rtpdata, int len) override;
int sendPLI() override;
int deliverFeedback_(std::shared_ptr<DataPacket> data_packet) override;

boost::future<void> close() override {
std::shared_ptr<boost::promise<void>> p = std::make_shared<boost::promise<void>>();
Expand Down Expand Up @@ -63,6 +65,7 @@ class ExternalInput : public MediaSource, public RTPDataReceiver {
int64_t lastPts_, lastAudioPts_;
int64_t startTime_;

RtpVP8Parser vp8_parser_;

void receiveLoop();
void encodeLoop();
Expand Down
10 changes: 8 additions & 2 deletions erizo/src/erizo/media/MediaProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ int InputProcessor::deliverVideoData_(std::shared_ptr<DataPacket> video_packet)
gotUnpackagedFrame_ = 0;
ELOG_DEBUG("Bytes dec = %d", c);
if (gotDecodedFrame && c > 0) {
ELOG_DEBUG("Tengo un frame decodificado!!");
gotDecodedFrame = 0;
RawDataPacket p;
p.data = decodedBuffer_;
Expand Down Expand Up @@ -387,6 +386,13 @@ void OutputProcessor::close() {
free(packagedAudioBuffer_); packagedAudioBuffer_ = NULL;
}

void OutputProcessor::requestKeyframe() {
vCoder.requestKeyframe();
}

void OutputProcessor::setTargetBitrate(uint64_t bitrate) {
vCoder.setTargetBitrate(bitrate);
}

void OutputProcessor::receiveRawData(const RawDataPacket& packet) {
if (packet.type == VIDEO) {
Expand Down Expand Up @@ -503,7 +509,7 @@ int OutputProcessor::packageVideo(unsigned char* inBuff, int buffSize, unsigned
rtpHeader.setTimestamp(av_rescale(pts, 90000, 1000));
}
rtpHeader.setSSRC(55543);
rtpHeader.setPayloadType(100);
rtpHeader.setPayloadType(96);
memcpy(rtpBuffer_, &rtpHeader, rtpHeader.getHeaderLength());
memcpy(&rtpBuffer_[rtpHeader.getHeaderLength()], outBuff, outlen);

Expand Down
5 changes: 5 additions & 0 deletions erizo/src/erizo/media/MediaProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class InputProcessor: public MediaSink {

int decodeAudio(unsigned char* inBuff, int inBuffLen, unsigned char* outBuff);
};

class OutputProcessor: public RawDataReceiver {
DECLARE_LOGGER();

Expand All @@ -142,6 +143,10 @@ class OutputProcessor: public RawDataReceiver {
void close();
void receiveRawData(const RawDataPacket& packet);

void requestKeyframe();

void setTargetBitrate(uint64_t bitrate);

int packageAudio(unsigned char* inBuff, int inBuffLen, unsigned char* outBuff, long int pts = 0); // NOLINT

int packageVideo(unsigned char* inBuff, int buffSize, unsigned char* outBuff, long int pts = 0); // NOLINT
Expand Down
159 changes: 118 additions & 41 deletions erizo/src/erizo/media/codecs/VideoCodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,12 @@ inline AVCodecID VideoCodecID2ffmpegDecoderID(VideoCodecID codec) {
VideoEncoder::VideoEncoder() {
avcodec_register_all();
vCoder = NULL;
vCoderContext = NULL;
coder_context_ = NULL;
cPicture = NULL;
keyframe_requested_ = false;
target_bitrate_ = 0;
target_width_ = 0;
target_height_ = 0;
}

VideoEncoder::~VideoEncoder() {
Expand All @@ -41,35 +45,38 @@ int VideoEncoder::initEncoder(const VideoCodecInfo& info) {
return -1;
}

vCoderContext = avcodec_alloc_context3(vCoder);
if (!vCoderContext) {
ELOG_DEBUG("Error allocating vCoderContext");
coder_context_ = avcodec_alloc_context3(vCoder);
if (!coder_context_) {
ELOG_DEBUG("Error allocating coder_context_");
return -2;
}

vCoderContext->bit_rate = info.bitRate;
vCoderContext->rc_min_rate = info.bitRate;
vCoderContext->rc_max_rate = info.bitRate; // VPX_CBR
vCoderContext->qmin = 0;
vCoderContext->qmax = 40; // rc_quantifiers
vCoderContext->profile = 3;
// vCoderContext->frame_skip_threshold = 30;
vCoderContext->rc_buffer_aggressivity = 0.95;
// vCoderContext->rc_buffer_size = vCoderContext->bit_rate;
// vCoderContext->rc_initial_buffer_occupancy = vCoderContext->bit_rate / 2;
vCoderContext->rc_initial_buffer_occupancy = 500;

vCoderContext->rc_buffer_size = 1000;

vCoderContext->width = info.width;
vCoderContext->height = info.height;
vCoderContext->pix_fmt = PIX_FMT_YUV420P;
vCoderContext->time_base = (AVRational) {1, 90000};

vCoderContext->sample_aspect_ratio = (AVRational) { info.width, info.height };
vCoderContext->thread_count = 4;

if (avcodec_open2(vCoderContext, vCoder, NULL) < 0) {
coder_context_->bit_rate = info.bitRate;
coder_context_->rc_min_rate = info.bitRate;
coder_context_->rc_max_rate = info.bitRate; // VPX_CBR
target_bitrate_ = info.bitRate;
coder_context_->qmin = 0;
coder_context_->qmax = 40; // rc_quantifiers
coder_context_->profile = 3;
// coder_context_->frame_skip_threshold = 30;
coder_context_->rc_buffer_aggressivity = 0.95;
// coder_context_->rc_buffer_size = coder_context_->bit_rate;
// coder_context_->rc_initial_buffer_occupancy = coder_context_->bit_rate / 2;
coder_context_->rc_initial_buffer_occupancy = 500;

coder_context_->rc_buffer_size = 1000;

coder_context_->width = info.width;
coder_context_->height = info.height;
target_width_ = info.width;
target_height_ = info.height;
coder_context_->pix_fmt = PIX_FMT_YUV420P;
coder_context_->time_base = (AVRational) {1, 90000};

coder_context_->sample_aspect_ratio = (AVRational) { info.width, info.height };
coder_context_->thread_count = 4;

if (avcodec_open2(coder_context_, vCoder, NULL) < 0) {
ELOG_DEBUG("Error opening video decoder");
return -3;
}
Expand All @@ -80,22 +87,90 @@ int VideoEncoder::initEncoder(const VideoCodecInfo& info) {
return -4;
}

ELOG_DEBUG("videoCoder configured successfully %d x %d", vCoderContext->width,
vCoderContext->height);
ELOG_DEBUG("videoCoder configured successfully %d x %d", coder_context_->width,
coder_context_->height);
return 0;
}

void VideoEncoder::requestKeyframe() {
keyframe_requested_ = true;
}

void VideoEncoder::setTargetBitrate(uint64_t bitrate) {
target_bitrate_ = bitrate;
restartContext();
}

void VideoEncoder::setResolution(int width, int height) {
target_width_ = width;
target_height_ = height;
restartContext();
}

void VideoEncoder::maybeSwapContext() {
if (next_coder_context_) {
avcodec_free_context(&coder_context_);
coder_context_ = next_coder_context_;
next_coder_context_ = nullptr;
if (avcodec_open2(coder_context_, vCoder, NULL) < 0) {
ELOG_DEBUG("Error opening video decoder");
}
}
}

void VideoEncoder::restartContext() {
// TODO(javier): restart coder_context_
next_coder_context_ = avcodec_alloc_context3(vCoder);
if (!next_coder_context_) {
ELOG_DEBUG("Error allocating next_coder_context_");
return;
}

next_coder_context_->bit_rate = target_bitrate_;
next_coder_context_->rc_min_rate = target_bitrate_;
next_coder_context_->rc_max_rate = target_bitrate_; // VPX_CBR
next_coder_context_->qmin = 0;
next_coder_context_->qmax = 40; // rc_quantifiers
next_coder_context_->profile = 3;
// next_coder_context_->frame_skip_threshold = 30;
next_coder_context_->rc_buffer_aggressivity = 0.95;
// next_coder_context_->rc_buffer_size = next_coder_context_->bit_rate;
// next_coder_context_->rc_initial_buffer_occupancy = next_coder_context_->bit_rate / 2;
next_coder_context_->rc_initial_buffer_occupancy = 500;

next_coder_context_->rc_buffer_size = 1000;

next_coder_context_->width = target_width_;
next_coder_context_->height = target_height_;
next_coder_context_->pix_fmt = PIX_FMT_YUV420P;
next_coder_context_->time_base = (AVRational) {1, 90000};

next_coder_context_->sample_aspect_ratio = (AVRational) { target_width_, target_height_ };
next_coder_context_->thread_count = 4;
}

int VideoEncoder::encodeVideo(unsigned char* inBuffer, int inLength, unsigned char* outBuffer, int outLength) {
int size = vCoderContext->width * vCoderContext->height;
// ELOG_DEBUG("vCoderContext width %d", vCoderContext->width);
// maybeSwapContext();

int size = coder_context_->width * coder_context_->height;
// ELOG_DEBUG("coder_context_ width %d", coder_context_->width);

cPicture->pts = AV_NOPTS_VALUE;
cPicture->data[0] = inBuffer;
cPicture->data[1] = inBuffer + size;
cPicture->data[2] = inBuffer + size + size / 4;
cPicture->linesize[0] = vCoderContext->width;
cPicture->linesize[1] = vCoderContext->width / 2;
cPicture->linesize[2] = vCoderContext->width / 2;
cPicture->linesize[0] = coder_context_->width;
cPicture->linesize[1] = coder_context_->width / 2;
cPicture->linesize[2] = coder_context_->width / 2;

if (keyframe_requested_) {
cPicture->pict_type = AV_PICTURE_TYPE_I;
cPicture->key_frame = 1;
keyframe_requested_ = false;
} else {
cPicture->key_frame = 0;
cPicture->pict_type = AV_PICTURE_TYPE_P;
}

AVPacket pkt;
av_init_packet(&pkt);
Expand All @@ -106,21 +181,21 @@ int VideoEncoder::encodeVideo(unsigned char* inBuffer, int inLength, unsigned ch
int got_packet = 0;
// ELOG_DEBUG(
// "Before encoding inBufflen %d, size %d, codecontext width %d pkt->size%d",
// inLength, size, vCoderContext->width, pkt.size);
ret = avcodec_encode_video2(vCoderContext, &pkt, cPicture, &got_packet);
// inLength, size, coder_context_->width, pkt.size);
ret = avcodec_encode_video2(coder_context_, &pkt, cPicture, &got_packet);
// ELOG_DEBUG("Encoded video size %u, ret %d, got_packet %d, pts %lld, dts %lld",
// pkt.size, ret, got_packet, pkt.pts, pkt.dts);
if (!ret && got_packet && vCoderContext->coded_frame) {
vCoderContext->coded_frame->pts = pkt.pts;
vCoderContext->coded_frame->key_frame =
if (!ret && got_packet && coder_context_->coded_frame) {
coder_context_->coded_frame->pts = pkt.pts;
coder_context_->coded_frame->key_frame =
!!(pkt.flags & AV_PKT_FLAG_KEY);
}
return ret ? ret : pkt.size;
}

int VideoEncoder::closeEncoder() {
if (vCoderContext != NULL)
avcodec_close(vCoderContext);
if (coder_context_ != NULL)
avcodec_close(coder_context_);
if (cPicture != NULL)
av_frame_free(&cPicture);

Expand Down Expand Up @@ -154,6 +229,7 @@ int VideoDecoder::initDecoder(const VideoCodecInfo& info) {
return -1;
}

ELOG_DEBUG("Decoder width %d height %d", info.width, info.height);
vDecoderContext->width = info.width;
vDecoderContext->height = info.height;

Expand All @@ -174,6 +250,7 @@ int VideoDecoder::initDecoder(const VideoCodecInfo& info) {
int VideoDecoder::initDecoder(AVCodecContext* context) {
ELOG_DEBUG("Init Decoder with context");
initWithContext_ = true;
ELOG_DEBUG("Decoder width %d height %d", context->width, context->height);
vDecoder = avcodec_find_decoder(context->codec_id);
if (!vDecoder) {
ELOG_DEBUG("Error getting video decoder");
Expand Down
Loading

0 comments on commit 69f1a97

Please sign in to comment.