1 | /* |
2 | Copyright (C) 2010-2021 Harald Sitter <sitter@kde.org> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2.1 of the License, or (at your option) version 3, or any |
8 | later version accepted by the membership of KDE e.V. (or its |
9 | successor approved by the membership of KDE e.V.), Nokia Corporation |
10 | (or its successors, if any) and the KDE Free Qt Foundation, which shall |
11 | act as a proxy defined in Section 6 of version 3 of the license. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Lesser General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Lesser General Public |
19 | License along with this library. If not, see <http://www.gnu.org/licenses/>. |
20 | */ |
21 | |
22 | #include "videodataoutput.h" |
23 | |
24 | #include <phonon/medianode.h> |
25 | #include <phonon/audiooutput.h> |
26 | #include <phonon/experimental/abstractvideodataoutput.h> |
27 | |
28 | #include <QMetaObject> |
29 | |
30 | #include "utils/debug.h" |
31 | #include "media.h" |
32 | #include "mediaobject.h" |
33 | |
34 | using namespace Phonon::Experimental; |
35 | |
36 | namespace Phonon |
37 | { |
38 | namespace VLC |
39 | { |
40 | |
41 | VideoDataOutput::VideoDataOutput(QObject *parent) |
42 | : QObject(parent) |
43 | , m_frontend(0) |
44 | { |
45 | } |
46 | |
47 | VideoDataOutput::~VideoDataOutput() |
48 | { |
49 | } |
50 | |
51 | void VideoDataOutput::handleConnectToMediaObject(MediaObject *mediaObject) |
52 | { |
53 | Q_UNUSED(mediaObject); |
54 | setCallbacks(m_player); |
55 | } |
56 | |
57 | void VideoDataOutput::handleDisconnectFromMediaObject(MediaObject *mediaObject) |
58 | { |
59 | Q_UNUSED(mediaObject); |
60 | unsetCallbacks(player: m_player); |
61 | } |
62 | |
63 | void VideoDataOutput::handleAddToMedia(Media *media) |
64 | { |
65 | media->addOption(option: ":video" ); |
66 | } |
67 | |
68 | Experimental::AbstractVideoDataOutput *VideoDataOutput::frontendObject() const |
69 | { |
70 | return m_frontend; |
71 | } |
72 | |
73 | void VideoDataOutput::setFrontendObject(Experimental::AbstractVideoDataOutput *frontend) |
74 | { |
75 | m_frontend = frontend; |
76 | } |
77 | |
78 | void *VideoDataOutput::lockCallback(void **planes) |
79 | { |
80 | m_mutex.lock(); |
81 | DEBUG_BLOCK; |
82 | planes[0] = reinterpret_cast<void *>(m_frame.data0.data()); |
83 | planes[1] = reinterpret_cast<void *>(m_frame.data1.data()); |
84 | planes[2] = reinterpret_cast<void *>(m_frame.data2.data()); |
85 | return 0; |
86 | } |
87 | |
88 | void VideoDataOutput::unlockCallback(void *picture, void *const*planes) |
89 | { |
90 | Q_UNUSED(picture); |
91 | Q_UNUSED(planes); |
92 | DEBUG_BLOCK; |
93 | |
94 | // For some reason VLC yields BGR24, so we swap it to RGB |
95 | if (m_frame.format == Experimental::VideoFrame2::Format_RGB888) { |
96 | uchar *data = (uchar *) m_frame.data0.data(); |
97 | uchar tmp; |
98 | for (int i = 0; i < m_frame.data0.size(); i += 3) { |
99 | tmp = data[i]; |
100 | data[i] = data[i+2]; |
101 | data[i+2] = tmp; |
102 | } |
103 | } |
104 | |
105 | if (m_frontend) |
106 | m_frontend->frameReady(m_frame); |
107 | |
108 | m_mutex.unlock(); |
109 | } |
110 | |
111 | void VideoDataOutput::displayCallback(void *picture) |
112 | { |
113 | Q_UNUSED(picture); |
114 | DEBUG_BLOCK; |
115 | // We send the frame while unlocking as we could loose syncing otherwise. |
116 | // With VDO the consumer is expected to ensure syncness while not blocking |
117 | // unlock for long periods of time. Good luck with that... -.- |
118 | } |
119 | |
120 | static VideoFrame2::Format fourccToFormat(const char *fourcc) |
121 | { |
122 | if (qstrcmp(str1: fourcc, str2: "RV24" )) |
123 | return VideoFrame2::Format_RGB888; |
124 | else if (qstrcmp(str1: fourcc, str2: "RV32" )) |
125 | return VideoFrame2::Format_RGB32; |
126 | else if (qstrcmp(str1: fourcc, str2: "YV12" )) |
127 | return VideoFrame2::Format_YV12; |
128 | else if (qstrcmp(str1: fourcc, str2: "YUY2" )) |
129 | return VideoFrame2::Format_YUY2; |
130 | else |
131 | return VideoFrame2::Format_Invalid; |
132 | } |
133 | |
134 | static uint32_t setFormat(VideoFrame2::Format format, char **chroma) |
135 | { |
136 | switch (format) { |
137 | case VideoFrame2::Format_Invalid: |
138 | *chroma = nullptr; |
139 | return 0; |
140 | case VideoFrame2::Format_RGB32: |
141 | qstrcpy(dst: *chroma, src: "RV32" ); |
142 | return VLC_CODEC_RGB32; |
143 | case VideoFrame2::Format_RGB888: |
144 | qstrcpy(dst: *chroma, src: "RV24" ); |
145 | return VLC_CODEC_RGB24; |
146 | case VideoFrame2::Format_YV12: |
147 | qstrcpy(dst: *chroma, src: "YV12" ); |
148 | return VLC_CODEC_YV12; |
149 | case VideoFrame2::Format_YUY2: |
150 | qstrcpy(dst: *chroma, src: "YUY2" ); |
151 | return VLC_CODEC_YUYV; |
152 | } |
153 | return 0; |
154 | } |
155 | |
156 | unsigned VideoDataOutput::formatCallback(char *chroma, |
157 | unsigned *width, unsigned *height, |
158 | unsigned *pitches, unsigned *lines) |
159 | { |
160 | DEBUG_BLOCK; |
161 | |
162 | m_frame.width = *width; |
163 | m_frame.height = *height; |
164 | |
165 | uint32_t fourcc = 0; |
166 | |
167 | QSet<VideoFrame2::Format> allowedFormats = m_frontend->allowedFormats(); |
168 | VideoFrame2::Format suggestedFormat = fourccToFormat(fourcc: chroma); |
169 | if (suggestedFormat != VideoFrame2::Format_Invalid |
170 | && allowedFormats.contains(value: suggestedFormat)) { // Use suggested |
171 | fourcc = setFormat(format: suggestedFormat, chroma: &chroma); |
172 | m_frame.format = suggestedFormat; |
173 | } else { // Pick first and use that |
174 | foreach (const VideoFrame2::Format &format, allowedFormats) { |
175 | fourcc = setFormat(format, chroma: &chroma); |
176 | if (fourcc > 0) { |
177 | m_frame.format = format; |
178 | break; |
179 | } |
180 | } |
181 | } |
182 | |
183 | Q_ASSERT(fourcc > 0); |
184 | |
185 | unsigned int bufferSize = setPitchAndLines(fourcc, width: *width, height: *height, pitches, lines); |
186 | |
187 | m_frame.data0.resize(size: pitches[0] * lines[0]); |
188 | m_frame.data1.resize(size: pitches[1] * lines[1]); |
189 | m_frame.data2.resize(size: pitches[2] * lines[0]); |
190 | |
191 | return bufferSize; |
192 | } |
193 | |
194 | void VideoDataOutput::formatCleanUpCallback() |
195 | { |
196 | DEBUG_BLOCK; |
197 | } |
198 | |
199 | } // namespace VLC |
200 | } // namespace Phonon |
201 | |