diff --git a/generic-pc/glfb.cpp b/generic-pc/glfb.cpp index 93c48d1..1560ff3 100644 --- a/generic-pc/glfb.cpp +++ b/generic-pc/glfb.cpp @@ -19,9 +19,8 @@ based on Carjay's neutrino-hd-dvbapi work, see http://gitorious.org/neutrino-hd/neutrino-hd-dvbapi - TODO: AV-Sync code is not existent yet + TODO: AV-Sync code is "experimental" at best cleanup carjay's crazy 3D stuff :-) - video mode setting (4:3, 16:9, panscan...) */ #include @@ -60,9 +59,19 @@ GLFramebuffer::GLFramebuffer(int x, int y): mReInit(true), mShutDown(false), mIn { mState.width = x; mState.height = y; - mX = y * 16 / 9; /* hard coded 16:9 initial aspect ratio for now */ - mY = y; - mVA = 1.0; + mX = &_mX[0]; + mY = &_mY[0]; + *mX = x; + *mY = y; + av_reduce(&mOA.num, &mOA.den, x, y, INT_MAX); + mVA = mOA; /* initial aspect ratios are from the FB resolution, those */ + _mVA = mVA; /* will be updated by the videoDecoder functions anyway */ + mVAchanged = true; + mCrop = DISPLAY_AR_MODE_PANSCAN; + zoom = 1.0; + xscale = 1.0; + const char *tmp = getenv("GLFB_FULLSCREEN"); + mFullscreen = !!(tmp); mState.blit = true; last_apts = 0; @@ -118,6 +127,7 @@ void GLFramebuffer::initKeys() mKeyMap[0x0d] = KEY_OK; mKeyMap[0x1b] = KEY_EXIT; + mKeyMap['e'] = KEY_EPG; mKeyMap['i'] = KEY_INFO; mKeyMap['m'] = KEY_MENU; @@ -162,6 +172,7 @@ void GLFramebuffer::run() glutDisplayFunc(GLFramebuffer::rendercb); glutKeyboardFunc(GLFramebuffer::keyboardcb); glutSpecialFunc(GLFramebuffer::specialcb); + glutReshapeFunc(GLFramebuffer::resizecb); setupGLObjects(); /* needs GLEW prototypes */ glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); glutMainLoop(); @@ -181,7 +192,7 @@ void GLFramebuffer::setupCtx() char const *argv[2] = { "neutrino", 0 }; lt_info("GLFB: GL thread starting\n"); glutInit(&argc, const_cast(argv)); - glutInitWindowSize(mX, mY); + glutInitWindowSize(mX[0], mY[0]); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutCreateWindow("Neutrino"); } @@ -247,6 +258,13 @@ void GLFramebuffer::releaseGLObjects() { lt_debug_c("GLFB::%s: 0x%x\n", __func__, key); struct input_event ev; + if (key == 'f') + { + lt_info_c("GLFB::%s: toggle fullscreen\n", __func__); + gThiz->mFullscreen = !(gThiz->mFullscreen); + gThiz->mReInit = true; + return; + } std::map::const_iterator i = gThiz->mKeyMap.find(key); if (i == gThiz->mKeyMap.end()) return; @@ -281,21 +299,39 @@ int sleep_us = 30000; void GLFramebuffer::render() { - if (!mReInit) /* for example if window is resized */ - checkReinit(); - if(mShutDown) glutLeaveMainLoop(); + mReInitLock.lock(); if (mReInit) { + int xoff = 0; + int yoff = 0; + mVAchanged = true; mReInit = false; - glViewport(0, 0, mX, mY); - glutReshapeWindow(mX, mY); + mX = &_mX[mFullscreen]; + mY = &_mY[mFullscreen]; + if (mFullscreen) { + int x = glutGet(GLUT_SCREEN_WIDTH); + int y = glutGet(GLUT_SCREEN_HEIGHT); + *mX = x; + *mY = y; + AVRational a = { x, y }; + if (av_cmp_q(a, mOA) < 0) + *mY = x * mOA.den / mOA.num; + else if (av_cmp_q(a, mOA) > 0) + *mX = y * mOA.num / mOA.den; + xoff = (x - *mX) / 2; + yoff = (y - *mY) / 2; + } else + *mX = *mY * mOA.num / mOA.den; + lt_info("%s: reinit mX:%d mY:%d xoff:%d yoff:%d fs %d\n", + __func__, *mX, *mY, xoff, yoff, mFullscreen); + glViewport(xoff, yoff, *mX, *mY); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - float aspect = static_cast(mX)/mY; - float osdaspect = 1.0/(static_cast(16.0)/9); + float aspect = static_cast(*mX)/ *mY; + float osdaspect = static_cast(mOA.den) / mOA.num; // if(!mState.go3d) { glOrtho(aspect*-osdaspect, aspect*osdaspect, -1.0, 1.0, -1.0, 1.0 ); @@ -316,6 +352,11 @@ void GLFramebuffer::render() glDisable(GL_DEPTH_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } + mReInitLock.unlock(); + if (mFullscreen) + glutFullScreen(); + else if (*mX != glutGet(GLUT_WINDOW_WIDTH) || *mY != glutGet(GLUT_WINDOW_HEIGHT)) + glutReshapeWindow(*mX, *mY); bltDisplayBuffer(); /* decoded video stream */ if (mState.blit) { @@ -345,12 +386,60 @@ void GLFramebuffer::render() } else #endif + if (mVAchanged) { - glBindTexture(GL_TEXTURE_2D, mState.displaytex); - drawSquare(1.0, mVA / (16.0/9)); - glBindTexture(GL_TEXTURE_2D, mState.osdtex); - drawSquare(1.0); + mVAchanged = false; + zoom = 1.0; + xscale = 1.0; + int cmp = (mCrop == DISPLAY_AR_MODE_NONE) ? 0 : av_cmp_q(mVA, mOA); + const AVRational a149 = { 14, 9 }; + switch (cmp) { + default: + case INT_MIN: /* invalid */ + case 0: /* identical */ + lt_debug("%s: mVA == mOA (or fullscreen mode :-)\n", __func__); + break; + case 1: /* mVA > mOA -- video is wider than display */ + lt_debug("%s: mVA > mOA\n", __func__); + xscale = av_q2d(mVA) / av_q2d(mOA); + switch (mCrop) { + case DISPLAY_AR_MODE_PANSCAN: + break; + case DISPLAY_AR_MODE_LETTERBOX: + zoom = av_q2d(mOA) / av_q2d(mVA); + break; + case DISPLAY_AR_MODE_PANSCAN2: + zoom = av_q2d(mOA) / av_q2d(a149); + break; + default: + break; + } + break; + case -1: /* mVA < mOA -- video is taller than display */ + lt_debug("%s: mVA < mOA\n", __func__); + xscale = av_q2d(mVA) / av_q2d(mOA); + switch (mCrop) { + case DISPLAY_AR_MODE_LETTERBOX: + break; + case DISPLAY_AR_MODE_PANSCAN2: + if (av_cmp_q(a149, mOA) < 0) { + zoom = av_q2d(mVA) * av_q2d(a149) / av_q2d(mOA); + break; + } + /* fallthrough for output format 14:9 */ + case DISPLAY_AR_MODE_PANSCAN: + zoom = av_q2d(mOA) / av_q2d(mVA); + break; + default: + break; + } + break; + } } + glBindTexture(GL_TEXTURE_2D, mState.displaytex); + drawSquare(zoom, xscale); + glBindTexture(GL_TEXTURE_2D, mState.osdtex); + drawSquare(1.0); glFlush(); glutSwapBuffers(); @@ -363,22 +452,29 @@ void GLFramebuffer::render() glutPostRedisplay(); } - -void GLFramebuffer::checkReinit() +/* static */ void GLFramebuffer::resizecb(int w, int h) { - int x = glutGet(GLUT_WINDOW_WIDTH); - int y = glutGet(GLUT_WINDOW_HEIGHT); - if ( x != mX || y != mY ) - { - mX = x; - mY = y; - /* fix aspect ratio */ - if (x < mY * 16 / 9) - mX = mY * 16 / 9; - else - mY = mX * 9 / 16; + gThiz->checkReinit(w, h); +} + +void GLFramebuffer::checkReinit(int x, int y) +{ + static int last_x = 0, last_y = 0; + + mReInitLock.lock(); + if (mReInit == false && (x != *mX || y != *mY)) { + if (x != *mX && abs(x - last_x) > 2) { + *mX = x; + *mY = *mX * mOA.den / mOA.num; + } else if (y != *mY && abs(y - last_y) > 2) { + *mY = y; + *mX = *mY * mOA.num / mOA.den; + } mReInit = true; } + mReInitLock.unlock(); + last_x = x; + last_y = y; } #if 0 @@ -489,8 +585,12 @@ void GLFramebuffer::bltDisplayBuffer() return; AVRational a = buf->AR(); - if (a.den != 0) - mVA = static_cast(w * a.num) / h / a.den; + if (a.den != 0 && a.num != 0 && av_cmp_q(a, _mVA)) { + _mVA = a; + /* _mVA is the raw buffer's aspect, mVA is the real scaled output aspect */ + av_reduce(&mVA.num, &mVA.den, w * a.num, h * a.den, INT_MAX); + mVAchanged = true; + } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, mState.displaypbo); glBufferData(GL_PIXEL_UNPACK_BUFFER, buf->size(), &(*buf)[0], GL_STREAM_DRAW_ARB); diff --git a/generic-pc/glfb.h b/generic-pc/glfb.h index 71c78b2..74eea35 100644 --- a/generic-pc/glfb.h +++ b/generic-pc/glfb.h @@ -19,12 +19,16 @@ #ifndef __glthread__ #define __glthread__ #include +#include #include #include #include #include #include #include /* for screeninfo etc. */ +extern "C" { +#include +} class GLFramebuffer : public OpenThreads::Thread { @@ -39,16 +43,28 @@ public: int getOSDHeight() { return mState.height; } void blit() { mState.blit = true; } + void setOutputFormat(AVRational a, int h, int c) { mOA = a; *mY = h; mCrop = c; mReInit = true; } + void clear(); fb_var_screeninfo getScreenInfo() { return screeninfo; } private: fb_var_screeninfo screeninfo; - int mX; /* window size */ - int mY; - float mVA; /* video aspect ratio */; + int *mX; + int *mY; + int _mX[2]; /* output window size */ + int _mY[2]; /* [0] = normal, [1] = fullscreen */ + AVRational mOA; /* output window aspect ratio */ + AVRational mVA; /* video aspect ratio */ + AVRational _mVA; /* for detecting changes in mVA */ + bool mVAchanged; + float zoom; /* for cropping */ + float xscale; /* and aspect ratio */ + int mCrop; /* DISPLAY_AR_MODE */ + bool mFullscreen; /* fullscreen? */ bool mReInit; /* setup things for GL */ + OpenThreads::Mutex mReInitLock; bool mShutDown; /* if set main loop is left */ bool mInitDone; /* condition predicate */ // OpenThreads::Condition mInitCond; /* condition variable for init */ @@ -61,11 +77,12 @@ private: int input_fd; int64_t last_apts; - void checkReinit(); /* e.g. in case window was resized */ static void rendercb(); /* callback for GLUT */ void render(); /* actual render function */ static void keyboardcb(unsigned char key, int x, int y); static void specialcb(int key, int x, int y); + static void resizecb(int w, int h); + void checkReinit(int w, int h); /* e.g. in case window was resized */ void initKeys(); /* setup key bindings for window */ void setupCtx(); /* create the window and make the context current */