This URL has Read-Only access.

Statistics
| Branch: | Tag: | Revision:

root / src / gst / engine / remoteConfig.cpp @ 0b392805

History | View | Annotate | Download (10.1 kB)

1
/* remoteConfig.cpp
2
 * Copyright (C) 2008-2009 Société des arts technologiques (SAT)
3
 * http://www.sat.qc.ca
4
 * All rights reserved.
5
 *
6
 * This file is part of [propulse]ART.
7
 *
8
 * [propulse]ART is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * [propulse]ART is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with [propulse]ART.  If not, see <http://www.gnu.org/licenses/>.
20
 *
21
 */
22

    
23
#include "util.h"
24

    
25
#include <algorithm>
26
#include <boost/assign.hpp>
27
#include <gst/gst.h>
28
#include "pipeline.h"
29
#include "remoteConfig.h"
30
#include "tcp/asio.h"
31
#include "mapMsg.h"
32
#include "codec.h"
33

    
34
const int RemoteConfig::PORT_MIN = 1024;
35
const int RemoteConfig::PORT_MAX = 65000;
36

    
37

    
38
std::set<int> RemoteConfig::usedPorts_;
39
        
40
RemoteConfig::RemoteConfig(MapMsg &msg, int msgId__) : 
41
    codec_(msg["codec"]), remoteHost_(msg["address"]), port_(msg["port"]), msgId_(msgId__)
42
{}
43

    
44

    
45
// Can't be called from destructor, must be called by this object's owner/client, 
46
// as sometime this object is copied and we don't want the ports destroyed prematurely
47
void RemoteConfig::cleanupPorts() const
48
{
49
    usedPorts_.erase(capsPort());
50
    usedPorts_.erase(rtcpSecondPort());
51
    usedPorts_.erase(rtcpFirstPort());
52
    usedPorts_.erase(port());
53
}
54

    
55

    
56
/// Make sure there are no port clashes (at least as far as this process can tell)
57
void RemoteConfig::checkPorts() const
58
{
59
    if (port_ < PORT_MIN || port_ > PORT_MAX)
60
        THROW_ERROR("Invalid port " << port_ << ", must be in range [" 
61
                << PORT_MIN << "," << PORT_MAX << "]");  
62

    
63
    if (usedPorts_.find(port()) != usedPorts_.end())
64
        THROW_ERROR("Invalid port " << port() << ", already in use");
65

    
66
    if (usedPorts_.find(rtcpFirstPort()) != usedPorts_.end())
67
        THROW_ERROR("Invalid port " << port() << ", its rtcp port " << rtcpFirstPort() << " is already in use");
68

    
69
    if (usedPorts_.find(rtcpSecondPort()) != usedPorts_.end())
70
        THROW_ERROR("Invalid port " << port() << ", its rtcp port " << rtcpSecondPort() << " is already in use");
71

    
72
    if (usedPorts_.find(capsPort()) != usedPorts_.end())
73
        THROW_ERROR("Invalid port " << port() << ", its caps port " << capsPort() << " is already in use");
74

    
75
    // add our ports now that we know they're available
76
    usedPorts_.insert(port());
77
    usedPorts_.insert(rtcpFirstPort());
78
    usedPorts_.insert(rtcpSecondPort());
79
    usedPorts_.insert(capsPort());
80
}
81
        
82

    
83
SenderConfig::SenderConfig(Pipeline &pipeline, MapMsg &msg, int msgId__) : 
84
    RemoteConfig(msg, msgId__), 
85
    BusMsgHandler(pipeline),
86
    message_(""), 
87
    capsOutOfBand_(false)    // this will be determined later
88
{}
89

    
90

    
91
VideoEncoder * SenderConfig::createVideoEncoder(Pipeline &pipeline, MapMsg &settings) const
92
{
93
    if (codec_.empty())
94
        THROW_ERROR("Can't make encoder without codec being specified.");
95

    
96
    if (codec_ == "h264")
97
        return new H264Encoder(pipeline, settings);
98
    else if (codec_ == "h263")
99
        return new H263Encoder(pipeline, settings);       // set caps from here?
100
    else if (codec_ == "mpeg4")
101
        return new Mpeg4Encoder(pipeline, settings);
102
    else if (codec_ == "theora")
103
        return new TheoraEncoder(pipeline, settings);
104
    else
105
    {
106
        THROW_ERROR(codec_ << " is an invalid codec!");
107
        return 0;
108
    }
109
    LOG_DEBUG("Video encoder " << codec_ << " built"); 
110
}
111

    
112

    
113
Encoder * SenderConfig::createAudioEncoder(Pipeline &pipeline) const
114
{
115
    if (codec_.empty())
116
        THROW_ERROR("Can't make encoder without codec being specified.");
117

    
118
    if (codec_ == "vorbis")
119
        return new VorbisEncoder(pipeline);
120
    else if (codec_ == "raw")
121
        return new RawEncoder(pipeline);
122
    else if (codec_ == "mp3")
123
        return new LameEncoder(pipeline);
124
    else
125
    {
126
        THROW_ERROR(codec_ << " is an invalid codec!");
127
        return 0;
128
    }
129
    LOG_DEBUG("Audio encoder " << codec_ << " built"); 
130
}
131

    
132

    
133
gboolean SenderConfig::sendMessage(gpointer data) 
134
{
135
    const SenderConfig *context = static_cast<const SenderConfig*>(data);
136
    LOG_DEBUG("\n\n\nSending tcp msg for host " 
137
            << context->remoteHost_ << " on port " << context->capsPort() 
138
            << " with id " << context->msgId_);
139

    
140
    /// FIXME: everytime a receiver starts, it should ask sender for caps, then the sender can
141
    /// send them.
142
    if (asio::tcpSendBuffer(context->remoteHost_, context->capsPort(), context->msgId_, context->message_))
143
        LOG_INFO("Caps sent successfully");
144
    return TRUE;    // try again later, in case we have a new receiver
145
}
146

    
147

    
148
/** 
149
 * The new caps message is posted on the bus by the src pad of our udpsink, 
150
 * received by this audiosender, and sent to our other host if needed. */
151
bool SenderConfig::handleBusMsg(GstMessage *msg)
152
{
153
    const GstStructure *s = gst_message_get_structure(msg);
154
    if (s != NULL and gst_structure_has_name(s, "caps-changed"))
155
    {   
156
        // this is our msg
157
        const gchar *newCapsStr = gst_structure_get_string(s, "caps");
158
        tassert(newCapsStr);
159
        std::string str(newCapsStr);
160

    
161
        GstStructure *structure = gst_caps_get_structure(gst_caps_from_string(str.c_str()), 0);
162
        const GValue *encodingStr = gst_structure_get_value(structure, "encoding-name");
163
        std::string encodingName(g_value_get_string(encodingStr));
164

    
165
        if (!capsMatchCodec(encodingName, codec()))
166
            return false;   // not our caps, ignore it
167
        else if (capsOutOfBand_) 
168
        { 
169
            LOG_DEBUG("Sending caps for codec " << codec());
170

    
171
            message_ = std::string(newCapsStr);
172
            enum {MESSAGE_SEND_TIMEOUT = 1000}; // send caps once every second
173
            g_timeout_add(MESSAGE_SEND_TIMEOUT, static_cast<GSourceFunc>(SenderConfig::sendMessage), 
174
                    static_cast<gpointer>(this));
175
            return true;
176
        }
177
        else
178
            return true;       // was our caps, but we don't need to send caps for it
179
    }
180

    
181
    return false;           // this wasn't our msg, someone else should handle it
182
}
183

    
184

    
185
bool ReceiverConfig::isSupportedCodec(const std::string &codec)
186
{
187
    using namespace boost::assign;
188
    using std::string;
189
    
190
    static const std::vector<string> CODECS = 
191
        list_of<string>("mpeg4")("theora")("vorbis")("raw")("h264")("h263")("mp3");
192

    
193
    bool result = std::find(CODECS.begin(), CODECS.end(), codec) != CODECS.end();
194
    return result;
195
}
196

    
197

    
198
ReceiverConfig::ReceiverConfig(MapMsg &msg,
199
        const std::string &caps__,
200
        int msgId__) : 
201
    RemoteConfig(msg, msgId__), 
202
    multicastInterface_(msg["multicast-interface"]), caps_(caps__), 
203
    capsOutOfBand_(msg["negotiate-caps"] or caps_ == ""),
204
    jitterbufferControlEnabled_(msg["enable-controls"])
205
{
206
    if (capsOutOfBand_) // couldn't find caps, need them from other host or we've explicitly been told to send caps
207
    {
208
        if (isSupportedCodec(codec_))   // this would fail later but we want to make sure we don't wait with a bogus codec
209
        { 
210
            LOG_INFO("Waiting for " << codec_ << " caps from other host");
211
            receiveCaps();  // wait for new caps from sender
212
        }
213
        else
214
            THROW_ERROR("Codec " << codec_ << " is not supported");
215
    }
216
}
217

    
218
VideoDecoder * ReceiverConfig::createVideoDecoder(Pipeline &pipeline, bool doDeinterlace) const
219
{
220
    if (codec_.empty())
221
        THROW_ERROR("Can't make decoder without codec being specified.");
222

    
223
    if (codec_ == "h264")
224
        return new H264Decoder(pipeline, doDeinterlace);
225
    else if (codec_ == "h263")
226
        return new H263Decoder(pipeline, doDeinterlace);
227
    else if (codec_ == "mpeg4")
228
        return new Mpeg4Decoder(pipeline, doDeinterlace);
229
    else if (codec_ == "theora")
230
        return new TheoraDecoder(pipeline, doDeinterlace);
231
    else
232
    {
233
        THROW_ERROR(codec_ << " is an invalid codec!");
234
        return 0;
235
    }
236
    LOG_DEBUG("Video decoder " << codec_ << " built"); 
237
}
238

    
239

    
240
Decoder * ReceiverConfig::createAudioDecoder(Pipeline &pipeline) const
241
{
242
    if (codec_.empty())
243
        THROW_ERROR("Can't make decoder without codec being specified.");
244

    
245
    if (codec_ == "vorbis")
246
        return new VorbisDecoder(pipeline);
247
    else if (codec_ == "raw")
248
        return new RawDecoder(pipeline);
249
    else if (codec_ == "mp3")
250
        return new MadDecoder(pipeline);
251
    else
252
    {
253
        THROW_ERROR(codec_ << " is an invalid codec!");
254
        return 0;
255
    }
256
    LOG_DEBUG("Audio decoder " << codec_ << " built"); 
257
}
258

    
259

    
260
/// compares internal codec names and RTP-header codec names
261
bool RemoteConfig::capsMatchCodec(const std::string &encodingName, const std::string &codec)
262
{
263
    return (encodingName == "VORBIS" and codec == "vorbis")
264
    or (encodingName == "L16" and codec == "raw")
265
    or (encodingName == "MPA" and codec == "mp3")
266
    or (encodingName == "MP4V-ES" and codec == "mpeg4")
267
    or (encodingName == "H264" and codec == "h264")
268
    or (encodingName == "H263-1998" and codec == "h263")
269
    or (encodingName == "THEORA" and codec == "theora");
270
}
271

    
272
/// This function makes sure that the caps set on this receiver by a sender, match the codec
273
/// that it expects. If it fails, it is probably due to a mismatch of codecs between sender and
274
/// receiver, or a change in the caps specification for a given codec from gstreamer.
275
//  TODO: maybe the whole receiver should be created based on this info, at least up to and including
276
//  the decoder?
277
bool ReceiverConfig::capsMatchCodec() const
278
{
279
    GstStructure *structure = gst_caps_get_structure(gst_caps_from_string(caps_.c_str()), 0);
280
    const GValue *str = gst_structure_get_value(structure, "encoding-name");
281
    std::string encodingName(g_value_get_string(str));
282

    
283
    return RemoteConfig::capsMatchCodec(encodingName, codec_);
284
}
285

    
286

    
287
void ReceiverConfig::receiveCaps()
288
{
289
    int id;
290
    // this blocks
291
    std::string msg(asio::tcpGetBuffer(capsPort(), id));
292
    //tassert(id == msgId_);
293
    caps_ = msg;
294
    LOG_DEBUG("Received caps " << caps_);
295
}
296