1 /* Webcamoid, webcam capture application.
2 * Copyright (C) 2019 Gonzalo Exequiel Pedone
3 *
4 * Webcamoid is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Webcamoid is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Web-Site: http://webcamoid.github.io/
18 */
19
20 #include <QQueue>
21 #include <QAbstractEventDispatcher>
22 #include <QEventLoop>
23 #include <QtConcurrent>
24 #include <QFuture>
25 #include <QThread>
26 #include <QThreadPool>
27 #include <QWaitCondition>
28 #include <akfrac.h>
29 #include <akcaps.h>
30 #include <akpacket.h>
31 #include <media/NdkMediaExtractor.h>
32
33 #include "abstractstream.h"
34 #include "clock.h"
35
36 template <typename T>
waitLoop(const QFuture<T> & loop)37 inline void waitLoop(const QFuture<T> &loop)
38 {
39 while (!loop.isFinished()) {
40 auto eventDispatcher = QThread::currentThread()->eventDispatcher();
41
42 if (eventDispatcher)
43 eventDispatcher->processEvents(QEventLoop::AllEvents);
44 }
45 }
46
47 class AbstractStreamPrivate
48 {
49 public:
50 AbstractStream *self;
51 AkFrac m_timeBase;
52 AMediaExtractor *m_mediaExtractor {nullptr};
53 AMediaCodec *m_codec {nullptr};
54 AMediaFormat *m_mediaFormat {nullptr};
55 QThreadPool m_threadPool;
56 QMutex m_dataMutex;
57 QWaitCondition m_dataQueueNotEmpty;
58 QWaitCondition m_dataQueueNotFull;
59 QQueue<AkPacket> m_packets;
60 qint64 m_packetQueueSize {-1};
61 Clock *m_globalClock {nullptr};
62 QFuture<void> m_dataLoopResult;
63 QString m_mimeType;
64 qint64 m_id {-1};
65 uint m_index {0};
66 bool m_runDataLoop {false};
67
68 explicit AbstractStreamPrivate(AbstractStream *self);
69 void dataLoop();
70 };
71
AbstractStream(AMediaExtractor * mediaExtractor,uint index,qint64 id,Clock * globalClock,QObject * parent)72 AbstractStream::AbstractStream(AMediaExtractor *mediaExtractor,
73 uint index, qint64 id, Clock *globalClock,
74 QObject *parent):
75 QObject(parent)
76 {
77 this->d = new AbstractStreamPrivate(this);
78 this->d->m_mediaExtractor = mediaExtractor;
79 this->m_paused = false;
80 this->m_isValid = false;
81 this->m_clockDiff = 0;
82 this->m_maxData = 0;
83 this->d->m_index = index;
84 this->d->m_id = id;
85
86 this->d->m_mediaFormat =
87 AMediaExtractor_getTrackFormat(this->d->m_mediaExtractor,
88 index);
89 const char *mime = nullptr;
90 AMediaFormat_getString(this->d->m_mediaFormat,
91 AMEDIAFORMAT_KEY_MIME,
92 &mime);
93 this->d->m_codec = AMediaCodec_createDecoderByType(mime);
94
95 if (!this->d->m_codec)
96 return;
97
98 if (QString(mime).startsWith("audio/")) {
99 this->d->m_mimeType = "audio/x-raw";
100 int32_t rate = 0;
101 AMediaFormat_getInt32(this->d->m_mediaFormat,
102 AMEDIAFORMAT_KEY_SAMPLE_RATE,
103 &rate);
104 this->d->m_timeBase = AkFrac(1, rate);
105 } else if (QString(mime).startsWith("video/")) {
106 this->d->m_mimeType = "video/x-raw";
107 int32_t frameRate;
108 AMediaFormat_getInt32(this->d->m_mediaFormat,
109 AMEDIAFORMAT_KEY_FRAME_RATE,
110 &frameRate);
111 this->d->m_timeBase = AkFrac(1, frameRate);
112 }
113
114 this->d->m_packetQueueSize = 0;
115 this->d->m_globalClock = globalClock;
116
117 this->m_isValid = true;
118
119 if (this->d->m_threadPool.maxThreadCount() < 2)
120 this->d->m_threadPool.setMaxThreadCount(2);
121 }
122
~AbstractStream()123 AbstractStream::~AbstractStream()
124 {
125 if (this->d->m_codec)
126 AMediaCodec_delete(this->d->m_codec);
127
128 if (this->d->m_mediaFormat)
129 AMediaFormat_delete(this->d->m_mediaFormat);
130
131 delete this->d;
132 }
133
paused() const134 bool AbstractStream::paused() const
135 {
136 return this->m_paused;
137 }
138
isValid() const139 bool AbstractStream::isValid() const
140 {
141 return this->m_isValid;
142 }
143
index() const144 uint AbstractStream::index() const
145 {
146 return this->d->m_index;
147 }
148
id() const149 qint64 AbstractStream::id() const
150 {
151 return this->d->m_id;
152 }
153
timeBase() const154 AkFrac AbstractStream::timeBase() const
155 {
156 return this->d->m_timeBase;
157 }
158
mimeType() const159 QString AbstractStream::mimeType() const
160 {
161 return this->d->m_mimeType;
162 }
163
codec() const164 AMediaCodec *AbstractStream::codec() const
165 {
166 return this->d->m_codec;
167 }
168
mediaFormat() const169 AMediaFormat *AbstractStream::mediaFormat() const
170 {
171 return this->d->m_mediaFormat;
172 }
173
caps() const174 AkCaps AbstractStream::caps() const
175 {
176 return AkCaps();
177 }
178
packetEnqueue(bool eos)179 bool AbstractStream::packetEnqueue(bool eos)
180 {
181 ssize_t timeOut = 5000;
182 auto bufferIndex =
183 AMediaCodec_dequeueInputBuffer(this->d->m_codec, timeOut);
184
185 if (bufferIndex < 0)
186 return false;
187
188 if (eos) {
189 AMediaCodec_queueInputBuffer(this->d->m_codec,
190 size_t(bufferIndex),
191 0,
192 0,
193 0,
194 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
195 } else {
196 size_t buffersize = 0;
197 auto buffer = AMediaCodec_getInputBuffer(this->d->m_codec,
198 size_t(bufferIndex),
199 &buffersize);
200
201 if (!buffer)
202 return false;
203
204 auto sampleSize =
205 AMediaExtractor_readSampleData(this->d->m_mediaExtractor,
206 buffer,
207 buffersize);
208
209 if (sampleSize < 1)
210 return false;
211
212 auto presentationTimeUs =
213 AMediaExtractor_getSampleTime(this->d->m_mediaExtractor);
214 AMediaCodec_queueInputBuffer(this->d->m_codec,
215 size_t(bufferIndex),
216 0,
217 size_t(sampleSize),
218 uint64_t(presentationTimeUs),
219 0);
220 }
221
222 return true;
223 }
224
avPacketEnqueue(const AkPacket & packet)225 void AbstractStream::avPacketEnqueue(const AkPacket &packet)
226 {
227 this->d->m_dataMutex.lock();
228
229 if (this->d->m_packets.size() >= this->m_maxData)
230 this->d->m_dataQueueNotFull.wait(&this->d->m_dataMutex);
231
232 if (packet)
233 this->d->m_packets.enqueue(packet);
234 else
235 this->d->m_packets.enqueue({});
236
237 this->d->m_dataQueueNotEmpty.wakeAll();
238 this->d->m_dataMutex.unlock();
239 }
240
queueSize()241 qint64 AbstractStream::queueSize()
242 {
243 return this->d->m_packetQueueSize;
244 }
245
globalClock()246 Clock *AbstractStream::globalClock()
247 {
248 return this->d->m_globalClock;
249 }
250
clockDiff() const251 qreal AbstractStream::clockDiff() const
252 {
253 return this->m_clockDiff;
254 }
255
clockDiff()256 qreal &AbstractStream::clockDiff()
257 {
258 return this->m_clockDiff;
259 }
260
decodeData()261 bool AbstractStream::decodeData()
262 {
263 return false;
264 }
265
mimeType(AMediaExtractor * mediaExtractor,uint index)266 QString AbstractStream::mimeType(AMediaExtractor *mediaExtractor,
267 uint index)
268 {
269 auto format = AMediaExtractor_getTrackFormat(mediaExtractor, index);
270
271 if (!format)
272 return {};
273
274 const char *mime = nullptr;
275 AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
276 auto mimeType = QString(mime).startsWith("audio/")?
277 "audio/x-raw":
278 QString(mime).startsWith("video/")?
279 "video/x-raw":
280 QString();
281 AMediaFormat_delete(format);
282
283 return mimeType;
284 }
285
processPacket(const AkPacket & packet)286 void AbstractStream::processPacket(const AkPacket &packet)
287 {
288 Q_UNUSED(packet)
289 }
290
AbstractStreamPrivate(AbstractStream * self)291 AbstractStreamPrivate::AbstractStreamPrivate(AbstractStream *self):
292 self(self)
293 {
294 }
295
dataLoop()296 void AbstractStreamPrivate::dataLoop()
297 {
298 if (this->m_mimeType == "audio/x-raw"
299 || this->m_mimeType == "video/x-raw") {
300 while (this->m_runDataLoop) {
301 this->m_dataMutex.lock();
302 bool gotFrame = true;
303
304 if (this->m_packets.isEmpty())
305 gotFrame = this->m_dataQueueNotEmpty.wait(&this->m_dataMutex,
306 THREAD_WAIT_LIMIT);
307
308 AkPacket packet;
309
310 if (gotFrame) {
311 packet = this->m_packets.dequeue();
312
313 if (this->m_packets.size() < self->m_maxData)
314 this->m_dataQueueNotFull.wakeAll();
315 }
316
317 this->m_dataMutex.unlock();
318
319 if (gotFrame) {
320 if (packet)
321 self->processPacket(packet);
322 else {
323 emit self->eof();
324 this->m_runDataLoop = false;
325 }
326 }
327 }
328 }
329 }
330
setPaused(bool paused)331 void AbstractStream::setPaused(bool paused)
332 {
333 if (this->m_paused == paused)
334 return;
335
336 this->d->m_runDataLoop = !paused;
337
338 if (paused)
339 this->d->m_dataLoopResult.waitForFinished();
340 else
341 this->d->m_dataLoopResult =
342 QtConcurrent::run(&this->d->m_threadPool,
343 this->d,
344 &AbstractStreamPrivate::dataLoop);
345
346 this->m_paused = paused;
347 emit this->pausedChanged(paused);
348 }
349
resetPaused()350 void AbstractStream::resetPaused()
351 {
352 this->setPaused(false);
353 }
354
init()355 bool AbstractStream::init()
356 {
357 if (!this->d->m_codec)
358 return false;
359
360 if (AMediaCodec_configure(this->d->m_codec,
361 this->d->m_mediaFormat,
362 nullptr,
363 nullptr,
364 0) != AMEDIA_OK)
365 return false;
366
367 if (AMediaCodec_start(this->d->m_codec) != AMEDIA_OK)
368 return false;
369
370 if (AMediaExtractor_selectTrack(this->d->m_mediaExtractor,
371 this->d->m_index) != AMEDIA_OK)
372 return false;
373
374 this->m_clockDiff = 0;
375 this->d->m_runDataLoop = true;
376 this->d->m_dataLoopResult =
377 QtConcurrent::run(&this->d->m_threadPool,
378 this->d,
379 &AbstractStreamPrivate::dataLoop);
380
381 return true;
382 }
383
uninit()384 void AbstractStream::uninit()
385 {
386 this->d->m_runDataLoop = false;
387 waitLoop(this->d->m_dataLoopResult);
388
389 AMediaCodec_stop(this->d->m_codec);
390 this->d->m_packets.clear();
391 }
392
393 #include "moc_abstractstream.cpp"
394