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