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 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 if (!Frame)
49 return nullptr;
50
51 if (Context && (Context != m_openglContext))
52 LOG(VB_GENERAL, LOG_WARNING, LOC + "Mismatched OpenGL contexts");
53
54 // Check size
55 QSize surfacesize(Frame->m_width, Frame->m_height);
56 if (m_textureSize != surfacesize)
57 {
58 if (!m_textureSize.isEmpty())
59 {
60 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Video texture size changed! %1x%2->%3x%4")
61 .arg(m_textureSize.width()).arg(m_textureSize.height())
62 .arg(Frame->m_width).arg(Frame->m_height));
63 }
65 m_textureSize = surfacesize;
66 }
67
68 // Update colourspace and initialise on first frame
69 if (ColourSpace)
70 {
71 if (m_openglTextures.isEmpty())
73 ColourSpace->UpdateColourSpace(Frame);
74 }
75
76 // Retrieve pixel buffer
77 // N.B. Buffer was retained in MythVTBContext and will be released in VideoBuffers
78 return reinterpret_cast<CVPixelBufferRef>(Frame->m_buffer);
79}
80
81std::vector<MythVideoTextureOpenGL*>
83 MythVideoColourSpace* ColourSpace,
85 FrameScanType /*Scan*/)
86{
87 std::vector<MythVideoTextureOpenGL*> result;
89
90 CVPixelBufferRef buffer = Verify(Context, ColourSpace, Frame);
91 if (!buffer)
92 return result;
93
94 // There are only ever one set of textures which are updated on each pass.
95 // Hence we cannot use reference frames without significant additional work here -
96 // but MythVTBSurfaceInterop will almost certainly always be used - so just drop back
97 // to Linearblend
98 Frame->m_deinterlaceAllowed = (Frame->m_deinterlaceAllowed & ~DEINT_HIGH) | DEINT_MEDIUM;
99
100 int planes = CVPixelBufferGetPlaneCount(buffer);
101 CVPixelBufferLockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
102
103 // FIXME? There is no P010 support here as I cannot test it - but SurfaceInterop
104 // should be used at all times - so this code is not going to be hit.
105 // Note - and the reason this code is retained is because it may at some point
106 // be useful for an iOS port (iOS has no surace interop)
107
108 // Create necessary textures
109 if (m_openglTextures.isEmpty())
110 {
111 for (int plane = 0; plane < planes; ++plane)
112 {
113 int width = CVPixelBufferGetWidthOfPlane(buffer, plane);
114 int height = CVPixelBufferGetHeightOfPlane(buffer, plane);
115 QSize size(width, height);
117 if (texture)
118 {
119 texture->m_frameType = FMT_VTB;
120 texture->m_frameFormat = FMT_NV12;
121 texture->m_plane = static_cast<uint>(plane);
122 texture->m_planeCount = static_cast<uint>(planes);
123 texture->m_allowGLSLDeint = true;
124 result.push_back(texture);
125 }
126 }
127
128 if (result.size() != static_cast<uint>(planes))
129 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create all textures");
130 m_openglTextures.insert(DUMMY_INTEROP_ID, result);
131 }
132
134 {
136 m_openglContext->glEnable(QOpenGLTexture::Target2D);
137
138 // Update textures
139 for (int plane = 0; plane < planes; ++plane)
140 {
141 if (static_cast<uint>(plane) >= result.size())
142 continue;
143
144 int width = CVPixelBufferGetWidthOfPlane(buffer, plane);
145 int height = CVPixelBufferGetHeightOfPlane(buffer, plane);
146 int bytes = CVPixelBufferGetBytesPerRowOfPlane(buffer, plane) / width;
147 void* buf = CVPixelBufferGetBaseAddressOfPlane(buffer, plane);
148 result[plane]->m_texture->bind();
149 if ((bytes == 1) && buf)
150 {
151 m_openglContext->glTexImage2D(QOpenGLTexture::Target2D, 0, QOpenGLTexture::RGBA, width, height,
152 0, QOpenGLTexture::Red, QOpenGLTexture::UInt8, buf);
153 }
154 else if ((bytes == 2) && buf)
155 {
156 m_openglContext->glTexImage2D(QOpenGLTexture::Target2D, 0, QOpenGLTexture::RGBA, width, height,
157 0, QOpenGLTexture::RG, QOpenGLTexture::UInt8, buf);
158 }
159 result[plane]->m_texture->release();
160 }
161 }
162 CVPixelBufferUnlockBaseAddress(buffer, kCVPixelBufferLock_ReadOnly);
163 return result;
164}
165
167 : MythVTBInterop(Player, Context, GL_VTBSURFACE)
168{
169}
170
171std::vector<MythVideoTextureOpenGL*>
173 MythVideoColourSpace* ColourSpace,
175 FrameScanType Scan)
176{
177 std::vector<MythVideoTextureOpenGL*> result;
179
180 CVPixelBufferRef buffer = Verify(Context, ColourSpace, Frame);
181 if (!buffer)
182 return result;
183
184 IOSurfaceRef surface = CVPixelBufferGetIOSurface(buffer);
185 if (!surface)
186 {
187 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve IOSurface from CV buffer");
188 return result;
189 }
190
191 IOSurfaceID surfaceid = IOSurfaceGetID(surface);
192 if (!surfaceid)
193 return result;
194
195 // check whether we need to return multiple frames for GLSL kernel deinterlacing
196 bool needreferences = false;
197 if (is_interlaced(Scan))
198 {
199 MythDeintType shader = Frame->GetDoubleRateOption(DEINT_SHADER);
200 if (shader)
201 needreferences = shader == DEINT_HIGH;
202 else
203 needreferences = Frame->GetSingleRateOption(DEINT_SHADER) == DEINT_HIGH;
204 }
205
206 if (needreferences)
207 {
208 if (qAbs(Frame->m_frameCounter - m_discontinuityCounter) > 1)
209 m_referenceFrames.clear();
210 RotateReferenceFrames(surfaceid);
211 }
212 else
213 {
214 m_referenceFrames.clear();
215 }
216 m_discontinuityCounter = Frame->m_frameCounter;
217
218 // return cached textures if available
219 if (m_openglTextures.contains(surfaceid))
220 {
221 if (needreferences)
222 return GetReferenceFrames();
223 return m_openglTextures[surfaceid];
224 }
225
226 // Create new and attach to IOSurface
227 int planes = IOSurfaceGetPlaneCount(surface);
228 IOSurfaceLock(surface, kIOSurfaceLockReadOnly, nullptr);
229
230 // Create raw rectangular textures
231 std::vector<QSize> sizes;
232 for (int plane = 0; plane < planes; ++plane)
233 {
234 int width = IOSurfaceGetWidthOfPlane(surface, plane);
235 int height = IOSurfaceGetHeightOfPlane(surface, plane);
236 sizes.emplace_back(width, height);
237 }
238 // NB P010 support is untested
239 // NB P010 support was added to FFmpeg in https://github.com/FFmpeg/FFmpeg/commit/036b4b0f85933f49a709
240 // which has not yet been merged into the MythTV FFmpeg version (as of 22/6/19)
241 VideoFrameType frameformat = MythAVUtil::PixelFormatToFrameType(static_cast<AVPixelFormat>(Frame->m_swPixFmt));
242 if ((frameformat != FMT_NV12) && (frameformat != FMT_P010))
243 {
244 IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, nullptr);
245 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Unsupported frame format %1")
246 .arg(MythVideoFrame::FormatDescription(frameformat)));
247 return result;
248 }
249
250 result = MythVideoTextureOpenGL::CreateTextures(m_openglContext, FMT_VTB, frameformat, sizes, QOpenGLTexture::TargetRectangle);
251
252 for (uint plane = 0; plane < result.size(); ++plane)
253 {
254 MythVideoTextureOpenGL* texture = result[plane];
255 if (!texture)
256 continue;
257 texture->m_allowGLSLDeint = true;
258 m_openglContext->glBindTexture(texture->m_target, texture->m_textureId);
259
260 GLenum format = (plane == 0) ? QOpenGLTexture::Red : QOpenGLTexture::RG;;
261 GLenum dataformat = (frameformat == FMT_NV12) ? QOpenGLTexture::UInt8 : QOpenGLTexture::UInt16;
262
263 CGLError error = CGLTexImageIOSurface2D(
264 CGLGetCurrentContext(), QOpenGLTexture::TargetRectangle, format,
265 texture->m_size.width(), texture->m_size.height(),
266 format, dataformat, surface, plane);
267 if (error != kCGLNoError)
268 LOG(VB_GENERAL, LOG_ERR, LOC + QString("CGLTexImageIOSurface2D error %1").arg(error));
269 m_openglContext->glBindTexture(QOpenGLTexture::TargetRectangle, 0);
270 }
271
272 IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, nullptr);
273 m_openglTextures.insert(surfaceid, result);
274
275 if (needreferences)
276 return GetReferenceFrames();
277 return result;
278}
279
281{
282 if (!Buffer)
283 return;
284
285 // don't retain twice for double rate
286 if (!m_referenceFrames.empty() && (m_referenceFrames[0] == Buffer))
287 return;
288
289 m_referenceFrames.push_front(Buffer);
290
291 // release old frames
292 while (m_referenceFrames.size() > 3)
293 m_referenceFrames.pop_back();
294}
295
296std::vector<MythVideoTextureOpenGL*> MythVTBSurfaceInterop::GetReferenceFrames(void)
297{
298 std::vector<MythVideoTextureOpenGL*> result;
299 int size = m_referenceFrames.size();
300 if (size < 1)
301 return result;
302
303 IOSurfaceID next = m_referenceFrames[0];
304 IOSurfaceID current = m_referenceFrames[size > 1 ? 1 : 0];
305 IOSurfaceID last = m_referenceFrames[size > 2 ? 2 : 0];
306
307 if (!m_openglTextures.contains(next) || !m_openglTextures.contains(current) ||
308 !m_openglTextures.contains(last))
309 {
310 LOG(VB_GENERAL, LOG_ERR, LOC + "Reference frame error");
311 return result;
312 }
313
314 result = m_openglTextures[last];
315 std::copy(m_openglTextures[current].cbegin(), m_openglTextures[current].cend(), std::back_inserter(result));
316 std::copy(m_openglTextures[next].cbegin(), m_openglTextures[next].cend(), std::back_inserter(result));
317 return result;
318}
static VideoFrameType PixelFormatToFrameType(AVPixelFormat Fmt)
Definition: mythavutil.cpp:75
bool IsEGL(void)
Definition: mythegl.cpp:32
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
MythVTBSurfaceInterop(MythPlayerUI *Player, MythRenderOpenGL *Context)
QVector< IOSurfaceID > m_referenceFrames
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:372
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: compat.h:60
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