1 /*
2     Copyright (C) 2011 Collabora Ltd. <info@collabora.co.uk>
3       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
4 
5     This library is free software; you can redistribute it and/or modify
6     it under the terms of the GNU Lesser General Public License as published
7     by the Free Software Foundation; either version 2.1 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "sink-controllers.h"
19 #include "libktpcall_debug.h"
20 #include <QGst/ElementFactory>
21 #include <QGst/Pipeline>
22 #include <QGst/GhostPad>
23 
24 namespace KTpCallPrivate {
25 
26 //BEGIN BaseSinkController
27 
contact() const28 Tp::ContactPtr BaseSinkController::contact() const
29 {
30     return m_contact;
31 }
32 
initFromMainThread(const Tp::ContactPtr & contact)33 void BaseSinkController::initFromMainThread(const Tp::ContactPtr & contact)
34 {
35     m_contact = contact;
36 }
37 
releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)38 void BaseSinkController::releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)
39 {
40     m_bin->setState(QGst::StateNull);
41 
42     // In GStreamer 1.6 it seems like the m_bin was removed from the
43     // pipeline during onPadUnlink, additionally 1.6 gives a warning
44     // when trying to remove an already removed bin.
45     // So, only remove the bin if still has a parent.
46     QGst::ObjectPtr parent = m_bin->parent();
47     if (!parent.isNull()) {
48         pipeline->remove(m_bin);
49     } else {
50       // Since we're no longer attached to the pipeline
51       // we need some place to make sure the pipeline is stopped
52       pipeline->setState(QGst::StateNull);
53     }
54     m_bin.clear();
55 }
56 
57 //END BaseSinkController
58 //BEGIN AudioSinkController
59 
AudioSinkController(const QGst::PadPtr & adderSinkPad)60 AudioSinkController::AudioSinkController(const QGst::PadPtr & adderSinkPad)
61     : m_adderRequestPad(adderSinkPad),
62       m_volumeController(NULL)
63 {
64 }
65 
~AudioSinkController()66 AudioSinkController::~AudioSinkController()
67 {
68     delete m_volumeController;
69 }
70 
adderRequestPad() const71 QGst::PadPtr AudioSinkController::adderRequestPad() const
72 {
73     return m_adderRequestPad;
74 }
75 
volumeController() const76 VolumeController *AudioSinkController::volumeController() const
77 {
78     return m_volumeController;
79 }
80 
initFromStreamingThread(const QGst::PadPtr & srcPad,const QGst::PipelinePtr & pipeline)81 void AudioSinkController::initFromStreamingThread(const QGst::PadPtr & srcPad,
82                                                   const QGst::PipelinePtr & pipeline)
83 {
84     m_bin = QGst::Bin::fromDescription(
85         "volume ! "
86         "audioconvert ! "
87         "audioresample ! "
88         "level"
89     );
90 
91     pipeline->add(m_bin);
92     qCDebug(LIBKTPCALL) << "add" << m_bin->name()
93                         << "to" << pipeline->name();
94 
95     m_bin->syncStateWithParent();
96 
97     m_bin->getStaticPad("src")->link(m_adderRequestPad);
98     srcPad->link(m_bin->getStaticPad("sink"));
99 }
100 
initFromMainThread(const Tp::ContactPtr & contact)101 void AudioSinkController::initFromMainThread(const Tp::ContactPtr & contact)
102 {
103     m_volumeController = new VolumeController;
104     m_volumeController->setElement(m_bin->getElementByInterface<QGst::StreamVolume>());
105 
106     BaseSinkController::initFromMainThread(contact);
107 }
108 
releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)109 void AudioSinkController::releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)
110 {
111     m_bin->getStaticPad("src")->unlink(m_adderRequestPad);
112     m_adderRequestPad.clear();
113 
114     BaseSinkController::releaseFromStreamingThread(pipeline);
115 }
116 
117 //END AudioSinkController
118 //BEGIN VideoSinkController
119 
VideoSinkController()120 VideoSinkController::VideoSinkController()
121     : m_padNameCounter(0),
122       m_videoSinkBin(NULL)
123 {
124 }
125 
~VideoSinkController()126 VideoSinkController::~VideoSinkController()
127 {
128 }
129 
requestSrcPad()130 QGst::PadPtr VideoSinkController::requestSrcPad()
131 {
132     QString newPadName = QStringLiteral("src%1").arg(m_padNameCounter);
133     m_padNameCounter++;
134 
135     QGst::PadPtr teeSrcPad = m_tee->getRequestPad("src_%u");
136     QGst::PadPtr ghostSrcPad = QGst::GhostPad::create(teeSrcPad, newPadName.toLatin1());
137 
138     ghostSrcPad->setActive(true);
139     m_bin->addPad(ghostSrcPad);
140     m_pads.insert(ghostSrcPad, teeSrcPad);
141 
142     return ghostSrcPad;
143 }
144 
releaseSrcPad(const QGst::PadPtr & pad)145 void VideoSinkController::releaseSrcPad(const QGst::PadPtr & pad)
146 {
147     QGst::PadPtr teeSrcPad = m_pads.take(pad);
148     Q_ASSERT(!teeSrcPad.isNull());
149 
150     m_bin->removePad(pad);
151     m_tee->releaseRequestPad(teeSrcPad);
152 }
153 
linkVideoSink(const QGst::ElementPtr & sink)154 void VideoSinkController::linkVideoSink(const QGst::ElementPtr & sink)
155 {
156     //initFromStreamingThread() is always called before the user knows
157     //anything about this content's src pad, so nobody can possibly link
158     //a video sink before the bin is created.
159     Q_ASSERT(m_bin);
160     qCDebug(LIBKTPCALL);
161 
162     QGst::PadPtr srcPad = m_tee->getRequestPad("src_%u");
163     m_videoSinkBin = new VideoSinkBin(sink);
164 
165     m_bin->add(m_videoSinkBin->bin());
166     m_videoSinkBin->bin()->syncStateWithParent();
167     srcPad->link(m_videoSinkBin->bin()->getStaticPad("sink"));
168 }
169 
unlinkVideoSink()170 void VideoSinkController::unlinkVideoSink()
171 {
172     //lock because releaseFromStreamingThread() is always called before the user
173     //knows about the removal of the content's src pad and may try to unlink
174     //externally while releaseFromStreamingThread() is running.
175     QMutexLocker l(&m_videoSinkMutex);
176 
177     if (m_videoSinkBin) {
178         qCDebug(LIBKTPCALL);
179 
180         QGst::PadPtr sinkPad = m_videoSinkBin->bin()->getStaticPad("sink");
181         QGst::PadPtr srcPad = sinkPad->peer();
182 
183         srcPad->unlink(sinkPad);
184         m_videoSinkBin->bin()->setState(QGst::StateNull);
185         m_bin->remove(m_videoSinkBin->bin());
186         delete m_videoSinkBin;
187         m_videoSinkBin = NULL;
188 
189         m_tee->releaseRequestPad(srcPad);
190     }
191 }
192 
initFromStreamingThread(const QGst::PadPtr & srcPad,const QGst::PipelinePtr & pipeline)193 void VideoSinkController::initFromStreamingThread(const QGst::PadPtr & srcPad,
194                                                   const QGst::PipelinePtr & pipeline)
195 {
196     m_bin = QGst::Bin::create();
197     m_tee = QGst::ElementFactory::make("tee");
198 
199     QGst::ElementPtr fakesink = QGst::ElementFactory::make("fakesink");
200     fakesink->setProperty("sync", false);
201     fakesink->setProperty("async", false);
202     fakesink->setProperty("silent", true);
203     fakesink->setProperty("enable-last-sample", false);
204 
205     m_bin->add(m_tee, fakesink);
206     m_tee->getRequestPad("src_%u")->link(fakesink->getStaticPad("sink"));
207 
208     QGst::PadPtr binSinkPad = QGst::GhostPad::create(m_tee->getStaticPad("sink"), "sink");
209     m_bin->addPad(binSinkPad);
210 
211     pipeline->add(m_bin);
212     m_bin->syncStateWithParent();
213 
214     srcPad->link(binSinkPad);
215 }
216 
releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)217 void VideoSinkController::releaseFromStreamingThread(const QGst::PipelinePtr & pipeline)
218 {
219     unlinkVideoSink();
220     BaseSinkController::releaseFromStreamingThread(pipeline);
221 }
222 
223 //END VideoSinkController
224 
225 } // KTpCallPrivate
226