WebRTC websocket implementation now uses generalized base version, removed unused code from MP4 output

This commit is contained in:
Thulinma 2023-10-19 12:07:12 +02:00
parent b9819eb40f
commit 16e22eadbe
6 changed files with 19 additions and 255 deletions

File diff suppressed because one or more lines are too long

View file

@ -273,7 +273,7 @@ p.prototype.build = function (MistVideo,callback) {
}
}
if (ev.type in me.listeners) {
return me.listeners[ev.type].call(me,ev);
return me.listeners[ev.type].call(me,("data" in ev)?ev.data:ev);
}
MistVideo.log("Unhandled WebRTC event "+ev.type+": "+JSON.stringify(ev));
return false;

View file

@ -486,7 +486,7 @@ namespace Mist{
if (command.isMember("seek_time")){
possiblyReselectTracks(command["seek_time"].asInt());
}else{
possiblyReselectTracks(currentTime());
if (parseData){possiblyReselectTracks(currentTime());}
}
return true;
}

View file

@ -78,13 +78,6 @@ namespace Mist{
}
};
struct fragSet{
uint64_t firstPart;
uint64_t lastPart;
uint64_t firstTime;
uint64_t lastTime;
};
class OutMP4 : public HTTPOutput{
public:
OutMP4(Socket::Connection &conn);
@ -143,9 +136,6 @@ namespace Mist{
int keysOnly;
uint64_t estimateFileSize() const;
// This is a dirty solution... but it prevents copying and copying and copying again
std::map<size_t, fragSet> currentPartSet;
std::string protectionHeader(size_t idx);
Util::ResizeablePointer webBuf;
};

View file

@ -66,10 +66,9 @@ namespace Mist{
vidTrack = INVALID_TRACK_ID;
prevVidTrack = INVALID_TRACK_ID;
audTrack = INVALID_TRACK_ID;
stayLive = true;
target_rate = 0.0;
firstKey = true;
repeatInit = true;
wsCmds = true;
lastTimeSync = 0;
maxSkipAhead = 0;
@ -297,6 +296,12 @@ namespace Mist{
config->addOptionsFromCapabilities(capa);
}
void OutWebRTC::onFail(const std::string &msg, bool critical){
if (!webSock){return HTTPOutput::onFail(msg, critical);}
sendSignalingError("error", msg);
Output::onFail(msg, critical);
}
void OutWebRTC::preWebsocketConnect(){
HTTP::URL tmpUrl("http://" + H.GetHeader("Host"));
externalAddr = tmpUrl.host;
@ -465,7 +470,6 @@ namespace Mist{
JSON::Value command = JSON::fromString(webSock->data, webSock->data.size());
JSON::Value commandResult;
if(command.isMember("encrypt")){
doDTLS = false;
volkswagenMode = false;
@ -570,164 +574,6 @@ namespace Mist{
return;
}
std::set<size_t> validTracks = M.getValidTracks();
if (command["type"] == "tracks"){
if (command.isMember("audio")){
if (!command["audio"].isNull()){
targetParams["audio"] = command["audio"].asString();
if (audTrack && command["audio"].asInt()){
uint64_t tId = command["audio"].asInt();
if (validTracks.count(tId) && M.getCodec(tId) != M.getCodec(audTrack)){
targetParams["audio"] = "none";
sendSignalingError("tracks", "Cannot select track because it is encoded as " +
M.getCodec(tId) + " but the already negotiated track is " +
M.getCodec(audTrack) + ". Please re-negotiate to play this track.");
}
}
}else{
targetParams.erase("audio");
}
}
if (command.isMember("video")){
if (!command["video"].isNull()){
targetParams["video"] = command["video"].asString();
if (vidTrack && command["video"].asInt()){
uint64_t tId = command["video"].asInt();
if (validTracks.count(tId) && M.getCodec(tId) != M.getCodec(vidTrack)){
targetParams["video"] = "none";
sendSignalingError("tracks", "Cannot select track because it is encoded as " +
M.getCodec(tId) + " but the already negotiated track is " +
M.getCodec(vidTrack) + ". Please re-negotiate to play this track.");
}
}
}else{
targetParams.erase("video");
}
}
// Remember the previous video track, if any.
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
if (M.getType(it->first) == "video"){
prevVidTrack = it->first;
break;
}
}
selectDefaultTracks();
// Add the previous video track back, if we had one.
if (prevVidTrack != INVALID_TRACK_ID && !userSelect.count(prevVidTrack)){
uint64_t seekTarget = currentTime();
userSelect[prevVidTrack].reload(streamName, prevVidTrack);
seek(seekTarget);
}
onIdle();
return;
}
if (command["type"] == "seek"){
if (!command.isMember("seek_time")){
sendSignalingError("on_seek", "Received a seek request but no `seek_time` property.");
return;
}
uint64_t seek_time = command["seek_time"].asInt();
if (!parseData){
parseData = true;
selectDefaultTracks();
}
stayLive = (target_rate == 0.0) && (endTime() < seek_time + 5000);
if (command["seek_time"].asStringRef() == "live"){stayLive = true;}
if (stayLive){seek_time = endTime();}
seek(seek_time, true);
JSON::Value commandResult;
commandResult["type"] = "on_seek";
commandResult["result"] = true;
if (M.getLive()){commandResult["live_point"] = stayLive;}
if (target_rate == 0.0){
commandResult["play_rate_curr"] = "auto";
}else{
commandResult["play_rate_curr"] = target_rate;
}
webSock->sendFrame(commandResult.toString());
onIdle();
return;
}
if (command["type"] == "set_speed"){
if (!command.isMember("play_rate")){
sendSignalingError("on_speed", "Received a playback speed setting request but no `play_rate` property.");
return;
}
double set_rate = command["play_rate"].asDouble();
if (!parseData){
parseData = true;
selectDefaultTracks();
}
JSON::Value commandResult;
commandResult["type"] = "on_speed";
if (target_rate == 0.0){
commandResult["play_rate_prev"] = "auto";
}else{
commandResult["play_rate_prev"] = target_rate;
}
if (set_rate == 0.0){
commandResult["play_rate_curr"] = "auto";
}else{
commandResult["play_rate_curr"] = set_rate;
}
if (target_rate != set_rate){
target_rate = set_rate;
if (target_rate == 0.0){
realTime = 1000;//set playback speed to default
firstTime = Util::bootMS() - currentTime();
maxSkipAhead = 0;//enabled automatic rate control
}else{
stayLive = false;
//Set new realTime speed
realTime = 1000 / target_rate;
firstTime = Util::bootMS() - (currentTime() / target_rate);
maxSkipAhead = 1;//disable automatic rate control
}
}
if (M.getLive()){commandResult["live_point"] = stayLive;}
webSock->sendFrame(commandResult.toString());
onIdle();
return;
}
if (command["type"] == "pause"){
parseData = !parseData;
JSON::Value commandResult;
commandResult["type"] = "on_time";
commandResult["paused"] = !parseData;
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
commandResult["tracks"].append((uint64_t)it->first);
}
webSock->sendFrame(commandResult.toString());
return;
}
if (command["type"] == "hold") {
parseData = false;
JSON::Value commandResult;
commandResult["type"] = "on_time";
commandResult["paused"] = !parseData;
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
commandResult["tracks"].append((uint64_t)it->first);
}
webSock->sendFrame(commandResult.toString());
return;
}
if (command["type"] == "stop"){
INFO_MSG("Received stop() command.");
myConn.close();
return;
}
if (command["type"] == "keyframe_interval"){
if (!command.isMember("keyframe_interval_millis")){
sendSignalingError("on_keyframe_interval", "No keyframe_interval_millis attribute found.");
@ -853,18 +699,8 @@ namespace Mist{
return true;
}
void OutWebRTC::onIdle(){
if (parseData){
JSON::Value commandResult;
commandResult["type"] = "on_time";
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
for (std::map<size_t, Comms::Users>::iterator it = userSelect.begin(); it != userSelect.end(); it++){
commandResult["tracks"].append((uint64_t)it->first);
}
webSock->sendFrame(commandResult.toString());
}else if (isPushing()){
void OutWebRTC::handleWebsocketIdle(){
if (!parseData && isPushing()){
JSON::Value commandResult;
commandResult["type"] = "on_media_receive";
commandResult["millis"] = endTime();
@ -876,7 +712,9 @@ namespace Mist{
commandResult["stats"]["jitter_ms"] = stats_jitter;
commandResult["stats"]["loss_perc"] = stats_lossperc;
webSock->sendFrame(commandResult.toString());
return;
}
HTTPOutput::handleWebsocketIdle();
}
bool OutWebRTC::onFinish(){
@ -1677,18 +1515,12 @@ namespace Mist{
}
// This function was implemented (it's virtual) to handle
// pushing of media to the browser. This function blocks until
// the DTLS handshake has been finished. This prevents
// `sendNext()` from being called which is correct because we
// don't want to send packets when we can't protect them with
// DTLS.
void OutWebRTC::sendHeader(){
void OutWebRTC::sendNext(){
HTTPOutput::sendNext();
// first make sure that we complete the DTLS handshake.
if(doDTLS){
while (keepGoing() && !dtlsHandshake.hasKeyingMaterial()){
if (!handleWebRTCInputOutput()){udp.sendPaced(10000);}
if (!handleWebRTCInputOutput()){udp.sendPaced(10);}else{udp.sendPaced(0);}
if (lastRecv < Util::bootMS() - 10000){
WARN_MSG("Killing idle connection in handshake phase");
onFail("idle connection in handshake phase", false);
@ -1696,10 +1528,6 @@ namespace Mist{
}
}
}
sentHeader = true;
}
void OutWebRTC::sendNext(){
if (lastRecv < Util::bootMS() - 10000){
WARN_MSG("Killing idle connection");
onFail("idle connection", false);

View file

@ -1,57 +1,3 @@
/*
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"
@ -128,14 +74,14 @@ namespace Mist{
OutWebRTC(Socket::Connection &myConn);
~OutWebRTC();
static void init(Util::Config *cfg);
virtual void sendHeader();
virtual void sendNext();
virtual void onWebsocketFrame();
virtual void respondHTTP(const HTTP::Parser & req, bool headersOnly);
virtual void preHTTP(){}
virtual void preWebsocketConnect();
virtual bool dropPushTrack(uint32_t trackId, const std::string & dropReason);
void onIdle();
void handleWebsocketIdle();
virtual void onFail(const std::string &msg, bool critical = false);
bool onFinish();
bool doesWebsockets(){return true;}
void handleWebRTCInputOutputFromThread();