1 /*
2 Copyright © 2015-2019 The qTox Project Contributors
3
4 This file is part of qTox, a Qt-based graphical interface for Tox.
5
6 qTox is libre software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 qTox is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with qTox. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 extern "C" {
21 #include <libavcodec/avcodec.h>
22 #include <libavutil/imgutils.h>
23 }
24
25 #include "corevideosource.h"
26 #include "videoframe.h"
27
28 /**
29 * @class CoreVideoSource
30 * @brief A VideoSource that emits frames received by Core.
31 */
32
33 /**
34 * @var std::atomic_int subscribers
35 * @brief Number of suscribers
36 *
37 * @var std::atomic_bool deleteOnClose
38 * @brief If true, self-delete after the last suscriber is gone
39 */
40
41 /**
42 * @brief CoreVideoSource constructor.
43 * @note Only CoreAV should create a CoreVideoSource since
44 * only CoreAV can push images to it.
45 */
CoreVideoSource()46 CoreVideoSource::CoreVideoSource()
47 : subscribers{0}
48 , deleteOnClose{false}
49 , stopped{false}
50 {
51 }
52
53 /**
54 * @brief Makes a copy of the vpx_image_t and emits it as a new VideoFrame.
55 * @param vpxframe Frame to copy.
56 */
pushFrame(const vpx_image_t * vpxframe)57 void CoreVideoSource::pushFrame(const vpx_image_t* vpxframe)
58 {
59 if (stopped)
60 return;
61
62 QMutexLocker locker(&biglock);
63
64 std::shared_ptr<VideoFrame> vframe;
65 int width = vpxframe->d_w;
66 int height = vpxframe->d_h;
67
68 if (subscribers <= 0)
69 return;
70
71 AVFrame* avframe = av_frame_alloc();
72 if (!avframe)
73 return;
74
75 avframe->width = width;
76 avframe->height = height;
77 avframe->format = AV_PIX_FMT_YUV420P;
78
79 int bufSize =
80 av_image_alloc(avframe->data, avframe->linesize, width, height,
81 static_cast<AVPixelFormat>(AV_PIX_FMT_YUV420P), VideoFrame::dataAlignment);
82
83 if (bufSize < 0) {
84 av_frame_free(&avframe);
85 return;
86 }
87
88 for (int i = 0; i < 3; ++i) {
89 int dstStride = avframe->linesize[i];
90 int srcStride = vpxframe->stride[i];
91 int minStride = std::min(dstStride, srcStride);
92 int size = (i == 0) ? height : height / 2;
93
94 for (int j = 0; j < size; ++j) {
95 uint8_t* dst = avframe->data[i] + dstStride * j;
96 uint8_t* src = vpxframe->planes[i] + srcStride * j;
97 memcpy(dst, src, minStride);
98 }
99 }
100
101 vframe = std::make_shared<VideoFrame>(id, avframe, true);
102 emit frameAvailable(vframe);
103 }
104
subscribe()105 void CoreVideoSource::subscribe()
106 {
107 QMutexLocker locker(&biglock);
108 ++subscribers;
109 }
110
unsubscribe()111 void CoreVideoSource::unsubscribe()
112 {
113 biglock.lock();
114 if (--subscribers == 0) {
115 if (deleteOnClose) {
116 biglock.unlock();
117 // DANGEROUS: No member access after this point, that's why we manually unlock
118 delete this;
119 return;
120 }
121 }
122 biglock.unlock();
123 }
124
125 /**
126 * @brief Setup delete on close
127 * @param If true, self-delete after the last suscriber is gone
128 */
setDeleteOnClose(bool newstate)129 void CoreVideoSource::setDeleteOnClose(bool newstate)
130 {
131 QMutexLocker locker(&biglock);
132 deleteOnClose = newstate;
133 }
134
135 /**
136 * @brief Stopping the source.
137 * @see The callers in CoreAV for the rationale
138 *
139 * Stopping the source will block any pushFrame calls from doing anything
140 */
stopSource()141 void CoreVideoSource::stopSource()
142 {
143 QMutexLocker locker(&biglock);
144 stopped = true;
145 emit sourceStopped();
146 }
147
restartSource()148 void CoreVideoSource::restartSource()
149 {
150 QMutexLocker locker(&biglock);
151 stopped = false;
152 }
153