[mmap] partial revert of 8cef8db4 to disable using mmap file reader
[videoplayer.git] / GLvideo_rt.cpp
blobfd84837250c14b37113eaf01c0b0817eb811643d
1 /* ***** BEGIN LICENSE BLOCK *****
3 * The MIT License
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
23 * THE SOFTWARE.
25 * ***** END LICENSE BLOCK ***** */
27 #include <stdio.h>
28 #include <sstream>
30 #include <QtGui>
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"
38 #ifdef Q_WS_X11
39 #include "GLvideo_x11rep.h"
40 #endif
42 #include "videoData.h"
43 #include "videoTransport.h"
44 #include "frameQueue.h"
45 #include "stats.h"
46 #include "util.h"
48 #include "GLvideo_params.h"
50 #include "shaders.h"
52 #ifdef Q_OS_MACX
53 #include "agl_getproc.h"
54 #endif
56 #define DEBUG 0
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();
65 #ifdef WITH_OSD
66 osd = new GLvideo_osd();
67 #else
68 osd = NULL;
69 #endif
72 GLvideo_rt::~GLvideo_rt()
74 doRendering = false;
75 wait();
77 if(renderer[0]) delete renderer[0];
78 if(renderer[1]) delete renderer[1];
79 if(osd) delete osd;
82 void GLvideo_rt::resizeViewport(int width, int height)
84 displaywidth = width;
85 displayheight = height;
86 doResize = true;
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);
103 if (DEBUG)
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,
110 &compiled[n]);
111 glGetObjectParameterivARB(shaders[n], GL_OBJECT_INFO_LOG_LENGTH_ARB,
112 &length);
113 char *s=(char *)malloc(length);
114 glGetInfoLogARB(shaders[n], length, &length, s);
115 if (DEBUG)
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);
120 throw /* */;
122 free(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. */
130 if (compiled[n]) {
131 glGetObjectParameterivARB(programs[n], GL_OBJECT_LINK_STATUS_ARB,
132 &linked[n]);
133 glGetObjectParameterivARB(shaders[n], GL_OBJECT_INFO_LOG_LENGTH_ARB,
134 &length);
135 s=(char *)malloc(length);
136 glGetInfoLogARB(shaders[n], length, &length, s);
137 if (DEBUG)
138 printf("Link Status %d, Log: (%d) %s\n", (int)linked[n], (int)length, s);
139 free(s);
144 void GLvideo_rt::updateShaderVars(int program, VideoData *videoData,
145 GLvideo_params &params, float colour_matrix[4][4])
147 if (DEBUG)
148 printf("Updating fragment shader variables\n");
150 glUseProgramObjectARB(program);
151 //data about the input file to the shader
152 int i;
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;
169 ss << d;
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()
185 if (DEBUG)
186 printf("Starting renderthread\n");
188 VideoData *videoData = NULL;
190 //monitoring
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
194 int fpsAvgPeriod=1;
196 //flags and status
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
203 doRendering = true;
204 int repeat = 0;
205 int field = 0;
206 int direction = 0;
207 int currentShader = 0;
208 float colour_matrix[4][4];
210 //initialise OpenGL
211 glw.makeCurrent();
212 const QGLContext* context = glw.context();
214 while (!context || !context->isValid()) {
215 msleep(50);
216 glw.makeCurrent();
217 context = glw.context();
220 GLVideoRenderer::GLVideoRenderer *rendererA = NULL;
221 GLVideoRenderer::GLVideoRenderer *rendererB = NULL;
222 int render_idx = 0;
224 GLenum err = glewInit();
225 if (GLEW_OK != err)
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";
237 #ifdef Q_OS_WIN32
238 wglSwapIntervalEXT(1);
239 #endif
240 #ifdef Q_OS_MAC
241 my_aglSwapInterval(1);
242 #endif
244 glMatrixMode(GL_PROJECTION);
245 glLoadIdentity();
246 glViewport(0, 0, displayheight, displaywidth);
247 glClearColor(0, 0, 0, 0);
249 compileFragmentShaders();
251 while (doRendering) {
253 bool createGLTextures = false;
255 perfTimer.start();
257 //read frame data, one frame at a time, or after we have displayed the second field
258 perfTimer.restart();
259 if ((params.interlaced_source == 0 || field == 0) && repeat == 0) {
260 //get the new frame
261 videoData = glw.vt->getNextFrame();
262 direction = glw.vt->getDirection();
264 addStatPerfInt("GetFrame", perfTimer.elapsed());
266 if (DEBUG)
267 printf("videoData=%p\n", videoData);
269 if (videoData) {
271 //perform any format conversions
272 perfTimer.restart();
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
278 if (DEBUG)
279 printf("Converting V210 frame\n");
280 videoData->convertV210();
281 break;
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
289 if (DEBUG)
290 printf("Converting 16 bit frame\n");
291 videoData->convertPlanar16();
292 break;
294 default:
295 //no conversion needed
296 break;
298 addStatPerfInt("ReadData", perfTimer.elapsed());
300 //check for video dimensions changing
301 if ((lastsrcwidth != videoData->Ywidth)
302 || (lastsrcheight != videoData->Yheight)
303 || (lastisplanar != videoData->isPlanar)) {
304 if (DEBUG)
305 printf("Changing video dimensions to %dx%d\n",
306 videoData->Ywidth, videoData->Yheight);
307 glOrtho(0, videoData->Ywidth, 0, videoData->Yheight, -1, 1);
309 doResize = true;
310 createGLTextures = true;
312 lastsrcwidth = videoData->Ywidth;
313 lastsrcheight = videoData->Yheight;
314 lastisplanar = videoData->isPlanar;
317 if (videoData->isPlanar)
318 currentShader = shaderPlanar;
319 else
320 currentShader = shaderUYVY;
322 if (params.deinterlace)
323 currentShader |= Deinterlace;
324 else
325 currentShader &= ~Deinterlace;
327 if (createGLTextures) {
328 if (DEBUG)
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
340 perfTimer.restart();
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);
358 else {
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);
365 else {
366 int height = (int)(displaywidth/sourceAspect);
367 int offset = (displayheight - height) / 2;
368 glViewport(0, offset, displaywidth, height);
371 doResize = false;
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],
380 "field");
382 /* swap the field order when playing interlaced pictures backwards */
383 if (direction >= 0)
384 glUniform1fARB(i, (float)field);
385 else
386 glUniform1fARB(i, 1.0 - (float)field);
388 glUseProgramObjectARB(0);
391 perfTimer.restart();
392 if (rendererB)
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
398 perfTimer.restart();
399 if ((params.interlaced_source == 0 || field == 0) && repeat == 0) {
401 //only upload new frames if they are different
402 if(lastframenum != videoData->frameNum) {
404 if(rendererA)
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());
417 #ifdef WITH_OSD
418 perfTimer.restart();
419 if(osd) osd->render(videoData, params);
420 addStatPerfInt("RenderOSD", perfTimer.elapsed());
421 #endif
426 perfTimer.restart();
427 glw.swapBuffers();
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
433 field++;
434 if (field > 1)
435 field = 0;
438 //repeat the frame the required number of times
439 if (repeat < params.frame_repeats && field == 0) {
440 repeat++;
441 if (repeat == params.frame_repeats) {
442 repeat = 0;
446 if (repeat == 0) {
447 //measure overall frame period
448 addStatPerfInt("Interval", frameIntervalTime.elapsed());
449 frameIntervalTime.restart();
451 //calculate FPS
452 if (fpsAvgPeriod == 0) {
453 int fintvl = fpsIntervalTime.elapsed();
454 if (fintvl)
455 addStatPerfFloat("RenderRate", 10*1e3/fintvl);
456 fpsIntervalTime.start();
458 fpsAvgPeriod = (fpsAvgPeriod + 1) %10;
461 GLenum error;
462 const GLubyte* errStr;
463 if ((error = glGetError()) != GL_NO_ERROR) {
464 errStr = gluErrorString(error);
465 fprintf(stderr, "OpenGL Error: %s\n", errStr);