| 1 | #include <cmath>
|
|---|
| 2 |
|
|---|
| 3 | using namespace std;
|
|---|
| 4 |
|
|---|
| 5 | // MythTV videout headers
|
|---|
| 6 | #include "videoout_opengl.h"
|
|---|
| 7 | #include "osd.h"
|
|---|
| 8 | #include "osdsurface.h"
|
|---|
| 9 | #include "util-opengl.h"
|
|---|
| 10 | #include "util-x11.h"
|
|---|
| 11 | #include "videodisplayprofile.h"
|
|---|
| 12 |
|
|---|
| 13 | // MythTV General headers
|
|---|
| 14 | #include "../libavcodec/avcodec.h"
|
|---|
| 15 | #include "mythconfig.h"
|
|---|
| 16 | #include "mythcontext.h"
|
|---|
| 17 | #include "filtermanager.h"
|
|---|
| 18 | #include "NuppelVideoPlayer.h"
|
|---|
| 19 | #define IGNORE_TV_PLAY_REC
|
|---|
| 20 | #include "tv.h"
|
|---|
| 21 |
|
|---|
| 22 | extern "C" {
|
|---|
| 23 | #define XMD_H 1
|
|---|
| 24 | #include <X11/extensions/xf86vmode.h>
|
|---|
| 25 | #include <X11/extensions/Xinerama.h>
|
|---|
| 26 | }
|
|---|
| 27 |
|
|---|
| 28 | #define LOC QString("VideoOutputOpengl: ")
|
|---|
| 29 | #define LOC_ERR QString("VideoOutputOpengl Error: ")
|
|---|
| 30 |
|
|---|
| 31 | VideoOutputOpengl::VideoOutputOpengl()
|
|---|
| 32 | : VideoOutput(),
|
|---|
| 33 | display_res(NULL), global_lock(true),
|
|---|
| 34 | gl(new OpenglContext), videochain(NULL), pipchain(NULL), osdchain(NULL),
|
|---|
| 35 | render_onscreen(true),
|
|---|
| 36 | gl_osd(false), actually_draw_pip(false), argb_frame(NULL)
|
|---|
| 37 | {
|
|---|
| 38 | bzero(&av_pause_frame, sizeof(av_pause_frame));
|
|---|
| 39 | use_colourcontrols = gContext->GetNumSetting(
|
|---|
| 40 | "UseOutputPictureControls", 0);
|
|---|
| 41 | allowpreviewepg = false;
|
|---|
| 42 | }
|
|---|
| 43 |
|
|---|
| 44 | VideoOutputOpengl::~VideoOutputOpengl()
|
|---|
| 45 | {
|
|---|
| 46 | X11L;
|
|---|
| 47 | if (videochain)
|
|---|
| 48 | delete videochain;
|
|---|
| 49 | if (pipchain)
|
|---|
| 50 | delete pipchain;
|
|---|
| 51 | if (osdchain)
|
|---|
| 52 | delete osdchain;
|
|---|
| 53 | if (gl)
|
|---|
| 54 | delete gl;
|
|---|
| 55 | X11U;
|
|---|
| 56 | if (argb_frame)
|
|---|
| 57 | delete argb_frame;
|
|---|
| 58 | DeleteBuffers(true);
|
|---|
| 59 | if (display_res)
|
|---|
| 60 | display_res->SwitchToGUI();
|
|---|
| 61 | }
|
|---|
| 62 |
|
|---|
| 63 | bool VideoOutputOpengl::Init(int width, int height, float aspect,
|
|---|
| 64 | WId winid, int winx, int winy, int winw,
|
|---|
| 65 | int winh, WId embedid)
|
|---|
| 66 | {
|
|---|
| 67 | if (piptype > kPIPOn)
|
|---|
| 68 | {
|
|---|
| 69 | VERBOSE(VB_PLAYBACK, LOC + "Use NullVideo for Opengl PiP.");
|
|---|
| 70 | return false;
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | if (gContext->GetNumSetting("UseVideoModes", 0) &&
|
|---|
| 74 | render_onscreen && !piptype)
|
|---|
| 75 | display_res = DisplayRes::GetDisplayRes();
|
|---|
| 76 |
|
|---|
| 77 | if (winid <= 0)
|
|---|
| 78 | {
|
|---|
| 79 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Invalid Window ID.");
|
|---|
| 80 | return false;
|
|---|
| 81 | }
|
|---|
| 82 | VideoOutput::Init(width, height, aspect,
|
|---|
| 83 | winid, winx, winy, winw, winh,
|
|---|
| 84 | embedid);
|
|---|
| 85 | if (!gl->Create(winid, display_visible_rect.width(),
|
|---|
| 86 | display_visible_rect.height(),
|
|---|
| 87 | render_onscreen))
|
|---|
| 88 | return false;
|
|---|
| 89 | X11S(gl->Init());
|
|---|
| 90 | InitDisplayMeasurements(width, height);
|
|---|
| 91 | MoveResize();
|
|---|
| 92 |
|
|---|
| 93 | X11L;
|
|---|
| 94 | gl->MakeCurrent(true);
|
|---|
| 95 | videochain = new OpenglVideo();
|
|---|
| 96 | bool success = videochain->Init(gl, use_colourcontrols,
|
|---|
| 97 | render_onscreen, video_dim,
|
|---|
| 98 | display_visible_rect,
|
|---|
| 99 | display_video_rect, true);
|
|---|
| 100 | gl->MakeCurrent(false);
|
|---|
| 101 | X11U;
|
|---|
| 102 | if (!success)
|
|---|
| 103 | return false;
|
|---|
| 104 | if (!InitSetupBuffers())
|
|---|
| 105 | return false;
|
|---|
| 106 |
|
|---|
| 107 | db_vdisp_profile->SetVideoRenderer("opengl");
|
|---|
| 108 | if (use_colourcontrols)
|
|---|
| 109 | InitPictureAttributes();
|
|---|
| 110 |
|
|---|
| 111 | if (db_vdisp_profile->GetOSDRenderer() == "opengl")
|
|---|
| 112 | gl_osd = true;
|
|---|
| 113 |
|
|---|
| 114 | if (gl_osd && render_onscreen && !piptype)
|
|---|
| 115 | {
|
|---|
| 116 | X11L;
|
|---|
| 117 | gl->MakeCurrent(true);
|
|---|
| 118 | osdchain = new OpenglVideo();
|
|---|
| 119 | if (!osdchain->Init(gl, use_colourcontrols,
|
|---|
| 120 | render_onscreen, QSize(display_visible_rect.width(),
|
|---|
| 121 | display_visible_rect.height()),
|
|---|
| 122 | display_visible_rect,
|
|---|
| 123 | display_visible_rect, false, true))
|
|---|
| 124 | {
|
|---|
| 125 | delete osdchain;
|
|---|
| 126 | osdchain = NULL;
|
|---|
| 127 | gl_osd = false;
|
|---|
| 128 | }
|
|---|
| 129 | gl->MakeCurrent(false);
|
|---|
| 130 | X11U;
|
|---|
| 131 | }
|
|---|
| 132 | return true;
|
|---|
| 133 | }
|
|---|
| 134 |
|
|---|
| 135 | bool VideoOutputOpengl::SetDeinterlacingEnabled(bool enable)
|
|---|
| 136 | {
|
|---|
| 137 | if (!videochain)
|
|---|
| 138 | return false;
|
|---|
| 139 |
|
|---|
| 140 | if (enable && m_deinterlacing)
|
|---|
| 141 | return m_deinterlacing;
|
|---|
| 142 |
|
|---|
| 143 | if (enable)
|
|---|
| 144 | {
|
|---|
| 145 | if (m_deintfiltername == "")
|
|---|
| 146 | return SetupDeinterlace(enable);
|
|---|
| 147 | if (m_deintfiltername.contains("opengl"))
|
|---|
| 148 | {
|
|---|
| 149 | if (videochain->GetDeinterlacer() == "")
|
|---|
| 150 | return SetupDeinterlace(enable);
|
|---|
| 151 | }
|
|---|
| 152 | else if (!m_deintfiltername.contains("opengl"))
|
|---|
| 153 | {
|
|---|
| 154 | // make sure opengl deinterlacing is disabled
|
|---|
| 155 | X11S(videochain->SetDeinterlacing(false));
|
|---|
| 156 | if (!m_deintFiltMan || !m_deintFilter)
|
|---|
| 157 | return VideoOutput::SetupDeinterlace(enable);
|
|---|
| 158 | }
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | if (videochain)
|
|---|
| 162 | X11S(videochain->SetDeinterlacing(enable));
|
|---|
| 163 | m_deinterlacing = enable;
|
|---|
| 164 | return m_deinterlacing;
|
|---|
| 165 | }
|
|---|
| 166 |
|
|---|
| 167 | bool VideoOutputOpengl::SetupDeinterlace(bool interlaced,
|
|---|
| 168 | const QString& overridefilter)
|
|---|
| 169 | {
|
|---|
| 170 | m_deintfiltername = db_vdisp_profile->GetFilteredDeint(overridefilter);
|
|---|
| 171 |
|
|---|
| 172 | if (!m_deintfiltername.contains("opengl"))
|
|---|
| 173 | {
|
|---|
| 174 | X11S(videochain->SetDeinterlacing(false));
|
|---|
| 175 | videochain->SetSoftwareDeinterlacer("");
|
|---|
| 176 | VideoOutput::SetupDeinterlace(interlaced, overridefilter);
|
|---|
| 177 | if (m_deinterlacing)
|
|---|
| 178 | videochain->SetSoftwareDeinterlacer(m_deintfiltername);
|
|---|
| 179 | return m_deinterlacing;
|
|---|
| 180 | }
|
|---|
| 181 |
|
|---|
| 182 | // clear any non opengl filters
|
|---|
| 183 | if (m_deintFiltMan)
|
|---|
| 184 | {
|
|---|
| 185 | delete m_deintFiltMan;
|
|---|
| 186 | m_deintFiltMan = NULL;
|
|---|
| 187 | }
|
|---|
| 188 | if (m_deintFilter)
|
|---|
| 189 | {
|
|---|
| 190 | delete m_deintFilter;
|
|---|
| 191 | m_deintFilter = NULL;
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | if (m_deinterlacing == interlaced)
|
|---|
| 195 | return m_deinterlacing;
|
|---|
| 196 | m_deinterlacing = interlaced;
|
|---|
| 197 |
|
|---|
| 198 | if (!videochain)
|
|---|
| 199 | return false;
|
|---|
| 200 |
|
|---|
| 201 | if (m_deinterlacing && !m_deintfiltername.isEmpty())
|
|---|
| 202 | {
|
|---|
| 203 | if (videochain->GetDeinterlacer() != m_deintfiltername)
|
|---|
| 204 | {
|
|---|
| 205 | X11L;
|
|---|
| 206 | if (!videochain->AddDeinterlacer(m_deintfiltername))
|
|---|
| 207 | {
|
|---|
| 208 | VERBOSE(VB_IMPORTANT, LOC +
|
|---|
| 209 | QString("Couldn't load deinterlace filter %1")
|
|---|
| 210 | .arg(m_deintfiltername));
|
|---|
| 211 | m_deinterlacing = false;
|
|---|
| 212 | m_deintfiltername = "";
|
|---|
| 213 | }
|
|---|
| 214 | else
|
|---|
| 215 | {
|
|---|
| 216 | VERBOSE(VB_PLAYBACK, LOC +
|
|---|
| 217 | QString("Using deinterlace method %1")
|
|---|
| 218 | .arg(m_deintfiltername));
|
|---|
| 219 | }
|
|---|
| 220 | X11U;
|
|---|
| 221 | }
|
|---|
| 222 | }
|
|---|
| 223 | X11S(videochain->SetDeinterlacing(m_deinterlacing));
|
|---|
| 224 | return m_deinterlacing;
|
|---|
| 225 | }
|
|---|
| 226 |
|
|---|
| 227 | void VideoOutputOpengl::GetOSDBounds(QRect &total, QRect &visible,
|
|---|
| 228 | float &visible_aspect,
|
|---|
| 229 | float &font_scaling) const
|
|---|
| 230 | {
|
|---|
| 231 | if (gl_osd)
|
|---|
| 232 | {
|
|---|
| 233 | total = display_visible_rect;
|
|---|
| 234 | visible = display_visible_rect;
|
|---|
| 235 | visible_aspect = (float) display_aspect;
|
|---|
| 236 | font_scaling = 1.0f;
|
|---|
| 237 | return;
|
|---|
| 238 | }
|
|---|
| 239 | VideoOutput::GetOSDBounds(total, visible, visible_aspect, font_scaling);
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | void VideoOutputOpengl::PrepareFrame(VideoFrame *buffer, FrameScanType t)
|
|---|
| 243 | {
|
|---|
| 244 | if (!buffer)
|
|---|
| 245 | buffer = vbuffers.GetScratchFrame();
|
|---|
| 246 |
|
|---|
| 247 | framesPlayed = buffer->frameNumber + 1;
|
|---|
| 248 |
|
|---|
| 249 | // TODO should cope with YUV422P, rgb24, argb32 etc
|
|---|
| 250 | if (buffer->codec != FMT_YV12)
|
|---|
| 251 | return;
|
|---|
| 252 |
|
|---|
| 253 | X11L;
|
|---|
| 254 | gl->MakeCurrent(true);
|
|---|
| 255 | videochain->Show(t, m_deinterlacing, framesPlayed);
|
|---|
| 256 |
|
|---|
| 257 | if (actually_draw_pip && render_onscreen && pipchain)
|
|---|
| 258 | pipchain->Show(t, m_deinterlacing, framesPlayed);
|
|---|
| 259 |
|
|---|
| 260 | if (osdchain && render_onscreen && actually_draw_osd)
|
|---|
| 261 | osdchain->Show(t, m_deinterlacing, framesPlayed);
|
|---|
| 262 | glFlush();
|
|---|
| 263 | gl->MakeCurrent(false);
|
|---|
| 264 | X11U;
|
|---|
| 265 | }
|
|---|
| 266 |
|
|---|
| 267 | void VideoOutputOpengl::ShowPip(VideoFrame *frame,
|
|---|
| 268 | NuppelVideoPlayer *pipplayer)
|
|---|
| 269 | {
|
|---|
| 270 | (void) frame;
|
|---|
| 271 | actually_draw_pip = false;
|
|---|
| 272 | if (!pipplayer)
|
|---|
| 273 | return;
|
|---|
| 274 |
|
|---|
| 275 | int pipw, piph;
|
|---|
| 276 | VideoFrame *pipimage = pipplayer->GetCurrentFrame(pipw, piph);
|
|---|
| 277 | float pipVideoAspect = pipplayer->GetVideoAspect();
|
|---|
| 278 | uint pipVideoWidth = pipplayer->GetVideoWidth();
|
|---|
| 279 | uint pipVideoHeight = pipplayer->GetVideoHeight();
|
|---|
| 280 |
|
|---|
| 281 | // If PiP is not initialized to values we like, silently ignore the frame.
|
|---|
| 282 | if ((pipVideoAspect <= 0) || !pipimage ||
|
|---|
| 283 | !pipimage->buf || pipimage->codec != FMT_YV12)
|
|---|
| 284 | {
|
|---|
| 285 | pipplayer->ReleaseCurrentFrame(pipimage);
|
|---|
| 286 | return;
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 | QRect position = GetPIPRect(db_pip_location, pipplayer);
|
|---|
| 290 |
|
|---|
| 291 | if (!pipchain)
|
|---|
| 292 | {
|
|---|
| 293 | VERBOSE(VB_PLAYBACK, LOC + "Initialise PiP.");
|
|---|
| 294 | pipchain = new OpenglVideo();
|
|---|
| 295 | bool success = pipchain->Init(gl, use_colourcontrols,
|
|---|
| 296 | true, QSize(pipVideoWidth, pipVideoHeight),
|
|---|
| 297 | position,
|
|---|
| 298 | position, false);
|
|---|
| 299 | success &= pipchain->AddDeinterlacer("openglonefield");
|
|---|
| 300 | pipchain->SetMasterViewport(videochain->GetViewPort());
|
|---|
| 301 | if (!success)
|
|---|
| 302 | {
|
|---|
| 303 | pipplayer->ReleaseCurrentFrame(pipimage);
|
|---|
| 304 | return;
|
|---|
| 305 | }
|
|---|
| 306 | }
|
|---|
| 307 | QSize current = pipchain->GetVideoSize();
|
|---|
| 308 | if ((uint)current.width() != pipVideoWidth ||
|
|---|
| 309 | (uint)current.height() != pipVideoHeight)
|
|---|
| 310 | {
|
|---|
| 311 | VERBOSE(VB_PLAYBACK, LOC + "Re-initialise PiP.");
|
|---|
| 312 | bool success =pipchain->ReInit(gl, use_colourcontrols,
|
|---|
| 313 | true, QSize(pipVideoWidth, pipVideoHeight),
|
|---|
| 314 | position, position, false);
|
|---|
| 315 | pipchain->SetMasterViewport(videochain->GetViewPort());
|
|---|
| 316 | if (!success)
|
|---|
| 317 | {
|
|---|
| 318 | pipplayer->ReleaseCurrentFrame(pipimage);
|
|---|
| 319 | return;
|
|---|
| 320 | }
|
|---|
| 321 |
|
|---|
| 322 | }
|
|---|
| 323 | pipchain->SetVideoRect(position);
|
|---|
| 324 | pipchain->UpdateInputFrame(pipimage);
|
|---|
| 325 | actually_draw_pip = true;
|
|---|
| 326 | pipplayer->ReleaseCurrentFrame(pipimage);
|
|---|
| 327 | }
|
|---|
| 328 |
|
|---|
| 329 | QRect VideoOutputOpengl::GetPIPRect(int location, NuppelVideoPlayer *pipplayer)
|
|---|
| 330 | {
|
|---|
| 331 | if (!pipplayer || pipplayer == NULL)
|
|---|
| 332 | return VideoOutput::GetPIPRect(location, pipplayer);
|
|---|
| 333 |
|
|---|
| 334 | QRect position;
|
|---|
| 335 | float pipVideoAspect = pipplayer ?
|
|---|
| 336 | (float)pipplayer->GetVideoAspect() : display_aspect;
|
|---|
| 337 | int tmph = (display_visible_rect.height() * db_pip_size) / 100;
|
|---|
| 338 | float pixel_adj = ((float)display_visible_rect.width() /
|
|---|
| 339 | (float)display_visible_rect.height()) / display_aspect;
|
|---|
| 340 | position.setHeight(tmph);
|
|---|
| 341 | position.setWidth((int)(tmph * pipVideoAspect * pixel_adj));
|
|---|
| 342 |
|
|---|
| 343 | int xoff = (int)(display_visible_rect.width() * 0.06);
|
|---|
| 344 | int yoff = (int)(display_visible_rect.height() * 0.06);
|
|---|
| 345 | switch (location)
|
|---|
| 346 | {
|
|---|
| 347 | default:
|
|---|
| 348 | case kPIPTopLeft:
|
|---|
| 349 | yoff = display_visible_rect.height()
|
|---|
| 350 | - position.height() - yoff;
|
|---|
| 351 | break;
|
|---|
| 352 | case kPIPBottomLeft:
|
|---|
| 353 | break;
|
|---|
| 354 | case kPIPTopRight:
|
|---|
| 355 | xoff = display_visible_rect.width()
|
|---|
| 356 | - position.width() - xoff;
|
|---|
| 357 | yoff = display_visible_rect.height()
|
|---|
| 358 | - position.height() - yoff;
|
|---|
| 359 | break;
|
|---|
| 360 | case kPIPBottomRight:
|
|---|
| 361 | xoff = display_visible_rect.width()
|
|---|
| 362 | - position.width() - xoff;
|
|---|
| 363 | break;
|
|---|
| 364 | }
|
|---|
| 365 | position.moveBy(xoff, yoff);
|
|---|
| 366 | return position;
|
|---|
| 367 | }
|
|---|
| 368 |
|
|---|
| 369 | void VideoOutputOpengl::Show(FrameScanType )
|
|---|
| 370 | {
|
|---|
| 371 | X11L;
|
|---|
| 372 | gl->SwapBuffers();
|
|---|
| 373 | X11U;
|
|---|
| 374 | }
|
|---|
| 375 |
|
|---|
| 376 | unsigned char* VideoOutputOpengl::GetARGBFrame(QSize &size)
|
|---|
| 377 | {
|
|---|
| 378 | // TODO
|
|---|
| 379 | (void) size;
|
|---|
| 380 | return NULL;
|
|---|
| 381 | }
|
|---|
| 382 |
|
|---|
| 383 | void VideoOutputOpengl::DrawUnusedRects(bool sync)
|
|---|
| 384 | {
|
|---|
| 385 | (void) sync;
|
|---|
| 386 | }
|
|---|
| 387 |
|
|---|
| 388 | void VideoOutputOpengl::Zoom(int direction)
|
|---|
| 389 | {
|
|---|
| 390 | VideoOutput::Zoom(direction);
|
|---|
| 391 | MoveResize();
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | void VideoOutputOpengl::InputChanged(int width, int height,
|
|---|
| 395 | float aspect, MythCodecID av_codec_id)
|
|---|
| 396 | {
|
|---|
| 397 | VERBOSE(VB_PLAYBACK, LOC + "InputChanged");
|
|---|
| 398 | QRect current_visible = display_visible_rect;
|
|---|
| 399 | VideoOutput::InputChanged(width, height, aspect, av_codec_id);
|
|---|
| 400 | ResizeForVideo((uint) width, (uint) height);
|
|---|
| 401 | MoveResize();
|
|---|
| 402 |
|
|---|
| 403 | if (videochain)
|
|---|
| 404 | {
|
|---|
| 405 | QSize size = videochain->GetVideoSize();
|
|---|
| 406 | if (width != size.width() ||
|
|---|
| 407 | height != size.height())
|
|---|
| 408 | {
|
|---|
| 409 | X11L;
|
|---|
| 410 | videochain->ReInit(gl, use_colourcontrols,
|
|---|
| 411 | render_onscreen, video_dim,
|
|---|
| 412 | display_visible_rect,
|
|---|
| 413 | display_video_rect, true);
|
|---|
| 414 | X11U;
|
|---|
| 415 | if (pipchain && pipchain != NULL)
|
|---|
| 416 | pipchain->SetMasterViewport(videochain->GetViewPort());
|
|---|
| 417 | CreateBuffers();
|
|---|
| 418 | CreatePauseFrame();
|
|---|
| 419 | }
|
|---|
| 420 | }
|
|---|
| 421 | }
|
|---|
| 422 |
|
|---|
| 423 | void VideoOutputOpengl::MoveResize(void)
|
|---|
| 424 | {
|
|---|
| 425 | VideoOutput::MoveResize();
|
|---|
| 426 | if (videochain)
|
|---|
| 427 | videochain->SetVideoRect(display_video_rect);
|
|---|
| 428 | }
|
|---|
| 429 |
|
|---|
| 430 | bool VideoOutputOpengl::InitSetupBuffers(void)
|
|---|
| 431 | {
|
|---|
| 432 | vbuffers.Init(31, true, 1, 12, 4, 2, false);
|
|---|
| 433 | CreateBuffers();
|
|---|
| 434 | CreatePauseFrame();
|
|---|
| 435 | return true;
|
|---|
| 436 | }
|
|---|
| 437 |
|
|---|
| 438 | void VideoOutputOpengl::CreateBuffers(void)
|
|---|
| 439 | {
|
|---|
| 440 | if (videochain)
|
|---|
| 441 | {
|
|---|
| 442 | QSize size = videochain->GetVideoSize();
|
|---|
| 443 | if (vbuffers.CreateBuffers(size.width(), size.height()))
|
|---|
| 444 | return;
|
|---|
| 445 | }
|
|---|
| 446 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create buffers");
|
|---|
| 447 | }
|
|---|
| 448 |
|
|---|
| 449 | void VideoOutputOpengl::CreatePauseFrame(void)
|
|---|
| 450 | {
|
|---|
| 451 | vbuffers.LockFrame(&av_pause_frame, "CreatePauseFrame");
|
|---|
| 452 | if (av_pause_frame.buf)
|
|---|
| 453 | {
|
|---|
| 454 | delete [] av_pause_frame.buf;
|
|---|
| 455 | av_pause_frame.buf = NULL;
|
|---|
| 456 | }
|
|---|
| 457 | av_pause_frame.height = vbuffers.GetScratchFrame()->height;
|
|---|
| 458 | av_pause_frame.width = vbuffers.GetScratchFrame()->width;
|
|---|
| 459 | av_pause_frame.bpp = vbuffers.GetScratchFrame()->bpp;
|
|---|
| 460 | av_pause_frame.size = vbuffers.GetScratchFrame()->size;
|
|---|
| 461 | av_pause_frame.frameNumber = vbuffers.GetScratchFrame()->frameNumber;
|
|---|
| 462 | av_pause_frame.buf = new unsigned char[av_pause_frame.size];
|
|---|
| 463 | av_pause_frame.qscale_table = NULL;
|
|---|
| 464 | av_pause_frame.qstride = 0;
|
|---|
| 465 |
|
|---|
| 466 | vbuffers.UnlockFrame(&av_pause_frame, "CreatePauseFrame");
|
|---|
| 467 | }
|
|---|
| 468 |
|
|---|
| 469 | void VideoOutputOpengl::DeleteBuffers(bool delete_pause_frame)
|
|---|
| 470 | {
|
|---|
| 471 | DiscardFrames(true);
|
|---|
| 472 | vbuffers.DeleteBuffers();
|
|---|
| 473 | if (delete_pause_frame)
|
|---|
| 474 | {
|
|---|
| 475 | if (av_pause_frame.buf)
|
|---|
| 476 | {
|
|---|
| 477 | delete [] av_pause_frame.buf;
|
|---|
| 478 | av_pause_frame.buf = NULL;
|
|---|
| 479 | }
|
|---|
| 480 | if (av_pause_frame.qscale_table)
|
|---|
| 481 | {
|
|---|
| 482 | delete [] av_pause_frame.qscale_table;
|
|---|
| 483 | av_pause_frame.qscale_table = NULL;
|
|---|
| 484 | }
|
|---|
| 485 | }
|
|---|
| 486 | }
|
|---|
| 487 |
|
|---|
| 488 | void VideoOutputOpengl::EmbedInWidget(WId wid, int x, int y, int w, int h)
|
|---|
| 489 | {
|
|---|
| 490 | (void) wid;
|
|---|
| 491 | (void) x;
|
|---|
| 492 | (void) y;
|
|---|
| 493 | (void) w;
|
|---|
| 494 | (void) h;
|
|---|
| 495 | }
|
|---|
| 496 |
|
|---|
| 497 | void VideoOutputOpengl::StopEmbedding(void)
|
|---|
| 498 | {
|
|---|
| 499 | }
|
|---|
| 500 |
|
|---|
| 501 | float VideoOutputOpengl::GetDisplayAspect(void)
|
|---|
| 502 | {
|
|---|
| 503 | return display_aspect;
|
|---|
| 504 | }
|
|---|
| 505 |
|
|---|
| 506 | void VideoOutputOpengl::UpdatePauseFrame(void)
|
|---|
| 507 | {
|
|---|
| 508 | vbuffers.LockFrame(&av_pause_frame, "UpdatePauseFrame -- pause");
|
|---|
| 509 |
|
|---|
| 510 | vbuffers.begin_lock(kVideoBuffer_used);
|
|---|
| 511 | VideoFrame *used_frame = NULL;
|
|---|
| 512 | if (vbuffers.size(kVideoBuffer_used) > 0)
|
|---|
| 513 | {
|
|---|
| 514 | used_frame = vbuffers.head(kVideoBuffer_used);
|
|---|
| 515 | if (!vbuffers.TryLockFrame(used_frame, "UpdatePauseFrame -- used"))
|
|---|
| 516 | used_frame = NULL;
|
|---|
| 517 | }
|
|---|
| 518 | if (used_frame)
|
|---|
| 519 | {
|
|---|
| 520 | CopyFrame(&av_pause_frame, used_frame);
|
|---|
| 521 | vbuffers.UnlockFrame(used_frame, "UpdatePauseFrame -- used");
|
|---|
| 522 | }
|
|---|
| 523 | vbuffers.end_lock();
|
|---|
| 524 | if (!used_frame &&
|
|---|
| 525 | vbuffers.TryLockFrame(vbuffers.GetScratchFrame(),
|
|---|
| 526 | "UpdatePauseFrame -- scratch"))
|
|---|
| 527 | {
|
|---|
| 528 | vbuffers.GetScratchFrame()->frameNumber = framesPlayed - 1;
|
|---|
| 529 | CopyFrame(&av_pause_frame, vbuffers.GetScratchFrame());
|
|---|
| 530 | vbuffers.UnlockFrame(vbuffers.GetScratchFrame(),
|
|---|
| 531 | "UpdatePauseFrame -- scratch");
|
|---|
| 532 | }
|
|---|
| 533 | vbuffers.UnlockFrame(&av_pause_frame, "UpdatePauseFrame - used");
|
|---|
| 534 | }
|
|---|
| 535 |
|
|---|
| 536 | void VideoOutputOpengl::ProcessFrame(VideoFrame *frame, OSD *osd,
|
|---|
| 537 | FilterChain *filterList,
|
|---|
| 538 | NuppelVideoPlayer *pipPlayer)
|
|---|
| 539 | {
|
|---|
| 540 | bool pauseframe = false;
|
|---|
| 541 | if (!frame)
|
|---|
| 542 | {
|
|---|
| 543 | frame = vbuffers.GetScratchFrame();
|
|---|
| 544 | CopyFrame(vbuffers.GetScratchFrame(), &av_pause_frame);
|
|---|
| 545 | pauseframe = true;
|
|---|
| 546 | }
|
|---|
| 547 |
|
|---|
| 548 | // disable image processing for offscreen rendering
|
|---|
| 549 | X11L;
|
|---|
| 550 | gl->MakeCurrent(true);
|
|---|
| 551 | if (render_onscreen)
|
|---|
| 552 | {
|
|---|
| 553 | if (filterList)
|
|---|
| 554 | filterList->ProcessFrame(frame);
|
|---|
| 555 | if (m_deinterlacing && m_deintFilter != NULL &&
|
|---|
| 556 | m_deinterlaceBeforeOSD &&
|
|---|
| 557 | !pauseframe)
|
|---|
| 558 | {
|
|---|
| 559 | m_deintFilter->ProcessFrame(frame);
|
|---|
| 560 | }
|
|---|
| 561 | ShowPip(frame, pipPlayer);
|
|---|
| 562 | gl_osd ? DisplayOSD(frame, osd) : VideoOutput::DisplayOSD(frame, osd);
|
|---|
| 563 |
|
|---|
| 564 | if (m_deinterlacing && m_deintFilter != NULL &&
|
|---|
| 565 | !m_deinterlaceBeforeOSD &&
|
|---|
| 566 | !pauseframe)
|
|---|
| 567 | {
|
|---|
| 568 | m_deintFilter->ProcessFrame(frame);
|
|---|
| 569 | }
|
|---|
| 570 | }
|
|---|
| 571 | if (videochain)
|
|---|
| 572 | videochain->UpdateInputFrame(frame);
|
|---|
| 573 | gl->MakeCurrent(false);
|
|---|
| 574 | X11U;
|
|---|
| 575 | }
|
|---|
| 576 |
|
|---|
| 577 | int VideoOutputOpengl::DisplayOSD(VideoFrame *frame, OSD *osd,
|
|---|
| 578 | int stride, int revision)
|
|---|
| 579 | {
|
|---|
| 580 | (void) stride;
|
|---|
| 581 | (void) frame;
|
|---|
| 582 | actually_draw_osd = false;
|
|---|
| 583 | if (!osd || !osdchain)
|
|---|
| 584 | return -1;
|
|---|
| 585 |
|
|---|
| 586 | if (vsz_enabled && videochain)
|
|---|
| 587 | videochain->SetVideoResize(&vsz_desired_display_rect);
|
|---|
| 588 |
|
|---|
| 589 | OSDSurface *surface = osd->Display();
|
|---|
| 590 | if (!surface)
|
|---|
| 591 | return -1;
|
|---|
| 592 |
|
|---|
| 593 | actually_draw_osd = true;
|
|---|
| 594 | bool changed = (-1 == revision) ?
|
|---|
| 595 | surface->Changed() : (surface->GetRevision()!=revision);
|
|---|
| 596 |
|
|---|
| 597 | if (changed)
|
|---|
| 598 | {
|
|---|
| 599 | QSize visible(display_visible_rect.width(),
|
|---|
| 600 | display_visible_rect.height());
|
|---|
| 601 | osdchain->UpdateInput(surface->yuvbuffer, 0, FMT_YV12, visible);
|
|---|
| 602 | osdchain->UpdateInput(surface->alpha, 1, FMT_ALPHA, visible);
|
|---|
| 603 | }
|
|---|
| 604 | return changed;
|
|---|
| 605 | }
|
|---|
| 606 |
|
|---|
| 607 | void VideoOutputOpengl::ShutdownVideoResize(void)
|
|---|
| 608 | {
|
|---|
| 609 | if (!osdchain)
|
|---|
| 610 | {
|
|---|
| 611 | VideoOutput::ShutdownVideoResize();
|
|---|
| 612 | return;
|
|---|
| 613 | }
|
|---|
| 614 | if (videochain)
|
|---|
| 615 | videochain->SetVideoResize(NULL);
|
|---|
| 616 | vsz_enabled = false;
|
|---|
| 617 | }
|
|---|
| 618 |
|
|---|
| 619 | int VideoOutputOpengl::SetPictureAttribute(int attributeType, int newValue)
|
|---|
| 620 | {
|
|---|
| 621 | if (kPictureAttribute_Hue == attributeType)
|
|---|
| 622 | return -1;
|
|---|
| 623 | SetPictureAttributeDBValue(attributeType, newValue);
|
|---|
| 624 | if (videochain && use_colourcontrols)
|
|---|
| 625 | videochain->SetPictureAttribute(attributeType, newValue);
|
|---|
| 626 | return newValue;
|
|---|
| 627 | }
|
|---|
| 628 |
|
|---|
| 629 | int VideoOutputOpengl::GetRefreshRate(void)
|
|---|
| 630 | {
|
|---|
| 631 | XF86VidModeModeLine mode_line;
|
|---|
| 632 | int dot_clock;
|
|---|
| 633 |
|
|---|
| 634 | int ret = False;
|
|---|
| 635 | X11S(ret = XF86VidModeGetModeLine(gl->GetDisplay(), gl->GetScreenNum(),
|
|---|
| 636 | &dot_clock, &mode_line));
|
|---|
| 637 | if (!ret)
|
|---|
| 638 | {
|
|---|
| 639 | VERBOSE(VB_IMPORTANT, LOC_ERR + "GetRefreshRate(): "
|
|---|
| 640 | "X11 ModeLine query failed");
|
|---|
| 641 | return -1;
|
|---|
| 642 | }
|
|---|
| 643 |
|
|---|
| 644 | double rate = (double)((double)(dot_clock * 1000.0) /
|
|---|
| 645 | (double)(mode_line.htotal * mode_line.vtotal));
|
|---|
| 646 |
|
|---|
| 647 | // Assume 60Hz if we can't otherwise determine it.
|
|---|
| 648 | if (rate == 0)
|
|---|
| 649 | rate = 60;
|
|---|
| 650 |
|
|---|
| 651 | if (rate < 20 || rate > 200)
|
|---|
| 652 | {
|
|---|
| 653 | VERBOSE(VB_PLAYBACK, LOC + QString("Unreasonable refresh rate %1Hz "
|
|---|
| 654 | "reported by X").arg(rate));
|
|---|
| 655 | rate = 60;
|
|---|
| 656 | }
|
|---|
| 657 |
|
|---|
| 658 | rate = 1000000.0 / rate;
|
|---|
| 659 |
|
|---|
| 660 | return (int)rate;
|
|---|
| 661 | }
|
|---|
| 662 | void VideoOutputOpengl::ResizeForGui(void)
|
|---|
| 663 | {
|
|---|
| 664 | if (display_res)
|
|---|
| 665 | display_res->SwitchToGUI();
|
|---|
| 666 | }
|
|---|
| 667 |
|
|---|
| 668 | void VideoOutputOpengl::ResizeForVideo(uint width, uint height)
|
|---|
| 669 | {
|
|---|
| 670 | if ((width == 1920 || width == 1440) && height == 1088)
|
|---|
| 671 | height = 1080; // ATSC 1920x1080
|
|---|
| 672 |
|
|---|
| 673 | if (display_res && display_res->SwitchToVideo(width, height))
|
|---|
| 674 | {
|
|---|
| 675 | // Switching to custom display resolution succeeded
|
|---|
| 676 | // Make a note of the new size
|
|---|
| 677 | display_dim = QSize(display_res->GetPhysicalWidth(),
|
|---|
| 678 | display_res->GetPhysicalHeight());
|
|---|
| 679 | display_aspect = display_res->GetAspectRatio();
|
|---|
| 680 |
|
|---|
| 681 | bool fullscreen = !gContext->GetNumSetting("GuiSizeForTV", 0);
|
|---|
| 682 |
|
|---|
| 683 | // if width && height are zero users expect fullscreen playback
|
|---|
| 684 | if (!fullscreen)
|
|---|
| 685 | {
|
|---|
| 686 | int gui_width = 0, gui_height = 0;
|
|---|
| 687 | gContext->GetResolutionSetting("Gui", gui_width, gui_height);
|
|---|
| 688 | fullscreen |= (0 == gui_width && 0 == gui_height);
|
|---|
| 689 | }
|
|---|
| 690 |
|
|---|
| 691 | if (fullscreen)
|
|---|
| 692 | {
|
|---|
| 693 | QSize sz(display_res->GetWidth(), display_res->GetHeight());
|
|---|
| 694 | display_visible_rect = QRect(QPoint(0,0), sz);
|
|---|
| 695 | // Resize X window to fill new resolution
|
|---|
| 696 | X11S(XMoveResizeWindow(gl->GetDisplay(), gl->GetWindow(),
|
|---|
| 697 | display_visible_rect.left(),
|
|---|
| 698 | display_visible_rect.top(),
|
|---|
| 699 | display_visible_rect.width(),
|
|---|
| 700 | display_visible_rect.height()));
|
|---|
| 701 | }
|
|---|
| 702 | }
|
|---|
| 703 | }
|
|---|
| 704 |
|
|---|
| 705 | void VideoOutputOpengl::ResizeForVideo(void)
|
|---|
| 706 | {
|
|---|
| 707 | if (videochain)
|
|---|
| 708 | {
|
|---|
| 709 | QSize size = videochain->GetVideoSize();
|
|---|
| 710 | ResizeForVideo(size.width(), size.height());
|
|---|
| 711 | }
|
|---|
| 712 | }
|
|---|
| 713 |
|
|---|
| 714 | void VideoOutputOpengl::InitDisplayMeasurements(uint width, uint height)
|
|---|
| 715 | {
|
|---|
| 716 | if (display_res)
|
|---|
| 717 | {
|
|---|
| 718 | // The very first Resize needs to be the maximum possible
|
|---|
| 719 | // desired res, because X will mask off anything outside
|
|---|
| 720 | // the initial dimensions
|
|---|
| 721 | X11S(XMoveResizeWindow(gl->GetDisplay(), gl->GetWindow(), 0, 0,
|
|---|
| 722 | display_res->GetMaxWidth(),
|
|---|
| 723 | display_res->GetMaxHeight()));
|
|---|
| 724 | ResizeForVideo(width, height);
|
|---|
| 725 | }
|
|---|
| 726 | else
|
|---|
| 727 | {
|
|---|
| 728 | display_dim = QSize(DisplayWidthMM(gl->GetDisplay(),
|
|---|
| 729 | gl->GetScreenNum()),
|
|---|
| 730 | DisplayHeightMM(gl->GetDisplay(),
|
|---|
| 731 | gl->GetScreenNum()));
|
|---|
| 732 |
|
|---|
| 733 | if (db_display_dim.width() > 0 && db_display_dim.height() > 0)
|
|---|
| 734 | display_dim = db_display_dim;
|
|---|
| 735 | }
|
|---|
| 736 |
|
|---|
| 737 | // Fetch pixel width and height of the display
|
|---|
| 738 | int xbase, ybase, w, h;
|
|---|
| 739 | gContext->GetScreenBounds(xbase, ybase, w, h);
|
|---|
| 740 | // Determine window dimensions in pixels
|
|---|
| 741 | int window_w = w, window_h = h;
|
|---|
| 742 | if (gContext->GetNumSetting("GuiSizeForTV", 0))
|
|---|
| 743 | gContext->GetResolutionSetting("Gui", window_w, window_h);
|
|---|
| 744 | else
|
|---|
| 745 | gContext->GetScreenBounds(xbase, ybase, window_w, window_h);
|
|---|
| 746 | window_w = (window_w) ? window_w : w;
|
|---|
| 747 | window_h = (window_h) ? window_h : h;
|
|---|
| 748 | float pixel_aspect = ((float)w) / ((float)h);
|
|---|
| 749 |
|
|---|
| 750 | VERBOSE(VB_PLAYBACK, LOC + QString(
|
|---|
| 751 | "Pixel dimensions: Screen %1x%2, window %3x%4")
|
|---|
| 752 | .arg(w).arg(h).arg(window_w).arg(window_h));
|
|---|
| 753 |
|
|---|
| 754 | // Determine if we are using Xinerama
|
|---|
| 755 | int event_base, error_base;
|
|---|
| 756 | bool usingXinerama = false;
|
|---|
| 757 | X11S(usingXinerama =
|
|---|
| 758 | (XineramaQueryExtension(gl->GetDisplay(), &event_base, &error_base) &&
|
|---|
| 759 | XineramaIsActive(gl->GetDisplay())));
|
|---|
| 760 |
|
|---|
| 761 | // If the dimensions are invalid, assume square pixels and 17" screen.
|
|---|
| 762 | // Only print warning if this isn't Xinerama, we will fix Xinerama later.
|
|---|
| 763 | if (((display_dim.width() <= 0) || (display_dim.height() <= 0)) &&
|
|---|
| 764 | !usingXinerama)
|
|---|
| 765 | {
|
|---|
| 766 | VERBOSE(VB_GENERAL, LOC + "Physical size of display unknown."
|
|---|
| 767 | "\n\t\t\tAssuming 17\" monitor with square pixels.");
|
|---|
| 768 | display_dim.setHeight(300);
|
|---|
| 769 | display_dim.setWidth((int) round(300 * pixel_aspect));
|
|---|
| 770 | }
|
|---|
| 771 |
|
|---|
| 772 | // If we are using Xinerama the display dimensions can not be trusted.
|
|---|
| 773 | // We need to use the Xinerama monitor aspect ratio from the DB to set
|
|---|
| 774 | // the physical screen width. This assumes the height is correct, which
|
|---|
| 775 | // is more or less true in the typical side-by-side monitor setup.
|
|---|
| 776 | if (usingXinerama)
|
|---|
| 777 | {
|
|---|
| 778 | float displayAspect = gContext->GetFloatSettingOnHost(
|
|---|
| 779 | "XineramaMonitorAspectRatio",
|
|---|
| 780 | gContext->GetHostName(), pixel_aspect);
|
|---|
| 781 | int height = display_dim.height();
|
|---|
| 782 | if (height <= 0)
|
|---|
| 783 | display_dim.setHeight(height = 300);
|
|---|
| 784 | display_dim.setWidth((int) round(height * displayAspect));
|
|---|
| 785 | }
|
|---|
| 786 |
|
|---|
| 787 | VERBOSE(VB_PLAYBACK, LOC +
|
|---|
| 788 | QString("Estimated display dimensions: %1x%2 mm Aspect: %3")
|
|---|
| 789 | .arg(display_dim.width()).arg(display_dim.height())
|
|---|
| 790 | .arg(((float) display_dim.width()) / display_dim.height()));
|
|---|
| 791 |
|
|---|
| 792 | // We must now scale the display measurements to our window size.
|
|---|
| 793 | // If we are running fullscreen this is a no-op.
|
|---|
| 794 | display_dim = QSize((display_dim.width() * window_w) / w,
|
|---|
| 795 | (display_dim.height() * window_h) / h);
|
|---|
| 796 |
|
|---|
| 797 | // Now that we know the physical monitor size, we can
|
|---|
| 798 | // calculate the display aspect ratio pretty simply...
|
|---|
| 799 | display_aspect = ((float)display_dim.width()) / display_dim.height();
|
|---|
| 800 |
|
|---|
| 801 | // If we are using XRandR, use the aspect ratio from it instead...
|
|---|
| 802 | if (display_res)
|
|---|
| 803 | display_aspect = display_res->GetAspectRatio();
|
|---|
| 804 |
|
|---|
| 805 | VERBOSE(VB_PLAYBACK, LOC +
|
|---|
| 806 | QString("Estimated window dimensions: %1x%2 mm Aspect: %3")
|
|---|
| 807 | .arg(display_dim.width()).arg(display_dim.height())
|
|---|
| 808 | .arg(display_aspect));
|
|---|
| 809 | }
|
|---|
| 810 |
|
|---|
| 811 | #undef LOC
|
|---|
| 812 | #undef LOC_ERR
|
|---|
| 813 |
|
|---|
| 814 | #define LOC QString("OpenglVid: ")
|
|---|
| 815 | #define LOC_ERR QString("OpenglVid Error: ")
|
|---|
| 816 |
|
|---|
| 817 | QMap<int,float> OpenglVideo::pictureAttribs;
|
|---|
| 818 |
|
|---|
| 819 | OpenglVideo::OpenglVideo() :
|
|---|
| 820 | convertBuf(NULL)
|
|---|
| 821 | {
|
|---|
| 822 | }
|
|---|
| 823 |
|
|---|
| 824 | OpenglVideo::~OpenglVideo()
|
|---|
| 825 | {
|
|---|
| 826 | Cleanup();
|
|---|
| 827 | }
|
|---|
| 828 |
|
|---|
| 829 | void OpenglVideo::Cleanup()
|
|---|
| 830 | {
|
|---|
| 831 | ShutDownYUV2RGB();
|
|---|
| 832 | gl->MakeCurrent(true);
|
|---|
| 833 | if (frameBuffer)
|
|---|
| 834 | gl->DeleteFrameBuffer(&frameBuffer);
|
|---|
| 835 | if (frameBufferTexture)
|
|---|
| 836 | gl->DeleteTexture(&frameBufferTexture);
|
|---|
| 837 |
|
|---|
| 838 | for (uint i = 0; i < inputTextures.size(); i++)
|
|---|
| 839 | gl->DeleteTexture(&inputTextures[i]);
|
|---|
| 840 | inputTextures.clear();
|
|---|
| 841 |
|
|---|
| 842 | if (!filters.empty())
|
|---|
| 843 | {
|
|---|
| 844 | map<OpenglFilter,Filter>::iterator it;
|
|---|
| 845 | for (it = filters.begin(); it != filters.end(); it++)
|
|---|
| 846 | {
|
|---|
| 847 | if (it->second.fragmentProgram)
|
|---|
| 848 | gl->DeleteProgram(&(it->second.fragmentProgram));
|
|---|
| 849 | vector<GLuint> temp = it->second.frameBuffers;
|
|---|
| 850 | for (uint i = 0; i < temp.size(); i++)
|
|---|
| 851 | gl->DeleteFrameBuffer(&(temp[i]));
|
|---|
| 852 | temp = it->second.frameBufferTextures;
|
|---|
| 853 | for (uint i = 0; i < temp.size(); i++)
|
|---|
| 854 | gl->DeleteTexture(&(temp[i]));
|
|---|
| 855 | }
|
|---|
| 856 | }
|
|---|
| 857 | filters.clear();
|
|---|
| 858 | }
|
|---|
| 859 |
|
|---|
| 860 | bool OpenglVideo::Init(OpenglContext *glcontext, bool colour_control,
|
|---|
| 861 | bool onscreen, QSize video_size, QRect visible_rect,
|
|---|
| 862 | QRect video_rect, bool viewport_control, bool osd)
|
|---|
| 863 | {
|
|---|
| 864 | gl = glcontext;
|
|---|
| 865 | videoSize = video_size;
|
|---|
| 866 | visibleRect = visible_rect;
|
|---|
| 867 | videoRect = video_rect;
|
|---|
| 868 | masterViewportSize = QSize(1920, 1080);
|
|---|
| 869 | QSize rect = videoSize;
|
|---|
| 870 | GetTextureSize(&rect, videoSize);
|
|---|
| 871 | frameBufferRect = QRect(QPoint(0,0), rect);
|
|---|
| 872 | invertVideo = true;
|
|---|
| 873 | softwareDeinterlacer = "";
|
|---|
| 874 | hardwareDeinterlacing = false;
|
|---|
| 875 | useColourControl = colour_control;
|
|---|
| 876 | viewportControl = viewport_control;
|
|---|
| 877 | inputTextureSize = QSize(0,0);
|
|---|
| 878 | convertSize = QSize(0,0);
|
|---|
| 879 | videoResize = false;
|
|---|
| 880 | videoResizeRect = QRect(0,0,0,0);
|
|---|
| 881 | frameBuffer = 0;
|
|---|
| 882 | currentFrameNum = -1;
|
|---|
| 883 | inputUpdated = false;
|
|---|
| 884 |
|
|---|
| 885 | if (!onscreen)
|
|---|
| 886 | {
|
|---|
| 887 | QSize fb_size(visibleRect.width(), visibleRect.height());
|
|---|
| 888 | GetTextureSize(&fb_size, fb_size);
|
|---|
| 889 | if (!AddFrameBuffer(&frameBuffer, &frameBufferTexture, fb_size))
|
|---|
| 890 | return false;
|
|---|
| 891 | }
|
|---|
| 892 |
|
|---|
| 893 | SetViewPort(visibleRect.width(), visibleRect.height());
|
|---|
| 894 | InitOpengl();
|
|---|
| 895 |
|
|---|
| 896 | if (osd)
|
|---|
| 897 | {
|
|---|
| 898 | QSize osdsize(visibleRect.width(),
|
|---|
| 899 | visibleRect.height());
|
|---|
| 900 | GLuint alphatex = CreateVideoTexture(osdsize, &inputTextureSize,
|
|---|
| 901 | gl->TextureSupport() ? false : true);
|
|---|
| 902 | GLuint yuv12tex = CreateVideoTexture(osdsize, &inputTextureSize, true);
|
|---|
| 903 | if ((alphatex && yuv12tex) && AddFilter(kYUV2RGBA))
|
|---|
| 904 | {
|
|---|
| 905 | inputTextures.push_back(yuv12tex);
|
|---|
| 906 | inputTextures.push_back(alphatex);
|
|---|
| 907 | }
|
|---|
| 908 | }
|
|---|
| 909 | else
|
|---|
| 910 | {
|
|---|
| 911 | GLuint yuv12tex = CreateVideoTexture(videoSize,
|
|---|
| 912 | &inputTextureSize, true);
|
|---|
| 913 | if (yuv12tex && AddFilter(kYUV2RGB))
|
|---|
| 914 | inputTextures.push_back(yuv12tex);
|
|---|
| 915 | }
|
|---|
| 916 |
|
|---|
| 917 | if (filters.empty())
|
|---|
| 918 | {
|
|---|
| 919 | if (osd)
|
|---|
| 920 | {
|
|---|
| 921 | Cleanup();
|
|---|
| 922 | return false;
|
|---|
| 923 | }
|
|---|
| 924 | VERBOSE(VB_PLAYBACK, LOC + "Opengl colour conversion failed.");
|
|---|
| 925 | VERBOSE(VB_PLAYBACK, LOC + "Falling back to software conversion.");
|
|---|
| 926 | VERBOSE(VB_PLAYBACK, LOC + "Any opengl filters will also be disabled.");
|
|---|
| 927 | GLuint yuv12tex = CreateVideoTexture(videoSize,
|
|---|
| 928 | &inputTextureSize, false);
|
|---|
| 929 | if (yuv12tex && AddFilter(kRESIZE))
|
|---|
| 930 | inputTextures.push_back(yuv12tex);
|
|---|
| 931 | else
|
|---|
| 932 | {
|
|---|
| 933 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Fatal error.");
|
|---|
| 934 | Cleanup();
|
|---|
| 935 | return false;
|
|---|
| 936 | }
|
|---|
| 937 | }
|
|---|
| 938 | return true;
|
|---|
| 939 | }
|
|---|
| 940 |
|
|---|
| 941 | OpenglFilter OpenglVideo::GetDeintFilter(void)
|
|---|
| 942 | {
|
|---|
| 943 | if (filters.count(kKDEINT))
|
|---|
| 944 | return kKDEINT;
|
|---|
| 945 | if (filters.count(kLINBLEND))
|
|---|
| 946 | return kLINBLEND;
|
|---|
| 947 | if (filters.count(kONEFIELD))
|
|---|
| 948 | return kONEFIELD;
|
|---|
| 949 | if (filters.count(kBOBDEINT))
|
|---|
| 950 | return kBOBDEINT;
|
|---|
| 951 | if (filters.count(kPROGONEFIELD))
|
|---|
| 952 | return kPROGONEFIELD;
|
|---|
| 953 | if (filters.count(kPROGLINBLEND))
|
|---|
| 954 | return kPROGLINBLEND;
|
|---|
| 955 | if (filters.count(kPROGKDEINT))
|
|---|
| 956 | return kPROGKDEINT;
|
|---|
| 957 | return kNOFILT;
|
|---|
| 958 | }
|
|---|
| 959 |
|
|---|
| 960 | bool OpenglVideo::OptimiseFilters(void)
|
|---|
| 961 | {
|
|---|
| 962 | // if video height does not match display rect height, add resize stage
|
|---|
| 963 | // to preserve field information N.B. assumes interlaced
|
|---|
| 964 | // if video rectangle is smaller than display rectangle, add resize stage
|
|---|
| 965 | // to improve performance
|
|---|
| 966 |
|
|---|
| 967 | bool needResize = ((videoSize.height() != videoRect.height()) ||
|
|---|
| 968 | (videoSize.width() < videoRect.width()));
|
|---|
| 969 | if (needResize && !filters.count(kRESIZE))
|
|---|
| 970 | AddFilter(kRESIZE);
|
|---|
| 971 |
|
|---|
| 972 | map<OpenglFilter, Filter>::reverse_iterator it;
|
|---|
| 973 |
|
|---|
| 974 | // add/remove required frame buffer objects
|
|---|
| 975 | // and link filters
|
|---|
| 976 | uint buffers_needed = 1;
|
|---|
| 977 | bool last_filter = true;
|
|---|
| 978 | bool needtorotate = false;
|
|---|
| 979 | int i = filters.size();
|
|---|
| 980 | for (it = filters.rbegin(); it != filters.rend(); it++, i--)
|
|---|
| 981 | {
|
|---|
| 982 | it->second.outputBuffer = kFrameBufferObject;
|
|---|
| 983 | it->second.rotateFrameBuffers = needtorotate;
|
|---|
| 984 | if (!last_filter)
|
|---|
| 985 | {
|
|---|
| 986 | uint buffers_have = it->second.frameBuffers.size();
|
|---|
| 987 | int buffers_diff = buffers_needed - buffers_have;
|
|---|
| 988 | if (buffers_diff > 0)
|
|---|
| 989 | {
|
|---|
| 990 | GLuint tmp_buf, tmp_tex;
|
|---|
| 991 | QSize fb_size(videoSize.width(), videoSize.height());
|
|---|
| 992 | GetTextureSize(&fb_size, fb_size);
|
|---|
| 993 | for (int i = 0; i < buffers_diff; i++)
|
|---|
| 994 | {
|
|---|
| 995 | if (!AddFrameBuffer(&tmp_buf, &tmp_tex, fb_size))
|
|---|
| 996 | return false;
|
|---|
| 997 | else
|
|---|
| 998 | {
|
|---|
| 999 | it->second.frameBuffers.push_back(tmp_buf);
|
|---|
| 1000 | it->second.frameBufferTextures.push_back(tmp_tex);
|
|---|
| 1001 | }
|
|---|
| 1002 | }
|
|---|
| 1003 | }
|
|---|
| 1004 | else if (buffers_diff < 0)
|
|---|
| 1005 | {
|
|---|
| 1006 | for (int i = 0; i > buffers_diff; i--)
|
|---|
| 1007 | {
|
|---|
| 1008 | gl->DeleteFrameBuffer(&(it->second.frameBuffers.back()));
|
|---|
| 1009 | gl->DeleteTexture(&(it->second.frameBufferTextures.back()));
|
|---|
| 1010 | it->second.frameBuffers.pop_back();
|
|---|
| 1011 | it->second.frameBufferTextures.pop_back();
|
|---|
| 1012 | }
|
|---|
| 1013 | }
|
|---|
| 1014 | }
|
|---|
| 1015 | else
|
|---|
| 1016 | {
|
|---|
| 1017 | last_filter = false;
|
|---|
| 1018 | }
|
|---|
| 1019 | buffers_needed = it->second.numInputs;
|
|---|
| 1020 | needtorotate = (it->first == kKDEINT ||
|
|---|
| 1021 | it->first == kLINBLEND ||
|
|---|
| 1022 | it->first == kPROGONEFIELD ||
|
|---|
| 1023 | it->first == kPROGLINBLEND ||
|
|---|
| 1024 | it->first == kPROGKDEINT);
|
|---|
| 1025 | }
|
|---|
| 1026 |
|
|---|
| 1027 | bool deinterlacing = hardwareDeinterlacing;
|
|---|
| 1028 | hardwareDeinterlacing = true;
|
|---|
| 1029 | SetDeinterlacing(false);
|
|---|
| 1030 | if (deinterlacing)
|
|---|
| 1031 | SetDeinterlacing(deinterlacing);
|
|---|
| 1032 | return true;
|
|---|
| 1033 | }
|
|---|
| 1034 |
|
|---|
| 1035 | void OpenglVideo::SetFiltering(void)
|
|---|
| 1036 | {
|
|---|
| 1037 | // filter settings included for performance only
|
|---|
| 1038 | // no (obvious) quality improvement over GL_LINEAR throughout
|
|---|
| 1039 | if (filters.empty())
|
|---|
| 1040 | return;
|
|---|
| 1041 |
|
|---|
| 1042 | if (filters.size() == 1)
|
|---|
| 1043 | {
|
|---|
| 1044 | SetTextureFilters(&inputTextures, GL_LINEAR);
|
|---|
| 1045 | return;
|
|---|
| 1046 | }
|
|---|
| 1047 |
|
|---|
| 1048 | SetTextureFilters(&inputTextures, GL_NEAREST);
|
|---|
| 1049 | vector<GLuint> textures;
|
|---|
| 1050 | map<OpenglFilter,Filter>::iterator it;
|
|---|
| 1051 | for (it = filters.begin(); it != filters.end(); it++)
|
|---|
| 1052 | SetTextureFilters(&(it->second.frameBufferTextures), GL_NEAREST);
|
|---|
| 1053 |
|
|---|
| 1054 | // resize or last active (ie don't need resize) need GL_LINEAR
|
|---|
| 1055 | map<OpenglFilter, Filter>::reverse_iterator rit;
|
|---|
| 1056 | bool next = false;
|
|---|
| 1057 | bool resize = filters.count(kRESIZE);
|
|---|
| 1058 | for (rit = filters.rbegin(); rit != filters.rend(); rit++)
|
|---|
| 1059 | {
|
|---|
| 1060 | if (next && (rit->second.outputBuffer != kNoBuffer))
|
|---|
| 1061 | {
|
|---|
| 1062 | SetTextureFilters(&(rit->second.frameBufferTextures), GL_LINEAR);
|
|---|
| 1063 | return;
|
|---|
| 1064 | break;
|
|---|
| 1065 | }
|
|---|
| 1066 | if (resize)
|
|---|
| 1067 | if (rit->first == kRESIZE)
|
|---|
| 1068 | next = true;
|
|---|
| 1069 | else
|
|---|
| 1070 | if (rit->second.outputBuffer == kDefaultBuffer)
|
|---|
| 1071 | next = true;
|
|---|
| 1072 | }
|
|---|
| 1073 | SetTextureFilters(&inputTextures, GL_LINEAR);
|
|---|
| 1074 | }
|
|---|
| 1075 |
|
|---|
| 1076 | bool OpenglVideo::ReInit(OpenglContext *glcontext, bool colour_control,
|
|---|
| 1077 | bool onscreen, QSize video_size, QRect visible_rect,
|
|---|
| 1078 | QRect video_rect, bool viewport_control, bool osd)
|
|---|
| 1079 | {
|
|---|
| 1080 | VERBOSE(VB_PLAYBACK, LOC + "Reinit");
|
|---|
| 1081 | gl->MakeCurrent(true);
|
|---|
| 1082 | QString harddeint = GetDeinterlacer(); // N.B. only adds back deinterlacer
|
|---|
| 1083 | QString softdeint = softwareDeinterlacer;
|
|---|
| 1084 | bool interlacing = hardwareDeinterlacing;
|
|---|
| 1085 | bool resize = videoResize;
|
|---|
| 1086 | QRect resize_rect = videoResizeRect;
|
|---|
| 1087 |
|
|---|
| 1088 | Cleanup();
|
|---|
| 1089 | bool success = Init(glcontext, colour_control, onscreen, video_size,
|
|---|
| 1090 | visible_rect, video_rect, viewport_control, osd);
|
|---|
| 1091 | if (harddeint != "")
|
|---|
| 1092 | success &= AddDeinterlacer(harddeint);
|
|---|
| 1093 | softwareDeinterlacer = softdeint;
|
|---|
| 1094 | SetDeinterlacing(interlacing);
|
|---|
| 1095 | if (resize)
|
|---|
| 1096 | SetVideoResize(&resize_rect);
|
|---|
| 1097 |
|
|---|
| 1098 | return success;
|
|---|
| 1099 | }
|
|---|
| 1100 |
|
|---|
| 1101 | bool OpenglVideo::AddFilter(OpenglFilter filter)
|
|---|
| 1102 | {
|
|---|
| 1103 | if (filters.count(filter))
|
|---|
| 1104 | return true;
|
|---|
| 1105 |
|
|---|
| 1106 | VERBOSE(VB_PLAYBACK, LOC + QString("Creating %1 filter.")
|
|---|
| 1107 | .arg(FilterToString(filter)));
|
|---|
| 1108 | gl->MakeCurrent(true);
|
|---|
| 1109 | Filter temp;
|
|---|
| 1110 | if (filter == kYUV2RGBA || filter == kLINBLEND || filter == kKDEINT)
|
|---|
| 1111 | temp.numInputs = 2;
|
|---|
| 1112 | else if (filter == kPROGONEFIELD || filter == kPROGKDEINT ||
|
|---|
| 1113 | filter == kPROGLINBLEND)
|
|---|
| 1114 | temp.numInputs = 3;
|
|---|
| 1115 | else
|
|---|
| 1116 | temp.numInputs = 1;
|
|---|
| 1117 |
|
|---|
| 1118 | GLuint program = 0;
|
|---|
| 1119 | if (filter != kNOFILT && filter != kRESIZE)
|
|---|
| 1120 | {
|
|---|
| 1121 | program = AddFragmentProgram(filter);
|
|---|
| 1122 | if (!program)
|
|---|
| 1123 | return false;
|
|---|
| 1124 | }
|
|---|
| 1125 | temp.fragmentProgram = program;
|
|---|
| 1126 | temp.outputBuffer = kDefaultBuffer;
|
|---|
| 1127 | temp.rotateFrameBuffers = false;
|
|---|
| 1128 | temp.frameBuffers.clear();
|
|---|
| 1129 | temp.frameBufferTextures.clear();
|
|---|
| 1130 | filters[filter] = temp;
|
|---|
| 1131 | if (OptimiseFilters())
|
|---|
| 1132 | return true;
|
|---|
| 1133 |
|
|---|
| 1134 | RemoveFilter(filter);
|
|---|
| 1135 | return false;
|
|---|
| 1136 | }
|
|---|
| 1137 |
|
|---|
| 1138 | bool OpenglVideo::RemoveFilter(OpenglFilter filter)
|
|---|
| 1139 | {
|
|---|
| 1140 | if (!filters.count(filter))
|
|---|
| 1141 | return true;
|
|---|
| 1142 |
|
|---|
| 1143 | gl->MakeCurrent(true);
|
|---|
| 1144 | gl->DeleteProgram(&(filters[filter].fragmentProgram));
|
|---|
| 1145 | vector<GLuint> temp;
|
|---|
| 1146 | vector<GLuint>::iterator it;
|
|---|
| 1147 | temp = filters[filter].frameBuffers;
|
|---|
| 1148 | for (it = temp.begin(); it != temp.end(); it++)
|
|---|
| 1149 | gl->DeleteFrameBuffer(&(*(it)));
|
|---|
| 1150 | temp = filters[filter].frameBufferTextures;
|
|---|
| 1151 | for (it = temp.begin(); it != temp.end(); it++)
|
|---|
| 1152 | gl->DeleteTexture(&(*(it)));
|
|---|
| 1153 | filters.erase(filter);
|
|---|
| 1154 | gl->MakeCurrent(false);
|
|---|
| 1155 |
|
|---|
| 1156 | return true;
|
|---|
| 1157 | }
|
|---|
| 1158 |
|
|---|
| 1159 | bool OpenglVideo::AddDeinterlacer(QString filter)
|
|---|
| 1160 | {
|
|---|
| 1161 | QString current_deinterlacer = GetDeinterlacer();
|
|---|
| 1162 | if (current_deinterlacer != "")
|
|---|
| 1163 | {
|
|---|
| 1164 | if (current_deinterlacer == filter)
|
|---|
| 1165 | return true;
|
|---|
| 1166 | else
|
|---|
| 1167 | RemoveFilter(current_deinterlacer);
|
|---|
| 1168 | }
|
|---|
| 1169 | return AddFilter(filter);
|
|---|
| 1170 | }
|
|---|
| 1171 |
|
|---|
| 1172 | GLuint OpenglVideo::AddFragmentProgram(OpenglFilter name)
|
|---|
| 1173 | {
|
|---|
| 1174 | if (!gl->FragProgSupport())
|
|---|
| 1175 | {
|
|---|
| 1176 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Fragment programs not supported");
|
|---|
| 1177 | return false;
|
|---|
| 1178 | }
|
|---|
| 1179 | GLuint ret;
|
|---|
| 1180 | QString program = GetProgramString(name);
|
|---|
| 1181 | program.replace("%1", gl->TextureSupport() ? "RECT" : "2D");
|
|---|
| 1182 | if (gl->CreateProgram(&program, &ret))
|
|---|
| 1183 | {
|
|---|
| 1184 | VERBOSE(VB_PLAYBACK, LOC + QString("Created fragment program %1.")
|
|---|
| 1185 | .arg(FilterToString(name)));
|
|---|
| 1186 | return ret;
|
|---|
| 1187 | }
|
|---|
| 1188 | return 0;
|
|---|
| 1189 | }
|
|---|
| 1190 |
|
|---|
| 1191 | bool OpenglVideo::AddFrameBuffer(GLuint *framebuffer,
|
|---|
| 1192 | GLuint *texture, QSize size)
|
|---|
| 1193 | {
|
|---|
| 1194 | if (!gl->FrameBufSupport())
|
|---|
| 1195 | {
|
|---|
| 1196 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Offscreen rendering not supported.");
|
|---|
| 1197 | return false;
|
|---|
| 1198 | }
|
|---|
| 1199 | GLuint tmp_buf, tmp_tex;
|
|---|
| 1200 | gl->CreateTexture(&tmp_tex);
|
|---|
| 1201 | if(gl->CreateFrameBuffer( &tmp_buf, &tmp_tex, size.width(), size.height()))
|
|---|
| 1202 | {
|
|---|
| 1203 | *framebuffer = tmp_buf;
|
|---|
| 1204 | *texture = tmp_tex;
|
|---|
| 1205 | return true;
|
|---|
| 1206 | }
|
|---|
| 1207 | gl->DeleteTexture(&tmp_tex);
|
|---|
| 1208 | return false;
|
|---|
| 1209 | }
|
|---|
| 1210 |
|
|---|
| 1211 | void OpenglVideo::SetViewPort(uint width, uint height)
|
|---|
| 1212 | {
|
|---|
| 1213 | uint w = max((int)width, videoSize.width());
|
|---|
| 1214 | uint h = max((int)height, videoSize.height());
|
|---|
| 1215 | viewportSize = QSize(w, h);
|
|---|
| 1216 | if (!viewportControl)
|
|---|
| 1217 | return;
|
|---|
| 1218 | VERBOSE(VB_PLAYBACK, LOC +
|
|---|
| 1219 | QString("Viewport: %1x%2").arg(w).arg(h));
|
|---|
| 1220 | ViewPort(w, h);
|
|---|
| 1221 | }
|
|---|
| 1222 |
|
|---|
| 1223 | void OpenglVideo::ViewPort(uint width, uint height)
|
|---|
| 1224 | {
|
|---|
| 1225 | glViewport( 0, 0, width, height);
|
|---|
| 1226 | glMatrixMode( GL_PROJECTION );
|
|---|
| 1227 | glLoadIdentity();
|
|---|
| 1228 | glOrtho( 0, width - 1, 0, height - 1, 1, -1 ); // aargh...
|
|---|
| 1229 | glMatrixMode( GL_MODELVIEW );
|
|---|
| 1230 | glLoadIdentity();
|
|---|
| 1231 | }
|
|---|
| 1232 |
|
|---|
| 1233 | void OpenglVideo::InitOpengl(void)
|
|---|
| 1234 | {
|
|---|
| 1235 | gl->MakeCurrent(true);
|
|---|
| 1236 | glDisable( GL_BLEND );
|
|---|
| 1237 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // for gl osd
|
|---|
| 1238 | glDisable( GL_DEPTH_TEST );
|
|---|
| 1239 | glDepthMask( GL_FALSE );
|
|---|
| 1240 | glDisable( GL_CULL_FACE );
|
|---|
| 1241 | gl->EnableTextures();;
|
|---|
| 1242 | glShadeModel( GL_FLAT );
|
|---|
| 1243 | glDisable( GL_POLYGON_SMOOTH );
|
|---|
| 1244 | glDisable( GL_LINE_SMOOTH );
|
|---|
| 1245 | glDisable( GL_POINT_SMOOTH );
|
|---|
| 1246 | glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
|
|---|
| 1247 | glClear( GL_COLOR_BUFFER_BIT );
|
|---|
| 1248 | glFlush();
|
|---|
| 1249 | gl->MakeCurrent(false);
|
|---|
| 1250 | }
|
|---|
| 1251 |
|
|---|
| 1252 | GLuint OpenglVideo::CreateVideoTexture(QSize size, QSize *tex_size, bool YUV12)
|
|---|
| 1253 | {
|
|---|
| 1254 | GLuint tmp_tex;
|
|---|
| 1255 | QSize temp = size;
|
|---|
| 1256 | gl->CreateTexture(&tmp_tex);
|
|---|
| 1257 | GetTextureSize(&temp, size);
|
|---|
| 1258 | if (YUV12)
|
|---|
| 1259 | {
|
|---|
| 1260 | if (gl->TextureSupport())
|
|---|
| 1261 | {
|
|---|
| 1262 | temp.rheight() = size.height() * 3 / 2;
|
|---|
| 1263 | }
|
|---|
| 1264 | else
|
|---|
| 1265 | {
|
|---|
| 1266 | if ((size.height() * 1.5) > temp.height())
|
|---|
| 1267 | temp.rheight() *= 2;
|
|---|
| 1268 | }
|
|---|
| 1269 | }
|
|---|
| 1270 | if (temp.width() > gl->GetMaxTexSize() ||
|
|---|
| 1271 | temp.height() > gl->GetMaxTexSize() ||
|
|---|
| 1272 | !gl->SetupTexture(temp, &tmp_tex, GL_LINEAR))
|
|---|
| 1273 | {
|
|---|
| 1274 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Could not create texture.");
|
|---|
| 1275 | gl->DeleteTexture(&tmp_tex);
|
|---|
| 1276 | return 0;
|
|---|
| 1277 | }
|
|---|
| 1278 |
|
|---|
| 1279 | tex_size->setWidth(temp.width());
|
|---|
| 1280 | tex_size->setHeight(temp.height());
|
|---|
| 1281 | VERBOSE(VB_PLAYBACK, LOC + QString("Created main input texture %1x%2")
|
|---|
| 1282 | .arg(temp.width()).arg(temp.height()));
|
|---|
| 1283 | return tmp_tex;
|
|---|
| 1284 | }
|
|---|
| 1285 |
|
|---|
| 1286 | void OpenglVideo::GetTextureSize(QSize *temp, QSize size)
|
|---|
| 1287 | {
|
|---|
| 1288 | if (gl->TextureSupport())
|
|---|
| 1289 | return; glOrtho( 0, width, 0, height, 1, -1 ); // aargh...
|
|---|
| 1290 | int w, h;
|
|---|
| 1291 | w = h = 64;
|
|---|
| 1292 | while (w < size.width()) { w *= 2; }
|
|---|
| 1293 | while (h < size.height()) { h *= 2; }
|
|---|
| 1294 | temp->setWidth(w);
|
|---|
| 1295 | temp->setHeight(h);
|
|---|
| 1296 | }
|
|---|
| 1297 |
|
|---|
| 1298 | void OpenglVideo::UpdateInputFrame(VideoFrame *frame)
|
|---|
| 1299 | {
|
|---|
| 1300 | bool errored = false;
|
|---|
| 1301 | if (frame->width != videoSize.width() ||
|
|---|
| 1302 | frame->height != videoSize.height() ||
|
|---|
| 1303 | frame->width < 1 ||
|
|---|
| 1304 | frame->height < 1)
|
|---|
| 1305 | errored = true;
|
|---|
| 1306 |
|
|---|
| 1307 | if (filters.count(kYUV2RGB) && !errored && frame->codec == FMT_YV12)
|
|---|
| 1308 | UpdateInput(frame->buf, 0, FMT_YV12, videoSize);
|
|---|
| 1309 | else // software yuv2rgb
|
|---|
| 1310 | {
|
|---|
| 1311 | if (errored)
|
|---|
| 1312 | {
|
|---|
| 1313 | ShutDownYUV2RGB();
|
|---|
| 1314 | return;
|
|---|
| 1315 | }
|
|---|
| 1316 | if (convertSize != videoSize)
|
|---|
| 1317 | {
|
|---|
| 1318 | ShutDownYUV2RGB();
|
|---|
| 1319 | VERBOSE(VB_PLAYBACK, LOC + "Init software conversion.");
|
|---|
| 1320 | convertSize = videoSize;
|
|---|
| 1321 | convertBuf = new unsigned char[(videoSize.width() *
|
|---|
| 1322 | videoSize.height() * 3) + 4];
|
|---|
| 1323 | }
|
|---|
| 1324 | if (convertBuf)
|
|---|
| 1325 | {
|
|---|
| 1326 | AVPicture img_in, img_out;
|
|---|
| 1327 | avpicture_fill(&img_out, (uint8_t *)convertBuf, PIX_FMT_RGB24,
|
|---|
| 1328 | convertSize.width(), convertSize.height());
|
|---|
| 1329 | avpicture_fill(&img_in, (uint8_t *)frame->buf, PIX_FMT_YUV420P,
|
|---|
| 1330 | convertSize.width(), convertSize.height());
|
|---|
| 1331 | img_convert(&img_out, PIX_FMT_RGB24,
|
|---|
| 1332 | &img_in, PIX_FMT_YUV420P,
|
|---|
| 1333 | convertSize.width(), convertSize.height());
|
|---|
| 1334 | UpdateInput(convertBuf, 0, FMT_RGB24, convertSize);
|
|---|
| 1335 | }
|
|---|
| 1336 | }
|
|---|
| 1337 | }
|
|---|
| 1338 |
|
|---|
| 1339 | void OpenglVideo::UpdateInput(unsigned char *buf, uint texture,
|
|---|
| 1340 | int format, QSize size)
|
|---|
| 1341 | {
|
|---|
| 1342 | inputUpdated = false;
|
|---|
| 1343 | if (texture > (inputTextures.size() - 1))
|
|---|
| 1344 | return;
|
|---|
| 1345 |
|
|---|
| 1346 | glBindTexture( gl->m_texture, inputTextures[texture] );
|
|---|
| 1347 | switch (format)
|
|---|
| 1348 | {
|
|---|
| 1349 | case FMT_YV12:
|
|---|
| 1350 | glTexSubImage2D( gl->m_texture, 0, 0, 0,
|
|---|
| 1351 | size.width(), size.height(),
|
|---|
| 1352 | GL_LUMINANCE, GL_UNSIGNED_BYTE, buf);
|
|---|
| 1353 | glTexSubImage2D( gl->m_texture, 0, 0, size.height() ,
|
|---|
| 1354 | size.width() / 2, size.height() / 2,
|
|---|
| 1355 | GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|---|
| 1356 | buf + (size.width() * size.height()));
|
|---|
| 1357 | glTexSubImage2D( gl->m_texture, 0, size.width() / 2,
|
|---|
| 1358 | size.height() , size.width() / 2,
|
|---|
| 1359 | size.height() / 2, GL_LUMINANCE,
|
|---|
| 1360 | GL_UNSIGNED_BYTE,buf + (size.width()
|
|---|
| 1361 | * size.height()) * 5 / 4);
|
|---|
| 1362 | break;
|
|---|
| 1363 | case FMT_RGB24:
|
|---|
| 1364 | glTexSubImage2D( gl->m_texture, 0, 0, 0,
|
|---|
| 1365 | size.width(), size.height(),
|
|---|
| 1366 | GL_RGB, GL_UNSIGNED_BYTE, buf);
|
|---|
| 1367 | break;
|
|---|
| 1368 | case FMT_ALPHA:
|
|---|
| 1369 | glTexSubImage2D( gl->m_texture, 0, 0, 0,
|
|---|
| 1370 | size.width(), size.height(),
|
|---|
| 1371 | GL_ALPHA, GL_UNSIGNED_BYTE,
|
|---|
| 1372 | buf);
|
|---|
| 1373 | break;
|
|---|
| 1374 | default:
|
|---|
| 1375 | break;
|
|---|
| 1376 | }
|
|---|
| 1377 | inputUpdated = true;
|
|---|
| 1378 | }
|
|---|
| 1379 |
|
|---|
| 1380 | void OpenglVideo::ShutDownYUV2RGB(void)
|
|---|
| 1381 | {
|
|---|
| 1382 | if (convertBuf)
|
|---|
| 1383 | {
|
|---|
| 1384 | delete convertBuf;
|
|---|
| 1385 | convertBuf = NULL;
|
|---|
| 1386 | }
|
|---|
| 1387 | convertSize = QSize(0,0);
|
|---|
| 1388 | }
|
|---|
| 1389 |
|
|---|
| 1390 | void OpenglVideo::SetVideoResize(QRect *size)
|
|---|
| 1391 | {
|
|---|
| 1392 | if (!size || size == NULL)
|
|---|
| 1393 | {
|
|---|
| 1394 | videoResize = false;
|
|---|
| 1395 | videoResizeRect = QRect(0, 0, 0, 0);
|
|---|
| 1396 | return;
|
|---|
| 1397 | }
|
|---|
| 1398 |
|
|---|
| 1399 | bool abort = (size->right() > videoSize.width() ||
|
|---|
| 1400 | size->bottom() > videoSize.height() ||
|
|---|
| 1401 | size->width() > videoSize.width() ||
|
|---|
| 1402 | size->height() > videoSize.height());
|
|---|
| 1403 | // if resize == existing frame, no need to carry on
|
|---|
| 1404 | abort |= !size->left() && !size->top() && (size->size() == videoSize);
|
|---|
| 1405 |
|
|---|
| 1406 | if (abort)
|
|---|
| 1407 | {
|
|---|
| 1408 | videoResize = false;
|
|---|
| 1409 | videoResizeRect = QRect(0, 0, 0, 0);
|
|---|
| 1410 | return;
|
|---|
| 1411 | }
|
|---|
| 1412 |
|
|---|
| 1413 | videoResize = true;
|
|---|
| 1414 | videoResizeRect = *size;
|
|---|
| 1415 | }
|
|---|
| 1416 |
|
|---|
| 1417 | void OpenglVideo::CalculateResize(float *left, float *top,
|
|---|
| 1418 | float *right, float *bottom)
|
|---|
| 1419 | {
|
|---|
| 1420 | // FIXME video aspect == display aspect
|
|---|
| 1421 | float height = visibleRect.height();
|
|---|
| 1422 | float new_top = height - ((float)videoResizeRect.bottom() /
|
|---|
| 1423 | (float)videoSize.height()) * height;
|
|---|
| 1424 | float new_bottom = height - ((float)videoResizeRect.top() /
|
|---|
| 1425 | (float)videoSize.height()) * height;
|
|---|
| 1426 | *left = ((float)videoResizeRect.left() / (float)videoSize.width()) *
|
|---|
| 1427 | visibleRect.width();
|
|---|
| 1428 | *right = ((float)videoResizeRect.right() / (float)videoSize.width()) *
|
|---|
| 1429 | visibleRect.width();
|
|---|
| 1430 | *top = new_top;
|
|---|
| 1431 | *bottom = new_bottom;
|
|---|
| 1432 | }
|
|---|
| 1433 |
|
|---|
| 1434 | void OpenglVideo::SetDeinterlacing(bool deinterlacing)
|
|---|
| 1435 | {
|
|---|
| 1436 | if (deinterlacing == hardwareDeinterlacing)
|
|---|
| 1437 | return;
|
|---|
| 1438 | VERBOSE(VB_PLAYBACK, LOC + QString("Turning %1 deinterlacing.")
|
|---|
| 1439 | .arg(deinterlacing ? "on" : "off"));
|
|---|
| 1440 | hardwareDeinterlacing = deinterlacing;
|
|---|
| 1441 | map<OpenglFilter, Filter>::iterator it;
|
|---|
| 1442 | for (it = filters.begin(); it !=filters.end(); it++)
|
|---|
| 1443 | {
|
|---|
| 1444 | it->second.outputBuffer = kFrameBufferObject;
|
|---|
| 1445 | if (it->first >= kKDEINT && it->first <= kPROGONEFIELD)
|
|---|
| 1446 | {
|
|---|
| 1447 | if (!deinterlacing)
|
|---|
| 1448 | it->second.outputBuffer = kNoBuffer;
|
|---|
| 1449 | }
|
|---|
| 1450 | }
|
|---|
| 1451 | map<OpenglFilter, Filter>::reverse_iterator rit;
|
|---|
| 1452 | for (rit = filters.rbegin(); rit !=filters.rend(); rit++)
|
|---|
| 1453 | {
|
|---|
| 1454 | if (rit->second.outputBuffer == kFrameBufferObject)
|
|---|
| 1455 | {
|
|---|
| 1456 | rit->second.outputBuffer = kDefaultBuffer;
|
|---|
| 1457 | break;
|
|---|
| 1458 | }
|
|---|
| 1459 | }
|
|---|
| 1460 | gl->MakeCurrent(true);
|
|---|
| 1461 | SetFiltering();
|
|---|
| 1462 | gl->MakeCurrent(false);
|
|---|
| 1463 | }
|
|---|
| 1464 |
|
|---|
| 1465 | void OpenglVideo::Show(FrameScanType scan, bool softwareDeinterlacing,
|
|---|
| 1466 | long long frame)
|
|---|
| 1467 | {
|
|---|
| 1468 | if (inputTextures.empty() || filters.empty())
|
|---|
| 1469 | return;
|
|---|
| 1470 |
|
|---|
| 1471 | vector<GLuint> inputs = inputTextures;
|
|---|
| 1472 | QSize inputsize = inputTextureSize;
|
|---|
| 1473 | uint numfilters = filters.size();
|
|---|
| 1474 |
|
|---|
| 1475 | map<OpenglFilter, Filter>::iterator it;
|
|---|
| 1476 | for (it = filters.begin(); it !=filters.end(); it++)
|
|---|
| 1477 | {
|
|---|
| 1478 | if (it->second.rotateFrameBuffers &&
|
|---|
| 1479 | !(it->first == kYUV2RGB && scan == kScan_Intr2ndField))
|
|---|
| 1480 | {
|
|---|
| 1481 | Rotate(&(it->second.frameBufferTextures));
|
|---|
| 1482 | Rotate(&(it->second.frameBuffers));
|
|---|
| 1483 | }
|
|---|
| 1484 |
|
|---|
| 1485 | // skip disabled filters
|
|---|
| 1486 | if (it->second.outputBuffer == kNoBuffer)
|
|---|
| 1487 | continue;
|
|---|
| 1488 |
|
|---|
| 1489 | OpenglFilter type = it->first;
|
|---|
| 1490 | Filter filter = it->second;
|
|---|
| 1491 |
|
|---|
| 1492 | // skip colour conversion for frames already in frame buffer
|
|---|
| 1493 | if (!inputUpdated)
|
|---|
| 1494 | if (frame == currentFrameNum && type == kYUV2RGB && frame != 0)
|
|---|
| 1495 | if (!(softwareDeinterlacing &&
|
|---|
| 1496 | softwareDeinterlacer == "bobdeint"))
|
|---|
| 1497 | {
|
|---|
| 1498 | inputs = filter.frameBufferTextures;
|
|---|
| 1499 | inputsize = videoSize;
|
|---|
| 1500 | continue;
|
|---|
| 1501 | }
|
|---|
| 1502 |
|
|---|
| 1503 | // texture coordinates
|
|---|
| 1504 | float t_right = (float)videoSize.width();
|
|---|
| 1505 | float t_bottom = (float)videoSize.height();
|
|---|
| 1506 | float t_top = 0.0f;
|
|---|
| 1507 |
|
|---|
| 1508 | if (!gl->TextureSupport())
|
|---|
| 1509 | {
|
|---|
| 1510 | t_right /= inputsize.width();
|
|---|
| 1511 | t_bottom /= inputsize.height();
|
|---|
| 1512 | }
|
|---|
| 1513 |
|
|---|
| 1514 | float full_height = t_bottom;
|
|---|
| 1515 | float line_height = (t_bottom / (float)videoSize.height());
|
|---|
| 1516 | float bob = line_height / 2.0f;
|
|---|
| 1517 |
|
|---|
| 1518 | if (type == kBOBDEINT)
|
|---|
| 1519 | {
|
|---|
| 1520 | if (scan == kScan_Interlaced)
|
|---|
| 1521 | {
|
|---|
| 1522 | t_bottom += bob;
|
|---|
| 1523 | t_top += bob;
|
|---|
| 1524 | }
|
|---|
| 1525 | if (scan == kScan_Intr2ndField)
|
|---|
| 1526 | {
|
|---|
| 1527 | t_bottom -= bob;
|
|---|
| 1528 | t_top -= bob;
|
|---|
| 1529 | }
|
|---|
| 1530 | }
|
|---|
| 1531 | if (softwareDeinterlacer == "bobdeint" &&
|
|---|
| 1532 | softwareDeinterlacing &&
|
|---|
| 1533 | type == kYUV2RGB)
|
|---|
| 1534 | {
|
|---|
| 1535 | bob = line_height / 4.0f;
|
|---|
| 1536 | if (scan == kScan_Interlaced)
|
|---|
| 1537 | {
|
|---|
| 1538 | t_bottom /= 2;
|
|---|
| 1539 | t_bottom += bob;
|
|---|
| 1540 | t_top += bob;
|
|---|
| 1541 | }
|
|---|
| 1542 | if (scan == kScan_Intr2ndField)
|
|---|
| 1543 | {
|
|---|
| 1544 | t_top = t_bottom / 2;
|
|---|
| 1545 | t_bottom -= bob;
|
|---|
| 1546 | t_top -= bob;
|
|---|
| 1547 | }
|
|---|
| 1548 | }
|
|---|
| 1549 |
|
|---|
| 1550 | // vertex coordinates
|
|---|
| 1551 | QRect display;
|
|---|
| 1552 | if (filter.outputBuffer == kDefaultBuffer)
|
|---|
| 1553 | display = videoRect;
|
|---|
| 1554 | else
|
|---|
| 1555 | display = frameBufferRect;
|
|---|
| 1556 |
|
|---|
| 1557 | float vleft = display.left();
|
|---|
| 1558 | float vright = display.right();
|
|---|
| 1559 | float vtop = display.top();
|
|---|
| 1560 | float vbot = display.bottom();
|
|---|
| 1561 |
|
|---|
| 1562 | // resize for interactive tv
|
|---|
| 1563 | if (videoResize && filter.outputBuffer == kDefaultBuffer)
|
|---|
| 1564 | CalculateResize(&vleft, &vtop, &vright, &vbot);
|
|---|
| 1565 |
|
|---|
| 1566 | if (invertVideo && (type == kYUV2RGB || type == kYUV2RGBA)
|
|---|
| 1567 | || (type == kRESIZE && numfilters == 1))
|
|---|
| 1568 | {
|
|---|
| 1569 | float temp = vtop;
|
|---|
| 1570 | vtop = vbot;
|
|---|
| 1571 | vbot = temp;
|
|---|
| 1572 | }
|
|---|
| 1573 |
|
|---|
| 1574 | // bind correct frame buffer (default onscreen) and set viewport
|
|---|
| 1575 | switch (filter.outputBuffer)
|
|---|
| 1576 | {
|
|---|
| 1577 | case kDefaultBuffer:
|
|---|
| 1578 | if (frameBuffer)
|
|---|
| 1579 | gl->m_glBindFramebufferEXT( GL_FRAMEBUFFER_EXT,
|
|---|
| 1580 | frameBuffer);
|
|---|
| 1581 | // clear the buffer
|
|---|
| 1582 | if (viewportControl)
|
|---|
| 1583 | {
|
|---|
| 1584 | glClear(GL_COLOR_BUFFER_BIT);
|
|---|
| 1585 | ViewPort(visibleRect.width(), visibleRect.height());
|
|---|
| 1586 | }
|
|---|
| 1587 | else
|
|---|
| 1588 | ViewPort(masterViewportSize.width(),
|
|---|
| 1589 | masterViewportSize.height());
|
|---|
| 1590 | break;
|
|---|
| 1591 | case kFrameBufferObject:
|
|---|
| 1592 | gl->m_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,
|
|---|
| 1593 | filter.frameBuffers[0] );
|
|---|
| 1594 | ViewPort(frameBufferRect.width(), frameBufferRect.height());
|
|---|
| 1595 | break;
|
|---|
| 1596 | case kNoBuffer:
|
|---|
| 1597 | continue;
|
|---|
| 1598 | }
|
|---|
| 1599 |
|
|---|
| 1600 | // bind correct textures
|
|---|
| 1601 | for (uint i = 0; i < inputs.size(); i++)
|
|---|
| 1602 | {
|
|---|
| 1603 | glActiveTexture( GL_TEXTURE0 + i );
|
|---|
| 1604 | glBindTexture( gl->m_texture, inputs[i] );
|
|---|
| 1605 | }
|
|---|
| 1606 |
|
|---|
| 1607 | // enable fragment program and set any environment variables
|
|---|
| 1608 | if (!(type == kNOFILT || type == kRESIZE))
|
|---|
| 1609 | {
|
|---|
| 1610 | glEnable ( GL_FRAGMENT_PROGRAM_ARB );
|
|---|
| 1611 | gl->m_glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB,
|
|---|
| 1612 | filter.fragmentProgram );
|
|---|
| 1613 | float field = -line_height;
|
|---|
| 1614 | switch (type)
|
|---|
| 1615 | {
|
|---|
| 1616 | case kYUV2RGB:
|
|---|
| 1617 | case kYUV2RGBA:
|
|---|
| 1618 | gl->m_glProgramEnvParameter4fARB (GL_FRAGMENT_PROGRAM_ARB,
|
|---|
| 1619 | 1, t_right / 2.0f, full_height, 0.0f, 0.0f);
|
|---|
| 1620 | if (useColourControl)
|
|---|
| 1621 | gl->m_glProgramEnvParameter4fARB(
|
|---|
| 1622 | GL_FRAGMENT_PROGRAM_ARB, 0,
|
|---|
| 1623 | (pictureAttribs[kPictureAttribute_Brightness] / 50 ) - 0.5,
|
|---|
| 1624 | (pictureAttribs[kPictureAttribute_Contrast] / 50),
|
|---|
| 1625 | (pictureAttribs[kPictureAttribute_Colour] / 50),
|
|---|
| 1626 | 0.0f);
|
|---|
| 1627 | break;
|
|---|
| 1628 | case kBOBDEINT:
|
|---|
| 1629 | case kPROGONEFIELD:
|
|---|
| 1630 | case kPROGKDEINT:
|
|---|
| 1631 | case kPROGLINBLEND:
|
|---|
| 1632 | if (scan == kScan_Intr2ndField)
|
|---|
| 1633 | field *= -1;
|
|---|
| 1634 | case kONEFIELD:
|
|---|
| 1635 | case kKDEINT:
|
|---|
| 1636 | case kLINBLEND:
|
|---|
| 1637 | gl->m_glProgramEnvParameter4fARB ( GL_FRAGMENT_PROGRAM_ARB,
|
|---|
| 1638 | 0, line_height * 2, field, 0.0f, 0.0f);
|
|---|
| 1639 | break;
|
|---|
| 1640 | case kNOFILT:
|
|---|
| 1641 | case kRESIZE:
|
|---|
| 1642 | break;
|
|---|
| 1643 | }
|
|---|
| 1644 | }
|
|---|
| 1645 |
|
|---|
| 1646 | // enable blending for osd
|
|---|
| 1647 | if (type == kYUV2RGBA)
|
|---|
| 1648 | glEnable(GL_BLEND);
|
|---|
| 1649 |
|
|---|
| 1650 | // draw quad
|
|---|
| 1651 | glBegin( GL_QUADS );
|
|---|
| 1652 | glTexCoord2f(0.0f, t_top); glVertex2f(vleft, vtop);
|
|---|
| 1653 | glTexCoord2f(t_right, t_top); glVertex2f(vright, vtop);
|
|---|
| 1654 | glTexCoord2f(t_right, t_bottom); glVertex2f(vright, vbot);
|
|---|
| 1655 | glTexCoord2f(0.0f, t_bottom); glVertex2f(vleft, vbot);
|
|---|
| 1656 | glEnd();
|
|---|
| 1657 |
|
|---|
| 1658 | // disable blending
|
|---|
| 1659 | if (type == kYUV2RGBA)
|
|---|
| 1660 | glDisable(GL_BLEND);
|
|---|
| 1661 |
|
|---|
| 1662 | // disable fragment program
|
|---|
| 1663 | if (!(type == kNOFILT || type == kRESIZE))
|
|---|
| 1664 | {
|
|---|
| 1665 | gl->m_glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, 0 );
|
|---|
| 1666 | glDisable ( GL_FRAGMENT_PROGRAM_ARB );
|
|---|
| 1667 | }
|
|---|
| 1668 |
|
|---|
| 1669 | // switch back to default framebuffer
|
|---|
| 1670 | if (filter.outputBuffer != kDefaultBuffer || frameBuffer)
|
|---|
| 1671 | gl->m_glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
|
|---|
| 1672 |
|
|---|
| 1673 | inputs = filter.frameBufferTextures;
|
|---|
| 1674 | inputsize = videoSize;
|
|---|
| 1675 | }
|
|---|
| 1676 | currentFrameNum = frame;
|
|---|
| 1677 | inputUpdated = false;
|
|---|
| 1678 | }
|
|---|
| 1679 |
|
|---|
| 1680 | void OpenglVideo::Rotate(vector<GLuint> *target)
|
|---|
| 1681 | {
|
|---|
| 1682 | if (target->size() < 2)
|
|---|
| 1683 | return;
|
|---|
| 1684 | GLuint tmp = (*target)[target->size() - 1];
|
|---|
| 1685 | for (uint i = target->size() - 1; i > 0; i--)
|
|---|
| 1686 | (*target)[i] = (*target)[i-1];
|
|---|
| 1687 | (*target)[0] = tmp;
|
|---|
| 1688 | }
|
|---|
| 1689 |
|
|---|
| 1690 | void OpenglVideo::SetPictureAttribute(int attributeType, int newValue)
|
|---|
| 1691 | {
|
|---|
| 1692 | pictureAttribs[attributeType] = (float) newValue;
|
|---|
| 1693 | }
|
|---|
| 1694 |
|
|---|
| 1695 | void OpenglVideo::SetTextureFilters(vector<GLuint> *textures, int filt)
|
|---|
| 1696 | {
|
|---|
| 1697 | if (textures->empty())
|
|---|
| 1698 | return;
|
|---|
| 1699 | for (uint i = 0; i < textures->size(); i++)
|
|---|
| 1700 | gl->SetupTextureFilters(&((*textures)[i]), filt);
|
|---|
| 1701 | }
|
|---|
| 1702 |
|
|---|
| 1703 | OpenglFilter OpenglVideo::StringToFilter(QString filter)
|
|---|
| 1704 | {
|
|---|
| 1705 | OpenglFilter ret = kNOFILT;
|
|---|
| 1706 | if (filter.contains("master"))
|
|---|
| 1707 | ret = kYUV2RGB;
|
|---|
| 1708 | else if (filter.contains("osd"))
|
|---|
| 1709 | ret = kYUV2RGBA;
|
|---|
| 1710 | else if (filter.contains("openglkerneldeint"))
|
|---|
| 1711 | ret = kKDEINT;
|
|---|
| 1712 | else if (filter.contains("opengllinearblend"))
|
|---|
| 1713 | ret = kLINBLEND;
|
|---|
| 1714 | else if (filter.contains("openglonefield"))
|
|---|
| 1715 | ret = kONEFIELD;
|
|---|
| 1716 | else if (filter.contains("openglbobdeint"))
|
|---|
| 1717 | ret = kBOBDEINT;
|
|---|
| 1718 | else if (filter.contains("openglprogressivelinearblend"))
|
|---|
| 1719 | ret = kPROGLINBLEND;
|
|---|
| 1720 | else if (filter.contains("openglprogressivekerneldeint"))
|
|---|
| 1721 | ret = kPROGKDEINT;
|
|---|
| 1722 | else if (filter.contains("openglprogressiveonefield"))
|
|---|
| 1723 | ret = kPROGONEFIELD;
|
|---|
| 1724 | else if (filter.contains("resize"))
|
|---|
| 1725 | ret = kRESIZE;
|
|---|
| 1726 | return ret;
|
|---|
| 1727 | }
|
|---|
| 1728 |
|
|---|
| 1729 | QString OpenglVideo::FilterToString(OpenglFilter filt)
|
|---|
| 1730 | {
|
|---|
| 1731 | switch (filt)
|
|---|
| 1732 | {
|
|---|
| 1733 | case kNOFILT:
|
|---|
| 1734 | break;
|
|---|
| 1735 | case kYUV2RGB:
|
|---|
| 1736 | return "master";
|
|---|
| 1737 | case kYUV2RGBA:
|
|---|
| 1738 | return "osd";
|
|---|
| 1739 | case kKDEINT:
|
|---|
| 1740 | return "openglkerneldeint";
|
|---|
| 1741 | case kLINBLEND:
|
|---|
| 1742 | return "opengllinearblend";
|
|---|
| 1743 | case kONEFIELD:
|
|---|
| 1744 | return "openglonefield";
|
|---|
| 1745 | case kBOBDEINT:
|
|---|
| 1746 | return "openglbobdeint";
|
|---|
| 1747 | case kPROGLINBLEND:
|
|---|
| 1748 | return "openglprogressivelinearblend";
|
|---|
| 1749 | case kPROGKDEINT:
|
|---|
| 1750 | return "openglprogressivekerneldeint";
|
|---|
| 1751 | case kPROGONEFIELD:
|
|---|
| 1752 | return "openglprogressiveonefield";
|
|---|
| 1753 | case kRESIZE:
|
|---|
| 1754 | return "resize";
|
|---|
| 1755 | }
|
|---|
| 1756 | return "";
|
|---|
| 1757 | }
|
|---|
| 1758 |
|
|---|
| 1759 | static const QString yuv2rgb1a =
|
|---|
| 1760 | "ATTRIB ytex = fragment.texcoord[0];"
|
|---|
| 1761 | "PARAM off = program.env[1];"
|
|---|
| 1762 | "TEMP res, tmp, tmp2;";
|
|---|
| 1763 |
|
|---|
| 1764 | static const QString yuv2rgb1b =
|
|---|
| 1765 | "TEMP alpha;"
|
|---|
| 1766 | "TEX alpha, ytex, texture[1], %1;";
|
|---|
| 1767 |
|
|---|
| 1768 | static const QString yuv2rgb1c =
|
|---|
| 1769 | "TEX res, ytex, texture[0], %1;"
|
|---|
| 1770 | "MAD tmp2, ytex, {0.5, 0.5}, off.wyww;"
|
|---|
| 1771 | "TEX tmp.x, tmp2, texture[0], %1;"
|
|---|
| 1772 | "ADD tmp2, tmp2, off.xwww;"
|
|---|
| 1773 | "TEX tmp.y, tmp2, texture[0], %1;";
|
|---|
| 1774 |
|
|---|
| 1775 | static const QString yuv2rgb2 =
|
|---|
| 1776 | "PARAM adj = program.env[0];"
|
|---|
| 1777 | "SUB res, res, 0.5;"
|
|---|
| 1778 | "MAD res, res, adj.yyyy, adj.xxxx;"
|
|---|
| 1779 | "SUB tmp, tmp, { 0.5, 0.5 };"
|
|---|
| 1780 | "MAD tmp, adj.zzzz, tmp, 0.5;";
|
|---|
| 1781 |
|
|---|
| 1782 | static const QString yuv2rgb3 =
|
|---|
| 1783 | "MAD res, res, 1.164, -0.063;"
|
|---|
| 1784 | "SUB tmp, tmp, { 0.5, 0.5 };"
|
|---|
| 1785 | "MAD res, { 0, -.392, 2.017 }, tmp.xxxw, res;";
|
|---|
| 1786 |
|
|---|
| 1787 | static const QString yuv2rgb4 =
|
|---|
| 1788 | "MAD result.color, { 1.596, -.813, 0, 0 }, tmp.yyyw, res;";
|
|---|
| 1789 |
|
|---|
| 1790 | static const QString yuv2rgb5 =
|
|---|
| 1791 | "MAD result.color, { 0, -.813, 1.596, 0 }, tmp.yyyw, res.bgra;";
|
|---|
| 1792 |
|
|---|
| 1793 | static const QString yuv2rgb6 =
|
|---|
| 1794 | "MOV result.color.a, alpha.a;";
|
|---|
| 1795 |
|
|---|
| 1796 |
|
|---|
| 1797 | QString OpenglVideo::GetProgramString(OpenglFilter name)
|
|---|
| 1798 | {
|
|---|
| 1799 | QString ret =
|
|---|
| 1800 | "!!ARBfp1.0\n"
|
|---|
| 1801 | "OPTION ARB_precision_hint_fastest;";
|
|---|
| 1802 | switch (name)
|
|---|
| 1803 | {
|
|---|
| 1804 | case kYUV2RGB:
|
|---|
| 1805 | ret = ret + yuv2rgb1a + yuv2rgb1c;
|
|---|
| 1806 | if (useColourControl)
|
|---|
| 1807 | ret += yuv2rgb2;
|
|---|
| 1808 | ret += yuv2rgb3;
|
|---|
| 1809 | ret += frameBuffer ? yuv2rgb5 : yuv2rgb4;
|
|---|
| 1810 | break;
|
|---|
| 1811 | case kYUV2RGBA:
|
|---|
| 1812 | ret = ret + yuv2rgb1a + yuv2rgb1b + yuv2rgb1c;
|
|---|
| 1813 | if (useColourControl)
|
|---|
| 1814 | ret += yuv2rgb2;
|
|---|
| 1815 | ret = ret + yuv2rgb3 + yuv2rgb4 + yuv2rgb6;
|
|---|
| 1816 | break;
|
|---|
| 1817 | case kKDEINT:
|
|---|
| 1818 | ret +=
|
|---|
| 1819 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1820 | "PARAM off = program.env[0];"
|
|---|
| 1821 | "TEMP sam, pos, cum, cur, field, mov;"
|
|---|
| 1822 | "RCP field, off.x;"
|
|---|
| 1823 | "MUL field, tex.yyyy, field;"
|
|---|
| 1824 | "FRC field, field;"
|
|---|
| 1825 | "SUB field, field, 0.5;"
|
|---|
| 1826 | "TEX sam, tex, texture[1], %1;"
|
|---|
| 1827 | "TEX cur, tex, texture[0], %1;"
|
|---|
| 1828 | "SUB mov, cur, sam;"
|
|---|
| 1829 | "MUL cum, sam, 0.125;"
|
|---|
| 1830 | "MAD cum, cur, 0.125, cum;"
|
|---|
| 1831 | "ABS mov, mov;"
|
|---|
| 1832 | "SUB mov, mov, 0.12;"
|
|---|
| 1833 | "ADD pos, tex, off.wyww;"
|
|---|
| 1834 | "TEX sam, pos, texture[0], %1;"
|
|---|
| 1835 | "MAD cum, sam, 0.5, cum;"
|
|---|
| 1836 | "SUB pos, tex, off.wyww;"
|
|---|
| 1837 | "TEX sam, pos, texture[0], %1;"
|
|---|
| 1838 | "MAD cum, sam, 0.5, cum;"
|
|---|
| 1839 | "MAD pos, off.wyww, 2.0, tex;"
|
|---|
| 1840 | "TEX sam, pos, texture[0], %1;"
|
|---|
| 1841 | "MAD cum, sam, -0.0625, cum;"
|
|---|
| 1842 | "TEX sam, pos, texture[1], %1;"
|
|---|
| 1843 | "MAD cum, sam, -0.0625, cum;"
|
|---|
| 1844 | "MAD pos, off.wyww, -2.0, tex;"
|
|---|
| 1845 | "TEX sam, pos, texture[0], %1;"
|
|---|
| 1846 | "MAD cum, sam, -0.0625, cum;"
|
|---|
| 1847 | "TEX sam, pos, texture[1], %1;"
|
|---|
| 1848 | "MAD cum, sam, -0.0625, cum;"
|
|---|
| 1849 | "CMP cum, mov, cur, cum;"
|
|---|
| 1850 | "CMP result.color, field, cum, cur;";
|
|---|
| 1851 | break;
|
|---|
| 1852 | case kPROGLINBLEND:
|
|---|
| 1853 | ret +=
|
|---|
| 1854 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1855 | "PARAM off = program.env[0];"
|
|---|
| 1856 | "TEMP field, top, bot, current, previous, next, other, mov;"
|
|---|
| 1857 | "TEX next, tex, texture[0], %1;"
|
|---|
| 1858 | "TEX current, tex, texture[1], %1;"
|
|---|
| 1859 | "TEX previous, tex, texture[2], %1;"
|
|---|
| 1860 | "ADD top, tex, off.wyww;"
|
|---|
| 1861 | "TEX other, top, texture[1], %1;"
|
|---|
| 1862 | "SUB top, tex, off.wyww;"
|
|---|
| 1863 | "TEX bot, top, texture[1], %1;"
|
|---|
| 1864 | "LRP other, 0.5, other, bot;"
|
|---|
| 1865 | "RCP field, off.x;"
|
|---|
| 1866 | "MUL field, tex.yyyy, field;"
|
|---|
| 1867 | "FRC field, field;"
|
|---|
| 1868 | "SUB field, field, 0.5;"
|
|---|
| 1869 | "SUB top, current, next;"
|
|---|
| 1870 | "SUB bot, current, previous;"
|
|---|
| 1871 | "CMP mov, field, bot, top;"
|
|---|
| 1872 | "ABS mov, mov;"
|
|---|
| 1873 | "SUB mov, mov, 0.12;"
|
|---|
| 1874 | "CMP other, mov, current, other;"
|
|---|
| 1875 | "CMP top, field, other, current;"
|
|---|
| 1876 | "CMP bot, field, current, other;"
|
|---|
| 1877 | "CMP result.color, off.y, top, bot;";
|
|---|
| 1878 | break;
|
|---|
| 1879 | case kPROGONEFIELD:
|
|---|
| 1880 | ret +=
|
|---|
| 1881 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1882 | "PARAM off = program.env[0];"
|
|---|
| 1883 | "TEMP field, top, bot, current, previous, next, other, mov;"
|
|---|
| 1884 | "TEX next, tex, texture[0], %1;"
|
|---|
| 1885 | "TEX current, tex, texture[1], %1;"
|
|---|
| 1886 | "TEX previous, tex, texture[2], %1;"
|
|---|
| 1887 | "ADD top, tex, off.wyww;"
|
|---|
| 1888 | "TEX other, top, texture[1], %1;"
|
|---|
| 1889 | "RCP field, off.x;"
|
|---|
| 1890 | "MUL field, tex.yyyy, field;"
|
|---|
| 1891 | "FRC field, field;"
|
|---|
| 1892 | "SUB field, field, 0.5;"
|
|---|
| 1893 | "SUB top, current, next;"
|
|---|
| 1894 | "SUB bot, current, previous;"
|
|---|
| 1895 | "CMP mov, field, bot, top;"
|
|---|
| 1896 | "ABS mov, mov;"
|
|---|
| 1897 | "SUB mov, mov, 0.12;"
|
|---|
| 1898 | "CMP other, mov, current, other;"
|
|---|
| 1899 | "CMP top, field, other, current;"
|
|---|
| 1900 | "CMP bot, field, current, other;"
|
|---|
| 1901 | "CMP result.color, off.y, top, bot;";
|
|---|
| 1902 | break;
|
|---|
| 1903 | case kPROGKDEINT:
|
|---|
| 1904 | ret +=
|
|---|
| 1905 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1906 | "PARAM off = program.env[0];"
|
|---|
| 1907 | "TEMP sam, pos, bot, top, cur, pre, nex, field, mov;"
|
|---|
| 1908 | "RCP field, off.x;"
|
|---|
| 1909 | "MUL field, tex.yyyy, field;"
|
|---|
| 1910 | "FRC field, field;"
|
|---|
| 1911 | "SUB field, field, 0.5;"
|
|---|
| 1912 | "TEX pre, tex, texture[2], %1;" // -1,0
|
|---|
| 1913 | "TEX cur, tex, texture[1], %1;" // 0,0
|
|---|
| 1914 | "TEX nex, tex, texture[0], %1;" // +1,0
|
|---|
| 1915 | "SUB top, nex, cur;"
|
|---|
| 1916 | "SUB bot, pre, cur;"
|
|---|
| 1917 | "CMP mov, field, bot, top;"
|
|---|
| 1918 | "ABS mov, mov;"
|
|---|
| 1919 | "SUB mov, mov, 0.12;"
|
|---|
| 1920 | "MUL bot, pre, 0.125;" // BOT -1,0
|
|---|
| 1921 | "MAD bot, cur, 0.125, bot;" // BOT +1,0
|
|---|
| 1922 | "MUL top, cur, 0.125;" // TOP -1,0
|
|---|
| 1923 | "MAD top, nex, 0.125, top;" // TOP +1,0
|
|---|
| 1924 | "ADD pos, tex, off.wyww;"
|
|---|
| 1925 | "TEX sam, pos, texture[1], %1;" // 0,+1
|
|---|
| 1926 | "MAD bot, sam, 0.5, bot;" // BOT 0,+1
|
|---|
| 1927 | "MAD top, sam, 0.5, top;" // TOP 0,+1
|
|---|
| 1928 | "SUB pos, tex, off.wyww;"
|
|---|
| 1929 | "TEX sam, pos, texture[1], %1;" // 0,-1
|
|---|
| 1930 | "MAD bot, sam, 0.5, bot;" // BOT 0,-1
|
|---|
| 1931 | "MAD top, sam, 0.5, top;" // TOP 0,-1
|
|---|
| 1932 | "MAD pos, off.wyww, 2.0, tex;"
|
|---|
| 1933 | "TEX sam, pos, texture[1], %1;" // 0,+2
|
|---|
| 1934 | "MAD bot, sam, -0.0625, bot;" // BOT +1,+2
|
|---|
| 1935 | "MAD top, sam, -0.0625, top;" // TOP -1,+2
|
|---|
| 1936 | "TEX sam, pos, texture[2], %1;" // -1,+2
|
|---|
| 1937 | "MAD bot, sam, -0.0625, bot;" // BOT -1,+2
|
|---|
| 1938 | "TEX sam, pos, texture[0], %1;" // +1,+2
|
|---|
| 1939 | "MAD top, sam, -0.0625, top;" // TOP +1,+2
|
|---|
| 1940 | "MAD pos, off.wyww, -2.0, tex;"
|
|---|
| 1941 | "TEX sam, pos, texture[1], %1;" // +1,-2
|
|---|
| 1942 | "MAD bot, sam, -0.0625, bot;" // BOT +1,-2
|
|---|
| 1943 | "MAD top, sam, -0.0625, top;" // TOP -1,-2
|
|---|
| 1944 | "TEX sam, pos, texture[2], %1;" // -1, -2 row
|
|---|
| 1945 | "MAD bot, sam, -0.0625, bot;" // BOT -1,-2
|
|---|
| 1946 | "TEX sam, pos, texture[0], %1;" // +1,-2
|
|---|
| 1947 | "MAD top, sam, -0.0625, top;" // TOP +1,-2
|
|---|
| 1948 | "CMP top, mov, cur, top;"
|
|---|
| 1949 | "CMP bot, mov, cur, bot;"
|
|---|
| 1950 | "CMP top, field, top, cur;"
|
|---|
| 1951 | "CMP bot, field, cur, bot;"
|
|---|
| 1952 | "CMP result.color, off.y, top, bot;";
|
|---|
| 1953 | break;
|
|---|
| 1954 | case kBOBDEINT:
|
|---|
| 1955 | case kONEFIELD:
|
|---|
| 1956 | ret +=
|
|---|
| 1957 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1958 | "PARAM off = program.env[0];"
|
|---|
| 1959 | "TEMP field, top, bottom, current, other;"
|
|---|
| 1960 | "TEX current, tex, texture[0], %1;"
|
|---|
| 1961 | "RCP field, off.x;"
|
|---|
| 1962 | "MUL field, tex.yyyy, field;"
|
|---|
| 1963 | "FRC field, field;"
|
|---|
| 1964 | "SUB field, field, 0.5;"
|
|---|
| 1965 | "ADD top, tex, off.wyww;"
|
|---|
| 1966 | "TEX other, top, texture[0], %1;"
|
|---|
| 1967 | "CMP top, field, other, current;"
|
|---|
| 1968 | "CMP bottom, field, current, other;"
|
|---|
| 1969 | "CMP result.color, off.y, top, bottom;";
|
|---|
| 1970 | break;
|
|---|
| 1971 | case kLINBLEND:
|
|---|
| 1972 | ret +=
|
|---|
| 1973 | "ATTRIB tex = fragment.texcoord[0];"
|
|---|
| 1974 | "PARAM off = program.env[0];"
|
|---|
| 1975 | "TEMP mov, field, cur, pre, pos;"
|
|---|
| 1976 | "RCP field, off.x;"
|
|---|
| 1977 | "MUL field, tex.yyyy, field;"
|
|---|
| 1978 | "FRC field, field;"
|
|---|
| 1979 | "SUB field, field, 0.5;"
|
|---|
| 1980 | "TEX cur, tex, texture[0], %1;"
|
|---|
| 1981 | "TEX pre, tex, texture[1], %1;"
|
|---|
| 1982 | "SUB mov, cur, pre;"
|
|---|
| 1983 | "ABS mov, mov;"
|
|---|
| 1984 | "SUB mov, mov, 0.12;"
|
|---|
| 1985 | "ADD pos, tex, off.wyww;"
|
|---|
| 1986 | "TEX pre, pos, texture[0], %1;"
|
|---|
| 1987 | "SUB pos, tex, off.wyww;"
|
|---|
| 1988 | "TEX pos, pos, texture[0], %1;"
|
|---|
| 1989 | "LRP pre, 0.5, pos, pre;"
|
|---|
| 1990 | "CMP pre, field, pre, cur;"
|
|---|
| 1991 | "CMP result.color, mov, cur, pre;";
|
|---|
| 1992 | break;
|
|---|
| 1993 | case kNOFILT:
|
|---|
| 1994 | case kRESIZE:
|
|---|
| 1995 | break;
|
|---|
| 1996 | default:
|
|---|
| 1997 | VERBOSE(VB_PLAYBACK, LOC_ERR + "Unknown fragment program.");
|
|---|
| 1998 | }
|
|---|
| 1999 | return ret + "END";
|
|---|
| 2000 | }
|
|---|
| 2001 |
|
|---|