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