MythTV master
mythvtbinterop.cpp
Go to the documentation of this file.
1// MythTV
6
7#define LOC QString("VTBInterop: ")
8
10{
11 if (Render->isOpenGLES() || Render->IsEGL())
12 return;
14 if (Render->hasExtension("GL_ARB_texture_rg"))
15 types.emplace_back(GL_VTBSURFACE);
16 types.emplace_back(GL_VTB);
17 Types[FMT_VTB] = types;
18}
19
21{
22 if (!(Context && Player))
23 return nullptr;
24
26 GetVTBTypes(Context, types);
27 if (auto vtb = types.find(FMT_VTB); vtb != types.end())
28 {
29 for (auto type : vtb->second)
30 {
31 if (type == GL_VTBSURFACE)
32 return new MythVTBSurfaceInterop(Player, Context);
33 else if (type == GL_VTB)
34 return new MythVTBInterop(Player, Context, GL_VTB);
35 }
36 }
37 return nullptr;
38}
39
42 : MythOpenGLInterop(Context, Type, Player)
43{
44}
45
47{
48}
49
51{
52 if (!Frame)
53 return nullptr;
54
55 if (Context && (Context != m_openglContext))
56 LOG(VB_GENERAL, LOG_WARNING, LOC + "Mismatched OpenGL contexts");
57
58 // Check size
59 QSize surfacesize(Frame->m_width, Frame->m_height);
60 if (m_textureSize != surfacesize)
61 {
62 if (!m_textureSize.isEmpty())
63 {
64 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Video texture size changed! %1x%2->%3x%4")
65 .arg(m_textureSize.width()).arg(m_textureSize.height())
66 .arg(Frame->m_width).arg(Frame->m_height));
67 }
69 m_textureSize = surfacesize;
70 }
71
72 // Update colourspace and initialise on first frame
73 if (ColourSpace)
74 {
75 if (m_openglTextures.isEmpty())
77 ColourSpace->UpdateColourSpace(Frame);
78 }
79
80 // Retrieve pixel buffer
81 // N.B. Buffer was retained in MythVTBContext and will be released in VideoBuffers
82 return reinterpret_cast<CVPixelBufferRef>(Frame->m_buffer);
83}
84
85std::vector<MythVideoTextureOpenGL*>
87 MythVideoColourSpace* ColourSpace,
90{
91 std::vector<MythVideoTextureOpenGL*> result;
93
94 CVPixelBufferRef buffer = Verify(Context, ColourSpace, Frame);
95 if (!buffer)
96 return result;
97
98 // There are only ever one set of textures which are updated on each pass.
99 // Hence we cannot use reference frames without significant additional work here -
100 // but MythVTBSurfaceInterop will almost certainly always be used - so just drop back
101 // to Linearblend
102 Frame->m_deinterlaceAllowed = (Frame->m_deinterlaceAllowed & ~DEINT_HIGH) | DEINT_MEDIUM;
103
104 int planes = CVPixelBufferGetPlaneCount(buffer);
105 CVPixelBufferLockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
106
107 // FIXME? There is no P010 support here as I cannot test it - but SurfaceInterop
108 // should be used at all times - so this code is not going to be hit.
109 // Note - and the reason this code is retained is because it may at some point
110 // be useful for an iOS port (iOS has no surace interop)
111
112 // Create necessary textures
113 if (m_openglTextures.isEmpty())
114 {
115 for (int plane = 0; plane < planes; ++plane)
116 {
117 int width = CVPixelBufferGetWidthOfPlane(buffer, plane);
118 int height = CVPixelBufferGetHeightOfPlane(buffer, plane);
119 QSize size(width, height);
121 if (texture)
122 {
123 texture->m_frameType = FMT_VTB;
124 texture->m_frameFormat = FMT_NV12;
125 texture->m_plane = static_cast<uint>(plane);
126 texture->m_planeCount = static_cast<uint>(planes);
127 texture->m_allowGLSLDeint = true;
128 result.push_back(texture);
129 }
130 }
131
132 if (result.size() != static_cast<uint>(planes))
133 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create all textures");
134 m_openglTextures.insert(DUMMY_INTEROP_ID, result);
135 }
136
138 {
140 m_openglContext->glEnable(QOpenGLTexture::Target2D);
141
142 // Update textures
143 for (int plane = 0; plane < planes; ++plane)
144 {
145 if (static_cast<uint>(plane) >= result.size())
146 continue;
147
148 int width = CVPixelBufferGetWidthOfPlane(buffer, plane);
149 int height = CVPixelBufferGetHeightOfPlane(buffer, plane);
150 int bytes = CVPixelBufferGetBytesPerRowOfPlane(buffer, plane) / width;
151 void* buf = CVPixelBufferGetBaseAddressOfPlane(buffer, plane);
152 result[plane]->m_texture->bind();
153 if ((bytes == 1) && buf)
154 {
155 m_openglContext->glTexImage2D(QOpenGLTexture::Target2D, 0, QOpenGLTexture::RGBA, width, height,
156 0, QOpenGLTexture::Red, QOpenGLTexture::UInt8, buf);
157 }
158 else if ((bytes == 2) && buf)
159 {
160 m_openglContext->glTexImage2D(QOpenGLTexture::Target2D, 0, QOpenGLTexture::RGBA, width, height,
161 0, QOpenGLTexture::RG, QOpenGLTexture::UInt8, buf);
162 }
163 result[plane]->m_texture->release();
164 }
165 }
166 CVPixelBufferUnlockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
167 return result;
168}
169
171 : MythVTBInterop(Player, Context, GL_VTBSURFACE)
172{
173}
174
176{
177}
178
179std::vector<MythVideoTextureOpenGL*>
181 MythVideoColourSpace* ColourSpace,
183 FrameScanType Scan)
184{
185 std::vector<MythVideoTextureOpenGL*> result;
187
188 CVPixelBufferRef buffer = Verify(Context, ColourSpace, Frame);
189 if (!buffer)
190 return result;
191
192 IOSurfaceRef surface = CVPixelBufferGetIOSurface(buffer);
193 if (!surface)
194 {
195 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve IOSurface from CV buffer");
196 return result;
197 }
198
199 IOSurfaceID surfaceid = IOSurfaceGetID(surface);
200 if (!surfaceid)
201 return result;
202
203 // check whether we need to return multiple frames for GLSL kernel deinterlacing
204 bool needreferences = false;
205 if (is_interlaced(Scan))
206 {
207 MythDeintType shader = Frame->GetDoubleRateOption(DEINT_SHADER);
208 if (shader)
209 needreferences = shader == DEINT_HIGH;
210 else
211 needreferences = Frame->GetSingleRateOption(DEINT_SHADER) == DEINT_HIGH;
212 }
213
214 if (needreferences)
215 {
216 if (qAbs(Frame->m_frameCounter - m_discontinuityCounter) > 1)
217 m_referenceFrames.clear();
218 RotateReferenceFrames(surfaceid);
219 }
220 else
221 {
222 m_referenceFrames.clear();
223 }
224 m_discontinuityCounter = Frame->m_frameCounter;
225
226 // return cached textures if available
227 if (m_openglTextures.contains(surfaceid))
228 {
229 if (needreferences)
230 return GetReferenceFrames();
231 return m_openglTextures[surfaceid];
232 }
233
234 // Create new and attach to IOSurface
235 int planes = IOSurfaceGetPlaneCount(surface);
236 IOSurfaceLock(surface, kIOSurfaceLockReadOnly, nullptr);
237
238 // Create raw rectangular textures
239 std::vector<QSize> sizes;
240 for (int plane = 0; plane < planes; ++plane)
241 {
242 int width = IOSurfaceGetWidthOfPlane(surface, plane);
243 int height = IOSurfaceGetHeightOfPlane(surface, plane);
244 sizes.push_back(QSize(width, height));
245 }
246 // NB P010 support is untested
247 // NB P010 support was added to FFmpeg in https://github.com/FFmpeg/FFmpeg/commit/036b4b0f85933f49a709
248 // which has not yet been merged into the MythTV FFmpeg version (as of 22/6/19)
249 VideoFrameType frameformat = MythAVUtil::PixelFormatToFrameType(static_cast<AVPixelFormat>(Frame->m_swPixFmt));
250 if ((frameformat != FMT_NV12) && (frameformat != FMT_P010))
251 {
252 IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, nullptr);
253 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported frame format %1")
254 .arg(MythVideoFrame::FormatDescription(frameformat)));
255 return result;
256 }
257
258 result = MythVideoTextureOpenGL::CreateTextures(m_openglContext, FMT_VTB, frameformat, sizes, QOpenGLTexture::TargetRectangle);
259
260 for (uint plane = 0; plane < result.size(); ++plane)
261 {
262 MythVideoTextureOpenGL* texture = result[plane];
263 if (!texture)
264 continue;
265 texture->m_allowGLSLDeint = true;
266 m_openglContext->glBindTexture(texture->m_target, texture->m_textureId);
267
268 GLenum format = (plane == 0) ? QOpenGLTexture::Red : QOpenGLTexture::RG;;
269 GLenum dataformat = (frameformat == FMT_NV12) ? QOpenGLTexture::UInt8 : QOpenGLTexture::UInt16;
270
271 CGLError error = CGLTexImageIOSurface2D(
272 CGLGetCurrentContext(), QOpenGLTexture::TargetRectangle, format,
273 texture->m_size.width(), texture->m_size.height(),
274 format, dataformat, surface, plane);
275 if (error != kCGLNoError)
276 LOG(VB_GENERAL, LOG_ERR, LOC + QString("CGLTexImageIOSurface2D error %1").arg(error));
277 m_openglContext->glBindTexture(QOpenGLTexture::TargetRectangle, 0);
278 }
279
280 IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, nullptr);
281 m_openglTextures.insert(surfaceid, result);
282
283 if (needreferences)
284 return GetReferenceFrames();
285 return result;
286}
287
289{
290 if (!Buffer)
291 return;
292
293 // don't retain twice for double rate
294 if ((m_referenceFrames.size() > 0) && (m_referenceFrames[0] == Buffer))
295 return;
296
297 m_referenceFrames.push_front(Buffer);
298
299 // release old frames
300 while (m_referenceFrames.size() > 3)
301 m_referenceFrames.pop_back();
302}
303
304std::vector<MythVideoTextureOpenGL*> MythVTBSurfaceInterop::GetReferenceFrames(void)
305{
306 std::vector<MythVideoTextureOpenGL*> result;
307 int size = m_referenceFrames.size();
308 if (size < 1)
309 return result;
310
311 IOSurfaceID next = m_referenceFrames[0];
312 IOSurfaceID current = m_referenceFrames[size > 1 ? 1 : 0];
313 IOSurfaceID last = m_referenceFrames[size > 2 ? 2 : 0];
314
315 if (!m_openglTextures.contains(next) || !m_openglTextures.contains(current) ||
316 !m_openglTextures.contains(last))
317 {
318 LOG(VB_GENERAL, LOG_ERR, LOC + "Reference frame error");
319 return result;
320 }
321
322 result = m_openglTextures[last];
323 std::copy(m_openglTextures[current].cbegin(), m_openglTextures[current].cend(), std::back_inserter(result));
324 std::copy(m_openglTextures[next].cbegin(), m_openglTextures[next].cend(), std::back_inserter(result));
325 return result;
326}
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:72
bool IsEGL(void)
Definition: mythegl.cpp:31
uint64_t m_discontinuityCounter
std::vector< InteropType > InteropTypes
std::map< VideoFrameType, InteropTypes > InteropMap
virtual void DeleteTextures()
MythRenderOpenGL * m_openglContext
QHash< unsigned long long, std::vector< MythVideoTextureOpenGL * > > m_openglTextures
CVPixelBufferRef Verify(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame)
static MythVTBInterop * CreateVTB(MythPlayerUI *Player, MythRenderOpenGL *Context)
static void GetVTBTypes(MythRenderOpenGL *Render, MythInteropGPU::InteropMap &Types)
MythVTBInterop(MythPlayerUI *Player, MythRenderOpenGL *Context, MythOpenGLInterop::InteropType Type)
std::vector< MythVideoTextureOpenGL * > Acquire(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan) override
~MythVTBInterop() override
MythVTBSurfaceInterop(MythPlayerUI *Player, MythRenderOpenGL *Context)
QVector< IOSurfaceID > m_referenceFrames
~MythVTBSurfaceInterop() override
std::vector< MythVideoTextureOpenGL * > GetReferenceFrames(void)
std::vector< MythVideoTextureOpenGL * > Acquire(MythRenderOpenGL *Context, MythVideoColourSpace *ColourSpace, MythVideoFrame *Frame, FrameScanType Scan) override
void RotateReferenceFrames(IOSurfaceID Buffer)
MythVideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
void SetSupportedAttributes(PictureAttributeSupported Supported)
Enable the given set of picture attributes.
bool UpdateColourSpace(const MythVideoFrame *Frame)
Set the current colourspace to use.
static QString FormatDescription(VideoFrameType Type)
Definition: mythframe.cpp:368
static std::vector< MythVideoTextureOpenGL * > CreateTextures(MythRenderOpenGL *Context, VideoFrameType Type, VideoFrameType Format, std::vector< QSize > Sizes, GLenum Target=QOpenGLTexture::Target2D)
Create a set of textures suitable for the given Type and Format.
static MythVideoTextureOpenGL * CreateTexture(MythRenderOpenGL *Context, QSize Size, GLenum Target=QOpenGLTexture::Target2D, QOpenGLTexture::PixelType PixelType=QOpenGLTexture::UInt8, QOpenGLTexture::PixelFormat PixelFormat=QOpenGLTexture::RGBA, QOpenGLTexture::TextureFormat Format=QOpenGLTexture::NoFormat, QOpenGLTexture::Filter Filter=QOpenGLTexture::Linear, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
Create and initialise a MythVideoTexture that is backed by a QOpenGLTexture.
unsigned int uint
Definition: freesurround.h:24
static const struct wl_interface * types[]
MythDeintType
Definition: mythframe.h:67
@ DEINT_HIGH
Definition: mythframe.h:71
@ DEINT_MEDIUM
Definition: mythframe.h:70
@ DEINT_SHADER
Definition: mythframe.h:73
VideoFrameType
Definition: mythframe.h:20
@ FMT_VTB
Definition: mythframe.h:61
@ FMT_P010
Definition: mythframe.h:53
@ FMT_NV12
Definition: mythframe.h:52
static constexpr uint64_t DUMMY_INTEROP_ID
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
def error(message)
Definition: smolt.py:409
FrameScanType
Definition: videoouttypes.h:95
bool is_interlaced(FrameScanType Scan)
#define ALL_PICTURE_ATTRIBUTES