diff --git a/erizo/src/erizo/media/ExternalInput.cpp b/erizo/src/erizo/media/ExternalInput.cpp index 5933a14314..7d25a79f1d 100644 --- a/erizo/src/erizo/media/ExternalInput.cpp +++ b/erizo/src/erizo/media/ExternalInput.cpp @@ -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; @@ -161,14 +161,73 @@ int ExternalInput::init() { } int ExternalInput::sendPLI() { + if (op_) { + op_->requestKeyframe(); + } return 0; } +int ExternalInput::deliverFeedback_(std::shared_ptr fb_packet) { + RtcpHeader *chead = reinterpret_cast(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(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(&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(rtpdata); + if (!head->isRtcp()) { + if (getVideoSourceSSRC() == 0) { + setVideoSourceSSRC(55543); + } + } std::shared_ptr packet = std::make_shared(0, reinterpret_cast(rtpdata), len, VIDEO_PACKET); + RtpHeader *rtp_header = reinterpret_cast(packet->data); + unsigned char* start_buffer = reinterpret_cast (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); } } diff --git a/erizo/src/erizo/media/ExternalInput.h b/erizo/src/erizo/media/ExternalInput.h index ede689ea69..9c4ad8affc 100644 --- a/erizo/src/erizo/media/ExternalInput.h +++ b/erizo/src/erizo/media/ExternalInput.h @@ -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: @@ -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 data_packet) override; boost::future close() override { std::shared_ptr> p = std::make_shared>(); @@ -63,6 +65,7 @@ class ExternalInput : public MediaSource, public RTPDataReceiver { int64_t lastPts_, lastAudioPts_; int64_t startTime_; + RtpVP8Parser vp8_parser_; void receiveLoop(); void encodeLoop(); diff --git a/erizo/src/erizo/media/MediaProcessor.cpp b/erizo/src/erizo/media/MediaProcessor.cpp index 636e22996a..ebd719a491 100644 --- a/erizo/src/erizo/media/MediaProcessor.cpp +++ b/erizo/src/erizo/media/MediaProcessor.cpp @@ -111,7 +111,6 @@ int InputProcessor::deliverVideoData_(std::shared_ptr 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_; @@ -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) { @@ -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); diff --git a/erizo/src/erizo/media/MediaProcessor.h b/erizo/src/erizo/media/MediaProcessor.h index ad97c7ca59..d1a9141738 100644 --- a/erizo/src/erizo/media/MediaProcessor.h +++ b/erizo/src/erizo/media/MediaProcessor.h @@ -132,6 +132,7 @@ class InputProcessor: public MediaSink { int decodeAudio(unsigned char* inBuff, int inBuffLen, unsigned char* outBuff); }; + class OutputProcessor: public RawDataReceiver { DECLARE_LOGGER(); @@ -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 diff --git a/erizo/src/erizo/media/codecs/VideoCodec.cpp b/erizo/src/erizo/media/codecs/VideoCodec.cpp index ea1b5c9618..ab9d28ecd2 100644 --- a/erizo/src/erizo/media/codecs/VideoCodec.cpp +++ b/erizo/src/erizo/media/codecs/VideoCodec.cpp @@ -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() { @@ -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; } @@ -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); @@ -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); @@ -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; @@ -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"); diff --git a/erizo/src/erizo/media/codecs/VideoCodec.h b/erizo/src/erizo/media/codecs/VideoCodec.h index ca3d38af7b..6d54e80a29 100644 --- a/erizo/src/erizo/media/codecs/VideoCodec.h +++ b/erizo/src/erizo/media/codecs/VideoCodec.h @@ -33,11 +33,22 @@ class VideoEncoder { int initEncoder(const VideoCodecInfo& info); int encodeVideo(unsigned char* inBuffer, int length, unsigned char* outBuffer, int outLength); int closeEncoder(); + void requestKeyframe(); + void setTargetBitrate(uint64_t bitrate); + void setResolution(int width, int height); + + private: + void restartContext(); + void maybeSwapContext(); private: AVCodec* vCoder; - AVCodecContext* vCoderContext; + AVCodecContext* coder_context_, *next_coder_context_; AVFrame* cPicture; + bool keyframe_requested_; + uint64_t target_bitrate_; + int target_width_; + int target_height_; }; class VideoDecoder { diff --git a/erizoAPI/ExternalInput.cc b/erizoAPI/ExternalInput.cc index 4bb8c22800..d69701ea7b 100644 --- a/erizoAPI/ExternalInput.cc +++ b/erizoAPI/ExternalInput.cc @@ -51,6 +51,7 @@ NAN_MODULE_INIT(ExternalInput::Init) { Nan::SetPrototypeMethod(tpl, "init", init); Nan::SetPrototypeMethod(tpl, "setAudioReceiver", setAudioReceiver); Nan::SetPrototypeMethod(tpl, "setVideoReceiver", setVideoReceiver); + Nan::SetPrototypeMethod(tpl, "generatePLIPacket", generatePLIPacket); constructor.Reset(tpl->GetFunction()); Nan::Set(target, Nan::New("ExternalInput").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); @@ -112,3 +113,13 @@ NAN_METHOD(ExternalInput::setVideoReceiver) { me->setVideoSink(mr); me->setEventSink(mr); } + +NAN_METHOD(ExternalInput::generatePLIPacket) { + ExternalInput* obj = ObjectWrap::Unwrap(info.Holder()); + std::shared_ptr me = obj->me; + + if (!me) { + return; + } + me->sendPLI(); +} diff --git a/erizoAPI/ExternalInput.h b/erizoAPI/ExternalInput.h index becf680a09..35056a4686 100644 --- a/erizoAPI/ExternalInput.h +++ b/erizoAPI/ExternalInput.h @@ -47,6 +47,10 @@ class ExternalInput : public Nan::ObjectWrap { * Param: the MediaSink */ static NAN_METHOD(setVideoReceiver); + /* + * Request a PLI packet from this ExternalInput + */ + static NAN_METHOD(generatePLIPacket); static Nan::Persistent constructor; }; diff --git a/erizo_controller/erizoClient/src/Stream.js b/erizo_controller/erizoClient/src/Stream.js index 6b30afe070..b1fa656a08 100644 --- a/erizo_controller/erizoClient/src/Stream.js +++ b/erizo_controller/erizoClient/src/Stream.js @@ -3,6 +3,7 @@ import { EventDispatcher, StreamEvent } from './Events'; import ConnectionHelpers from './utils/ConnectionHelpers'; import ErizoMap from './utils/ErizoMap'; +import Random from './utils/Random'; import VideoPlayer from './views/VideoPlayer'; import AudioPlayer from './views/AudioPlayer'; import Logger from './utils/Logger'; @@ -37,7 +38,9 @@ const Stream = (altConnectionHelpers, specInput) => { that.p2p = false; that.ConnectionHelpers = altConnectionHelpers === undefined ? ConnectionHelpers : altConnectionHelpers; - + if (that.url !== undefined) { + spec.label = `ei_${Random.getRandomValue()}`; + } const onStreamAddedToPC = (evt) => { if (evt.stream.id === that.getLabel()) { that.emit(StreamEvent({ type: 'added', stream: evt.stream })); diff --git a/erizo_controller/erizoClient/src/utils/Random.js b/erizo_controller/erizoClient/src/utils/Random.js new file mode 100644 index 0000000000..08d3967e9d --- /dev/null +++ b/erizo_controller/erizoClient/src/utils/Random.js @@ -0,0 +1,15 @@ +/* global window */ + +const Random = (() => { + const getRandomValue = () => { + const array = new Uint16Array(1); + window.crypto.getRandomValues(array); + return array[0]; + }; + + return { + getRandomValue, + }; +})(); + +export default Random; diff --git a/erizo_controller/erizoController/models/Client.js b/erizo_controller/erizoController/models/Client.js index b1d5c60725..64bdef5c28 100644 --- a/erizo_controller/erizoController/models/Client.js +++ b/erizo_controller/erizoController/models/Client.js @@ -375,7 +375,7 @@ class Client extends events.EventEmitter { url = `/tmp/${recordingId}.mkv`; } } - this.room.controller.addExternalInput(id, url, (result) => { + this.room.controller.addExternalInput(this.id, id, url, options.label, (result) => { if (result === 'success') { const st = ST.Stream({ id, client: this.id, diff --git a/erizo_controller/erizoController/roomController.js b/erizo_controller/erizoController/roomController.js index b7bd4d35c4..e26b046bcb 100644 --- a/erizo_controller/erizoController/roomController.js +++ b/erizo_controller/erizoController/roomController.js @@ -126,13 +126,13 @@ exports.RoomController = (spec) => { eventListeners.push(eventListener); }; - that.addExternalInput = (publisherId, url, callback) => { + that.addExternalInput = (clientId, publisherId, url, label, callback) => { if (publishers[publisherId] === undefined) { - log.info(`message: addExternalInput, streamId: ${publisherId}, url:${url}`); + log.info(`message: addExternalInput, clientId ${clientId}, streamId: ${publisherId}, url:${url}`); getErizoJS((erizoId) => { // then we call its addPublisher method. - const args = [publisherId, url]; + const args = [erizoControllerId, publisherId, url, label]; // Track publisher locally publishers[publisherId] = erizoId; diff --git a/erizo_controller/erizoJS/erizoJSController.js b/erizo_controller/erizoJS/erizoJSController.js index 1aa3d8fa43..d5bfb83c3d 100644 --- a/erizo_controller/erizoJS/erizoJSController.js +++ b/erizo_controller/erizoJS/erizoJSController.js @@ -140,11 +140,11 @@ exports.ErizoJSController = (erizoJSId, threadPool, ioThreadPool) => { replManager.processRpcMessage(args, callback); }; - that.addExternalInput = (erizoControllerId, streamId, url, callbackRpc) => { + that.addExternalInput = (erizoControllerId, streamId, url, label, callbackRpc) => { updateUptimeInfo(); if (publishers[streamId] === undefined) { const client = getOrCreateClient(erizoControllerId, url); - publishers[streamId] = new ExternalInput(url, streamId, threadPool); + publishers[streamId] = new ExternalInput(url, streamId, label, threadPool); const ei = publishers[streamId]; const answer = ei.init(); // We add the connection manually to the client diff --git a/erizo_controller/erizoJS/models/Publisher.js b/erizo_controller/erizoJS/models/Publisher.js index f9950256bd..4148139752 100644 --- a/erizo_controller/erizoJS/models/Publisher.js +++ b/erizo_controller/erizoJS/models/Publisher.js @@ -256,13 +256,22 @@ class Source extends NodeClass { this.mediaStream.periodicPlis = undefined; } this.mediaStream.periodicPlis = setInterval(() => { - this.mediaStream.generatePLIPacket(); + if (this.ei) { + log.warn('sending keyframe!!'); + this.ei.generatePLIPacket(); + } else if (this.mediaStream) { + this.mediaStream.generatePLIPacket(); + } }, period); } else { const result = Source._updateMediaStreamSubscriberSlideshow(subscriber, false, isFallback); if (!result) { for (let pliIndex = 0; pliIndex < PLIS_TO_RECOVER; pliIndex += 1) { - this.mediaStream.generatePLIPacket(); + if (this.ei) { + this.ei.generatePLIPacket(); + } else if (this.mediaStream) { + this.mediaStream.generatePLIPacket(); + } } } this.maybeStopSlideShow(); @@ -409,11 +418,11 @@ class Publisher extends Source { } class ExternalInput extends Source { - constructor(url, streamId, threadPool) { + constructor(url, streamId, label, threadPool) { super(url, streamId, threadPool); const eiId = `${streamId}_${url}`; - log.info(`message: Adding ExternalInput, id: ${eiId}`); + log.warn(`message: Adding ExternalInput, id: ${eiId}, url: ${url}`); const ei = new addon.ExternalInput(url); @@ -424,6 +433,7 @@ class ExternalInput extends Source { this.externalOutputs = {}; this.mediaStream = {}; this.connection = ei; + this.label = label; ei.setAudioReceiver(this.muxer); ei.setVideoReceiver(this.muxer); diff --git a/erizo_controller/test/erizoController/erizoController.js b/erizo_controller/test/erizoController/erizoController.js index 6eb1552503..b37a8fb718 100644 --- a/erizo_controller/test/erizoController/erizoController.js +++ b/erizo_controller/test/erizoController/erizoController.js @@ -424,7 +424,7 @@ describe('Erizo Controller / Erizo Controller', () => { const options = { state: 'url' }; const sdp = ''; const aCallback = sinon.stub(); - mocks.roomControllerInstance.addExternalInput.callsArgWith(2, 'success'); + mocks.roomControllerInstance.addExternalInput.callsArgWith(4, 'success'); onPublish(options, sdp, aCallback); diff --git a/erizo_controller/test/erizoController/roomController.js b/erizo_controller/test/erizoController/roomController.js index 4f11cf7596..500a9f7776 100644 --- a/erizo_controller/test/erizoController/roomController.js +++ b/erizo_controller/test/erizoController/roomController.js @@ -80,12 +80,14 @@ describe('Erizo Controller / Room Controller', () => { const kArbitraryOptions = {}; const kArbitraryUnknownId = 'unknownId'; const kArbitraryOutputUrl = 'url2'; + const kArbitraryLabel = 'label'; beforeEach(() => { const callback = sinon.stub(); ecchInstanceMock.getErizoJS.callsArgWith(2, 'erizoId'); - controller.addExternalInput(kArbitraryId, kArbitraryUrl, callback); + controller.addExternalInput(kArbitraryId, kArbitraryId, + kArbitraryUrl, kArbitraryLabel, callback); }); it('should call Erizo\'s addExternalOutput', () => { diff --git a/erizo_controller/test/erizoJS/erizoJSController.js b/erizo_controller/test/erizoJS/erizoJSController.js index 10e0e81d4e..da9250288e 100644 --- a/erizo_controller/test/erizoJS/erizoJSController.js +++ b/erizo_controller/test/erizoJS/erizoJSController.js @@ -120,6 +120,7 @@ describe('Erizo JS Controller', () => { const kArbitraryErizoControllerId = 'erizoControllerId1'; const kArbitraryStreamId = 'streamId1'; const kArbitraryUrl = 'url1'; + const kArbitraryLabel = 'label1'; beforeEach(() => { callback = sinon.stub(); @@ -128,7 +129,7 @@ describe('Erizo JS Controller', () => { it('should succeed creating OneToManyProcessor and ExternalInput', () => { mocks.ExternalInput.init.returns(1); controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + kArbitraryUrl, kArbitraryLabel, callback); expect(erizoApiMock.OneToManyProcessor.callCount).to.equal(1); expect(erizoApiMock.ExternalInput.args[0][0]).to.equal(kArbitraryUrl); @@ -144,7 +145,7 @@ describe('Erizo JS Controller', () => { it('should fail if ExternalInput is not intialized', () => { mocks.ExternalInput.init.returns(-1); controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + kArbitraryUrl, kArbitraryLabel, callback); expect(callback.callCount).to.equal(1); expect(callback.args[0]).to.deep.equal(['callback', -1]); @@ -153,9 +154,9 @@ describe('Erizo JS Controller', () => { it('should fail if it already exists', () => { const secondCallback = sinon.stub(); controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + kArbitraryUrl, kArbitraryLabel, callback); controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryStreamId, - kArbitraryUrl, callback); + kArbitraryUrl, kArbitraryLabel, callback); expect(callback.callCount).to.equal(1); expect(secondCallback.callCount).to.equal(0); @@ -167,12 +168,13 @@ describe('Erizo JS Controller', () => { const kArbitraryEoOptions = {}; const kArbitraryEiId = 'ei_id1'; const kArbitraryEiUrl = 'ei_url1'; + const kArbitraryEiLabel = 'ei_label1'; const kArbitraryErizoControllerId = 'erizoControllerId1'; beforeEach(() => { const eiCallback = () => {}; controller.addExternalInput(kArbitraryErizoControllerId, kArbitraryEiId, - kArbitraryEiUrl, eiCallback); + kArbitraryEiUrl, kArbitraryEiLabel, eiCallback); }); it('should succeed creating ExternalOutput', () => {