|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include <QtCore/qdebug.h> |
|
43 #include <QtCore/qthread.h> |
|
44 #include <QtCore/qvariant.h> |
|
45 #include <QtGui/qx11info_x11.h> |
|
46 |
|
47 #include "qgstxvimagebuffer.h" |
|
48 #include "qvideosurfacegstsink.h" |
|
49 |
|
50 GstBufferClass *QGstXvImageBuffer::parent_class = NULL; |
|
51 |
|
52 GType QGstXvImageBuffer::get_type(void) |
|
53 { |
|
54 static GType buffer_type = 0; |
|
55 |
|
56 if (buffer_type == 0) { |
|
57 static const GTypeInfo buffer_info = { |
|
58 sizeof (GstBufferClass), |
|
59 NULL, |
|
60 NULL, |
|
61 QGstXvImageBuffer::class_init, |
|
62 NULL, |
|
63 NULL, |
|
64 sizeof(QGstXvImageBuffer), |
|
65 0, |
|
66 (GInstanceInitFunc)QGstXvImageBuffer::buffer_init, |
|
67 NULL |
|
68 }; |
|
69 buffer_type = g_type_register_static(GST_TYPE_BUFFER, |
|
70 "QGstXvImageBuffer", &buffer_info, GTypeFlags(0)); |
|
71 } |
|
72 return buffer_type; |
|
73 } |
|
74 |
|
75 void QGstXvImageBuffer::class_init(gpointer g_class, gpointer class_data) |
|
76 { |
|
77 Q_UNUSED(class_data); |
|
78 GST_MINI_OBJECT_CLASS(g_class)->finalize = |
|
79 (GstMiniObjectFinalizeFunction)buffer_finalize; |
|
80 parent_class = (GstBufferClass*)g_type_class_peek_parent(g_class); |
|
81 } |
|
82 |
|
83 void QGstXvImageBuffer::buffer_init(QGstXvImageBuffer *xvImage, gpointer g_class) |
|
84 { |
|
85 Q_UNUSED(g_class); |
|
86 xvImage->pool = 0; |
|
87 xvImage->shmInfo.shmaddr = ((char *) -1); |
|
88 xvImage->shmInfo.shmid = -1; |
|
89 xvImage->markedForDeletion = false; |
|
90 } |
|
91 |
|
92 void QGstXvImageBuffer::buffer_finalize(QGstXvImageBuffer * xvImage) |
|
93 { |
|
94 if (xvImage->pool) { |
|
95 if (xvImage->markedForDeletion) |
|
96 xvImage->pool->destroyBuffer(xvImage); |
|
97 else |
|
98 xvImage->pool->recycleBuffer(xvImage); |
|
99 } |
|
100 } |
|
101 |
|
102 |
|
103 QGstXvImageBufferPool::QGstXvImageBufferPool(QObject *parent) |
|
104 :QObject(parent) |
|
105 { |
|
106 } |
|
107 |
|
108 QGstXvImageBufferPool::~QGstXvImageBufferPool() |
|
109 { |
|
110 } |
|
111 |
|
112 bool QGstXvImageBufferPool::isFormatSupported(const QVideoSurfaceFormat &surfaceFormat) |
|
113 { |
|
114 bool ok = true; |
|
115 surfaceFormat.property("portId").toULongLong(&ok); |
|
116 if (!ok) |
|
117 return false; |
|
118 |
|
119 int xvFormatId = surfaceFormat.property("xvFormatId").toInt(&ok); |
|
120 if (!ok || xvFormatId < 0) |
|
121 return false; |
|
122 |
|
123 int dataSize = surfaceFormat.property("dataSize").toInt(&ok); |
|
124 if (!ok || dataSize<=0) |
|
125 return false; |
|
126 |
|
127 return true; |
|
128 } |
|
129 |
|
130 QGstXvImageBuffer *QGstXvImageBufferPool::takeBuffer(const QVideoSurfaceFormat &format, GstCaps *caps) |
|
131 { |
|
132 m_poolMutex.lock(); |
|
133 |
|
134 m_caps = caps; |
|
135 if (format != m_format) { |
|
136 doClear(); |
|
137 m_format = format; |
|
138 } |
|
139 |
|
140 |
|
141 if (m_pool.isEmpty()) { |
|
142 //qDebug() << "QGstXvImageBufferPool::takeBuffer: no buffer available, allocate the new one"; |
|
143 if (QThread::currentThread() == thread()) { |
|
144 m_poolMutex.unlock(); |
|
145 queuedAlloc(); |
|
146 m_poolMutex.lock(); |
|
147 } else { |
|
148 QMetaObject::invokeMethod(this, "queuedAlloc", Qt::QueuedConnection); |
|
149 m_allocWaitCondition.wait(&m_poolMutex, 300); |
|
150 } |
|
151 } |
|
152 QGstXvImageBuffer *res = 0; |
|
153 |
|
154 if (!m_pool.isEmpty()) { |
|
155 res = m_pool.takeLast(); |
|
156 } |
|
157 |
|
158 m_poolMutex.unlock(); |
|
159 |
|
160 return res; |
|
161 } |
|
162 |
|
163 void QGstXvImageBufferPool::queuedAlloc() |
|
164 { |
|
165 QMutexLocker lock(&m_poolMutex); |
|
166 |
|
167 Q_ASSERT(QThread::currentThread() == thread()); |
|
168 |
|
169 QGstXvImageBuffer *xvBuffer = (QGstXvImageBuffer *)gst_mini_object_new(QGstXvImageBuffer::get_type()); |
|
170 |
|
171 quint64 portId = m_format.property("portId").toULongLong(); |
|
172 int xvFormatId = m_format.property("xvFormatId").toInt(); |
|
173 |
|
174 xvBuffer->xvImage = XvShmCreateImage( |
|
175 QX11Info::display(), |
|
176 portId, |
|
177 xvFormatId, |
|
178 0, |
|
179 m_format.frameWidth(), |
|
180 m_format.frameHeight(), |
|
181 &xvBuffer->shmInfo |
|
182 ); |
|
183 |
|
184 if (!xvBuffer->xvImage) { |
|
185 //qDebug() << "QGstXvImageBufferPool: XvShmCreateImage failed"; |
|
186 m_allocWaitCondition.wakeOne(); |
|
187 return; |
|
188 } |
|
189 |
|
190 xvBuffer->shmInfo.shmid = shmget(IPC_PRIVATE, xvBuffer->xvImage->data_size, IPC_CREAT | 0777); |
|
191 xvBuffer->shmInfo.shmaddr = xvBuffer->xvImage->data = (char*)shmat(xvBuffer->shmInfo.shmid, 0, 0); |
|
192 xvBuffer->shmInfo.readOnly = False; |
|
193 |
|
194 if (!XShmAttach(QX11Info::display(), &xvBuffer->shmInfo)) { |
|
195 //qDebug() << "QGstXvImageBufferPool: XShmAttach failed"; |
|
196 m_allocWaitCondition.wakeOne(); |
|
197 return; |
|
198 } |
|
199 |
|
200 shmctl (xvBuffer->shmInfo.shmid, IPC_RMID, NULL); |
|
201 |
|
202 xvBuffer->pool = this; |
|
203 GST_MINI_OBJECT_CAST(xvBuffer)->flags = 0; |
|
204 gst_buffer_set_caps(GST_BUFFER_CAST(xvBuffer), m_caps); |
|
205 GST_BUFFER_DATA(xvBuffer) = (uchar*)xvBuffer->xvImage->data; |
|
206 GST_BUFFER_SIZE(xvBuffer) = xvBuffer->xvImage->data_size; |
|
207 |
|
208 m_allBuffers.append(xvBuffer); |
|
209 m_pool.append(xvBuffer); |
|
210 |
|
211 m_allocWaitCondition.wakeOne(); |
|
212 } |
|
213 |
|
214 |
|
215 void QGstXvImageBufferPool::clear() |
|
216 { |
|
217 QMutexLocker lock(&m_poolMutex); |
|
218 doClear(); |
|
219 } |
|
220 |
|
221 void QGstXvImageBufferPool::doClear() |
|
222 { |
|
223 foreach (QGstXvImageBuffer *xvBuffer, m_allBuffers) { |
|
224 xvBuffer->markedForDeletion = true; |
|
225 } |
|
226 m_allBuffers.clear(); |
|
227 |
|
228 foreach (QGstXvImageBuffer *xvBuffer, m_pool) { |
|
229 gst_buffer_unref(GST_BUFFER(xvBuffer)); |
|
230 } |
|
231 m_pool.clear(); |
|
232 |
|
233 m_format = QVideoSurfaceFormat(); |
|
234 } |
|
235 |
|
236 void QGstXvImageBufferPool::queuedDestroy() |
|
237 { |
|
238 QMutexLocker lock(&m_destroyMutex); |
|
239 |
|
240 foreach(XvShmImage xvImage, m_imagesToDestroy) { |
|
241 if (xvImage.shmInfo.shmaddr != ((void *) -1)) { |
|
242 XShmDetach(QX11Info::display(), &xvImage.shmInfo); |
|
243 XSync(QX11Info::display(), false); |
|
244 |
|
245 shmdt(xvImage.shmInfo.shmaddr); |
|
246 } |
|
247 |
|
248 if (xvImage.xvImage) |
|
249 XFree(xvImage.xvImage); |
|
250 } |
|
251 |
|
252 m_imagesToDestroy.clear(); |
|
253 |
|
254 XSync(QX11Info::display(), false); |
|
255 } |
|
256 |
|
257 void QGstXvImageBufferPool::recycleBuffer(QGstXvImageBuffer *xvBuffer) |
|
258 { |
|
259 QMutexLocker lock(&m_poolMutex); |
|
260 gst_buffer_ref(GST_BUFFER_CAST(xvBuffer)); |
|
261 m_pool.append(xvBuffer); |
|
262 } |
|
263 |
|
264 void QGstXvImageBufferPool::destroyBuffer(QGstXvImageBuffer *xvBuffer) |
|
265 { |
|
266 XvShmImage imageToDestroy; |
|
267 imageToDestroy.xvImage = xvBuffer->xvImage; |
|
268 imageToDestroy.shmInfo = xvBuffer->shmInfo; |
|
269 |
|
270 m_destroyMutex.lock(); |
|
271 m_imagesToDestroy.append(imageToDestroy); |
|
272 m_destroyMutex.unlock(); |
|
273 |
|
274 if (m_imagesToDestroy.size() == 1) |
|
275 QMetaObject::invokeMethod(this, "queuedDestroy", Qt::QueuedConnection); |
|
276 } |