00001 #include "mythplayer.h"
00002
00003
00004
00005
00006 #include <cstdlib>
00007 #include <cstring>
00008 #include <cmath>
00009 #include <ctime>
00010 #include <cerrno>
00011
00012 #if HAVE_MALLOC_H
00013 #include <malloc.h>
00014 #endif
00015 #include <fcntl.h>
00016 #include <unistd.h>
00017 #include <sys/time.h>
00018 #include <sys/ipc.h>
00019 #include <sys/shm.h>
00020 #include <X11/keysym.h>
00021
00022 #include <algorithm>
00023 #include <iostream>
00024 using namespace std;
00025
00026 #include "mythconfig.h"
00027
00028
00029 #include "yuv2rgb.h"
00030 #include "osd.h"
00031 #include "osdchromakey.h"
00032
00033
00034 #include "videoout_xv.h"
00035 #include "mythxdisplay.h"
00036 #include "util-xv.h"
00037
00038
00039 #include "mythcorecontext.h"
00040 #include "mythlogging.h"
00041 #include "filtermanager.h"
00042 #include "videodisplayprofile.h"
00043 #define IGNORE_TV_PLAY_REC
00044 #include "tv.h"
00045 #include "fourcc.h"
00046 #include "mythmainwindow.h"
00047 #include "myth_imgconvert.h"
00048 #include "mythuihelper.h"
00049
00050 #define LOC QString("VideoOutputXv: ")
00051
00052 extern "C" {
00053 #include <X11/extensions/xf86vmode.h>
00054 #include <X11/extensions/Xinerama.h>
00055 #ifndef _XSHM_H_
00056 extern int XShmQueryExtension(Display*);
00057 extern int XShmGetEventBase(Display*);
00058 #endif // silences warning when these are already defined
00059
00060 #include "libswscale/swscale.h"
00061 }
00062
00063 #if ! HAVE_ROUND
00064 #define round(x) ((int) ((x) + 0.5))
00065 #endif
00066
00067 static QStringList allowed_video_renderers(
00068 MythCodecID codec_id, MythXDisplay *display, Window curwin = 0);
00069
00070 static void SetFromEnv(bool &useXV, bool &useShm);
00071 static void SetFromHW(MythXDisplay *d, Window curwin,
00072 bool &useXV, bool &useShm);
00073
00074 const char *vr_str[] =
00075 {
00076 "unknown", "xlib", "xshm", "xv-blit",
00077 };
00078
00079 void VideoOutputXv::GetRenderOptions(render_opts &opts,
00080 QStringList &cpudeints)
00081 {
00082 opts.renderers->append("xlib");
00083 opts.renderers->append("xshm");
00084 opts.renderers->append("xv-blit");
00085
00086 opts.deints->insert("xlib", cpudeints);
00087 opts.deints->insert("xshm", cpudeints);
00088 opts.deints->insert("xv-blit", cpudeints);
00089 (*opts.deints)["xv-blit"].append("bobdeint");
00090
00091 (*opts.osds)["xlib"].append("softblend");
00092 (*opts.osds)["xshm"].append("softblend");
00093 (*opts.osds)["xv-blit"].append("softblend");
00094 (*opts.osds)["xv-blit"].append("chromakey");
00095
00096 (*opts.safe_renderers)["dummy"].append("xlib");
00097 (*opts.safe_renderers)["dummy"].append("xshm");
00098 (*opts.safe_renderers)["dummy"].append("xv-blit");
00099 (*opts.safe_renderers)["nuppel"].append("xlib");
00100 (*opts.safe_renderers)["nuppel"].append("xshm");
00101 (*opts.safe_renderers)["nuppel"].append("xv-blit");
00102
00103 (*opts.render_group)["x11"].append("xlib");
00104 (*opts.render_group)["x11"].append("xshm");
00105 (*opts.render_group)["x11"].append("xv-blit");
00106
00107 opts.priorities->insert("xlib", 20);
00108 opts.priorities->insert("xshm", 30);
00109 opts.priorities->insert("xv-blit", 90);
00110
00111 if (opts.decoders->contains("ffmpeg"))
00112 {
00113 (*opts.safe_renderers)["ffmpeg"].append("xlib");
00114 (*opts.safe_renderers)["ffmpeg"].append("xshm");
00115 (*opts.safe_renderers)["ffmpeg"].append("xv-blit");
00116 }
00117
00118 if (opts.decoders->contains("crystalhd"))
00119 {
00120 (*opts.safe_renderers)["crystalhd"].append("xlib");
00121 (*opts.safe_renderers)["crystalhd"].append("xshm");
00122 (*opts.safe_renderers)["crystalhd"].append("xv-blit");
00123 }
00124 }
00125
00136 VideoOutputXv::VideoOutputXv()
00137 : VideoOutput(),
00138 video_output_subtype(XVUnknown),
00139 global_lock(QMutex::Recursive),
00140
00141 XJ_win(0), XJ_curwin(0), disp(NULL), XJ_letterbox_colour(0),
00142 XJ_started(false),
00143
00144 XJ_non_xv_image(0), non_xv_frames_shown(0), non_xv_show_frame(1),
00145 non_xv_fps(0), non_xv_av_format(PIX_FMT_NB), non_xv_stop_time(0),
00146
00147 xv_port(-1), xv_hue_base(0),
00148 xv_colorkey(0), xv_draw_colorkey(false),
00149 xv_chroma(0), xv_set_defaults(false),
00150 xv_use_picture_controls(true),
00151
00152 chroma_osd(NULL)
00153 {
00154 LOG(VB_PLAYBACK, LOG_INFO, LOC + "ctor");
00155 memset(&av_pause_frame, 0, sizeof(av_pause_frame));
00156
00157 if (gCoreContext->GetNumSetting("UseVideoModes", 0))
00158 display_res = DisplayRes::GetDisplayRes(true);
00159 }
00160
00161 VideoOutputXv::~VideoOutputXv()
00162 {
00163 LOG(VB_PLAYBACK, LOG_INFO, LOC + "dtor");
00164
00165 const QRect tmp_display_visible_rect =
00166 window.GetTmpDisplayVisibleRect();
00167
00168 if (window.GetPIPState() == kPIPStandAlone &&
00169 !tmp_display_visible_rect.isEmpty())
00170 {
00171 window.SetDisplayVisibleRect(tmp_display_visible_rect);
00172 }
00173
00174 if (XJ_started)
00175 {
00176 const QRect display_visible_rect = window.GetDisplayVisibleRect();
00177 disp->SetForeground(disp->GetBlack());
00178 disp->FillRectangle(XJ_curwin, display_visible_rect);
00179 m_deinterlacing = false;
00180 }
00181
00182
00183 DeleteBuffers(VideoOutputSubType(), true);
00184
00185
00186 if (xv_port >= 0 && XJ_started)
00187 {
00188 XLOCK(disp, XvStopVideo(disp->GetDisplay(), xv_port, XJ_curwin));
00189 UngrabXvPort(disp, xv_port);
00190 xv_port = -1;
00191 }
00192
00193 if (XJ_started)
00194 {
00195 XJ_started = false;
00196 delete disp;
00197 disp = NULL;
00198 }
00199 }
00200
00201
00202 void VideoOutputXv::Zoom(ZoomDirection direction)
00203 {
00204 QMutexLocker locker(&global_lock);
00205 VideoOutput::Zoom(direction);
00206 MoveResize();
00207 }
00208
00209
00210 void VideoOutputXv::MoveResize(void)
00211 {
00212 QMutexLocker locker(&global_lock);
00213 VideoOutput::MoveResize();
00214 if (chroma_osd)
00215 window.SetNeedRepaint(true);
00216 }
00217
00218 void VideoOutputXv::WindowResized(const QSize &new_size)
00219 {
00220
00221
00222
00223
00224 QMutexLocker locker(&global_lock);
00225
00226 window.SetDisplayVisibleRect(QRect(QPoint(0, 0), new_size));
00227
00228 const QSize display_dim = QSize(
00229 (monitor_dim.width() * new_size.width()) / monitor_sz.width(),
00230 (monitor_dim.height() * new_size.height())/ monitor_sz.height());
00231 window.SetDisplayDim(display_dim);
00232
00233 window.SetDisplayAspect(
00234 ((float)display_dim.width()) / display_dim.height());
00235
00236 MoveResize();
00237 }
00238
00239
00240 bool VideoOutputXv::InputChanged(const QSize &input_size,
00241 float aspect,
00242 MythCodecID av_codec_id,
00243 void *codec_private,
00244 bool &aspect_only)
00245 {
00246 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00247 QString("InputChanged(%1,%2,%3) '%4'->'%5'")
00248 .arg(input_size.width()).arg(input_size.height()).arg(aspect)
00249 .arg(toString(video_codec_id)).arg(toString(av_codec_id)));
00250
00251 QMutexLocker locker(&global_lock);
00252
00253 bool cid_changed = (video_codec_id != av_codec_id);
00254 bool res_changed = input_size != window.GetActualVideoDim();
00255 bool asp_changed = aspect != window.GetVideoAspect();
00256
00257 if (!res_changed && !cid_changed)
00258 {
00259 aspect_only = true;
00260 if (asp_changed)
00261 {
00262 VideoAspectRatioChanged(aspect);
00263 MoveResize();
00264 }
00265 return true;
00266 }
00267
00268 VideoOutput::InputChanged(input_size, aspect, av_codec_id, codec_private,
00269 aspect_only);
00270
00271 bool delete_pause_frame = cid_changed;
00272 DeleteBuffers(VideoOutputSubType(), delete_pause_frame);
00273
00274 const QSize video_disp_dim = window.GetVideoDispDim();
00275 ResizeForVideo((uint) video_disp_dim.width(),
00276 (uint) video_disp_dim.height());
00277
00278 bool ok = true;
00279 if (cid_changed)
00280 {
00281
00282 if (xv_port >= 0)
00283 {
00284 UngrabXvPort(disp, xv_port);
00285 xv_port = -1;
00286 }
00287
00288 ok = InitSetupBuffers();
00289 }
00290 else
00291 {
00292 ok = CreateBuffers(VideoOutputSubType());
00293 }
00294
00295 InitColorKey(true);
00296 InitOSD();
00297
00298 MoveResize();
00299
00300 if (!ok)
00301 {
00302 LOG(VB_GENERAL, LOG_ERR, LOC +
00303 "InputChanged(): Failed to recreate buffers");
00304 errorState = kError_Unknown;
00305 }
00306
00307 return ok;
00308 }
00309
00310 void VideoOutputXv::MoveResizeWindow(QRect new_rect)
00311 {
00312 if (disp)
00313 disp->MoveResizeWin(XJ_win, new_rect);
00314 }
00315
00316 class XvAttributes
00317 {
00318 public:
00319 XvAttributes() :
00320 description(QString::null), xv_flags(0), feature_flags(0) {}
00321 XvAttributes(const QString &a, uint b, uint c) :
00322 description(a), xv_flags(b), feature_flags(c)
00323 { description.detach(); }
00324
00325 public:
00326 QString description;
00327 uint xv_flags;
00328 uint feature_flags;
00329
00330 static const uint kFeatureNone = 0x00;
00331 static const uint kFeatureChromakey = 0x01;
00332 static const uint kFeaturePicCtrl = 0x02;
00333 };
00334
00338 void VideoOutputXv::UngrabXvPort(MythXDisplay *disp, int port)
00339 {
00340 if (!disp)
00341 return;
00342
00343 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00344 QString("Closing XVideo port %1").arg(port));
00345 disp->Lock();
00346 restore_port_attributes(port);
00347 XvUngrabPort(disp->GetDisplay(), port, CurrentTime);
00348 del_open_xv_port(port);
00349 disp->Unlock();
00350 }
00351
00357 int VideoOutputXv::GrabSuitableXvPort(MythXDisplay* disp, Window root,
00358 MythCodecID mcodecid,
00359 uint width, uint height,
00360 bool &xvsetdefaults,
00361 QString *adaptor_name)
00362 {
00363 if (adaptor_name)
00364 *adaptor_name = QString::null;
00365
00366
00367 vector<XvAttributes> req;
00368 req.push_back(XvAttributes(
00369 "XVideo surface found on port %1",
00370 XvInputMask | XvImageMask,
00371 XvAttributes::kFeatureNone));
00372
00373
00374 uint end = req.size();
00375 for (uint i = 0; i < end; i++)
00376 {
00377 req.push_back(req[i]);
00378 req[i].feature_flags |= XvAttributes::kFeaturePicCtrl;
00379 }
00380
00381
00382 VideoDisplayProfile vdp;
00383 vdp.SetInput(QSize(width, height));
00384 if (vdp.GetOSDRenderer() == "chromakey")
00385 {
00386 uint end = req.size();
00387 for (uint i = 0; i < end; i++)
00388 {
00389 req.push_back(req[i]);
00390 req[i].feature_flags |= XvAttributes::kFeatureChromakey;
00391 }
00392 }
00393
00394
00395 XvAdaptorInfo *ai = NULL;
00396 uint p_num_adaptors = 0;
00397 int ret = Success;
00398 XLOCK(disp, ret = XvQueryAdaptors(disp->GetDisplay(), root,
00399 &p_num_adaptors, &ai));
00400 if (Success != ret)
00401 {
00402 LOG(VB_GENERAL, LOG_ERR, LOC +
00403 "XVideo supported, but no free Xv ports found."
00404 "\n\t\t\tYou may need to reload video driver.");
00405 return -1;
00406 }
00407
00408 QString lastAdaptorName = QString::null;
00409 int port = -1;
00410
00411
00412 for (uint j = 0; j < req.size(); ++j)
00413 {
00414 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00415 QString("@ j=%1 Looking for flag[s]: %2 %3")
00416 .arg(j).arg(xvflags2str(req[j].xv_flags))
00417 .arg(req[j].feature_flags,0,16));
00418
00419 for (int i = 0; i < (int)p_num_adaptors && (port == -1); ++i)
00420 {
00421 lastAdaptorName = ai[i].name;
00422 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00423 QString("Adaptor#%1: %2 has flag[s]: %3")
00424 .arg(i).arg(lastAdaptorName).arg(xvflags2str(ai[i].type)));
00425
00426 if ((ai[i].type & req[j].xv_flags) != req[j].xv_flags)
00427 {
00428 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00429 "Missing XVideo flags, rejecting.");
00430 continue;
00431 }
00432 else
00433 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Has XVideo flags...");
00434
00435 const XvPortID firstPort = ai[i].base_id;
00436 const XvPortID lastPort = ai[i].base_id + ai[i].num_ports - 1;
00437 XvPortID p = 0;
00438
00439 if ((req[j].feature_flags & XvAttributes::kFeaturePicCtrl) &&
00440 (!xv_is_attrib_supported(disp, firstPort, "XV_BRIGHTNESS")))
00441 {
00442 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00443 "Missing XV_BRIGHTNESS, rejecting.");
00444 continue;
00445 }
00446 else if (req[j].feature_flags & XvAttributes::kFeaturePicCtrl)
00447 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Has XV_BRIGHTNESS...");
00448
00449 if ((req[j].feature_flags & XvAttributes::kFeatureChromakey) &&
00450 (!xv_is_attrib_supported(disp, firstPort, "XV_COLORKEY")))
00451 {
00452 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00453 "Missing XV_COLORKEY, rejecting.");
00454 continue;
00455 }
00456 else if (req[j].feature_flags & XvAttributes::kFeatureChromakey)
00457 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Has XV_COLORKEY...");
00458
00459 for (p = firstPort; (p <= lastPort) && (port == -1); ++p)
00460 {
00461 disp->Lock();
00462 ret = XvGrabPort(disp->GetDisplay(), p, CurrentTime);
00463 if (Success == ret)
00464 {
00465 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00466 QString("Grabbed xv port %1").arg(p));
00467 port = p;
00468 xvsetdefaults = add_open_xv_port(disp, p);
00469 }
00470 disp->Unlock();
00471 }
00472 }
00473
00474 if (port != -1)
00475 {
00476 LOG(VB_PLAYBACK, LOG_INFO, LOC + req[j].description.arg(port));
00477 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00478 QString("XV_SET_DEFAULTS is %1supported on this port")
00479 .arg(xvsetdefaults ? "" : "not "));
00480
00481 bool xv_vsync = xv_is_attrib_supported(disp, port,
00482 "XV_SYNC_TO_VBLANK");
00483 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00484 QString("XV_SYNC_TO_VBLANK %1supported")
00485 .arg(xv_vsync ? "" : "not "));
00486 if (xv_vsync)
00487 {
00488 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00489 QString("XVideo Sync to VBlank %1set")
00490 .arg(xv_set_attrib(disp, port, "XV_SYNC_TO_VBLANK", 1) ?
00491 "" : "NOT "));
00492 }
00493
00494 break;
00495 }
00496 }
00497
00498 if (port == -1)
00499 LOG(VB_PLAYBACK, LOG_INFO, LOC + "No suitable XVideo port found");
00500
00501
00502 if (ai)
00503 XLOCK(disp, XvFreeAdaptorInfo(ai));
00504
00505 if ((port != -1) && adaptor_name)
00506 *adaptor_name = lastAdaptorName;
00507
00508 return port;
00509 }
00510
00519 void VideoOutputXv::CreatePauseFrame(VOSType subtype)
00520 {
00521 if (av_pause_frame.buf)
00522 {
00523 delete [] av_pause_frame.buf;
00524 av_pause_frame.buf = NULL;
00525 }
00526
00527 init(&av_pause_frame, FMT_YV12,
00528 new unsigned char[vbuffers.GetScratchFrame()->size + 128],
00529 vbuffers.GetScratchFrame()->width,
00530 vbuffers.GetScratchFrame()->height,
00531 vbuffers.GetScratchFrame()->size);
00532
00533 av_pause_frame.frameNumber = vbuffers.GetScratchFrame()->frameNumber;
00534
00535 clear(&av_pause_frame);
00536 }
00537
00545 bool VideoOutputXv::InitVideoBuffers(bool use_xv, bool use_shm)
00546 {
00547 bool done = false;
00548
00549
00550 if (!done)
00551 vbuffers.Init(31, true, 1, 12, 4, 2);
00552
00553
00554 if (!done && use_xv)
00555 done = InitXVideo();
00556
00557
00558 if (!done && window.GetPIPState() > kPIPOff)
00559 return done;
00560
00561
00562 if (!done && use_shm)
00563 done = InitXShm();
00564
00565
00566 if (!done)
00567 done = InitXlib();
00568
00569 if (done)
00570 db_vdisp_profile->SetVideoRenderer(vr_str[VideoOutputSubType()]);
00571
00572 return done;
00573 }
00574
00575 static bool has_format(XvImageFormatValues *formats, int format_cnt, int id)
00576 {
00577 for (int i = 0; i < format_cnt; i++)
00578 {
00579 if ((formats[i].id == id))
00580 return true;
00581 }
00582
00583 return false;
00584 }
00585
00594 bool VideoOutputXv::InitXVideo()
00595 {
00596 MythXLocker lock(disp);
00597 disp->StartLog();
00598 QString adaptor_name = QString::null;
00599 const QSize video_dim = window.GetVideoDim();
00600 xv_port = GrabSuitableXvPort(disp, disp->GetRoot(), kCodec_MPEG2,
00601 video_dim.width(), video_dim.height(),
00602 xv_set_defaults, &adaptor_name);
00603 if (xv_port == -1)
00604 {
00605 LOG(VB_GENERAL, LOG_ERR, LOC +
00606 "Could not find suitable XVideo surface.");
00607 return false;
00608 }
00609
00610 LOG(VB_GENERAL, LOG_INFO, LOC + QString("XVideo Adaptor Name: '%1'")
00611 .arg(adaptor_name));
00612
00613 xv_hue_base = VideoOutput::CalcHueBase(adaptor_name);
00614 xv_use_picture_controls =
00615 adaptor_name != "Intel(R) Video Overlay";
00616
00617 bool foundimageformat = false;
00618 int ids[] = { GUID_YV12_PLANAR, GUID_I420_PLANAR, GUID_IYUV_PLANAR, };
00619 int format_cnt = 0;
00620 XvImageFormatValues *formats;
00621 formats = XvListImageFormats(disp->GetDisplay(), xv_port, &format_cnt);
00622
00623 for (int i = 0; i < format_cnt; i++)
00624 {
00625 char *chr = (char*) &(formats[i].id);
00626 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00627 QString("XVideo Format #%1 is '%2%3%4%5'")
00628 .arg(i).arg(chr[0]).arg(chr[1]).arg(chr[2]).arg(chr[3]));
00629 }
00630
00631 for (uint i = 0; i < sizeof(ids)/sizeof(int); i++)
00632 {
00633 if (has_format(formats, format_cnt, ids[i]))
00634 {
00635 xv_chroma = ids[i];
00636 foundimageformat = true;
00637 break;
00638 }
00639 }
00640
00641
00642 xv_chroma = (GUID_IYUV_PLANAR == xv_chroma) ? GUID_I420_PLANAR : xv_chroma;
00643
00644 if (formats)
00645 XFree(formats);
00646
00647 if (foundimageformat)
00648 {
00649 char *chr = (char*) &xv_chroma;
00650 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00651 QString("Using XVideo Format '%1%2%3%4'")
00652 .arg(chr[0]).arg(chr[1]).arg(chr[2]).arg(chr[3]));
00653 }
00654 else
00655 {
00656 LOG(VB_GENERAL, LOG_ERR, LOC +
00657 "Couldn't find the proper XVideo image format.");
00658 UngrabXvPort(disp, xv_port);
00659 xv_port = -1;
00660 }
00661
00662 bool ok = xv_port >= 0;
00663 if (ok)
00664 ok = CreateBuffers(XVideo);
00665
00666 if (!disp->StopLog())
00667 {
00668 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create XVideo Buffers.");
00669 DeleteBuffers(XVideo, false);
00670 UngrabXvPort(disp, xv_port);
00671 xv_port = -1;
00672 ok = false;
00673 }
00674 else
00675 {
00676 video_output_subtype = XVideo;
00677 window.SetAllowPreviewEPG(true);
00678 }
00679
00680 return ok;
00681 }
00682
00691 bool VideoOutputXv::InitXShm()
00692 {
00693 MythXLocker lock(disp);
00694 disp->StartLog();
00695
00696 LOG(VB_GENERAL, LOG_ERR, LOC +
00697 "Falling back to X shared memory video output."
00698 "\n\t\t\t *** May be slow ***");
00699
00700 bool ok = CreateBuffers(XShm);
00701
00702 if (!disp->StopLog())
00703 {
00704 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to allocate X shared memory.");
00705 DeleteBuffers(XShm, false);
00706 ok = false;
00707 }
00708 else
00709 {
00710 video_output_subtype = XShm;
00711 window.SetAllowPreviewEPG(false);
00712 }
00713
00714 return ok;
00715 }
00716
00725 bool VideoOutputXv::InitXlib()
00726 {
00727 MythXLocker lock(disp);
00728 disp->StartLog();
00729
00730 LOG(VB_GENERAL, LOG_ERR, LOC +
00731 "Falling back to X11 video output over a network socket."
00732 "\n\t\t\t *** May be very slow ***");
00733
00734 bool ok = CreateBuffers(Xlib);
00735
00736 if (!disp->StopLog())
00737 {
00738 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create X buffers.");
00739 DeleteBuffers(Xlib, false);
00740 ok = false;
00741 }
00742 else
00743 {
00744 video_output_subtype = Xlib;
00745 window.SetAllowPreviewEPG(false);
00746 }
00747
00748 return ok;
00749 }
00750
00754 MythCodecID VideoOutputXv::GetBestSupportedCodec(uint stream_type)
00755 {
00756 return (MythCodecID)(kCodec_MPEG1 + (stream_type-1));
00757 }
00758
00759 bool VideoOutputXv::InitOSD(void)
00760 {
00761 QString osdrenderer = db_vdisp_profile->GetOSDRenderer();
00762
00763 if (osdrenderer == "chromakey")
00764 {
00765 if ((xv_colorkey == (int)XJ_letterbox_colour) ||
00766 (video_output_subtype < XVideo))
00767 {
00768 LOG(VB_PLAYBACK, LOG_ERR, LOC +
00769 "Disabling ChromaKeyOSD as colorkeying will not work.");
00770 }
00771 else if (!((32 == disp->GetDepth()) || (24 == disp->GetDepth())))
00772 {
00773 LOG(VB_GENERAL, LOG_ERR, LOC +
00774 QString("Number of bits per pixel is %1, \n\t\t\t"
00775 "but we only support ARGB 32 bbp for ChromaKeyOSD.")
00776 .arg(disp->GetDepth()));
00777 }
00778 else
00779 {
00780 chroma_osd = new ChromaKeyOSD(this);
00781 }
00782 return chroma_osd;
00783 }
00784
00785 return true;
00786 }
00787
00788 #define XV_INIT_FATAL_ERROR_TEST(test,msg) \
00789 do { \
00790 if (test) \
00791 { \
00792 LOG(VB_GENERAL, LOG_ERR, LOC + msg + " Exiting playback."); \
00793 errorState = kError_Unknown; \
00794 return false; \
00795 } \
00796 } while (false)
00797
00798 static QString toCommaList(const QStringList &list)
00799 {
00800 QString ret = "";
00801 for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it)
00802 ret += *it + ",";
00803
00804 if (ret.length())
00805 return ret.left(ret.length()-1);
00806
00807 return "";
00808 }
00809
00810 bool VideoOutputXv::InitSetupBuffers(void)
00811 {
00812
00813 db_vdisp_profile->SetInput(window.GetVideoDim());
00814 QStringList renderers = allowed_video_renderers(
00815 video_codec_id, disp, XJ_curwin);
00816 QString renderer = QString::null;
00817
00818 QString tmp = db_vdisp_profile->GetVideoRenderer();
00819 LOG(VB_PLAYBACK, LOG_INFO, LOC + "InitSetupBuffers() " +
00820 QString("render: %1, allowed: %2")
00821 .arg(tmp).arg(toCommaList(renderers)));
00822
00823 if (renderers.contains(tmp))
00824 renderer = tmp;
00825 else if (renderers.empty())
00826 XV_INIT_FATAL_ERROR_TEST(false, "Failed to find a video renderer");
00827 else
00828 {
00829 QString tmp;
00830 QStringList::const_iterator it = renderers.begin();
00831 for (; it != renderers.end(); ++it)
00832 tmp += *it + ",";
00833
00834 renderer = renderers[0];
00835
00836 LOG(VB_GENERAL, LOG_ERR, LOC +
00837 QString("Desired video renderer '%1' not available.\n\t\t\t"
00838 "codec '%2' makes '%3' available, using '%4' instead.")
00839 .arg(db_vdisp_profile->GetVideoRenderer())
00840 .arg(toString(video_codec_id)).arg(tmp).arg(renderer));
00841 db_vdisp_profile->SetVideoRenderer(renderer);
00842 }
00843
00844
00845 bool use_xv = (renderer.left(2) == "xv");
00846 bool use_shm = (renderer == "xshm");
00847 bool ok = InitVideoBuffers(use_xv, use_shm);
00848
00849 if (!ok && window.GetPIPState() == kPIPOff)
00850 {
00851 use_xv |= (bool) renderers.contains("xv-blit");
00852 use_shm |= (bool) renderers.contains("xshm");
00853 ok = InitVideoBuffers(use_xv, use_shm);
00854 }
00855 XV_INIT_FATAL_ERROR_TEST(!ok, "Failed to get any video output");
00856
00857 if (xv_port && (VideoOutputSubType() >= XVideo))
00858 save_port_attributes(xv_port);
00859
00860
00861 if (xv_use_picture_controls)
00862 InitPictureAttributes();
00863
00864 return true;
00865 }
00866
00873 bool VideoOutputXv::Init(int width, int height, float aspect,
00874 WId winid, const QRect &win_rect, MythCodecID codec_id)
00875 {
00876 window.SetNeedRepaint(true);
00877
00878 XV_INIT_FATAL_ERROR_TEST(winid <= 0, "Invalid Window ID.");
00879
00880 disp = OpenMythXDisplay();
00881 XV_INIT_FATAL_ERROR_TEST(!disp, "Failed to open display.");
00882
00883
00884 MythXLocker lock(disp);
00885
00886 XJ_curwin = winid;
00887 XJ_win = winid;
00888 XV_INIT_FATAL_ERROR_TEST(!disp->CreateGC(XJ_win), "Failed to create GC.");
00889
00890
00891 XJ_letterbox_colour = disp->GetBlack();
00892 Colormap cmap = XDefaultColormap(disp->GetDisplay(), disp->GetScreen());
00893 XColor colour, colour_exact;
00894 QString name = toXString(db_letterbox_colour);
00895 QByteArray ascii_name = name.toAscii();
00896 const char *cname = ascii_name.constData();
00897 if (XAllocNamedColor(disp->GetDisplay(), cmap, cname, &colour, &colour_exact))
00898 XJ_letterbox_colour = colour.pixel;
00899
00900 XJ_started = true;
00901
00902
00903 VideoOutput::Init(width, height, aspect,winid, win_rect,codec_id);
00904
00905
00906 InitDisplayMeasurements(width, height, true);
00907
00908 if (!InitSetupBuffers())
00909 return false;
00910
00911 InitColorKey(true);
00912 InitOSD();
00913
00914 MoveResize();
00915
00916 return true;
00917 }
00918 #undef XV_INIT_FATAL_ERROR_TEST
00919
00926 void VideoOutputXv::InitColorKey(bool turnoffautopaint)
00927 {
00928 if (video_output_subtype < XVideo)
00929 return;
00930
00931 static const char *attr_autopaint = "XV_AUTOPAINT_COLORKEY";
00932 int xv_val=0;
00933
00934
00935
00936 xv_draw_colorkey = true;
00937 if (xv_is_attrib_supported(disp, xv_port, attr_autopaint, &xv_val))
00938 {
00939 if (turnoffautopaint && xv_val)
00940 {
00941 xv_set_attrib(disp, xv_port, attr_autopaint, 0);
00942 if (!xv_get_attrib(disp, xv_port, attr_autopaint, xv_val) ||
00943 xv_val)
00944 {
00945 LOG(VB_GENERAL, LOG_ERR, "Failed to disable autopaint");
00946 xv_draw_colorkey = false;
00947 }
00948 }
00949 else if (!turnoffautopaint && !xv_val)
00950 {
00951 xv_set_attrib(disp, xv_port, attr_autopaint, 1);
00952 if (!xv_get_attrib(disp, xv_port, attr_autopaint, xv_val) ||
00953 !xv_val)
00954 {
00955 LOG(VB_GENERAL, LOG_ERR, "Failed to enable autopaint");
00956 }
00957 }
00958 else if (!turnoffautopaint && xv_val)
00959 {
00960 xv_draw_colorkey = false;
00961 }
00962 }
00963
00964
00965
00966
00967
00968 int letterbox_color = XJ_letterbox_colour;
00969 static const char *attr_chroma = "XV_COLORKEY";
00970 if (!xv_is_attrib_supported(disp, xv_port, attr_chroma, &xv_colorkey))
00971 {
00972
00973 xv_colorkey = letterbox_color;
00974 }
00975 else if (xv_colorkey == letterbox_color)
00976 {
00977
00978 xv_set_attrib(disp, xv_port, attr_chroma, 1);
00979
00980 if (xv_get_attrib(disp, xv_port, attr_chroma, xv_val) &&
00981 xv_val != letterbox_color)
00982 {
00983 xv_colorkey = xv_val;
00984 }
00985 }
00986
00987 if (xv_colorkey == letterbox_color)
00988 {
00989 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00990 "Chromakeying not possible with this XVideo port.");
00991 }
00992 }
00993
00994
00995 bool VideoOutputXv::SetDeinterlacingEnabled(bool enable)
00996 {
00997 bool deint = VideoOutput::SetDeinterlacingEnabled(enable);
00998 xv_need_bobdeint_repaint = (m_deintfiltername == "bobdeint");
00999 return deint;
01000 }
01001
01002 bool VideoOutputXv::SetupDeinterlace(bool interlaced,
01003 const QString& overridefilter)
01004 {
01005 bool deint = VideoOutput::SetupDeinterlace(interlaced, overridefilter);
01006 window.SetNeedRepaint(true);
01007 return deint;
01008 }
01009
01017 bool VideoOutputXv::ApproveDeintFilter(const QString &filtername) const
01018 {
01019
01020 VOSType vos = VideoOutputSubType();
01021
01022 if (filtername == "bobdeint" && (vos >= XVideo))
01023 return true;
01024
01025 return VideoOutput::ApproveDeintFilter(filtername);
01026 }
01027
01039 vector<unsigned char*> VideoOutputXv::CreateShmImages(uint num, bool use_xv)
01040 {
01041 const QSize video_dim = window.GetVideoDim();
01042 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01043 QString("CreateShmImages(%1): video_dim: %2x%3")
01044 .arg(num).arg(video_dim.width()).arg(video_dim.height()));
01045
01046 MythXLocker lock(disp);
01047 Display *d = disp->GetDisplay();
01048 vector<unsigned char*> bufs;
01049 for (uint i = 0; i < num; i++)
01050 {
01051 XShmSegmentInfo *info = new XShmSegmentInfo;
01052 void *image = NULL;
01053 int size = 0;
01054 int desiredsize = 0;
01055
01056 if (use_xv)
01057 {
01058 XvImage *img =
01059 XvShmCreateImage(d, xv_port, xv_chroma, 0,
01060 video_dim.width(), video_dim.height(), info);
01061 size = img->data_size + 64;
01062 image = img;
01063 desiredsize = video_dim.width() * video_dim.height() * 3 / 2;
01064
01065 if (image && size < desiredsize)
01066 {
01067 LOG(VB_GENERAL, LOG_ERR, LOC + "CreateXvShmImages(): "
01068 "XvShmCreateImage() failed to create image of the "
01069 "requested size.");
01070 XFree(image);
01071 image = NULL;
01072 delete info;
01073 }
01074
01075 if (image && (3 == img->num_planes))
01076 {
01077 XJ_shm_infos.push_back(info);
01078 YUVInfo tmp(img->width, img->height, img->data_size,
01079 img->pitches, img->offsets);
01080 if (xv_chroma == GUID_YV12_PLANAR)
01081 {
01082 swap(tmp.pitches[1], tmp.pitches[2]);
01083 swap(tmp.offsets[1], tmp.offsets[2]);
01084 }
01085
01086 XJ_yuv_infos.push_back(tmp);
01087 }
01088 else if (image)
01089 {
01090 LOG(VB_GENERAL, LOG_ERR, LOC + "CreateXvShmImages(): "
01091 "XvShmCreateImage() failed to create image "
01092 "with the correct number of pixel planes.");
01093 XFree(image);
01094 image = NULL;
01095 delete info;
01096 }
01097 }
01098 else
01099 {
01100 int width = window.GetDisplayVisibleRect().width() & ~0x1;
01101 int height = window.GetDisplayVisibleRect().height() & ~0x1;
01102 XImage *img =
01103 XShmCreateImage(d, DefaultVisual(d, disp->GetScreen()),
01104 disp->GetDepth(), ZPixmap, 0, info,
01105 width, height);
01106 size = img->bytes_per_line * img->height + 64;
01107 image = img;
01108 desiredsize = (width * height * 3 / 2);
01109 if (image && size < desiredsize)
01110 {
01111 LOG(VB_GENERAL, LOG_ERR, LOC + "CreateXvShmImages(): "
01112 "XShmCreateImage() failed to create image of the "
01113 "requested size.");
01114 XDestroyImage((XImage *)image);
01115 image = NULL;
01116 delete info;
01117 }
01118
01119 if (image)
01120 {
01121 YUVInfo tmp(img->width, img->height,
01122 img->bytes_per_line * img->height, NULL, NULL);
01123 XJ_yuv_infos.push_back(tmp);
01124 XJ_shm_infos.push_back(info);
01125 }
01126 }
01127
01128 if (image)
01129 {
01130 XJ_shm_infos[i]->shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
01131 if (XJ_shm_infos[i]->shmid >= 0)
01132 {
01133 XJ_shm_infos[i]->shmaddr = (char*)
01134 shmat(XJ_shm_infos[i]->shmid, 0, 0);
01135 if (use_xv)
01136 ((XvImage*)image)->data = XJ_shm_infos[i]->shmaddr;
01137 else
01138 ((XImage*)image)->data = XJ_shm_infos[i]->shmaddr;
01139 xv_buffers[(unsigned char*) XJ_shm_infos[i]->shmaddr] = image;
01140 XJ_shm_infos[i]->readOnly = False;
01141
01142 XShmAttach(d, XJ_shm_infos[i]);
01143 disp->Sync();
01144
01145
01146
01147 shmctl(XJ_shm_infos[i]->shmid, IPC_RMID, 0);
01148
01149 bufs.push_back((unsigned char*) XJ_shm_infos[i]->shmaddr);
01150 }
01151 else
01152 {
01153 LOG(VB_GENERAL, LOG_ERR, LOC +
01154 "CreateXvShmImages(): shmget() failed." + ENO);
01155 break;
01156 }
01157 }
01158 else
01159 {
01160 LOG(VB_GENERAL, LOG_ERR, LOC + "CreateXvShmImages(): "
01161 "XvShmCreateImage() failed to create image.");
01162 break;
01163 }
01164 }
01165 return bufs;
01166 }
01167
01168 bool VideoOutputXv::CreateBuffers(VOSType subtype)
01169 {
01170 bool ok = false;
01171
01172 const QSize video_dim = window.GetVideoDim();
01173 const QRect display_visible_rect = window.GetDisplayVisibleRect();
01174
01175 if (subtype == XVideo && xv_port >= 0)
01176 {
01177 vector<unsigned char*> bufs =
01178 CreateShmImages(vbuffers.Size(), true);
01179
01180 ok = (bufs.size() >= vbuffers.Size()) &&
01181 vbuffers.CreateBuffers(FMT_YV12,
01182 video_dim.width(), video_dim.height(),
01183 bufs, XJ_yuv_infos);
01184
01185 disp->Sync();
01186 }
01187 else if (subtype == XShm || subtype == Xlib)
01188 {
01189 if (subtype == XShm)
01190 {
01191 vector<unsigned char*> bufs = CreateShmImages(1, false);
01192 if (bufs.empty())
01193 return false;
01194 XJ_non_xv_image = (XImage*) xv_buffers.begin()->second;
01195 }
01196 else
01197 {
01198 MythXLocker lock(disp);
01199 Display *d = disp->GetDisplay();
01200 int bytes_per_line;
01201 int scrn = disp->GetScreen();
01202 Visual *visual = DefaultVisual(d, scrn);
01203 XJ_non_xv_image = XCreateImage(d, visual, disp->GetDepth(),
01204 ZPixmap, 0, 0,
01205 display_visible_rect.width() & ~0x1,
01206 display_visible_rect.height() & ~0x1,
01207 8, 0);
01208
01209 if (!XJ_non_xv_image)
01210 {
01211 LOG(VB_GENERAL, LOG_ERR, LOC + "XCreateImage failed: " +
01212 QString("XJ_disp(0x%1) visual(0x%2) \n")
01213 .arg((uint64_t)d,0,16).arg((uint64_t)visual,0,16) +
01214 QString(" ") +
01215 QString("depth(%1) ").arg(disp->GetDepth()) +
01216 QString("WxH(%1""x%2) ")
01217 .arg(display_visible_rect.width())
01218 .arg(display_visible_rect.height()));
01219 return false;
01220 }
01221 bytes_per_line = XJ_non_xv_image->bytes_per_line;
01222 XJ_non_xv_image->data = (char*) malloc(
01223 bytes_per_line * display_visible_rect.height());
01224 }
01225
01226 switch (XJ_non_xv_image->bits_per_pixel)
01227 {
01228 case 16: non_xv_av_format = PIX_FMT_RGB565; break;
01229 case 24: non_xv_av_format = PIX_FMT_RGB24; break;
01230 case 32: non_xv_av_format = PIX_FMT_RGB32; break;
01231 default: non_xv_av_format = PIX_FMT_NB;
01232 }
01233 if (PIX_FMT_NB == non_xv_av_format)
01234 {
01235 QString msg = QString(
01236 "Non XVideo modes only support displays with 16,\n\t\t\t"
01237 "24, or 32 bits per pixel. But you have a %1 bpp display.")
01238 .arg(disp->GetDepth()*8);
01239
01240 LOG(VB_GENERAL, LOG_ERR, LOC + msg);
01241 }
01242 else
01243 ok = vbuffers.CreateBuffers(FMT_YV12,
01244 video_dim.width(), video_dim.height());
01245 }
01246
01247 if (ok)
01248 CreatePauseFrame(subtype);
01249
01250 return ok;
01251 }
01252
01253 void VideoOutputXv::DeleteBuffers(VOSType subtype, bool delete_pause_frame)
01254 {
01255 (void) subtype;
01256 DiscardFrames(true);
01257
01258 if (chroma_osd)
01259 {
01260 delete chroma_osd;
01261 chroma_osd = NULL;
01262 }
01263
01264 Display *d = disp->GetDisplay();
01265
01266 vbuffers.DeleteBuffers();
01267
01268 if (delete_pause_frame)
01269 {
01270 if (av_pause_frame.buf)
01271 {
01272 delete [] av_pause_frame.buf;
01273 av_pause_frame.buf = NULL;
01274 }
01275 if (av_pause_frame.qscale_table)
01276 {
01277 delete [] av_pause_frame.qscale_table;
01278 av_pause_frame.qscale_table = NULL;
01279 }
01280 }
01281
01282 for (uint i = 0; i < XJ_shm_infos.size(); i++)
01283 {
01284 MythXLocker lock(disp);
01285 XShmDetach(d, XJ_shm_infos[i]);
01286 XvImage *image = (XvImage*)
01287 xv_buffers[(unsigned char*) XJ_shm_infos[i]->shmaddr];
01288 if (image)
01289 {
01290 if ((XImage*)image == (XImage*)XJ_non_xv_image)
01291 XDestroyImage((XImage*)XJ_non_xv_image);
01292 else
01293 XFree(image);
01294 }
01295 if (XJ_shm_infos[i]->shmaddr)
01296 shmdt(XJ_shm_infos[i]->shmaddr);
01297 if (XJ_shm_infos[i]->shmid > 0)
01298 shmctl(XJ_shm_infos[i]->shmid, IPC_RMID, 0);
01299 delete XJ_shm_infos[i];
01300 }
01301 XJ_shm_infos.clear();
01302 xv_buffers.clear();
01303 XJ_yuv_infos.clear();
01304 XJ_non_xv_image = NULL;
01305 }
01306
01307 void VideoOutputXv::EmbedInWidget(const QRect &rect)
01308 {
01309 QMutexLocker locker(&global_lock);
01310
01311 if (!window.IsEmbedding())
01312 VideoOutput::EmbedInWidget(rect);
01313 MoveResize();
01314 }
01315
01316 void VideoOutputXv::StopEmbedding(void)
01317 {
01318 if (!window.IsEmbedding())
01319 return;
01320
01321 QMutexLocker locker(&global_lock);
01322
01323 VideoOutput::StopEmbedding();
01324 MoveResize();
01325 }
01326
01334 void VideoOutputXv::DiscardFrame(VideoFrame *frame)
01335 {
01336 if (!frame)
01337 return;
01338
01339 vbuffers.DiscardFrame(frame);
01340 }
01341
01342 void VideoOutputXv::ClearAfterSeek(void)
01343 {
01344 LOG(VB_PLAYBACK, LOG_INFO, LOC + "ClearAfterSeek()");
01345 DiscardFrames(false);
01346 }
01347
01348 void VideoOutputXv::DiscardFrames(bool next_frame_keyframe)
01349 {
01350 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01351 QString("DiscardFrames(%1)").arg(next_frame_keyframe));
01352 if (VideoOutputSubType() <= XVideo)
01353 {
01354 vbuffers.DiscardFrames(next_frame_keyframe);
01355 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01356 QString("DiscardFrames() 3: %1 -- done()")
01357 .arg(vbuffers.GetStatus()));
01358 return;
01359 }
01360 }
01361
01367 void VideoOutputXv::PrepareFrameXv(VideoFrame *frame)
01368 {
01369 if (!frame)
01370 frame = vbuffers.GetScratchFrame();
01371
01372 global_lock.lock();
01373 framesPlayed = frame->frameNumber + 1;
01374 global_lock.unlock();
01375
01376 if (vbuffers.GetScratchFrame() == frame)
01377 vbuffers.SetLastShownFrameToScratch();
01378 }
01379
01385 void VideoOutputXv::PrepareFrameMem(VideoFrame *buffer, FrameScanType )
01386 {
01387 if (!buffer)
01388 buffer = vbuffers.GetScratchFrame();
01389
01390 framesPlayed = buffer->frameNumber + 1;
01391 int width = buffer->width;
01392 int height = buffer->height;
01393
01394
01395
01396 if (non_xv_frames_shown == 0)
01397 non_xv_stop_time = time(NULL) + 4;
01398
01399 const QRect display_visible_rect = window.GetDisplayVisibleRect();
01400
01401 if ((!non_xv_fps) && (time(NULL) > non_xv_stop_time))
01402 {
01403 non_xv_fps = (int)(non_xv_frames_shown / 4);
01404
01405 if (non_xv_fps < 25)
01406 {
01407 non_xv_show_frame = 120 / (non_xv_frames_shown + 1);
01408 LOG(VB_GENERAL, LOG_ERR, LOC +
01409 QString("\n***\n"
01410 "* Your system is not capable of displaying the\n"
01411 "* full framerate at %1""x%2 resolution. Frames\n"
01412 "* will be skipped in order to keep the audio and\n"
01413 "* video in sync.\n").arg(display_visible_rect.width())
01414 .arg(display_visible_rect.height()));
01415 }
01416 }
01417
01418 non_xv_frames_shown++;
01419
01420 if ((non_xv_show_frame != 1) && (non_xv_frames_shown % non_xv_show_frame))
01421 return;
01422
01423 if (!XJ_non_xv_image)
01424 {
01425 LOG(VB_GENERAL, LOG_ERR, LOC + "XJ_non_xv_image == NULL");
01426 return;
01427 }
01428
01429 int out_width = display_visible_rect.width() & ~0x1;
01430 int out_height = display_visible_rect.height() & ~0x1;
01431 unsigned char *sbuf = new unsigned char[out_width * out_height * 3 / 2];
01432 AVPicture image_in, image_out;
01433 static struct SwsContext *scontext;
01434
01435 avpicture_fill(&image_out, (uint8_t *)sbuf, PIX_FMT_YUV420P,
01436 out_width, out_height);
01437
01438 if ((out_width == width) &&
01439 (out_height == height))
01440 {
01441 memcpy(sbuf, buffer->buf, width * height * 3 / 2);
01442 }
01443 else
01444 {
01445 avpicture_fill(&image_in, buffer->buf, PIX_FMT_YUV420P,
01446 width, height);
01447 scontext = sws_getCachedContext(scontext, width, height,
01448 PIX_FMT_YUV420P, out_width,
01449 out_height, PIX_FMT_YUV420P,
01450 SWS_FAST_BILINEAR, NULL, NULL, NULL);
01451
01452 sws_scale(scontext, image_in.data, image_in.linesize, 0, height,
01453 image_out.data, image_out.linesize);
01454 }
01455
01456 avpicture_fill(&image_in, (uint8_t *)XJ_non_xv_image->data,
01457 non_xv_av_format, out_width, out_height);
01458
01459
01460
01461 myth_sws_img_convert(
01462 &image_in, non_xv_av_format, &image_out, PIX_FMT_YUV420P,
01463 out_width, out_height);
01464
01465 {
01466 QMutexLocker locker(&global_lock);
01467 disp->Lock();
01468 if (XShm == video_output_subtype)
01469 XShmPutImage(disp->GetDisplay(), XJ_curwin, disp->GetGC(), XJ_non_xv_image,
01470 0, 0, 0, 0, out_width, out_height, False);
01471 else
01472 XPutImage(disp->GetDisplay(), XJ_curwin, disp->GetGC(), XJ_non_xv_image,
01473 0, 0, 0, 0, out_width, out_height);
01474 disp->Unlock();
01475 }
01476
01477 delete [] sbuf;
01478 }
01479
01480
01481 void VideoOutputXv::PrepareFrame(VideoFrame *buffer, FrameScanType scan,
01482 OSD *osd)
01483 {
01484 (void)osd;
01485 if (IsErrored())
01486 {
01487 LOG(VB_GENERAL, LOG_ERR, LOC + "IsErrored() in PrepareFrame()");
01488 return;
01489 }
01490
01491 if (VideoOutputSubType() == XVideo)
01492 PrepareFrameXv(buffer);
01493 else
01494 PrepareFrameMem(buffer, scan);
01495 }
01496
01497 static void calc_bob(FrameScanType scan, int imgh, int disphoff,
01498 int imgy, int dispyoff,
01499 int frame_height, int top_field_first,
01500 int &field, int &src_y, int &dest_y,
01501 int& xv_src_y_incr, int &xv_dest_y_incr)
01502 {
01503 int dst_half_line_in_src = 0, dest_y_incr = 0, src_y_incr = 0;
01504 field = 3;
01505 src_y = imgy;
01506 dest_y = dispyoff;
01507 xv_src_y_incr = 0;
01508
01509 if ((kScan_Interlaced != scan) && (kScan_Intr2ndField != scan))
01510 return;
01511
01512
01513 if (dispyoff < 0)
01514 {
01515 dest_y_incr = -dispyoff;
01516 src_y_incr = (int) (dest_y_incr * imgh / disphoff);
01517 xv_src_y_incr -= (int) (0.5 * dest_y_incr * imgh / disphoff);
01518 }
01519
01520 if ((scan == kScan_Interlaced && top_field_first == 1) ||
01521 (scan == kScan_Intr2ndField && top_field_first == 0))
01522 {
01523 field = 1;
01524 xv_src_y_incr += - imgy / 2;
01525 }
01526 else if ((scan == kScan_Interlaced && top_field_first == 0) ||
01527 (scan == kScan_Intr2ndField && top_field_first == 1))
01528 {
01529 field = 2;
01530 xv_src_y_incr += (frame_height - imgy) / 2;
01531
01532 dst_half_line_in_src =
01533 max((int) round((((double)disphoff)/imgh) - 0.00001), 0);
01534 }
01535 src_y += src_y_incr;
01536 dest_y += dest_y_incr;
01537
01538 #define NVIDIA_6629
01539 #ifdef NVIDIA_6629
01540 xv_dest_y_incr = dst_half_line_in_src;
01541
01542
01543 int mod = 0;
01544 if (frame_height>=(int)(imgh+(0.05*frame_height)) && 2==field)
01545 {
01546
01547 mod = -dst_half_line_in_src;
01548 dest_y += mod;
01549 xv_dest_y_incr -= mod;
01550 }
01551 #else
01552 dest_y += dst_half_line_in_src;
01553 #endif
01554
01555
01556 #if 0
01557 static int last_dest_y_field[3] = { -1000, -1000, -1000, };
01558 int last_dest_y = last_dest_y_field[field];
01559
01560 if (last_dest_y != dest_y)
01561 {
01562 LOG(VB_GENERAL, LOG_DEBUG,
01563 QString("####### Field %1 #######").arg(field));
01564 LOG(VB_GENERAL, LOG_DEBUG, QString(" src_y: %1").arg(src_y));
01565 LOG(VB_GENERAL, LOG_DEBUG, QString(" dest_y: %1").arg(dest_y));
01566 LOG(VB_GENERAL, LOG_DEBUG,
01567 QString(" xv_src_y_incr: %1").arg(xv_src_y_incr));
01568 LOG(VB_GENERAL, LOG_DEBUG,
01569 QString("xv_dest_y_incr: %1").arg(xv_dest_y_incr));
01570 LOG(VB_GENERAL, LOG_DEBUG, QString(" disphoff: %1").arg(disphoff));
01571 LOG(VB_GENERAL, LOG_DEBUG, QString(" imgh: %1").arg(imgh));
01572 LOG(VB_GENERAL, LOG_DEBUG, QString(" mod: %1").arg(mod));
01573 }
01574 last_dest_y_field[field] = dest_y;
01575 #endif
01576 }
01577
01578 void VideoOutputXv::ShowXVideo(FrameScanType scan)
01579 {
01580 VideoFrame *frame = GetLastShownFrame();
01581
01582 XvImage *image = (XvImage*) xv_buffers[frame->buf];
01583 if (!image)
01584 return;
01585
01586 const QRect video_rect = window.GetVideoRect();
01587 const QRect display_video_rect = (vsz_enabled && chroma_osd) ?
01588 vsz_desired_display_rect :
01589 window.GetDisplayVideoRect();
01590 int field = 3, src_y = video_rect.top(), dest_y = display_video_rect.top(),
01591 xv_src_y_incr = 0, xv_dest_y_incr = 0;
01592 if (m_deinterlacing && (m_deintfiltername == "bobdeint"))
01593 {
01594 calc_bob(scan,
01595 video_rect.height(), display_video_rect.height(),
01596 video_rect.top(), display_video_rect.top(),
01597 frame->height, frame->top_field_first,
01598 field, src_y, dest_y, xv_src_y_incr, xv_dest_y_incr);
01599 src_y += xv_src_y_incr;
01600 dest_y += xv_dest_y_incr;
01601 }
01602
01603 {
01604 QMutexLocker locker(&global_lock);
01605 int video_height = (3 != field) ?
01606 (video_rect.height()/2) : video_rect.height();
01607 disp->Lock();
01608 XvShmPutImage(disp->GetDisplay(), xv_port, XJ_curwin,
01609 disp->GetGC(), image,
01610 video_rect.left(), src_y,
01611 video_rect.width(), video_height,
01612 display_video_rect.left(), dest_y,
01613 display_video_rect.width(),
01614 display_video_rect.height(), False);
01615 disp->Unlock();
01616 }
01617 }
01618
01619
01620 void VideoOutputXv::Show(FrameScanType scan)
01621 {
01622 if (IsErrored())
01623 {
01624 LOG(VB_GENERAL, LOG_ERR, LOC + "IsErrored() is true in Show()");
01625 return;
01626 }
01627
01628 if ((window.IsRepaintNeeded() || xv_need_bobdeint_repaint) &&
01629 VideoOutputSubType() >= XVideo)
01630 {
01631 DrawUnusedRects(false);
01632 }
01633
01634 if (VideoOutputSubType() == XVideo)
01635 ShowXVideo(scan);
01636
01637 disp->Sync();
01638 }
01639
01640 void VideoOutputXv::DrawUnusedRects(bool sync)
01641 {
01642
01643 bool use_bob = (m_deinterlacing && m_deintfiltername == "bobdeint");
01644 const QRect display_visible_rect = window.GetDisplayVisibleRect();
01645 const QRect display_video_rect = window.GetDisplayVideoRect();
01646 int boboff_raw = (int)round(((double)display_video_rect.height()) /
01647 456 - 0.00001);
01648 int boboff = use_bob ? boboff_raw : 0;
01649
01650 xv_need_bobdeint_repaint |= window.IsRepaintNeeded();
01651
01652 Display *d = disp->GetDisplay();
01653 if (chroma_osd && chroma_osd->GetImage() && xv_need_bobdeint_repaint)
01654 {
01655 XLOCK(disp, XShmPutImage(d, XJ_curwin, disp->GetGC(),
01656 chroma_osd->GetImage(), 0, 0, 0, 0,
01657 display_visible_rect.width(),
01658 display_visible_rect.height(), False));
01659 if (sync)
01660 disp->Sync();
01661
01662 window.SetNeedRepaint(false);
01663 xv_need_bobdeint_repaint = false;
01664 return;
01665 }
01666
01667 if (xv_draw_colorkey && window.IsRepaintNeeded())
01668 {
01669 disp->SetForeground(xv_colorkey);
01670 disp->FillRectangle(XJ_curwin,
01671 QRect(display_visible_rect.left(),
01672 display_visible_rect.top() + boboff,
01673 display_visible_rect.width(),
01674 display_visible_rect.height() - 2 * boboff));
01675 }
01676 else if (xv_draw_colorkey && xv_need_bobdeint_repaint)
01677 {
01678
01679
01680 disp->SetForeground(xv_colorkey);
01681 disp->FillRectangle(XJ_curwin,
01682 QRect(display_visible_rect.left(),
01683 display_visible_rect.top(),
01684 display_visible_rect.width(),
01685 boboff_raw));
01686 disp->FillRectangle(XJ_curwin,
01687 QRect(display_visible_rect.left(),
01688 display_visible_rect.height() - 2 * boboff_raw,
01689 display_visible_rect.width(),
01690 display_visible_rect.height()));
01691 }
01692
01693 window.SetNeedRepaint(false);
01694 xv_need_bobdeint_repaint = false;
01695
01696
01697 disp->SetForeground(XJ_letterbox_colour);
01698
01699 if (display_video_rect.left() > display_visible_rect.left())
01700 {
01701 disp->FillRectangle(XJ_curwin,
01702 QRect(display_visible_rect.left(),
01703 display_visible_rect.top(),
01704 display_video_rect.left() - display_visible_rect.left(),
01705 display_visible_rect.height()));
01706 }
01707 if (display_video_rect.left() + display_video_rect.width() <
01708 display_visible_rect.left() + display_visible_rect.width())
01709 {
01710 disp->FillRectangle(XJ_curwin,
01711 QRect(display_video_rect.left() + display_video_rect.width(),
01712 display_visible_rect.top(),
01713 (display_visible_rect.left() +
01714 display_visible_rect.width()) -
01715 (display_video_rect.left() +
01716 display_video_rect.width()),
01717 display_visible_rect.height()));
01718 }
01719 if (display_video_rect.top() + boboff > display_visible_rect.top())
01720 {
01721 disp->FillRectangle(XJ_curwin,
01722 QRect(display_visible_rect.left(),
01723 display_visible_rect.top(),
01724 display_visible_rect.width(),
01725 display_video_rect.top() + boboff -
01726 display_visible_rect.top()));
01727 }
01728 if (display_video_rect.top() + display_video_rect.height() <
01729 display_visible_rect.top() + display_visible_rect.height())
01730 {
01731 disp->FillRectangle(XJ_curwin,
01732 QRect(display_visible_rect.left(),
01733 display_video_rect.top() + display_video_rect.height(),
01734 display_visible_rect.width(),
01735 (display_visible_rect.top() +
01736 display_visible_rect.height()) -
01737 (display_video_rect.top() +
01738 display_video_rect.height())));
01739 }
01740
01741 if (sync)
01742 disp->Sync();
01743 }
01744
01745
01746 void VideoOutputXv::VideoAspectRatioChanged(float aspect)
01747 {
01748 QMutexLocker locker(&global_lock);
01749 VideoOutput::VideoAspectRatioChanged(aspect);
01750 }
01751
01752 void VideoOutputXv::UpdatePauseFrame(int64_t &disp_timecode)
01753 {
01754 QMutexLocker locker(&global_lock);
01755
01756 LOG(VB_PLAYBACK, LOG_INFO, LOC + "UpdatePauseFrame() " +
01757 vbuffers.GetStatus());
01758
01759 if (VideoOutputSubType() <= XVideo)
01760 {
01761
01762 vbuffers.begin_lock(kVideoBuffer_used);
01763
01764 VideoFrame *used_frame = NULL;
01765 if (vbuffers.size(kVideoBuffer_used) > 0)
01766 used_frame = vbuffers.head(kVideoBuffer_used);
01767
01768 if (used_frame)
01769 CopyFrame(&av_pause_frame, used_frame);
01770
01771 vbuffers.end_lock();
01772
01773 if (!used_frame)
01774 {
01775 vbuffers.GetScratchFrame()->frameNumber = framesPlayed - 1;
01776 CopyFrame(&av_pause_frame, vbuffers.GetScratchFrame());
01777 }
01778
01779 disp_timecode = av_pause_frame.disp_timecode;
01780 }
01781 }
01782
01783 void VideoOutputXv::ProcessFrame(VideoFrame *frame, OSD *osd,
01784 FilterChain *filterList,
01785 const PIPMap &pipPlayers,
01786 FrameScanType scan)
01787 {
01788 if (IsErrored())
01789 {
01790 LOG(VB_GENERAL, LOG_ERR, LOC + "IsErrored() in ProcessFrame()");
01791 return;
01792 }
01793
01794 bool deint_proc = m_deinterlacing && (m_deintFilter != NULL);
01795 bool pauseframe = false;
01796 if (!frame)
01797 {
01798 frame = vbuffers.GetScratchFrame();
01799 vector<const VideoFrame*> locks;
01800 locks.push_back(frame);
01801 locks.push_back(&av_pause_frame);
01802 CopyFrame(frame, &av_pause_frame);
01803 pauseframe = true;
01804 }
01805
01806 bool safepauseframe = pauseframe && !IsBobDeint();
01807 if (!pauseframe || safepauseframe)
01808 {
01809 if (filterList)
01810 filterList->ProcessFrame(frame);
01811
01812 if (deint_proc && m_deinterlaceBeforeOSD)
01813 m_deintFilter->ProcessFrame(frame, scan);
01814 }
01815
01816 ShowPIPs(frame, pipPlayers);
01817
01818 if (osd && !window.IsEmbedding())
01819 {
01820 if (!chroma_osd)
01821 DisplayOSD(frame, osd);
01822 else
01823 {
01824 QMutexLocker locker(&global_lock);
01825 window.SetNeedRepaint(
01826 chroma_osd->ProcessOSD(osd) || window.IsRepaintNeeded());
01827 }
01828 }
01829
01830 if ((!pauseframe || safepauseframe) &&
01831 deint_proc && !m_deinterlaceBeforeOSD)
01832 {
01833 m_deintFilter->ProcessFrame(frame, scan);
01834 }
01835 }
01836
01837
01838 int VideoOutputXv::SetPictureAttribute(
01839 PictureAttribute attribute, int newValue)
01840 {
01841 newValue = videoColourSpace.SetPictureAttribute(attribute, newValue);
01842 if (newValue >= 0)
01843 newValue = SetXVPictureAttribute(attribute, newValue);
01844 return newValue;
01845 }
01846
01847 int VideoOutputXv::SetXVPictureAttribute(PictureAttribute attribute, int newValue)
01848 {
01849 QString attrName = toXVString(attribute);
01850 QByteArray ascii_attr_name = attrName.toAscii();
01851 const char *cname = ascii_attr_name.constData();
01852
01853 if (attrName.isEmpty())
01854 {
01855 LOG(VB_GENERAL, LOG_ERR, "\n\n\n attrName.isEmpty() \n\n\n");
01856 return -1;
01857 }
01858
01859 int port_min = xv_attribute_min[attribute];
01860 int port_max = xv_attribute_max[attribute];
01861 int port_def = xv_attribute_def[attribute];
01862 int range = port_max - port_min;
01863
01864 int valAdj = (kPictureAttribute_Hue == attribute) ? xv_hue_base : 0;
01865
01866 if (xv_set_defaults && range && (kPictureAttribute_Hue == attribute))
01867 {
01868 float tmp = (((float)(port_def - port_min) / (float)range) * 100.0);
01869 valAdj = (int)(tmp + 0.5);
01870 }
01871
01872 int tmpval2 = (newValue + valAdj) % 100;
01873 int tmpval3 = (int) roundf(range * 0.01f * tmpval2);
01874 int value = min(tmpval3 + port_min, port_max);
01875
01876 xv_set_attrib(disp, xv_port, cname, value);
01877
01878 return newValue;
01879 }
01880
01881 void VideoOutputXv::InitPictureAttributes(void)
01882 {
01883 PictureAttributeSupported supported = kPictureAttributeSupported_None;
01884
01885 if (VideoOutputSubType() >= XVideo)
01886 {
01887 if (xv_set_defaults)
01888 {
01889 QByteArray ascii_name = "XV_SET_DEFAULTS";
01890 const char *name = ascii_name.constData();
01891 xv_set_attrib(disp, xv_port, name, 0);
01892 }
01893
01894 int val, min_val, max_val;
01895 for (uint i = 0; i < kPictureAttribute_MAX; i++)
01896 {
01897 QString attrName = toXVString((PictureAttribute)i);
01898 QByteArray ascii_attr_name = attrName.toAscii();
01899 const char *cname = ascii_attr_name.constData();
01900
01901 if (attrName.isEmpty())
01902 continue;
01903
01904 if (xv_is_attrib_supported(disp, xv_port, cname,
01905 &val, &min_val, &max_val))
01906 {
01907 supported = (PictureAttributeSupported)
01908 (supported | toMask((PictureAttribute)i));
01909 xv_attribute_min[(PictureAttribute)i] = min_val;
01910 xv_attribute_max[(PictureAttribute)i] = max_val;
01911 xv_attribute_def[(PictureAttribute)i] = val;
01912 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1: %2:%3:%4")
01913 .arg(cname).arg(min_val).arg(val).arg(max_val));
01914 SetXVPictureAttribute((PictureAttribute)i,
01915 videoColourSpace.GetPictureAttribute((PictureAttribute)i));
01916 }
01917 }
01918
01919 videoColourSpace.SetSupportedAttributes(supported);
01920 }
01921 }
01922
01923 QRect VideoOutputXv::GetPIPRect(PIPLocation location,
01924 MythPlayer *pipplayer,
01925 bool do_pixel_adj) const
01926 {
01927 (void)do_pixel_adj;
01928
01929 if (!pipplayer || !pipplayer->UsingNullVideo())
01930 return VideoOutput::GetPIPRect(location, pipplayer);
01931
01932 QRect position;
01933 const QSize video_disp_dim = window.GetVideoDispDim();
01934 const QRect video_rect = window.GetVideoRect();
01935 const QRect display_video_rect = window.GetDisplayVideoRect();
01936 const QRect display_visible_rect = window.GetDisplayVisibleRect();
01937 float video_aspect = window.GetVideoAspect();
01938 if (video_aspect < 0.01f)
01939 video_aspect = 1.3333f;
01940
01941 const float pip_size = (float)window.GetPIPSize();
01942 const float pipVideoAspect = pipplayer->GetVideoAspect();
01943
01944
01945 int letterXadj = 0;
01946 int letterYadj = 0;
01947 float letterAdj = 1.0f;
01948 if (window.GetAspectOverride() != kAspect_Off)
01949 {
01950 letterXadj = max(-display_video_rect.left(), 0);
01951 float xadj = (float)video_rect.width() / display_visible_rect.width();
01952 letterXadj = (int)(letterXadj * xadj);
01953 float yadj = (float)video_rect.height() / display_visible_rect.height();
01954 letterYadj = max(-display_video_rect.top(), 0);
01955 letterYadj = (int)(letterYadj * yadj);
01956 letterAdj = window.GetVideoAspect() /
01957 window.GetOverridenVideoAspect();
01958 }
01959
01960 float aspectAdj = pipVideoAspect / video_aspect;
01961
01962 int tmph = (int) ((float)video_disp_dim.height() * pip_size * 0.01f);
01963 int tmpw = (int) ((float)video_disp_dim.width() * pip_size * 0.01f *
01964 aspectAdj * letterAdj);
01965 position.setWidth((tmpw >> 1) << 1);
01966 position.setHeight((tmph >> 1) << 1);
01967
01968 int xoff = 30;
01969 int yoff = 40;
01970
01971 switch (location)
01972 {
01973 default:
01974 case kPIPTopLeft:
01975 xoff += letterXadj;
01976 yoff += letterYadj;
01977 break;
01978 case kPIPBottomLeft:
01979 xoff += letterXadj;
01980 yoff = video_disp_dim.height() - position.height() -
01981 yoff - letterYadj;
01982 break;
01983 case kPIPTopRight:
01984 xoff = video_disp_dim.width() - position.width() -
01985 xoff - letterXadj;
01986 yoff = yoff + letterYadj;
01987 break;
01988 case kPIPBottomRight:
01989 xoff = video_disp_dim.width() - position.width() -
01990 xoff - letterXadj;
01991 yoff = video_disp_dim.height() - position.height() -
01992 yoff - letterYadj;
01993 break;
01994 }
01995
01996 position.translate(xoff, yoff);
01997 return position;
01998 }
01999
02000 QStringList VideoOutputXv::GetAllowedRenderers(
02001 MythCodecID myth_codec_id, const QSize &video_dim)
02002 {
02003 (void) video_dim;
02004
02005 QStringList list;
02006
02007 MythXDisplay *disp = OpenMythXDisplay();
02008
02009 if (!disp)
02010 return list;
02011
02012 list = allowed_video_renderers(myth_codec_id, disp);
02013
02014 delete disp;
02015 return list;
02016 }
02017
02018 MythPainter* VideoOutputXv::GetOSDPainter(void)
02019 {
02020 if (chroma_osd)
02021 return chroma_osd->GetPainter();
02022
02023 return VideoOutput::GetOSDPainter();
02024 }
02025
02026 static void SetFromEnv(bool &useXVideo, bool &useShm)
02027 {
02028
02029 if (getenv("NO_XV"))
02030 useXVideo = false;
02031 if (getenv("NO_SHM"))
02032 useXVideo = useShm = false;
02033 }
02034
02035 static void SetFromHW(MythXDisplay *d, Window curwin,
02036 bool &useXVideo, bool &useShm)
02037 {
02038 (void)d;
02039 (void)curwin;
02040
02041 if (!d)
02042 return;
02043
02044 MythXLocker lock(d);
02045
02046
02047 if (useXVideo)
02048 {
02049 uint p_ver, p_rel, p_req, p_event, p_err, ret;
02050 ret = XvQueryExtension(d->GetDisplay(), &p_ver, &p_rel,
02051 &p_req, &p_event, &p_err);
02052 if (Success != ret)
02053 {
02054 LOG(VB_GENERAL, LOG_ERR, LOC +
02055 "XVideo output requested, but is not supported by display.");
02056 useXVideo = false;
02057 }
02058 }
02059
02060 if (useShm)
02061 {
02062 const char *dispname = DisplayString(d->GetDisplay());
02063 if ((dispname) && (*dispname == ':'))
02064 useShm = (bool) XShmQueryExtension(d->GetDisplay());
02065 }
02066 }
02067
02068 static QStringList allowed_video_renderers(
02069 MythCodecID myth_codec_id, MythXDisplay *display, Window curwin)
02070 {
02071 if (!curwin)
02072 curwin = display->GetRoot();
02073
02074 bool xv = true;
02075 bool shm = true;
02076
02077 myth2av_codecid(myth_codec_id);
02078
02079 SetFromEnv(xv, shm);
02080 SetFromHW(display, curwin, xv, shm);
02081
02082 QStringList list;
02083 if (codec_is_std(myth_codec_id))
02084 {
02085 if (xv)
02086 list += "xv-blit";
02087 if (shm)
02088 list += "xshm";
02089 list += "xlib";
02090 }
02091
02092 return list;
02093 }