00001 #include <cstdlib>
00002 #include <cmath>
00003
00004 #include "videooutbase.h"
00005 #include "osd.h"
00006 #include "osdsurface.h"
00007 #include "NuppelVideoPlayer.h"
00008 #include "videodisplayprofile.h"
00009 #include "decoderbase.h"
00010
00011 #include "../libmyth/mythcontext.h"
00012
00013 #ifdef USING_XV
00014 #include "videoout_xv.h"
00015 #endif
00016
00017 #ifdef USING_IVTV
00018 #include "videoout_ivtv.h"
00019 #endif
00020
00021 #ifdef USING_DIRECTFB
00022 #include "videoout_directfb.h"
00023 #endif
00024
00025 #ifdef USING_DIRECTX
00026 #include "videoout_dx.h"
00027 #endif
00028
00029 #ifdef USING_D3D
00030 #include "videoout_d3d.h"
00031 #endif
00032
00033 #ifdef Q_OS_MACX
00034 #include "videoout_quartz.h"
00035 #endif
00036
00037 #include "videoout_null.h"
00038
00039 #include "dithertable.h"
00040
00041 extern "C" {
00042 #include "../libavcodec/avcodec.h"
00043 }
00044
00045 #include "filtermanager.h"
00046
00047 #define LOC QString("VideoOutput: ")
00048 #define LOC_ERR QString("VideoOutput, Error: ")
00049
00050 static QSize fix_1080i(QSize raw);
00051 static QSize fix_alignment(QSize raw);
00052 static float fix_aspect(float raw);
00053 static QString to_comma_list(const QStringList &list);
00054
00055 const float VideoOutput::kManualZoomMaxHorizontalZoom = 2.0f;
00056 const float VideoOutput::kManualZoomMaxVerticalZoom = 2.0f;
00057 const float VideoOutput::kManualZoomMinHorizontalZoom = 0.5f;
00058 const float VideoOutput::kManualZoomMinVerticalZoom = 0.5f;
00059 const int VideoOutput::kManualZoomMaxMove = 50;
00060
00066 VideoOutput *VideoOutput::Create(
00067 const QString &decoder, MythCodecID codec_id,
00068 void *codec_priv,
00069 const QSize &video_dim, float video_aspect,
00070 WId win_id, const QRect &display_rect,
00071 WId embed_id)
00072 {
00073 (void) codec_priv;
00074
00075 QStringList renderers;
00076
00077 #ifdef USING_IVTV
00078 renderers += VideoOutputIvtv::GetAllowedRenderers(codec_id, video_dim);
00079 #endif // USING_IVTV
00080
00081 #ifdef USING_DIRECTFB
00082 renderers += VideoOutputDirectfb::GetAllowedRenderers(codec_id, video_dim);
00083 #endif // USING_DIRECTFB
00084
00085 #ifdef USING_D3D
00086 renderers += VideoOutputD3D::GetAllowedRenderers(codec_id, video_dim);
00087 #endif
00088
00089 #ifdef USING_DIRECTX
00090 renderers += VideoOutputDX::GetAllowedRenderers(codec_id, video_dim);
00091 #endif // USING_DIRECTX
00092
00093 #ifdef USING_XV
00094 const QStringList xvlist =
00095 VideoOutputXv::GetAllowedRenderers(codec_id, video_dim);
00096 renderers += xvlist;
00097 #endif // USING_XV
00098
00099 #ifdef Q_OS_MACX
00100 const QStringList osxlist =
00101 VideoOutputQuartz::GetAllowedRenderers(codec_id, video_dim);
00102 renderers += osxlist;
00103 #endif // Q_OS_MACX
00104
00105 VERBOSE(VB_PLAYBACK, LOC + "Allowed renderers: " +
00106 to_comma_list(renderers));
00107
00108 renderers = VideoDisplayProfile::GetFilteredRenderers(decoder, renderers);
00109
00110 VERBOSE(VB_PLAYBACK, LOC + "Allowed renderers (filt: " + decoder + "): " +
00111 to_comma_list(renderers));
00112
00113 QString renderer = QString::null;
00114 if (renderers.size() > 1)
00115 {
00116 VideoDisplayProfile vprof;
00117 vprof.SetInput(video_dim);
00118
00119 QString tmp = vprof.GetVideoRenderer();
00120 if (vprof.IsDecoderCompatible(decoder) && renderers.contains(tmp))
00121 {
00122 renderer = tmp;
00123 VERBOSE(VB_PLAYBACK, LOC + "Preferred renderer: " + renderer);
00124 }
00125 }
00126
00127 if (renderer.isEmpty())
00128 renderer = VideoDisplayProfile::GetBestVideoRenderer(renderers);
00129
00130 while (!renderers.empty())
00131 {
00132 VERBOSE(VB_PLAYBACK, LOC + "Trying video renderer: " + renderer);
00133 QStringList::iterator it = renderers.find(renderer);
00134 if (it != renderers.end())
00135 renderers.erase(it);
00136
00137 VideoOutput *vo = NULL;
00138 #ifdef USING_IVTV
00139 if (renderer == "ivtv")
00140 vo = new VideoOutputIvtv();
00141 #endif // USING_IVTV
00142
00143 #ifdef USING_DIRECTFB
00144 if (renderer == "directfb")
00145 vo = new VideoOutputDirectfb();
00146 #endif // USING_DIRECTFB
00147
00148 #ifdef USING_D3D
00149 if (renderer == "direct3d")
00150 vo = new VideoOutputD3D();
00151 #endif // USING_D3D
00152
00153 #ifdef USING_DIRECTX
00154 if (renderer == "directx")
00155 vo = new VideoOutputDX();
00156 #endif // USING_DIRECTX
00157
00158 #ifdef Q_OS_MACX
00159 if (osxlist.contains(renderer))
00160 vo = new VideoOutputQuartz(codec_id, codec_priv);
00161 #endif // Q_OS_MACX
00162
00163 #ifdef USING_XV
00164 if (xvlist.contains(renderer))
00165 vo = new VideoOutputXv(codec_id);
00166 #endif // USING_XV
00167
00168 if (vo)
00169 {
00170 if (vo->Init(
00171 video_dim.width(), video_dim.height(), video_aspect,
00172 win_id, display_rect.x(), display_rect.y(),
00173 display_rect.width(), display_rect.height(), embed_id))
00174 {
00175 return vo;
00176 }
00177
00178 delete vo;
00179 vo = NULL;
00180 }
00181
00182 renderer = VideoDisplayProfile::GetBestVideoRenderer(renderers);
00183 }
00184
00185 VERBOSE(VB_IMPORTANT, LOC_ERR +
00186 "Not compiled with any useable video output method.");
00187
00188 return NULL;
00189 }
00190
00260 VideoOutput::VideoOutput() :
00261
00262 db_display_dim(0,0), db_move(0,0),
00263 db_scale_horiz(0.0f), db_scale_vert(0.0f),
00264 db_pip_location(kPIPTopLeft), db_pip_size(26),
00265 db_aspectoverride(kAspect_Off), db_adjustfill(kAdjustFill_Off),
00266 db_letterbox_colour(kLetterBoxColour_Black),
00267 db_deint_filtername(QString::null),
00268 db_use_picture_controls(false),
00269 db_vdisp_profile(new VideoDisplayProfile()),
00270
00271
00272 mz_scale_v(1.0f), mz_scale_h(1.0f),
00273 mz_move(0,0),
00274
00275
00276 display_dim(400,300), display_aspect(1.3333f),
00277
00278
00279 video_dim(640,480), video_aspect(1.3333f),
00280
00281
00282 overriden_video_aspect(1.3333f), aspectoverride(kAspect_Off),
00283
00284
00285 adjustfill(kAdjustFill_Off),
00286
00287
00288 video_rect(0,0,0,0), display_video_rect(0,0,0,0),
00289 display_visible_rect(0,0,0,0), tmp_display_visible_rect(0,0,0,0),
00290
00291
00292 pip_desired_display_size(160,128), pip_display_size(0,0),
00293 pip_video_size(0,0),
00294 pip_tmp_buf(NULL), pip_scaling_context(NULL),
00295
00296
00297 vsz_enabled(false),
00298 vsz_desired_display_rect(0,0,0,0), vsz_display_size(0,0),
00299 vsz_video_size(0,0),
00300 vsz_tmp_buf(NULL), vsz_scale_context(NULL),
00301
00302
00303 m_deinterlacing(false), m_deintfiltername("linearblend"),
00304 m_deintFiltMan(NULL), m_deintFilter(NULL),
00305 m_deinterlaceBeforeOSD(true),
00306
00307
00308 embedding(false), needrepaint(false),
00309 allowpreviewepg(true), errored(false),
00310 framesPlayed(0), db_scaling_allowed(true),
00311 supported_attributes(kPictureAttributeSupported_None)
00312 {
00313 db_display_dim = QSize(gContext->GetNumSetting("DisplaySizeWidth", 0),
00314 gContext->GetNumSetting("DisplaySizeHeight", 0));
00315
00316 db_move = QPoint(gContext->GetNumSetting("xScanDisplacement", 0),
00317 gContext->GetNumSetting("yScanDisplacement", 0));
00318
00319 db_pip_location = (PIPLocation)
00320 gContext->GetNumSetting("PIPLocation", (int) kPIPTopLeft);
00321 db_pip_size =
00322 gContext->GetNumSetting("PIPSize", 26);
00323
00324 db_pict_attr[kPictureAttribute_Brightness] =
00325 gContext->GetNumSetting("PlaybackBrightness", 50);
00326 db_pict_attr[kPictureAttribute_Contrast] =
00327 gContext->GetNumSetting("PlaybackContrast", 50);
00328 db_pict_attr[kPictureAttribute_Colour] =
00329 gContext->GetNumSetting("PlaybackColour", 50);
00330 db_pict_attr[kPictureAttribute_Hue] =
00331 gContext->GetNumSetting("PlaybackHue", 0);
00332
00333 db_aspectoverride = (AspectOverrideMode)
00334 gContext->GetNumSetting("AspectOverride", 0);
00335 db_adjustfill = (AdjustFillMode)
00336 gContext->GetNumSetting("AdjustFill", 0);
00337 db_letterbox_colour = (LetterBoxColour)
00338 gContext->GetNumSetting("LetterboxColour", 0);
00339 db_use_picture_controls =
00340 gContext->GetNumSetting("UseOutputPictureControls", 0);
00341 }
00342
00347 VideoOutput::~VideoOutput()
00348 {
00349 ShutdownPipResize();
00350
00351 ShutdownVideoResize();
00352
00353 if (m_deintFilter)
00354 delete m_deintFilter;
00355 if (m_deintFiltMan)
00356 delete m_deintFiltMan;
00357 if (db_vdisp_profile)
00358 delete db_vdisp_profile;
00359 }
00360
00366 bool VideoOutput::Init(int width, int height, float aspect, WId winid,
00367 int winx, int winy, int winw, int winh, WId embedid)
00368 {
00369 (void)winid;
00370 (void)embedid;
00371
00372 if (winw && winh)
00373 {
00374 VERBOSE(VB_PLAYBACK,
00375 QString("XOff: %1, YOff: %2")
00376 .arg(db_move.x()).arg(db_move.y()));
00377 }
00378
00379 display_visible_rect = QRect(0, 0, winw, winh);
00380 video_disp_dim = fix_1080i(QSize(width, height));
00381 video_dim = fix_alignment(QSize(width, height));
00382 video_rect = QRect(QPoint(winx, winy), video_disp_dim);
00383
00384 db_vdisp_profile->SetInput(video_dim);
00385
00386 aspectoverride = db_aspectoverride;
00387 adjustfill = db_adjustfill;
00388
00389 VideoAspectRatioChanged(aspect);
00390
00391 embedding = false;
00392
00393 return true;
00394 }
00395
00396 void VideoOutput::InitOSD(OSD *osd)
00397 {
00398 if (!db_vdisp_profile->IsOSDFadeEnabled())
00399 osd->DisableFade();
00400 }
00401
00402 QString VideoOutput::GetFilters(void) const
00403 {
00404 return db_vdisp_profile->GetFilters();
00405 }
00406
00407 void VideoOutput::SetVideoFrameRate(float playback_fps)
00408 {
00409 db_vdisp_profile->SetOutput(playback_fps);
00410 }
00411
00417 bool VideoOutput::SetDeinterlacingEnabled(bool enable)
00418 {
00419 if (enable && m_deinterlacing)
00420 return m_deinterlacing;
00421
00422
00423 if (enable && (!m_deintFiltMan || !m_deintFilter))
00424 return SetupDeinterlace(enable);
00425
00426 m_deinterlacing = enable;
00427 return m_deinterlacing;
00428 }
00429
00436 bool VideoOutput::SetupDeinterlace(bool interlaced,
00437 const QString& overridefilter)
00438 {
00439 if (m_deinterlacing == interlaced)
00440 return m_deinterlacing;
00441
00442 if (m_deintFiltMan)
00443 {
00444 delete m_deintFiltMan;
00445 m_deintFiltMan = NULL;
00446 }
00447 if (m_deintFilter)
00448 {
00449 delete m_deintFilter;
00450 m_deintFilter = NULL;
00451 }
00452
00453 m_deinterlacing = interlaced;
00454
00455 if (m_deinterlacing)
00456 {
00457 m_deinterlaceBeforeOSD = true;
00458
00459 VideoFrameType itmp = FMT_YV12;
00460 VideoFrameType otmp = FMT_YV12;
00461 int btmp;
00462
00463 m_deintfiltername = db_vdisp_profile->GetFilteredDeint(overridefilter);
00464
00465 m_deintFiltMan = new FilterManager;
00466 m_deintFilter = NULL;
00467
00468 if (!m_deintfiltername.isEmpty())
00469 {
00470 if (!ApproveDeintFilter(m_deintfiltername))
00471 {
00472 VERBOSE(VB_IMPORTANT,
00473 QString("Failed to approve '%1' deinterlacer")
00474 .arg(m_deintfiltername));
00475 m_deintfiltername = QString::null;
00476 }
00477 else
00478 {
00479 int width = video_dim.width();
00480 int height = video_dim.height();
00481 m_deintFilter = m_deintFiltMan->LoadFilters(
00482 m_deintfiltername, itmp, otmp, width, height, btmp);
00483 video_dim = QSize(width, height);
00484 }
00485 }
00486
00487 if (m_deintFilter == NULL)
00488 {
00489 VERBOSE(VB_IMPORTANT,QString("Couldn't load deinterlace filter %1")
00490 .arg(m_deintfiltername));
00491 m_deinterlacing = false;
00492 m_deintfiltername = "";
00493 }
00494
00495 VERBOSE(VB_PLAYBACK, QString("Using deinterlace method %1")
00496 .arg(m_deintfiltername));
00497
00498 if (m_deintfiltername == "bobdeint")
00499 m_deinterlaceBeforeOSD = false;
00500 }
00501
00502 return m_deinterlacing;
00503 }
00504
00508 void VideoOutput::FallbackDeint(void)
00509 {
00510 SetupDeinterlace(false);
00511 SetupDeinterlace(true, db_vdisp_profile->GetFallbackDeinterlacer());
00512 }
00513
00517 void VideoOutput::BestDeint(void)
00518 {
00519 SetupDeinterlace(false);
00520 SetupDeinterlace(true);
00521 }
00522
00532 bool VideoOutput::IsExtraProcessingRequired(void) const
00533 {
00534 return (m_deintfiltername.contains("doubleprocess")) && m_deinterlacing;
00535 }
00542 bool VideoOutput::NeedsDoubleFramerate() const
00543 {
00544
00545 return ((m_deintfiltername.contains("bobdeint") ||
00546 m_deintfiltername.contains("doublerate") ||
00547 m_deintfiltername.contains("doubleprocess")) &&
00548 m_deinterlacing);
00549 }
00550
00551 bool VideoOutput::IsBobDeint(void) const
00552 {
00553 return (m_deinterlacing && m_deintfiltername == "bobdeint");
00554 }
00555
00561 bool VideoOutput::ApproveDeintFilter(const QString& filtername) const
00562 {
00563
00564 return (!filtername.contains("bobdeint") &&
00565 !filtername.contains("doublerate") &&
00566 !filtername.contains("opengl"));
00567 }
00568
00577 void VideoOutput::SetVideoAspectRatio(float aspect)
00578 {
00579 video_aspect = aspect;
00580 overriden_video_aspect = get_aspect_override(aspectoverride, aspect);
00581 }
00582
00589 void VideoOutput::VideoAspectRatioChanged(float aspect)
00590 {
00591 SetVideoAspectRatio(aspect);
00592 MoveResize();
00593 }
00594
00601 bool VideoOutput::InputChanged(const QSize &input_size,
00602 float aspect,
00603 MythCodecID myth_codec_id,
00604 void *codec_private)
00605 {
00606 (void)myth_codec_id;
00607 (void)codec_private;
00608
00609 video_disp_dim = fix_1080i(input_size);
00610 video_dim = fix_alignment(input_size);
00611
00612 db_vdisp_profile->SetInput(video_dim);
00613
00614 SetVideoAspectRatio(aspect);
00615
00616 BestDeint();
00617
00618 DiscardFrames(true);
00619
00620 return true;
00621 }
00622
00633 void VideoOutput::EmbedInWidget(WId wid, int x, int y, int w, int h)
00634 {
00635 (void)wid;
00636
00637 if (!allowpreviewepg)
00638 return;
00639
00640 bool save_visible_rect = !embedding;
00641
00642 embedding = true;
00643
00644 if (save_visible_rect)
00645 tmp_display_visible_rect = display_visible_rect;
00646
00647 display_visible_rect = QRect(x, y, w, h);
00648 display_video_rect = QRect(x, y, w, h);
00649
00650 MoveResize();
00651 }
00652
00658 void VideoOutput::StopEmbedding(void)
00659 {
00660 display_visible_rect = tmp_display_visible_rect;
00661
00662 MoveResize();
00663
00664 embedding = false;
00665 }
00666
00672 void VideoOutput::DrawSlice(VideoFrame *frame, int x, int y, int w, int h)
00673 {
00674 (void)frame;
00675 (void)x;
00676 (void)y;
00677 (void)w;
00678 (void)h;
00679 }
00680
00689 void VideoOutput::GetDrawSize(int &xoff, int &yoff, int &width, int &height)
00690 {
00691 xoff = video_rect.left();
00692 yoff = video_rect.top();
00693 width = video_rect.width();
00694 height = video_rect.height();
00695 }
00696
00697 void VideoOutput::GetOSDBounds(QRect &total, QRect &visible,
00698 float &visible_aspect,
00699 float &font_scaling,
00700 float themeaspect) const
00701 {
00702 total = GetTotalOSDBounds();
00703 visible = GetVisibleOSDBounds(visible_aspect, font_scaling, themeaspect);
00704 }
00705
00706 static float sq(float a) { return a*a; }
00707
00708
00709
00716 QRect VideoOutput::GetVisibleOSDBounds(
00717 float &visible_aspect, float &font_scaling, float themeaspect) const
00718 {
00719 float dv_w = (((float)video_disp_dim.width()) /
00720 display_video_rect.width());
00721 float dv_h = (((float)video_disp_dim.height()) /
00722 display_video_rect.height());
00723
00724 uint right_overflow = max((display_video_rect.width()
00725 + display_video_rect.left())
00726 - display_visible_rect.width(), 0);
00727 uint lower_overflow = max((display_video_rect.height()
00728 + display_video_rect.top())
00729 - display_visible_rect.height(), 0);
00730
00731
00732 QPoint tl = QPoint((uint) ceil(max(-display_video_rect.left(),0)*dv_w),
00733 (uint) ceil(max(-display_video_rect.top(),0)*dv_h));
00734 QPoint br = QPoint(
00735 (uint) floor(video_disp_dim.width() - (right_overflow * dv_w)),
00736 (uint) floor(video_disp_dim.height() - (lower_overflow * dv_h)));
00737
00738 if ((db_scale_vert > 0.0f) || (db_scale_horiz > 0.0f))
00739 {
00740 QRect v(tl, br);
00741 float xs = (db_scale_horiz > 0.0f) ? db_scale_horiz : 0.0f;
00742 float ys = (db_scale_vert > 0.0f) ? db_scale_vert : 0.0f;
00743 QPoint s((int)(v.width() * xs), (int)(v.height() * ys));
00744 tl += s;
00745 br -= s;
00746 }
00747
00748 QRect vb(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y());
00749
00750
00751
00752
00753 vb = QRect(vb.x(), vb.y(), abs(vb.width()), abs(vb.height()));
00754
00755
00756 float dispPixelAdj = (GetDisplayAspect() * display_visible_rect.height()) / display_visible_rect.width();
00757
00758 float vs = ((float)vb.width())/vb.height();
00759 visible_aspect = themeaspect * (vs/overriden_video_aspect) * dispPixelAdj;
00760
00761
00762 font_scaling = 1.0f/sqrtf(2.0/(sq(visible_aspect / themeaspect) + 1.0f));
00763
00764 font_scaling *= sqrtf(overriden_video_aspect / themeaspect);
00765 return vb;
00766 }
00767
00772 QRect VideoOutput::GetTotalOSDBounds(void) const
00773 {
00774 return QRect(QPoint(0, 0), video_disp_dim);
00775 }
00776
00780 void VideoOutput::ApplyManualScaleAndMove(void)
00781 {
00782 if ((mz_scale_v != 1.0f) || (mz_scale_h != 1.0f))
00783 {
00784 QSize newsz =
00785 QSize((int) (display_video_rect.width() * mz_scale_h),
00786 (int) (display_video_rect.height() * mz_scale_v));
00787 QSize tmp = (display_video_rect.size() - newsz) / 2;
00788 QPoint chgloc = QPoint(tmp.width(), tmp.height());
00789 QPoint newloc = display_video_rect.topLeft() + chgloc;
00790
00791 display_video_rect = QRect(newloc, newsz);
00792 }
00793
00794 if (mz_move.y())
00795 {
00796 int move_vert = mz_move.y() * display_video_rect.height() / 100;
00797 display_video_rect.moveTop(display_video_rect.top() + move_vert);
00798 }
00799
00800 if (mz_move.x())
00801 {
00802 int move_horiz = mz_move.x() * display_video_rect.width() / 100;
00803 display_video_rect.moveLeft(display_video_rect.left() + move_horiz);
00804 }
00805 }
00806
00817 void VideoOutput::ApplyDBScaleAndMove(void)
00818 {
00819 if (db_scale_vert > 0)
00820 {
00821
00822 float tmp = 1.0f - 2.0f * db_scale_vert;
00823 video_rect.moveTop((int) round(video_rect.height() * db_scale_vert));
00824 video_rect.setHeight((int) round(video_rect.height() * tmp));
00825
00826
00827 int yoff = db_move.y();
00828 if (yoff > 0)
00829 {
00830
00831
00832 yoff = min(video_rect.top(), yoff);
00833 video_rect.moveTop(video_rect.top() - yoff);
00834 }
00835 else if (yoff < 0)
00836 {
00837
00838
00839 if (abs(yoff) > video_rect.top())
00840 yoff = 0 - video_rect.top();
00841 video_rect.moveTop(video_rect.top() - yoff);
00842 }
00843 }
00844 else if (db_scale_vert < 0)
00845 {
00846
00847
00848 float vscanf = fabs(db_scale_vert);
00849 float tmp = 1.0f - 2.0f * vscanf;
00850
00851 display_video_rect.moveTop(
00852 (int) round(display_visible_rect.height() * vscanf) +
00853 display_visible_rect.top());
00854
00855 display_video_rect.setHeight(
00856 (int) round(display_visible_rect.height() * tmp));
00857
00858
00859
00860
00861 int yoff = db_move.y();
00862 if (yoff > 0)
00863 {
00864
00865 yoff = min(display_video_rect.top(), yoff);
00866 display_video_rect.moveTop(display_video_rect.top() + yoff);
00867 }
00868 else if (yoff < 0)
00869 {
00870
00871 if (abs(yoff) > display_video_rect.top())
00872 yoff = 0 - display_video_rect.top();
00873 display_video_rect.moveTop(display_video_rect.top() + yoff);
00874 }
00875 }
00876
00877
00878 if (db_scale_horiz > 0)
00879 {
00880 float tmp = 1.0f - 2.0f * db_scale_horiz;
00881 video_rect.moveLeft(
00882 (int) round(video_disp_dim.width() * db_scale_horiz));
00883 video_rect.setWidth((int) round(video_disp_dim.width() * tmp));
00884
00885 int xoff = db_move.x();
00886 if (xoff > 0)
00887 {
00888 xoff = min(video_rect.left(), xoff);
00889 video_rect.moveLeft(video_rect.left() - xoff);
00890 }
00891 else if (xoff < 0)
00892 {
00893 if (abs(xoff) > video_rect.left())
00894 xoff = 0 - video_rect.left();
00895 video_rect.moveLeft(video_rect.left() - xoff);
00896 }
00897 }
00898 else if (db_scale_horiz < 0)
00899 {
00900 float hscanf = fabs(db_scale_horiz);
00901 float tmp = 1.0f - 2.0f * hscanf;
00902
00903 display_video_rect.moveLeft(
00904 (int) round(display_visible_rect.width() * hscanf) +
00905 display_visible_rect.left());
00906
00907 display_video_rect.setWidth(
00908 (int) round(display_visible_rect.width() * tmp));
00909
00910 int xoff = db_move.x();
00911 if (xoff > 0)
00912 {
00913 xoff = min(display_video_rect.left(), xoff);
00914 display_video_rect.moveLeft(display_video_rect.left() + xoff);
00915 }
00916 else if (xoff < 0)
00917 {
00918 if (abs(xoff) > display_video_rect.left())
00919 xoff = 0 - display_video_rect.left();
00920 display_video_rect.moveLeft(display_video_rect.left() + xoff);
00921 }
00922 }
00923
00924 }
00925
00926
00927
00928 void VideoOutput::ApplyLetterboxing(void)
00929 {
00930 float disp_aspect = fix_aspect(GetDisplayAspect());
00931 float aspect_diff = disp_aspect - overriden_video_aspect;
00932 bool aspects_match = abs(aspect_diff / disp_aspect) <= 0.1f;
00933 bool nomatch_with_fill = (!aspects_match &&
00934 (adjustfill == kAdjustFill_Stretch));
00935 bool nomatch_without_fill = (!aspects_match) && !nomatch_with_fill;
00936
00937
00938 if (nomatch_with_fill && (disp_aspect > overriden_video_aspect))
00939 {
00940 float pixNeeded =
00941 ((disp_aspect / overriden_video_aspect) *
00942 (float)display_video_rect.height()) + 0.5f;
00943
00944 display_video_rect.moveTop(
00945 display_video_rect.top() +
00946 (display_video_rect.height() - (int)pixNeeded) / 2);
00947 display_video_rect.setHeight((int)pixNeeded);
00948 }
00949 else if (nomatch_with_fill)
00950 {
00951 float pixNeeded =
00952 ((overriden_video_aspect / disp_aspect) *
00953 (float)display_video_rect.width()) + 0.5f;
00954
00955 display_video_rect.moveLeft(
00956 display_video_rect.left() +
00957 (display_video_rect.width() - (int)pixNeeded) / 2);
00958
00959 display_video_rect.setWidth((int)pixNeeded);
00960 }
00961 else if (nomatch_without_fill && (disp_aspect > overriden_video_aspect))
00962 {
00963 float pixNeeded =
00964 ((overriden_video_aspect / disp_aspect) *
00965 (float)display_video_rect.width()) + 0.5f;
00966
00967 display_video_rect.moveLeft(
00968 display_video_rect.left() +
00969 (display_video_rect.width() - (int)pixNeeded) / 2);
00970
00971 display_video_rect.setWidth((int)pixNeeded);
00972 }
00973 else if (nomatch_without_fill)
00974 {
00975 float pixNeeded =
00976 ((disp_aspect / overriden_video_aspect) *
00977 (float)display_video_rect.height()) + 0.5f;
00978
00979 display_video_rect.moveTop(
00980 display_video_rect.top() +
00981 (display_video_rect.height() - (int)pixNeeded) / 2);
00982
00983 display_video_rect.setHeight((int)pixNeeded);
00984 }
00985
00986
00987 if (adjustfill == kAdjustFill_Full)
00988 {
00989
00990
00991 display_video_rect = QRect(
00992 display_video_rect.left() - (display_video_rect.width() / 6),
00993 display_video_rect.top() - (display_video_rect.height() / 6),
00994 display_video_rect.width() * 4 / 3,
00995 display_video_rect.height() * 4 / 3);
00996 }
00997 else if (adjustfill == kAdjustFill_Half)
00998 {
00999
01000
01001
01002
01003
01004 display_video_rect = QRect(
01005 display_video_rect.left() - (display_video_rect.width() / 12),
01006 display_video_rect.top() - (display_video_rect.height() / 12),
01007 display_video_rect.width() * 7 / 6,
01008 display_video_rect.height() * 7 / 6);
01009 }
01010
01011 else if (adjustfill == kAdjustFill_Stretch)
01012 {
01013
01014
01015
01016 display_video_rect.moveLeft(
01017 display_video_rect.left() - (display_video_rect.width() / 6));
01018
01019 display_video_rect.setWidth(display_video_rect.width() * 4 / 3);
01020 }
01021 }
01022
01031 void VideoOutput::ApplySnapToVideoRect(void)
01032 {
01033 float ydiff = abs(display_video_rect.height() - video_rect.height());
01034 if ((ydiff / display_video_rect.height()) < 0.05)
01035 {
01036 display_video_rect.moveTop(
01037 display_video_rect.top() +
01038 (display_video_rect.height() - video_rect.height()) / 2);
01039
01040 display_video_rect.setHeight(video_rect.height());
01041
01042 VERBOSE(VB_PLAYBACK,
01043 QString("Snapping height to avoid scaling: "
01044 "height: %1, top: %2")
01045 .arg(display_video_rect.height())
01046 .arg(display_video_rect.top()));
01047 }
01048
01049 float xdiff = abs(display_video_rect.width() - video_rect.width());
01050 if ((xdiff / display_video_rect.width()) < 0.05)
01051 {
01052 display_video_rect.moveLeft(
01053 display_video_rect.left() +
01054 (display_video_rect.width() - video_rect.width()) / 2);
01055
01056 display_video_rect.setWidth(video_rect.width());
01057
01058 VERBOSE(VB_PLAYBACK,
01059 QString("Snapping width to avoid scaling: "
01060 "width: %1, left: %2")
01061 .arg(display_video_rect.width())
01062 .arg(display_video_rect.left()));
01063 }
01064 }
01065
01066 void VideoOutput::PrintMoveResizeDebug(void)
01067 {
01068 #if 0
01069 printf("VideoOutput::MoveResize:\n");
01070 printf("Img(%d,%d %d,%d)\n",
01071 video_rect.left(), video_rect.top(),
01072 video_rect.width(), video_rect.height());
01073 printf("Disp(%d,%d %d,%d)\n",
01074 display_video_rect.left(), display_video_rect.top(),
01075 display_video_rect.width(), display_video_rect.height());
01076 printf("Offset(%d,%d)\n", xoff, yoff);
01077 printf("Vscan(%f, %f)\n", db_scale_vert, db_scale_vert);
01078 printf("DisplayAspect: %f\n", GetDisplayAspect());
01079 printf("VideoAspect(%f)\n", video_aspect);
01080 printf("overriden_video_aspect(%f)\n", overriden_video_aspect);
01081 printf("CDisplayAspect: %f\n", fix_aspect(GetDisplayAspect()));
01082 printf("AspectOverride: %d\n", aspectoverride);
01083 printf("AdjustFill: %d\n", adjustfill);
01084 #endif
01085
01086 VERBOSE(VB_PLAYBACK,
01087 QString("Display Rect left: %1, top: %2, width: %3, "
01088 "height: %4, aspect: %5")
01089 .arg(display_video_rect.left()).arg(display_video_rect.top())
01090 .arg(display_video_rect.width()).arg(display_video_rect.height())
01091 .arg(fix_aspect(GetDisplayAspect())));
01092
01093 VERBOSE(VB_PLAYBACK,
01094 QString("Video Rect left: %1, top: %2, width: %3, "
01095 "height: %4, aspect: %5")
01096 .arg(video_rect.left()).arg(video_rect.top())
01097 .arg(video_rect.width()).arg(video_rect.height())
01098 .arg(overriden_video_aspect));
01099
01100 }
01101
01112 void VideoOutput::MoveResize(void)
01113 {
01114
01115 video_rect = QRect(QPoint(0, 0), video_disp_dim);
01116 display_video_rect = display_visible_rect;
01117
01118
01119 if ((video_rect.width() <= 0) || (video_rect.height() <= 0))
01120 {
01121 video_disp_dim = display_visible_rect.size();
01122 video_dim = fix_alignment(display_visible_rect.size());
01123 video_rect = QRect(QPoint(0, 0), video_dim);
01124 }
01125
01126
01127 ApplyDBScaleAndMove();
01128 ApplyLetterboxing();
01129 ApplyManualScaleAndMove();
01130 if ((db_scale_vert == 0) && (db_scale_horiz == 0) &&
01131 (mz_scale_v == 1.0f) && (mz_scale_h == 1.0f))
01132 {
01133 ApplySnapToVideoRect();
01134 }
01135
01136 PrintMoveResizeDebug();
01137 needrepaint = true;
01138 }
01139
01140 static float snap(float value, float snapto, float diff)
01141 {
01142 if ((value + diff > snapto) && (value - diff < snapto))
01143 return snapto;
01144 return value;
01145 }
01146
01153 void VideoOutput::Zoom(ZoomDirection direction)
01154 {
01155 if (kZoomHome == direction)
01156 {
01157 mz_scale_v = 1.0f;
01158 mz_scale_h = 1.0f;
01159 mz_move = QPoint(0,0);
01160 }
01161 else if (kZoomIn == direction)
01162 {
01163 if ((mz_scale_h < kManualZoomMaxHorizontalZoom) &&
01164 (mz_scale_v < kManualZoomMaxVerticalZoom))
01165 {
01166 mz_scale_h *= 1.1f;
01167 mz_scale_v *= 1.1f;
01168 }
01169 else
01170 {
01171 float ratio = mz_scale_v / mz_scale_h;
01172 mz_scale_h = 1.0f;
01173 mz_scale_v = ratio * mz_scale_h;
01174 }
01175 }
01176 else if (kZoomOut == direction)
01177 {
01178 if ((mz_scale_h > kManualZoomMinHorizontalZoom) &&
01179 (mz_scale_v > kManualZoomMinVerticalZoom))
01180 {
01181 mz_scale_h *= 1.0f/1.1f;
01182 mz_scale_v *= 1.0f/1.1f;
01183 }
01184 else
01185 {
01186 float ratio = mz_scale_v / mz_scale_h;
01187 mz_scale_h = 1.0f;
01188 mz_scale_v = ratio * mz_scale_h;
01189 }
01190 }
01191 else if (kZoomAspectUp == direction)
01192 {
01193 if ((mz_scale_h < kManualZoomMaxHorizontalZoom) &&
01194 (mz_scale_v > kManualZoomMinVerticalZoom))
01195 {
01196 mz_scale_h *= 1.1f;
01197 mz_scale_v *= 1.0/1.1f;
01198 }
01199 }
01200 else if (kZoomAspectDown == direction)
01201 {
01202 if ((mz_scale_h > kManualZoomMinHorizontalZoom) &&
01203 (mz_scale_v < kManualZoomMaxVerticalZoom))
01204 {
01205 mz_scale_h *= 1.0/1.1f;
01206 mz_scale_v *= 1.1f;
01207 }
01208 }
01209 else if (kZoomUp == direction && (mz_move.y() <= +kManualZoomMaxMove))
01210 mz_move.setY(mz_move.y() + 2);
01211 else if (kZoomDown == direction && (mz_move.y() >= -kManualZoomMaxMove))
01212 mz_move.setY(mz_move.y() - 2);
01213 else if (kZoomLeft == direction && (mz_move.x() <= +kManualZoomMaxMove))
01214 mz_move.setX(mz_move.x() + 2);
01215 else if (kZoomRight == direction && (mz_move.x() >= -kManualZoomMaxMove))
01216 mz_move.setX(mz_move.x() - 2);
01217
01218 mz_scale_v = snap(mz_scale_v, 1.0f, 0.05f);
01219 mz_scale_h = snap(mz_scale_h, 1.0f, 0.05f);
01220 }
01221
01229 void VideoOutput::ToggleAspectOverride(AspectOverrideMode aspectMode)
01230 {
01231 if (aspectMode == kAspect_Toggle)
01232 {
01233 aspectMode = (AspectOverrideMode)
01234 ((int)(aspectoverride + 1) % kAspect_END);
01235 }
01236
01237 aspectoverride = aspectMode;
01238
01239 VideoAspectRatioChanged(video_aspect);
01240 }
01241
01249 void VideoOutput::ToggleAdjustFill(AdjustFillMode adjustFill)
01250 {
01251 if (adjustFill == kAdjustFill_Toggle)
01252 adjustFill = (AdjustFillMode) ((int)(adjustfill + 1) % kAspect_END);
01253
01254 adjustfill = adjustFill;
01255
01256 MoveResize();
01257 }
01258
01259 int VideoOutput::ChangePictureAttribute(
01260 PictureAttribute attributeType, bool direction)
01261 {
01262 int curVal = GetPictureAttribute(attributeType);
01263 if (curVal < 0)
01264 return -1;
01265
01266 int newVal = curVal + ((direction) ? +1 : -1);
01267
01268 if (kPictureAttribute_Hue == attributeType)
01269 newVal = newVal % 100;
01270
01271 newVal = min(max(newVal, 0), 100);
01272
01273 return SetPictureAttribute(attributeType, newVal);
01274 }
01275
01283 int VideoOutput::SetPictureAttribute(PictureAttribute attribute, int newValue)
01284 {
01285 (void)attribute;
01286 (void)newValue;
01287 return -1;
01288 }
01289
01290 int VideoOutput::GetPictureAttribute(PictureAttribute attributeType) const
01291 {
01292 PictureSettingMap::const_iterator it = db_pict_attr.find(attributeType);
01293 if (it == db_pict_attr.end())
01294 return -1;
01295 return *it;
01296 }
01297
01298 void VideoOutput::InitPictureAttributes(void)
01299 {
01300 PictureSettingMap::const_iterator it = db_pict_attr.begin();
01301 for (; it != db_pict_attr.end(); ++it)
01302 SetPictureAttribute(it.key(), *it);
01303 }
01304
01305 void VideoOutput::SetPictureAttributeDBValue(
01306 PictureAttribute attributeType, int newValue)
01307 {
01308 QString dbName = QString::null;
01309 if (kPictureAttribute_Brightness == attributeType)
01310 dbName = "PlaybackBrightness";
01311 else if (kPictureAttribute_Contrast == attributeType)
01312 dbName = "PlaybackContrast";
01313 else if (kPictureAttribute_Colour == attributeType)
01314 dbName = "PlaybackColour";
01315 else if (kPictureAttribute_Hue == attributeType)
01316 dbName = "PlaybackHue";
01317
01318 if (!dbName.isEmpty())
01319 gContext->SaveSetting(dbName, newValue);
01320
01321 db_pict_attr[attributeType] = newValue;
01322 }
01323
01324
01325
01326 QRect VideoOutput::GetPIPRect(int location, NuppelVideoPlayer *pipplayer)
01327 {
01328 QRect position;
01329 float pipVideoAspect = pipplayer ? (float)pipplayer->GetVideoAspect():(4.0f/3.0f);
01330 int tmph = (display_visible_rect.height() * db_pip_size) / 100;
01331 float pixel_adj = ((float)display_visible_rect.width() /
01332 (float) display_visible_rect.height()) / display_aspect;
01333 position.setHeight(tmph);
01334 position.setWidth((int)(tmph * pipVideoAspect * pixel_adj));
01335
01336 int xoff = (int)(display_visible_rect.width() * 0.06);
01337 int yoff = (int)(display_visible_rect.height() * 0.06);
01338 switch (location)
01339 {
01340 case kPIP_END:
01341 case kPIPTopLeft:
01342 break;
01343 case kPIPBottomLeft:
01344 yoff = display_visible_rect.height() - position.height() - yoff;
01345 break;
01346 case kPIPTopRight:
01347 xoff = display_visible_rect.width() - position.width() - xoff;
01348 break;
01349 case kPIPBottomRight:
01350 xoff = display_visible_rect.width() - position.width() - xoff;
01351 yoff = display_visible_rect.height() - position.height() - yoff;
01352 break;
01353 }
01354 position.moveBy(xoff, yoff);
01355 return position;
01356 }
01357
01365 void VideoOutput::DoPipResize(int pipwidth, int pipheight)
01366 {
01367 QSize vid_size = QSize(pipwidth, pipheight);
01368 if (vid_size == pip_desired_display_size)
01369 return;
01370
01371 ShutdownPipResize();
01372
01373 pip_video_size = vid_size;
01374 pip_display_size = pip_desired_display_size;
01375
01376 int sz = pip_display_size.height() * pip_display_size.width() * 3 / 2;
01377 pip_tmp_buf = new unsigned char[sz];
01378
01379 pip_scaling_context = img_resample_init(
01380 pip_display_size.width(), pip_display_size.height(),
01381 pip_video_size.width(), pip_video_size.height());
01382 }
01383
01390 void VideoOutput::ShutdownPipResize(void)
01391 {
01392 if (pip_tmp_buf)
01393 {
01394 delete [] pip_tmp_buf;
01395 pip_tmp_buf = NULL;
01396 }
01397
01398 if (pip_scaling_context)
01399 {
01400 img_resample_close(pip_scaling_context);
01401 pip_scaling_context = NULL;
01402 }
01403
01404 pip_video_size = QSize(0,0);
01405 pip_display_size = QSize(0,0);
01406 }
01407
01416 void VideoOutput::ShowPip(VideoFrame *frame, NuppelVideoPlayer *pipplayer)
01417 {
01418 if (!pipplayer)
01419 return;
01420
01421 int pipw, piph;
01422
01423 VideoFrame *pipimage = pipplayer->GetCurrentFrame(pipw, piph);
01424 float pipVideoAspect = pipplayer->GetVideoAspect();
01425 QSize pipVideoDim = pipplayer->GetVideoBufferSize();
01426 uint pipVideoWidth = pipVideoDim.width();
01427 uint pipVideoHeight = pipVideoDim.height();
01428
01429
01430 if ((video_aspect <= 0) || (pipVideoAspect <= 0) ||
01431 (frame->height <= 0) || (frame->width <= 0) ||
01432 !pipimage || !pipimage->buf || pipimage->codec != FMT_YV12)
01433 {
01434 pipplayer->ReleaseCurrentFrame(pipimage);
01435 return;
01436 }
01437
01438
01439 int tmph = (frame->height * db_pip_size) / 100;
01440 pip_desired_display_size.setHeight((tmph >> 1) << 1);
01441
01442
01443 int letterXadj = 0;
01444 int letterYadj = 0;
01445 float letterAdj = 1.0f;
01446 if (aspectoverride != kAspect_Off)
01447 {
01448 letterXadj = max(-display_video_rect.left(), 0);
01449 float xadj = (float) video_rect.width() / display_visible_rect.width();
01450 letterXadj = (int) (letterXadj * xadj);
01451
01452 float yadj = (float)video_rect.height() /display_visible_rect.height();
01453 letterYadj = max(-display_video_rect.top(), 0);
01454 letterYadj = (int) (letterYadj * yadj);
01455
01456 letterAdj = video_aspect / overriden_video_aspect;
01457 }
01458
01459
01460 float dispPixelAdj =
01461 (GetDisplayAspect() * video_disp_dim.height())/video_disp_dim.width();
01462
01463
01464 float vidPixelAdj = pipVideoWidth / (pipVideoAspect * pipVideoHeight);
01465
01466
01467 int tmpw = (int) (pip_desired_display_size.height() * pipVideoAspect *
01468 vidPixelAdj * dispPixelAdj * letterAdj);
01469 pip_desired_display_size.setWidth((tmpw >> 1) << 1);
01470
01471
01472 unsigned char *pipbuf = pipimage->buf;
01473 if (pipw != pip_desired_display_size.width() ||
01474 piph != pip_desired_display_size.height())
01475 {
01476 DoPipResize(pipw, piph);
01477
01478 bzero(&pip_tmp_image, sizeof(pip_tmp_image));
01479
01480 if (pip_tmp_buf && pip_scaling_context)
01481 {
01482 AVPicture img_in, img_out;
01483
01484 avpicture_fill(
01485 &img_out, (uint8_t *)pip_tmp_buf, PIX_FMT_YUV420P,
01486 pip_display_size.width(), pip_display_size.height());
01487
01488 avpicture_fill(&img_in, (uint8_t *)pipimage->buf, PIX_FMT_YUV420P,
01489 pipw, piph);
01490
01491 img_resample(pip_scaling_context, &img_out, &img_in);
01492
01493 pipw = pip_display_size.width();
01494 piph = pip_display_size.height();
01495
01496 pipbuf = pip_tmp_buf;
01497 init(&pip_tmp_image,
01498 FMT_YV12,
01499 pipbuf,
01500 pipw, piph,
01501 pipimage->bpp, sizeof(pipbuf));
01502 }
01503 }
01504
01505
01506
01507 int xoff = 0;
01508 int yoff = 0;
01509 switch (db_pip_location)
01510 {
01511 case kPIP_END:
01512 case kPIPTopLeft:
01513 xoff = 30 + letterXadj;
01514 yoff = 40 + letterYadj;
01515 break;
01516 case kPIPBottomLeft:
01517 xoff = 30 + letterXadj;
01518 yoff = frame->height - piph - 40 - letterYadj;
01519 break;
01520 case kPIPTopRight:
01521 xoff = frame->width - pipw - 30 - letterXadj;
01522 yoff = 40 + letterYadj;
01523 break;
01524 case kPIPBottomRight:
01525 xoff = frame->width - pipw - 30 - letterXadj;
01526 yoff = frame->height - piph - 40 - letterYadj;
01527 break;
01528 }
01529
01530 uint xoff2[3] = { xoff, xoff>>1, xoff>>1 };
01531 uint yoff2[3] = { yoff, yoff>>1, yoff>>1 };
01532 uint pip_height = pip_tmp_image.height;
01533 uint height[3] = { pip_height, pip_height>>1, pip_height>>1 };
01534
01535 for (int p = 0; p < 3; p++)
01536 {
01537 for (uint h = 0; h < height[p]; h++)
01538 {
01539 memcpy((frame->buf + frame->offsets[p]) + (h + yoff2[p]) *
01540 frame->pitches[p] + xoff2[p],
01541 (pip_tmp_image.buf + pip_tmp_image.offsets[p]) + h *
01542 pip_tmp_image.pitches[p], pip_tmp_image.pitches[p]);
01543 }
01544 }
01545
01546
01547 pipplayer->ReleaseCurrentFrame(pipimage);
01548 }
01549
01556 void VideoOutput::DoVideoResize(const QSize &inDim, const QSize &outDim)
01557 {
01558 if ((inDim == vsz_video_size) && (outDim == vsz_display_size))
01559 return;
01560
01561 ShutdownVideoResize();
01562
01563 vsz_video_size = inDim;
01564 vsz_display_size = outDim;
01565
01566 int sz = vsz_display_size.height() * vsz_display_size.width() * 3 / 2;
01567 vsz_tmp_buf = new unsigned char[sz];
01568
01569 vsz_scale_context = img_resample_init(
01570 vsz_display_size.width(), vsz_display_size.height(),
01571 vsz_video_size.width(), vsz_video_size.height());
01572 }
01573
01574 void VideoOutput::ResizeVideo(VideoFrame *frame)
01575 {
01576 if (vsz_desired_display_rect.isNull() || frame->codec != FMT_YV12)
01577 return;
01578
01579 QRect resize = vsz_desired_display_rect;
01580 QSize frameDim(frame->width, frame->height);
01581
01582
01583 bool abort =
01584 (resize.right() > frame->width || resize.bottom() > frame->height ||
01585 resize.width() > frame->width || resize.height() > frame->height);
01586
01587 abort |= !resize.left() && !resize.top() && (resize.size() == frameDim);
01588
01589 if (abort)
01590 {
01591 vsz_enabled = false;
01592 ShutdownVideoResize();
01593 vsz_desired_display_rect.setRect(0,0,0,0);
01594 return;
01595 }
01596
01597 DoVideoResize(frameDim, resize.size());
01598
01599 if (vsz_tmp_buf && vsz_scale_context)
01600 {
01601 AVPicture img_in, img_out;
01602
01603 avpicture_fill(&img_out, (uint8_t *)vsz_tmp_buf, PIX_FMT_YUV420P,
01604 resize.width(), resize.height());
01605 avpicture_fill(&img_in, (uint8_t *)frame->buf, PIX_FMT_YUV420P,
01606 frame->width, frame->height);
01607 img_resample(vsz_scale_context, &img_out, &img_in);
01608 }
01609
01610 int xoff = resize.left();
01611 int yoff = resize.top();
01612 int resw = resize.width();
01613
01614
01615 for (int i = 0; i < resize.height(); i++)
01616 {
01617 memcpy(frame->buf + (i + yoff) * frame->width + xoff,
01618 vsz_tmp_buf + i * resw, resw);
01619 }
01620
01621
01622 xoff /= 2;
01623 yoff /= 2;
01624
01625 unsigned char *uptr = frame->buf + frame->width * frame->height;
01626 unsigned char *vptr = frame->buf + frame->width * frame->height * 5 / 4;
01627 int vidw = frame->width / 2;
01628
01629 unsigned char *videouptr = vsz_tmp_buf + resw * resize.height();
01630 unsigned char *videovptr = vsz_tmp_buf + resw * resize.height() * 5 / 4;
01631 resw /= 2;
01632 for (int i = 0; i < resize.height() / 2; i ++)
01633 {
01634 memcpy(uptr + (i + yoff) * vidw + xoff, videouptr + i * resw, resw);
01635 memcpy(vptr + (i + yoff) * vidw + xoff, videovptr + i * resw, resw);
01636 }
01637 }
01638
01639 void VideoOutput::ShutdownVideoResize(void)
01640 {
01641 if (vsz_tmp_buf)
01642 {
01643 delete [] vsz_tmp_buf;
01644 vsz_tmp_buf = NULL;
01645 }
01646
01647 if (vsz_scale_context)
01648 {
01649 img_resample_close(vsz_scale_context);
01650 vsz_scale_context = NULL;
01651 }
01652
01653 vsz_video_size = QSize(0,0);
01654 vsz_display_size = QSize(0,0);
01655 vsz_enabled = false;
01656 }
01657
01658 void VideoOutput::SetVideoResize(const QRect &videoRect)
01659 {
01660 if (!videoRect.isValid() ||
01661 videoRect.width() < 1 || videoRect.height() < 1 ||
01662 videoRect.left() < 0 || videoRect.top() < 0)
01663 {
01664 vsz_enabled = false;
01665 ShutdownVideoResize();
01666 vsz_desired_display_rect.setRect(0,0,0,0);
01667 }
01668 else
01669 {
01670 vsz_enabled = true;
01671 vsz_desired_display_rect = videoRect;
01672 }
01673 }
01674
01678 void VideoOutput::SetVideoScalingAllowed(bool change)
01679 {
01680 if (change)
01681 {
01682 db_scale_vert = gContext->GetNumSetting("VertScanPercentage", 0) / 100.0;
01683 db_scale_horiz = gContext->GetNumSetting("HorizScanPercentage", 0) / 100.0;
01684 db_scaling_allowed = true;
01685 }
01686 else
01687 {
01688 db_scale_vert = 0.0f;
01689 db_scale_horiz = 0.0f;
01690 db_scaling_allowed = false;
01691 }
01692
01693 VERBOSE(VB_PLAYBACK, QString("Over/underscan. V: %1, H: %2")
01694 .arg(db_scale_vert).arg(db_scale_horiz));
01695
01696 MoveResize();
01697 }
01698
01709 int VideoOutput::DisplayOSD(VideoFrame *frame, OSD *osd, int stride,
01710 int revision)
01711 {
01712 if (!osd)
01713 return -1;
01714
01715 if (vsz_enabled)
01716 ResizeVideo(frame);
01717
01718 OSDSurface *surface = osd->Display();
01719 if (!surface)
01720 return -1;
01721
01722 bool changed = (-1 == revision) ?
01723 surface->Changed() : (surface->GetRevision()!=revision);
01724
01725 switch (frame->codec)
01726 {
01727 case FMT_YV12:
01728 {
01729 surface->BlendToYV12(frame->buf + frame->offsets[0],
01730 frame->buf + frame->offsets[1],
01731 frame->buf + frame->offsets[2],
01732 frame->pitches[0],
01733 frame->pitches[1],
01734 frame->pitches[2]);
01735 break;
01736 }
01737 case FMT_AI44:
01738 {
01739 if (stride < 0)
01740 stride = video_dim.width();
01741 if (changed)
01742 surface->DitherToAI44(frame->buf, stride, video_dim.height());
01743 break;
01744 }
01745 case FMT_IA44:
01746 {
01747 if (stride < 0)
01748 stride = video_dim.width();
01749 if (changed)
01750 surface->DitherToIA44(frame->buf, stride, video_dim.height());
01751 break;
01752 }
01753 case FMT_ARGB32:
01754 {
01755 if (stride < 0)
01756 stride = video_dim.width()*4;
01757 if (changed)
01758 surface->BlendToARGB(frame->buf, stride, video_dim.height());
01759 break;
01760 }
01761 default:
01762 break;
01763 }
01764 return (changed) ? 1 : 0;
01765 }
01766
01775 void VideoOutput::CopyFrame(VideoFrame *to, const VideoFrame *from)
01776 {
01777 if (to == NULL || from == NULL)
01778 return;
01779
01780 to->frameNumber = from->frameNumber;
01781
01782
01783 if (from->size == to->size)
01784 memcpy(to->buf, from->buf, from->size);
01785 else if ((to->pitches[0] == from->pitches[0]) &&
01786 (to->pitches[1] == from->pitches[1]) &&
01787 (to->pitches[2] == from->pitches[2]))
01788 {
01789 memcpy(to->buf + to->offsets[0], from->buf + from->offsets[0],
01790 from->pitches[0] * from->height);
01791 memcpy(to->buf + to->offsets[1], from->buf + from->offsets[1],
01792 from->pitches[1] * (from->height>>1));
01793 memcpy(to->buf + to->offsets[2], from->buf + from->offsets[2],
01794 from->pitches[2] * (from->height>>1));
01795 }
01796 else
01797 {
01798 uint f[3] = { from->height, from->height>>1, from->height>>1, };
01799 uint t[3] = { to->height, to->height>>1, to->height>>1, };
01800 uint h[3] = { min(f[0],t[0]), min(f[1],t[1]), min(f[2],t[2]), };
01801 for (uint i = 0; i < 3; i++)
01802 {
01803 for (uint j = 0; j < h[i]; j++)
01804 {
01805 memcpy(to->buf + to->offsets[i] + (j * to->pitches[i]),
01806 from->buf + from->offsets[i] + (j * from->pitches[i]),
01807 min(from->pitches[i], to->pitches[i]));
01808 }
01809 }
01810 }
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829 }
01830
01832 static QSize fix_1080i(QSize raw)
01833 {
01834 if (QSize(1920, 1088) == raw)
01835 return QSize(1920, 1080);
01836 if (QSize(1440, 1088) == raw)
01837 return QSize(1440, 1080);
01838 return raw;
01839 }
01840
01842 static QSize fix_alignment(QSize raw)
01843 {
01844 return QSize((raw.width() + 15) & (~0xf),
01845 (raw.height() + 15) & (~0xf));
01846 }
01847
01849 static float fix_aspect(float raw)
01850 {
01851
01852 if (fabs(raw - 1.333333f) < 0.05f)
01853 raw = 1.333333f;
01854
01855
01856 if (fabs(raw - 1.777777f) < 0.05f)
01857 raw = 1.777777f;
01858
01859 return raw;
01860 }
01861
01862 static QString to_comma_list(const QStringList &list)
01863 {
01864 QString ret = "";
01865 for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
01866 ret += *it + ",";
01867
01868 if (ret.length())
01869 return ret.left(ret.length()-1);
01870
01871 return "";
01872 }