WebRTC websocket implementation now uses generalized base version, removed unused code from MP4 output
This commit is contained in:
parent
b9819eb40f
commit
16e22eadbe
6 changed files with 19 additions and 255 deletions
File diff suppressed because one or more lines are too long
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue