240 lines
10 KiB
C++
240 lines
10 KiB
C++
/*
|
|
|
|
SOME NOTES ON MIST
|
|
|
|
- When a user wants to start pushing video into Mist we need to
|
|
check if the user is actually allowed to do this. When the user
|
|
is allowed to push we have to call the function `allowPush("")`.
|
|
|
|
SIGNALING
|
|
|
|
1. Client sends the offer:
|
|
|
|
{
|
|
type: "offer_sdp",
|
|
offer_sdp: <the-client-offer-sdp>,
|
|
}
|
|
|
|
Server responds with:
|
|
|
|
SUCCESS:
|
|
{
|
|
type: "on_answer_sdp",
|
|
result: true,
|
|
answer_sdp: <the-server-answer-sdp>,
|
|
}
|
|
|
|
ERROR:
|
|
{
|
|
type: "on_answer_sdp",
|
|
result: false,
|
|
}
|
|
|
|
2. Client request new bitrate:
|
|
|
|
{
|
|
type: "video_bitrate"
|
|
video_bitrate: 600000
|
|
}
|
|
|
|
Server responds with:
|
|
|
|
SUCCESS:
|
|
{
|
|
type: "on_video_bitrate"
|
|
result: true
|
|
}
|
|
|
|
ERROR:
|
|
{
|
|
type: "on_video_bitrate"
|
|
result: false
|
|
}
|
|
|
|
*/
|
|
#pragma once
|
|
|
|
#include "output.h"
|
|
#include "output_http.h"
|
|
#include <mist/certificate.h>
|
|
#include <mist/dtls_srtp_handshake.h>
|
|
#include <mist/h264.h>
|
|
#include <mist/http_parser.h>
|
|
#include <mist/rtp_fec.h>
|
|
#include <mist/sdp_media.h>
|
|
#include <mist/socket.h>
|
|
#include <mist/srtp.h>
|
|
#include <mist/stun.h>
|
|
#include <mist/tinythread.h>
|
|
#include <mist/websocket.h>
|
|
#include <fstream>
|
|
|
|
#define NACK_BUFFER_SIZE 1024
|
|
|
|
#if defined(WEBRTC_PCAP)
|
|
#include <mist/pcap.h>
|
|
#endif
|
|
|
|
namespace Mist{
|
|
|
|
/* ------------------------------------------------ */
|
|
|
|
class nackBuffer{
|
|
public:
|
|
bool isBuffered(uint16_t seq){
|
|
if (!bufs[seq % NACK_BUFFER_SIZE].size()){return false;}
|
|
RTP::Packet tmpPkt(bufs[seq % NACK_BUFFER_SIZE], bufs[seq % NACK_BUFFER_SIZE].size());
|
|
return (tmpPkt.getSequence() == seq);
|
|
}
|
|
const char *getData(uint16_t seq){return bufs[seq % NACK_BUFFER_SIZE];}
|
|
size_t getSize(uint16_t seq){return bufs[seq % NACK_BUFFER_SIZE].size();}
|
|
void assign(uint16_t seq, const char *p, size_t s){
|
|
bufs[seq % NACK_BUFFER_SIZE].assign(p, s);
|
|
}
|
|
|
|
private:
|
|
Util::ResizeablePointer bufs[NACK_BUFFER_SIZE];
|
|
};
|
|
|
|
class WebRTCTrack{
|
|
public:
|
|
WebRTCTrack(); ///< Initializes to some defaults.
|
|
|
|
RTP::toDTSC rtpToDTSC; ///< Converts RTP packets into DTSC packets.
|
|
RTP::FECSorter sorter; ///< Takes care of sorting the received RTP packet and keeps track of some
|
|
///< statistics. Will call a callback whenever a packet can be used. (e.g. not lost, in correct order).
|
|
RTP::Packet rtpPacketizer; ///< Used when we're sending RTP data back to the other peer.
|
|
uint64_t payloadType; ///< The payload type that was extracted from the `m=` media line in the SDP.
|
|
std::string localIcePwd;
|
|
std::string localIceUFrag;
|
|
uint32_t SSRC; ///< The SSRC of the RTP packets.
|
|
uint8_t ULPFECPayloadType; ///< When we've enabled FEC for a video stream this holds the payload
|
|
///< type that is used to distinguish between ordinary video RTP
|
|
///< packets and FEC packets.
|
|
uint8_t REDPayloadType; ///< When using RED and ULPFEC this holds the payload type of the RED
|
|
///< stream.
|
|
uint8_t RTXPayloadType; ///< The retransmission payload type when we use RTX (retransmission
|
|
///< with separate SSRC/payload type)
|
|
void gotPacket(uint32_t ts);
|
|
uint32_t lastTransit;
|
|
double jitter;
|
|
};
|
|
|
|
/* ------------------------------------------------ */
|
|
|
|
class OutWebRTC : public HTTPOutput{
|
|
public:
|
|
OutWebRTC(Socket::Connection &myConn);
|
|
~OutWebRTC();
|
|
bool hasSessionIDs(){return !config->getBool("mergesessions");}
|
|
static void init(Util::Config *cfg);
|
|
virtual void sendHeader();
|
|
virtual void sendNext();
|
|
virtual void onWebsocketFrame();
|
|
virtual void preWebsocketConnect();
|
|
virtual bool dropPushTrack(uint32_t trackId, const std::string & dropReason);
|
|
void onIdle();
|
|
bool onFinish();
|
|
bool doesWebsockets(){return true;}
|
|
void handleWebRTCInputOutputFromThread();
|
|
int onDTLSHandshakeWantsToWrite(const uint8_t *data, int *nbytes);
|
|
void onRTPSorterHasPacket(size_t tid, const RTP::Packet &pkt);
|
|
void onDTSCConverterHasPacket(const DTSC::Packet &pkt);
|
|
void onDTSCConverterHasInitData(const uint64_t trackID, const std::string &initData);
|
|
void onRTPPacketizerHasRTPPacket(const char *data, size_t nbytes);
|
|
void onRTPPacketizerHasRTCPPacket(const char *data, uint32_t nbytes);
|
|
|
|
private:
|
|
uint64_t lastPackMs;
|
|
std::ofstream jitterLog;
|
|
std::ofstream packetLog;
|
|
std::string externalAddr;
|
|
void ackNACK(uint32_t SSRC, uint16_t seq);
|
|
bool handleWebRTCInputOutput(); ///< Reads data from the UDP socket. Returns true when we read
|
|
///< some data, othewise false.
|
|
void handleReceivedSTUNPacket();
|
|
void handleReceivedDTLSPacket();
|
|
void handleReceivedRTPOrRTCPPacket();
|
|
bool handleSignalingCommandRemoteOfferForInput(SDP::Session &sdpSession);
|
|
bool handleSignalingCommandRemoteOfferForOutput(SDP::Session &sdpSession);
|
|
void sendSignalingError(const std::string &commandType, const std::string &errorMessage);
|
|
|
|
bool createWebRTCTrackFromAnswer(const SDP::Media &mediaAnswer,
|
|
const SDP::MediaFormat &formatAnswer, WebRTCTrack &result);
|
|
void sendRTCPFeedbackREMB(const WebRTCTrack &rtcTrack);
|
|
void sendRTCPFeedbackPLI(const WebRTCTrack &rtcTrack); ///< Picture Los Indication: request keyframe.
|
|
void sendRTCPFeedbackRR(WebRTCTrack &rtcTrack);
|
|
void sendRTCPFeedbackNACK(const WebRTCTrack &rtcTrack,
|
|
uint16_t missingSequenceNumber); ///< Notify sender that we're missing a sequence number.
|
|
void sendSPSPPS(size_t dtscIdx,
|
|
WebRTCTrack &rtcTrack); ///< When we're streaming H264 to e.g. the browser we
|
|
///< inject the PPS and SPS nals.
|
|
void extractFrameSizeFromVP8KeyFrame(const DTSC::Packet &pkt);
|
|
void updateCapabilitiesWithSDPOffer(SDP::Session &sdpSession);
|
|
bool bindUDPSocketOnLocalCandidateAddress(uint16_t port); ///< Binds our UDP socket onto the IP address that we shared via our SDP
|
|
///< answer. We *have to* bind on a specific IP, see
|
|
///< https://gist.github.com/roxlu/6c5ab696840256dac71b6247bab59ce9
|
|
std::string getLocalCandidateAddress();
|
|
|
|
SDP::Session sdp; ///< SDP parser.
|
|
SDP::Answer sdpAnswer; ///< WIP: Replacing our `sdp` member ..
|
|
Certificate cert; ///< The TLS certificate. Used to generate a fingerprint in SDP answers.
|
|
DTLSSRTPHandshake dtlsHandshake; ///< Implements the DTLS handshake using the mbedtls library (fork).
|
|
SRTPReader srtpReader; ///< Used to unprotect incoming RTP and RTCP data. Uses the keys that
|
|
///< were exchanged with DTLS.
|
|
SRTPWriter srtpWriter; ///< Used to protect our RTP and RTCP data when sending data to another
|
|
///< peer. Uses the keys that were exchanged with DTLS.
|
|
Socket::UDPConnection udp; ///< Our UDP socket over which WebRTC data is received and sent.
|
|
StunReader stunReader; ///< Decodes STUN messages; during a session we keep receiving STUN
|
|
///< messages to which we need to reply.
|
|
std::map<uint64_t, WebRTCTrack> webrtcTracks; ///< WebRTCTracks indexed by payload type for incoming data and indexed by
|
|
///< myMeta.tracks[].trackID for outgoing data.
|
|
tthread::thread *webRTCInputOutputThread; ///< The thread in which we read WebRTC data when
|
|
///< we're receive media from another peer.
|
|
uint16_t udpPort; ///< The port on which our webrtc socket is bound. This is where we receive
|
|
///< RTP, STUN, DTLS, etc. */
|
|
uint32_t SSRC; ///< The SSRC for this local instance. Is used when generating RTCP reports. */
|
|
uint64_t rtcpTimeoutInMillis; ///< When current time in millis exceeds this timeout we have to
|
|
///< send a new RTCP packet.
|
|
uint64_t rtcpKeyFrameTimeoutInMillis;
|
|
uint64_t rtcpKeyFrameDelayInMillis;
|
|
Util::ResizeablePointer rtpOutBuffer; ///< Buffer into which we copy (unprotected) RTP data that we need to deliver
|
|
///< to the other peer. This gets protected.
|
|
uint32_t videoBitrate; ///< The bitrate to use for incoming video streams. Can be configured via
|
|
///< the signaling channel. Defaults to 6mbit.
|
|
uint32_t videoConstraint;
|
|
|
|
size_t audTrack, vidTrack, prevVidTrack;
|
|
double target_rate; ///< Target playback speed rate (1.0 = normal, 0 = auto)
|
|
|
|
bool didReceiveKeyFrame; /* TODO burst delay */
|
|
int64_t packetOffset; ///< For timestamp rewrite with BMO
|
|
uint64_t lastTimeSync;
|
|
bool firstKey;
|
|
bool repeatInit;
|
|
bool stayLive;
|
|
bool doDTLS;
|
|
bool volkswagenMode;
|
|
|
|
double stats_jitter;
|
|
uint64_t stats_nacknum;
|
|
uint64_t stats_lossnum;
|
|
double stats_lossperc;
|
|
std::deque<double> stats_loss_avg;
|
|
|
|
#if defined(WEBRTC_PCAP)
|
|
PCAPWriter pcapOut; ///< Used during development to write unprotected packets that can be
|
|
///< inspected in e.g. wireshark.
|
|
PCAPWriter pcapIn; ///< Used during development to write unprotected packets that can be
|
|
///< inspected in e.g. wireshark.
|
|
#endif
|
|
|
|
std::map<uint8_t, uint64_t> payloadTypeToWebRTCTrack; ///< Maps e.g. RED to the corresponding track. Used when input
|
|
///< supports RED/ULPFEC; can also be used to map RTX in the
|
|
///< future.
|
|
std::map<uint32_t, nackBuffer> outBuffers;
|
|
std::map<size_t, uint64_t> lastSR;
|
|
};
|
|
}// namespace Mist
|
|
|
|
typedef Mist::OutWebRTC mistOut;
|