video_rtp_session.cpp 7.45 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
3
 *  Author: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
4
 *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
19 20 21 22 23 24 25 26 27 28 29 30 31
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */

32
#include "client/video_controls.h"
33
#include "video_rtp_session.h"
34
#include "video_sender.h"
35
#include "video_receive_thread.h"
36
#include "video_mixer.h"
37
#include "socket_pair.h"
38
#include "sip/sdp.h"
39
#include "sip/sipvoiplink.h"
40
#include "manager.h"
Tristan Matthews's avatar
Tristan Matthews committed
41
#include "logger.h"
42

43 44 45 46
#include <sstream>
#include <map>
#include <string>

47
namespace sfl_video {
48

49 50 51
using std::map;
using std::string;

52 53
VideoRtpSession::VideoRtpSession(const string &callID,
								 const map<string, string> &txArgs) :
54
    socketPair_(), sender_(), receiveThread_(), txArgs_(txArgs),
55
    rxArgs_(), sending_(false), receiving_(false), callID_(callID),
56
    videoMixerSP_(), videoLocal_()
57
{}
58

59
VideoRtpSession::~VideoRtpSession()
60
{ stop(); }
61

62
void VideoRtpSession::updateSDP(const Sdp &sdp)
63
{
64
    string desc(sdp.getIncomingVideoDescription());
65
    // if port has changed
66
    if (not desc.empty() and desc != rxArgs_["receiving_sdp"]) {
67
        rxArgs_["receiving_sdp"] = desc;
68
        DEBUG("Updated incoming SDP to:\n%s",
69
              rxArgs_["receiving_sdp"].c_str());
70
    }
71

72 73 74 75 76
    if (desc.empty()) {
        DEBUG("Video is inactive");
        receiving_ = false;
        sending_ = false;
    } else if (desc.find("sendrecv") != string::npos) {
77
        DEBUG("Sending and receiving video");
78 79
        receiving_ = true;
        sending_ = true;
80
    } else if (desc.find("inactive") != string::npos) {
81
        DEBUG("Video is inactive");
82
        receiving_ = false;
83
        sending_ = false;
84
    } else if (desc.find("sendonly") != string::npos) {
85
        DEBUG("Receiving video disabled, video set to sendonly");
86 87
        receiving_ = false;
        sending_ = true;
88
    } else if (desc.find("recvonly") != string::npos) {
89
        DEBUG("Sending video disabled, video set to recvonly");
90 91 92
        sending_ = false;
        receiving_ = true;
    }
93 94
    // even if it says sendrecv or recvonly, our peer may disable video by
    // setting the port to 0
95
    if (desc.find("m=video 0") != string::npos) {
96
        DEBUG("Receiving video disabled, port was set to 0");
97 98
        receiving_ = false;
    }
99

100 101
    if (sending_)
        sending_ = sdp.getOutgoingVideoSettings(txArgs_);
102 103
}

104 105
void VideoRtpSession::updateDestination(const string &destination,
                                        unsigned int port)
106
{
107 108 109 110
    if (destination.empty()) {
        ERROR("Destination is empty, ignoring");
        return;
    }
111 112

    std::stringstream tmp;
113
    tmp << "rtp://" << destination << ":" << port;
114
    // if destination has changed
115
    if (tmp.str() != txArgs_["destination"]) {
116
        if (sender_.get() != 0) {
117 118 119
            ERROR("Video is already being sent");
            return;
        }
120
        txArgs_["destination"] = tmp.str();
121
        DEBUG("updated dest to %s",  txArgs_["destination"].c_str());
122
    }
123

124
    if (port == 0) {
125
        DEBUG("Sending video disabled, port was set to 0");
126 127
        sending_ = false;
    }
128 129
}

130
void VideoRtpSession::start(int localPort)
131
{
132 133 134
    if (not sending_ and not receiving_)
        return;

135 136 137
    try {
        socketPair_.reset(new SocketPair(txArgs_["destination"].c_str(), localPort));
    } catch (const std::runtime_error &e) {
138
        ERROR("Socket creation failed on port %d: %s", localPort, e.what());
139 140
        return;
    }
141

142 143 144 145 146
	if (sending_) {
        // Local video startup if needed
        auto videoCtrl = Manager::instance().getVideoControls();
        if (!videoCtrl->hasPreviewStarted()) {
            videoCtrl->startPreview();
147
            MYSLEEP(1);
148 149 150
        }

        videoLocal_ = videoCtrl->getVideoPreview();
151
        if (sender_.get())
152 153
            WARN("Restarting video sender");

154
        sender_.reset(new VideoSender(callID_, txArgs_, *socketPair_));
155 156
    } else {
        DEBUG("Video sending disabled");
157
        sender_.reset();
158 159
    }

160
    if (receiving_) {
161 162
        if (receiveThread_.get())
            WARN("restarting video receiver");
163
        receiveThread_.reset(new VideoReceiveThread(callID_, rxArgs_));
164
        receiveThread_->setRequestKeyFrameCallback(&SIPVoIPLink::enqueueKeyframeRequest);
165
        receiveThread_->addIOContext(*socketPair_);
166 167
        receiveThread_->start();
    } else {
168
        DEBUG("Video receiving disabled");
169
        receiveThread_.reset();
170
    }
171 172

    // Setup pipeline
173 174
    if (videoMixerSP_) {
        setupConferenceVideoPipeline();
175
    } else {
176 177
        if (sender_)
            videoLocal_->attach(sender_.get());
178
    }
179 180
}

181
void VideoRtpSession::stop()
182
{
183 184 185
    if (videoLocal_) {
        videoLocal_->detach(sender_.get());
    }
186

187 188 189
    if (videoMixerSP_) {
        videoMixerSP_->detach(sender_.get());
        receiveThread_->detach(videoMixerSP_.get());
190
    }
191

192 193
    if (socketPair_.get())
        socketPair_->interrupt();
194

195
    receiveThread_.reset();
196
    sender_.reset();
197
    socketPair_.reset();
198
}
199

200 201
void VideoRtpSession::forceKeyFrame()
{
202 203
    if (sender_.get())
        sender_->forceKeyFrame();
204 205
}

206
void VideoRtpSession::addReceivingDetails(std::map<std::string, std::string> &details)
207
{
208 209 210 211
    if (receiveThread_)
        receiveThread_->addReceivingDetails(details);
}

212
void VideoRtpSession::setupConferenceVideoPipeline()
213
{
214 215 216 217 218 219 220 221 222 223
    if (sender_) {
        videoMixerSP_->setDimensions(atol(txArgs_["width"].c_str()),
                                     atol(txArgs_["height"].c_str()));
        videoLocal_->detach(sender_.get());
        videoMixerSP_->attach(sender_.get());

        if (receiveThread_) {
            receiveThread_->enterConference();
            receiveThread_->attach(videoMixerSP_.get());
        }
224
    }
225
}
226

227 228
void VideoRtpSession::enterConference(Conference *conf)
{
229 230
    /* Detach from a possible previous conference */
    exitConference();
231
    videoMixerSP_ = std::move(conf->getVideoMixer());
232

233
    setupConferenceVideoPipeline();
234 235 236 237
}

void VideoRtpSession::exitConference()
{
238 239 240
    if (videoMixerSP_) {
        if (sender_)
            videoMixerSP_->detach(sender_.get());
241 242

        if (receiveThread_) {
243
            receiveThread_->detach(videoMixerSP_.get());
244 245 246
            receiveThread_->exitConference();
        }

247
        videoMixerSP_.reset();
248 249 250
    }

    if (videoLocal_)
251
        videoLocal_->attach(sender_.get());
252 253
}

254
} // end namespace sfl_video