1 /* ***** BEGIN LICENSE BLOCK *****
5 * Copyright (c) 2008 BBC Research
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * ***** END LICENSE BLOCK ***** */
31 #include "GLvideo_rt.h"
32 #include "GLvideo_mt.h"
33 #include "GLvideo_rt.h"
34 #include "GLvideo_tradtex.h"
35 #include "GLvideo_pbotex.h"
36 #include "GLvideo_osd.h"
39 #include "GLvideo_x11rep.h"
42 #include "videoData.h"
43 #include "videoTransport.h"
44 #include "frameQueue.h"
48 #include "GLvideo_params.h"
53 #include "agl_getproc.h"
58 GLvideo_rt::GLvideo_rt(GLvideo_mt
&gl
, GLvideo_params
& params
) :
59 QThread(), glw(gl
), params(params
)
61 renderer
[0] = new GLVideoRenderer::PboTex();
62 renderer
[1] = new GLVideoRenderer::PboTex();
63 //renderer = new GLVideoRenderer::PboTex();
66 osd
= new GLvideo_osd();
72 GLvideo_rt::~GLvideo_rt()
77 if(renderer
[0]) delete renderer
[0];
78 if(renderer
[1]) delete renderer
[1];
82 void GLvideo_rt::resizeViewport(int width
, int height
)
85 displayheight
= height
;
89 void GLvideo_rt::compileFragmentShaders()
91 compileFragmentShader(shaderPlanar
| Progressive
, shaderPlanarProSrc
);
92 compileFragmentShader(shaderPlanar
| Deinterlace
, shaderPlanarDeintSrc
);
93 compileFragmentShader(shaderUYVY
| Progressive
, shaderUYVYProSrc
);
94 compileFragmentShader(shaderUYVY
| Deinterlace
, shaderUYVYDeintSrc
);
97 void GLvideo_rt::compileFragmentShader(int n
, const char *src
)
99 GLint length
= 0; //log length
101 shaders
[n
] = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB
);
104 printf("Shader program is %s\n", src
);
106 glShaderSourceARB(shaders
[n
], 1, (const GLcharARB
**)&src
, NULL
);
107 glCompileShaderARB(shaders
[n
]);
109 glGetObjectParameterivARB(shaders
[n
], GL_OBJECT_COMPILE_STATUS_ARB
,
111 glGetObjectParameterivARB(shaders
[n
], GL_OBJECT_INFO_LOG_LENGTH_ARB
,
113 char *s
=(char *)malloc(length
);
114 glGetInfoLogARB(shaders
[n
], length
, &length
, s
);
116 printf("Compile Status %d, Log: (%d) %s\n", (int)compiled
[n
], (int)length
,
117 (int)length
? s
: NULL
);
118 if (compiled
[n
] <= 0) {
119 printf("Compile Failed: %s\n", s
);
124 /* Set up program objects. */
125 programs
[n
]=glCreateProgramObjectARB();
126 glAttachObjectARB(programs
[n
], shaders
[n
]);
127 glLinkProgramARB(programs
[n
]);
129 /* And print the link log. */
131 glGetObjectParameterivARB(programs
[n
], GL_OBJECT_LINK_STATUS_ARB
,
133 glGetObjectParameterivARB(shaders
[n
], GL_OBJECT_INFO_LOG_LENGTH_ARB
,
135 s
=(char *)malloc(length
);
136 glGetInfoLogARB(shaders
[n
], length
, &length
, s
);
138 printf("Link Status %d, Log: (%d) %s\n", (int)linked
[n
], (int)length
, s
);
144 void GLvideo_rt::updateShaderVars(int program
, VideoData
*videoData
,
145 GLvideo_params
¶ms
, float colour_matrix
[4][4])
148 printf("Updating fragment shader variables\n");
150 glUseProgramObjectARB(program
);
151 //data about the input file to the shader
153 i
= glGetUniformLocationARB(program
, "CHsubsample");
154 glUniform1fARB(i
, (float)(videoData
->Ywidth
/ videoData
->Cwidth
));
156 i
= glGetUniformLocationARB(program
, "CVsubsample");
157 glUniform1fARB(i
, (float)(videoData
->Yheight
/ videoData
->Cheight
));
159 i
= glGetUniformLocationARB(program
, "colour_matrix");
160 glUniformMatrix4fvARB(i
, 1, true, (float*) colour_matrix
);
162 glUseProgramObjectARB(0);
165 void addStatPerfFloat(const char *timer
, double d
)
167 Stats
&stat
= Stats::getInstance();
168 std::stringstream ss
;
171 stat
.addStat("OpenGL", timer
, ss
.str());
174 void addStatPerfInt(const char *timer
, int time
)
176 Stats
&stat
= Stats::getInstance();
177 std::stringstream ss
;
178 ss
<< time
<< " " << "ms";
180 stat
.addStat("OpenGL", timer
, ss
.str());
183 void GLvideo_rt::run()
186 printf("Starting renderthread\n");
188 VideoData
*videoData
= NULL
;
191 QTime fpsIntervalTime
; //measures FPS, averaged over several frames
192 QTime frameIntervalTime
; //measured the total period of each frame
193 QTime perfTimer
; //performance timer for measuring indivdual processes during rendering
197 int lastsrcwidth
= 0;
198 int lastsrcheight
= 0;
199 bool lastisplanar
= false;
200 unsigned long lastframenum
= 1;
202 //declare shadow variables for the thread worker and initialise them
207 int currentShader
= 0;
208 float colour_matrix
[4][4];
212 const QGLContext
* context
= glw
.context();
214 while (!context
|| !context
->isValid()) {
217 context
= glw
.context();
220 GLVideoRenderer::GLVideoRenderer
*rendererA
= NULL
;
221 GLVideoRenderer::GLVideoRenderer
*rendererB
= NULL
;
224 GLenum err
= glewInit();
226 qWarning() << "failed to init glew\n";
228 if(!GLEW_ARB_shader_objects
)
229 qWarning() << "opengl implementation does not support shader objects\n";
231 if(!GLEW_ARB_pixel_buffer_object
)
232 qWarning() << "opengl implementation does not support pixel buffer objects\n";
234 if(!GLEW_EXT_framebuffer_object
)
235 qWarning() << "opengl implementation does not support framebuffer objects\n";
238 wglSwapIntervalEXT(1);
241 my_aglSwapInterval(1);
244 glMatrixMode(GL_PROJECTION
);
246 glViewport(0, 0, displayheight
, displaywidth
);
247 glClearColor(0, 0, 0, 0);
249 compileFragmentShaders();
251 while (doRendering
) {
253 bool createGLTextures
= false;
257 //read frame data, one frame at a time, or after we have displayed the second field
259 if ((params
.interlaced_source
== 0 || field
== 0) && repeat
== 0) {
261 videoData
= glw
.vt
->getNextFrame();
262 direction
= glw
.vt
->getDirection();
264 addStatPerfInt("GetFrame", perfTimer
.elapsed());
267 printf("videoData=%p\n", videoData
);
271 //perform any format conversions
273 switch (videoData
->renderFormat
) {
275 case VideoData::V210
:
276 //check for v210 data - it's very difficult to write a shader for this
277 //due to the lack of GLSL bitwise operators, and it's insistance on filtering the textures
279 printf("Converting V210 frame\n");
280 videoData
->convertV210();
283 case VideoData::V16P4
:
284 case VideoData::V16P2
:
285 case VideoData::V16P0
:
286 //convert 16 bit data to 8bit
287 //this avoids endian-ness issues between the host and the GPU
288 //and reduces the bandwidth to the GPU
290 printf("Converting 16 bit frame\n");
291 videoData
->convertPlanar16();
295 //no conversion needed
298 addStatPerfInt("ReadData", perfTimer
.elapsed());
300 //check for video dimensions changing
301 if ((lastsrcwidth
!= videoData
->Ywidth
)
302 || (lastsrcheight
!= videoData
->Yheight
)
303 || (lastisplanar
!= videoData
->isPlanar
)) {
305 printf("Changing video dimensions to %dx%d\n",
306 videoData
->Ywidth
, videoData
->Yheight
);
307 glOrtho(0, videoData
->Ywidth
, 0, videoData
->Yheight
, -1, 1);
310 createGLTextures
= true;
312 lastsrcwidth
= videoData
->Ywidth
;
313 lastsrcheight
= videoData
->Yheight
;
314 lastisplanar
= videoData
->isPlanar
;
317 if (videoData
->isPlanar
)
318 currentShader
= shaderPlanar
;
320 currentShader
= shaderUYVY
;
322 if (params
.deinterlace
)
323 currentShader
|= Deinterlace
;
325 currentShader
&= ~Deinterlace
;
327 if (createGLTextures
) {
329 printf("Creating GL textures\n");
330 renderer
[0]->createTextures(videoData
);
331 renderer
[1]->createTextures(videoData
);
334 if (params
.matrix_valid
== false) {
335 buildColourMatrix(colour_matrix
, params
);
336 params
.matrix_valid
= true;
339 //update the uniform variables in the fragment shader
341 updateShaderVars(programs
[currentShader
], videoData
, params
, colour_matrix
);
342 addStatPerfInt("UpdateVars", perfTimer
.elapsed());
344 //if the size of the window has changed (doResize)
345 //or the shape of the video in the window has changed
346 if (doResize
|| (params
.view_valid
== false)) {
347 //resize the viewport, once we have some video
348 //if(DEBUG) printf("Resizing to %d, %d\n", displaywidth, displayheight);
350 float sourceAspect
= (float)videoData
->Ywidth
351 / (float)videoData
->Yheight
;
352 float displayAspect
= (float)displaywidth
353 / (float)displayheight
;
355 if (sourceAspect
== displayAspect
|| !params
.aspect_ratio_lock
) {
356 glViewport(0, 0, displaywidth
, displayheight
);
359 if (displayAspect
> sourceAspect
) {
360 //window is wider than image should be
361 int width
= (int)(displayheight
*sourceAspect
);
362 int offset
= (displaywidth
- width
) / 2;
363 glViewport(offset
, 0, width
, displayheight
);
366 int height
= (int)(displaywidth
/sourceAspect
);
367 int offset
= (displayheight
- height
) / 2;
368 glViewport(0, offset
, displaywidth
, height
);
372 params
.view_valid
= true;
374 glClear(GL_COLOR_BUFFER_BIT
);
377 if (params
.deinterlace
) {
378 glUseProgramObjectARB(programs
[currentShader
]);
379 int i
= glGetUniformLocationARB(programs
[currentShader
],
382 /* swap the field order when playing interlaced pictures backwards */
384 glUniform1fARB(i
, (float)field
);
386 glUniform1fARB(i
, 1.0 - (float)field
);
388 glUseProgramObjectARB(0);
393 rendererB
->renderVideo(videoData
, programs
[currentShader
]);
394 addStatPerfInt("RenderVid", perfTimer
.elapsed());
396 //upload the texture data for each new frame, or for before we render the first field
397 //of interlaced material
399 if ((params
.interlaced_source
== 0 || field
== 0) && repeat
== 0) {
401 //only upload new frames if they are different
402 if(lastframenum
!= videoData
->frameNum
) {
405 rendererA
->uploadTextures(videoData
);
407 //rotate the renderers for upload and display
408 rendererB
= renderer
[render_idx
];
409 render_idx
= (render_idx
) ? 0 : 1;
410 rendererA
= renderer
[render_idx
];
412 lastframenum
= videoData
->frameNum
;
415 addStatPerfInt("Upload", perfTimer
.elapsed());
419 if(osd
) osd
->render(videoData
, params
);
420 addStatPerfInt("RenderOSD", perfTimer
.elapsed());
428 addStatPerfInt("SwapBuffers", perfTimer
.elapsed());
430 //move to the next field when the first has been repeated the required number of times
431 if (params
.interlaced_source
&& (repeat
== params
.frame_repeats
- 1)) {
432 //move to the next field
438 //repeat the frame the required number of times
439 if (repeat
< params
.frame_repeats
&& field
== 0) {
441 if (repeat
== params
.frame_repeats
) {
447 //measure overall frame period
448 addStatPerfInt("Interval", frameIntervalTime
.elapsed());
449 frameIntervalTime
.restart();
452 if (fpsAvgPeriod
== 0) {
453 int fintvl
= fpsIntervalTime
.elapsed();
455 addStatPerfFloat("RenderRate", 10*1e3
/fintvl
);
456 fpsIntervalTime
.start();
458 fpsAvgPeriod
= (fpsAvgPeriod
+ 1) %10;
462 const GLubyte
* errStr
;
463 if ((error
= glGetError()) != GL_NO_ERROR
) {
464 errStr
= gluErrorString(error
);
465 fprintf(stderr
, "OpenGL Error: %s\n", errStr
);