00001 #include "math.h"
00002
00003 #include <QSize>
00004
00005 #include "mthread.h"
00006 #include "mythlogging.h"
00007 #include "mythmainwindow.h"
00008 #include "mythrender_vdpau.h"
00009
00010
00011 #ifndef VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1
00012 #define VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 (VdpVideoMixerFeature)11
00013 #endif
00014 #define NUM_SCALING_LEVELS 9
00015
00016 #define LOC QString("VDPAU: ")
00017
00018 #define LOCK_RENDER QMutexLocker locker1(&m_render_lock);
00019 #define LOCK_DECODE QMutexLocker locker2(&m_decode_lock);
00020 #define LOCK_ALL LOCK_RENDER; LOCK_DECODE;
00021
00022 #define INIT_ST \
00023 VdpStatus vdp_st; \
00024 bool ok = true;
00025
00026 #define CHECK_ST \
00027 ok &= (vdp_st == VDP_STATUS_OK); \
00028 if (!ok) \
00029 { \
00030 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Error at %1:%2 (#%3, %4)") \
00031 .arg(__FILE__).arg( __LINE__).arg(vdp_st) \
00032 .arg(vdp_get_error_string(vdp_st))); \
00033 }
00034
00035 #define CHECK_STATUS(arg1) \
00036 if (m_preempted) \
00037 { \
00038 m_render_lock.lock(); \
00039 m_decode_lock.lock(); \
00040 Preempted(); \
00041 m_decode_lock.unlock(); \
00042 m_render_lock.unlock(); \
00043 } \
00044 if (m_errored) \
00045 return arg1;
00046
00047 #define GET_PROC(FUNC_ID, PROC) \
00048 vdp_st = vdp_get_proc_address( \
00049 m_device, FUNC_ID, (void **)&PROC); \
00050 CHECK_ST
00051
00052 #define CREATE_CHECK(arg1, arg2) \
00053 if (ok) \
00054 { \
00055 ok = arg1; \
00056 if (!ok) \
00057 LOG(VB_GENERAL, LOG_ERR, LOC + arg2); \
00058 }
00059
00060 #define CHECK_VIDEO_SURFACES(arg1) \
00061 if (m_reset_video_surfaces) \
00062 ResetVideoSurfaces(); \
00063 if (m_reset_video_surfaces) \
00064 return arg1;
00065
00066 static const VdpOutputSurfaceRenderBlendState VDPBlends[3] = {
00067 {
00068 VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
00069 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
00070 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
00071 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
00072 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
00073 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00074 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00075 },
00076 {
00077 VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
00078 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
00079 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
00080 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
00081 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
00082 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00083 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00084 },
00085 {
00086 VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
00087 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
00088 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
00089 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
00090 VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
00091 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00092 VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
00093 }};
00094
00095 class VDPAUColor
00096 {
00097 public:
00098 VDPAUColor(int color = 0x0) : m_color(color)
00099 {
00100 SetColor(m_color);
00101 }
00102
00103 void SetColor(uint color)
00104 {
00105 m_color = color;
00106 m_vdp_color.red = (float)((m_color & 0xFF000000) >> 24) / 255.0f;
00107 m_vdp_color.green = (float)((m_color & 0xFF0000) >> 16) / 255.0f;
00108 m_vdp_color.blue = (float)((m_color & 0xFF00) >> 8)/ 255.0f;
00109 m_vdp_color.alpha = (float)( m_color & 0xFF) / 255.0f;
00110 }
00111
00112 int m_color;
00113 VdpColor m_vdp_color;
00114 };
00115
00116 class VDPAULayer
00117 {
00118 public:
00119 VDPAULayer() {}
00120 VDPAULayer(uint surface, const QRect *src, const QRect *dst)
00121 {
00122 if (src)
00123 {
00124 m_src.x0 = src->left();
00125 m_src.y0 = src->top();
00126 m_src.x1 = src->left() + src->width();
00127 m_src.y1 = src->top() + src->height();
00128 }
00129 if (dst)
00130 {
00131 m_dst.x0 = dst->left();
00132 m_dst.y0 = dst->top();
00133 m_dst.x1 = dst->left() + dst->width();
00134 m_dst.y1 = dst->top() + dst->height();
00135 }
00136 m_layer.struct_version = VDP_LAYER_VERSION;
00137 m_layer.source_surface = surface;
00138 m_layer.source_rect = src ? &m_src : NULL;
00139 m_layer.destination_rect = dst ? &m_dst : NULL;
00140 }
00141
00142 VdpLayer m_layer;
00143 VdpRect m_src;
00144 VdpRect m_dst;
00145 };
00146
00147 class VDPAUResource
00148 {
00149 public:
00150 VDPAUResource() {}
00151 VDPAUResource(uint id, QSize size) : m_id(id), m_size(size) { }
00152 virtual ~VDPAUResource() {}
00153
00154 uint m_id;
00155 QSize m_size;
00156 };
00157
00158 class VDPAUOutputSurface : public VDPAUResource
00159 {
00160 public:
00161 VDPAUOutputSurface() {}
00162 VDPAUOutputSurface(uint id, QSize size, VdpRGBAFormat fmt)
00163 : VDPAUResource(id, size), m_fmt(fmt) { }
00164
00165 VdpRGBAFormat m_fmt;
00166 };
00167
00168 class VDPAUVideoSurface : public VDPAUResource
00169 {
00170 public:
00171 VDPAUVideoSurface() : m_needs_reset(false), m_owner(NULL)
00172 {
00173 memset(&m_render, 0, sizeof(struct vdpau_render_state));
00174 }
00175 VDPAUVideoSurface(uint id, QSize size, VdpChromaType type)
00176 : VDPAUResource(id, size), m_type(type), m_needs_reset(false)
00177 {
00178 m_owner = QThread::currentThread();
00179 memset(&m_render, 0, sizeof(struct vdpau_render_state));
00180 m_render.surface = m_id;
00181 }
00182 void SetID(uint id)
00183 {
00184
00185
00186 m_id = id;
00187 m_render.surface = m_id;
00188 }
00189
00190 VdpChromaType m_type;
00191 vdpau_render_state m_render;
00192 bool m_needs_reset;
00193 QThread* m_owner;
00194 };
00195
00196 class VDPAUBitmapSurface : public VDPAUResource
00197 {
00198 public:
00199 VDPAUBitmapSurface() {}
00200 VDPAUBitmapSurface(uint id, QSize size, VdpRGBAFormat fmt)
00201 : VDPAUResource(id, size), m_fmt(fmt) { }
00202
00203 VdpRGBAFormat m_fmt;
00204 };
00205
00206 class VDPAUDecoder : public VDPAUResource
00207 {
00208 public:
00209 VDPAUDecoder() {}
00210 VDPAUDecoder(uint id, QSize size, VdpDecoderProfile profile, uint refs)
00211 : VDPAUResource(id, size), m_profile(profile), m_max_refs(refs) { }
00212
00213 VdpDecoderProfile m_profile;
00214 uint m_max_refs;
00215 };
00216
00217 class VDPAUVideoMixer : public VDPAUResource
00218 {
00219 public:
00220 VDPAUVideoMixer() :
00221 m_layers(0), m_features(0),
00222 m_noise_reduction(NULL), m_sharpness(NULL),
00223 m_skip_chroma(NULL), m_background(NULL)
00224 {
00225 }
00226 VDPAUVideoMixer(uint id, QSize size, uint layers, uint features,
00227 VdpChromaType type)
00228 : VDPAUResource(id, size), m_layers(layers), m_features(features),
00229 m_type(type), m_noise_reduction(NULL), m_sharpness(NULL),
00230 m_skip_chroma(NULL), m_background(NULL)
00231 {
00232 memset(&m_csc, 0, sizeof(VdpCSCMatrix));
00233 }
00234
00235 ~VDPAUVideoMixer()
00236 {
00237 if (m_noise_reduction)
00238 delete m_noise_reduction;
00239 if (m_sharpness)
00240 delete m_sharpness;
00241 if (m_skip_chroma)
00242 delete m_skip_chroma;
00243 if (m_background)
00244 delete m_background;
00245 }
00246
00247 uint m_layers;
00248 uint m_features;
00249 VdpChromaType m_type;
00250 VdpCSCMatrix m_csc;
00251 float *m_noise_reduction;
00252 float *m_sharpness;
00253 uint8_t *m_skip_chroma;
00254 VDPAUColor *m_background;
00255 };
00256
00257 static void vdpau_preemption_callback(VdpDevice device, void *myth_render)
00258 {
00259 (void)device;
00260 LOG(VB_GENERAL, LOG_WARNING, LOC + "Display pre-empted.");
00261 MythRenderVDPAU *render = (MythRenderVDPAU*)myth_render;
00262 if (render)
00263 render->SetPreempted();
00264 }
00265
00266 bool MythRenderVDPAU::gVDPAUSupportChecked = false;
00267 bool MythRenderVDPAU::gVDPAUMPEG4Accel = false;
00268 uint MythRenderVDPAU::gVDPAUBestScaling = 0;
00269
00270 MythRenderVDPAU::MythRenderVDPAU()
00271 : MythRender(kRenderVDPAU), m_preempted(false), m_recreating(false),
00272 m_recreated(false), m_reset_video_surfaces(false),
00273 m_render_lock(QMutex::Recursive), m_decode_lock(QMutex::Recursive),
00274 m_display(NULL), m_window(0), m_device(0), m_surface(0),
00275 m_flipQueue(0), m_flipTarget(0), m_flipReady(false), m_colorKey(0),
00276 vdp_get_proc_address(NULL), vdp_get_error_string(NULL)
00277 {
00278 LOCK_ALL
00279 ResetProcs();
00280 memset(&m_rect, 0, sizeof(VdpRect));
00281 }
00282
00283 MythRenderVDPAU::~MythRenderVDPAU(void)
00284 {
00285 LOCK_ALL
00286 Destroy();
00287 }
00288
00289 bool MythRenderVDPAU::IsMPEG4Available(void)
00290 {
00291 if (gVDPAUSupportChecked)
00292 return gVDPAUMPEG4Accel;
00293
00294 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Checking VDPAU capabilities.");
00295 MythRenderVDPAU *dummy = new MythRenderVDPAU();
00296 if (dummy)
00297 {
00298 if (dummy->CreateDummy())
00299 return gVDPAUMPEG4Accel;
00300 delete dummy;
00301 }
00302
00303 return false;
00304 }
00305
00306 bool MythRenderVDPAU::H264DecoderSizeSupported(uint width, uint height)
00307 {
00308 int mbs = ceil((double)width / 16.0f);
00309
00310 int check = (mbs == 49 ) || (mbs == 54 ) || (mbs == 59 ) || (mbs == 64) ||
00311 (mbs == 113) || (mbs == 118) || (mbs == 123) || (mbs == 128);
00312 if (!check)
00313 return true;
00314
00315 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00316 QString("Checking support for H.264 video with width %1").arg(width));
00317 bool supported = true;
00318 MythRenderVDPAU *dummy = new MythRenderVDPAU();
00319 if (dummy && dummy->CreateDummy())
00320 {
00321 uint test = dummy->CreateDecoder(QSize(width, height),
00322 VDP_DECODER_PROFILE_H264_HIGH, 2);
00323 if (!test)
00324 supported = false;
00325 else
00326 dummy->DestroyDecoder(test);
00327 }
00328 LOG(VB_GENERAL, (supported ? LOG_INFO : LOG_WARNING), LOC +
00329 QString("Hardware decoding of this H.264 video is %1supported "
00330 "on this video card.").arg(supported ? "" : "NOT "));
00331 if (dummy)
00332 delete dummy;
00333 return supported;
00334 }
00335
00336 bool MythRenderVDPAU::CreateDummy(void)
00337 {
00338 LOCK_ALL
00339
00340 bool ok = true;
00341 m_display = OpenMythXDisplay();
00342 CREATE_CHECK(m_display != NULL, "Invalid display")
00343 CREATE_CHECK(CreateDevice(), "No VDPAU device")
00344 CREATE_CHECK(GetProcs(), "No VDPAU procedures")
00345 CREATE_CHECK(CheckHardwareSupport(), "")
00346
00347 if (!ok)
00348 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create dummy device.");
00349
00350 return ok;
00351 }
00352
00353 bool MythRenderVDPAU::CreateDecodeOnly(void)
00354 {
00355 LOCK_ALL
00356
00357 bool ok = true;
00358 m_display = OpenMythXDisplay();
00359 CREATE_CHECK(m_display != NULL, "Invalid display")
00360 CREATE_CHECK(CreateDevice(), "No VDPAU device")
00361 CREATE_CHECK(GetProcs(), "No VDPAU procedures")
00362 CREATE_CHECK(RegisterCallback(), "No callback")
00363 CREATE_CHECK(CheckHardwareSupport(), "")
00364
00365 if (!ok)
00366 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create dummy device.");
00367
00368 return ok;
00369 }
00370
00371 bool MythRenderVDPAU::Create(const QSize &size, WId window, uint colorkey)
00372 {
00373 LOCK_ALL
00374
00375 m_size = size;
00376 m_rect.x0 = 0;
00377 m_rect.y0 = 0;
00378 m_rect.x1 = size.width();
00379 m_rect.y1 = size.height();
00380 m_display = OpenMythXDisplay();
00381 m_window = window;
00382
00383 bool ok = true;
00384
00385 CREATE_CHECK(!m_size.isEmpty(), "Invalid size")
00386 CREATE_CHECK(m_display != NULL, "Invalid display")
00387 CREATE_CHECK(m_window > 0, "Invalid window")
00388 CREATE_CHECK(m_display->CreateGC(m_window), "No GC")
00389 CREATE_CHECK(CreateDevice(), "No VDPAU device")
00390 CREATE_CHECK(GetProcs(), "No VDPAU procedures")
00391 CREATE_CHECK(CreatePresentationQueue(), "No presentation queue")
00392 CREATE_CHECK(CreatePresentationSurfaces(), "No presentation surfaces")
00393 CREATE_CHECK(SetColorKey(colorkey), "No colorkey")
00394 CREATE_CHECK(RegisterCallback(), "No callback")
00395 CREATE_CHECK(CheckHardwareSupport(), "")
00396
00397 if (ok)
00398 {
00399 LOG(VB_GENERAL, LOG_INFO, LOC +
00400 QString("Created VDPAU render device %1x%2")
00401 .arg(size.width()).arg(size.height()));
00402 return ok;
00403 }
00404
00405 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VDPAU render device.");
00406 return ok;
00407 }
00408
00409 bool MythRenderVDPAU::WasPreempted(void)
00410 {
00411
00412 if (m_recreated)
00413 {
00414 m_recreated = false;
00415 return true;
00416 }
00417 return false;
00418 }
00419
00420 bool MythRenderVDPAU::SetColorKey(uint colorkey)
00421 {
00422 CHECK_STATUS(false)
00423 LOCK_RENDER
00424 INIT_ST
00425
00426 if (!m_flipQueue)
00427 return false;
00428
00429 m_colorKey = colorkey;
00430 if (m_display && (m_display->GetDepth() < 24))
00431 m_colorKey = 0x0;
00432
00433 VDPAUColor color((colorkey << 8) + 0xFF);
00434 vdp_st = vdp_presentation_queue_set_background_color(m_flipQueue,
00435 &(color.m_vdp_color));
00436 CHECK_ST
00437
00438 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Set colorkey to 0x%1")
00439 .arg(m_colorKey, 0, 16));
00440 return ok;
00441 }
00442
00443 void MythRenderVDPAU::WaitForFlip(void)
00444 {
00445 if (!m_flipReady)
00446 return;
00447
00448 VdpOutputSurface surface = 0;
00449 {
00450 CHECK_STATUS()
00451 LOCK_RENDER
00452 if (m_surface >= (uint)m_surfaces.size())
00453 return;
00454 surface = m_outputSurfaces[m_surfaces[m_surface]].m_id;
00455 }
00456
00457 INIT_ST
00458 VdpTime dummy = 0;
00459 usleep(2000);
00460 vdp_st = vdp_presentation_queue_block_until_surface_idle(
00461 m_flipQueue, surface, &dummy);
00462 CHECK_ST
00463 }
00464
00465 void MythRenderVDPAU::Flip(void)
00466 {
00467 if (!m_flipReady || !m_display)
00468 return;
00469
00470 VdpOutputSurface surface = 0;
00471 {
00472 CHECK_STATUS()
00473 LOCK_RENDER
00474 if (m_surface >= (uint)m_surfaces.size())
00475 return;
00476 surface = m_outputSurfaces[m_surfaces[m_surface]].m_id;
00477 m_surface++;
00478 if (m_surface >= (uint)m_surfaces.size())
00479 m_surface = 0;
00480 }
00481
00482 INIT_ST
00483 vdp_st = vdp_presentation_queue_display(m_flipQueue, surface, m_rect.x1, m_rect.y1, 0);
00484 CHECK_ST
00485 SyncDisplay();
00486 }
00487
00488 void MythRenderVDPAU::SyncDisplay(void)
00489 {
00490 LOCK_RENDER
00491 if (m_display)
00492 m_display->Sync();
00493 }
00494
00495 void MythRenderVDPAU::DrawDisplayRect(const QRect &rect, bool use_colorkey)
00496 {
00497 LOCK_RENDER
00498 if (!m_display || !m_window)
00499 return;
00500
00501 uint color = use_colorkey ? m_colorKey : m_display->GetBlack();
00502 m_display->SetForeground(color);
00503 m_display->FillRectangle(m_window, rect);
00504 }
00505
00506 void MythRenderVDPAU::MoveResizeWin(QRect &rect)
00507 {
00508 LOCK_RENDER
00509 if (m_display && m_window)
00510 m_display->MoveResizeWin(m_window, rect);
00511 }
00512
00513 bool MythRenderVDPAU::GetScreenShot(int width, int height, QString filename)
00514 {
00515 CHECK_STATUS(false)
00516 LOCK_RENDER
00517 if (m_surface >= (uint)m_surfaces.size())
00518 return false;
00519
00520 VdpRGBAFormat fmt;
00521 uint32_t w, h;
00522 VdpOutputSurface surface = m_outputSurfaces[m_surfaces[m_surface]].m_id;
00523 INIT_ST
00524 vdp_st = vdp_output_surface_get_parameters(surface, &fmt, &w, &h);
00525 CHECK_ST
00526
00527 if (!ok || fmt != VDP_RGBA_FORMAT_B8G8R8A8 || w <= 0 || h <= 0)
00528 return false;
00529
00530 int size = w * h * 4;
00531 unsigned char* buffer = new unsigned char[size];
00532 void* const data[1] = { buffer };
00533 const uint32_t pitches[1] = { w * 4 };
00534 vdp_st = vdp_output_surface_get_bits_native(surface, NULL, data, pitches);
00535 CHECK_ST
00536
00537 if (!ok)
00538 {
00539 delete [] buffer;
00540 return false;
00541 }
00542
00543 bool success = false;
00544 QImage img(buffer, w, h, QImage::Format_RGB32);
00545 MythMainWindow *window = GetMythMainWindow();
00546 if (window)
00547 {
00548 if (width <= 0)
00549 width = img.width();
00550 if (height <= 0)
00551 height = img.height();
00552
00553 img = img.scaled(width, height, Qt::KeepAspectRatio,
00554 Qt::SmoothTransformation);
00555 success = window->SaveScreenShot(img, filename);
00556 }
00557 delete [] buffer;
00558 return success;
00559 }
00560
00561 void MythRenderVDPAU::CheckOutputSurfaces(void)
00562 {
00563 CHECK_STATUS()
00564 LOCK_RENDER
00565 int need = MAX_OUTPUT_SURFACES;
00566 int have = m_surfaces.size();
00567 int created = 0;
00568
00569
00570 if (have >= need)
00571 return;
00572
00573 for (; have < need; have++)
00574 {
00575 uint id = CreateOutputSurface(m_size);
00576 if (id)
00577 {
00578 m_surfaces.push_back(id);
00579 created++;
00580 }
00581 else
00582 break;
00583 }
00584
00585 LOG(VB_GENERAL, LOG_INFO, LOC +
00586 QString("Added %1 output surfaces (total %2, max %3)")
00587 .arg(created).arg(m_surfaces.size()).arg(need));
00588 }
00589
00590 uint MythRenderVDPAU::CreateOutputSurface(const QSize &size, VdpRGBAFormat fmt,
00591 uint existing)
00592 {
00593 CHECK_STATUS(0)
00594 LOCK_RENDER
00595 INIT_ST
00596
00597 if ((existing && !m_outputSurfaces.contains(existing)) || size.isEmpty())
00598 return 0;
00599
00600 VdpOutputSurface tmp;
00601 vdp_st = vdp_output_surface_create(m_device, fmt, size.width(),
00602 size.height(), &tmp);
00603 CHECK_ST
00604
00605 if (!ok || !tmp)
00606 {
00607 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create output surface.");
00608 return 0;
00609 }
00610
00611 if (existing)
00612 {
00613 m_outputSurfaces[existing].m_id = tmp;
00614 return existing;
00615 }
00616
00617 static uint32_t next_id = 1;
00618 static QMutex id_lock(QMutex::Recursive);
00619
00620 id_lock.lock();
00621 while (m_outputSurfaces.contains(next_id))
00622 if ((++next_id) == 0)
00623 next_id = 1;
00624
00625 uint id = next_id;
00626 m_outputSurfaces.insert(id, VDPAUOutputSurface(tmp, size, fmt));
00627 id_lock.unlock();
00628
00629 DrawBitmap(0, id, NULL, NULL);
00630 return id;
00631 }
00632
00633 uint MythRenderVDPAU::CreateVideoSurface(const QSize &size, VdpChromaType type,
00634 uint existing)
00635 {
00636 CHECK_STATUS(0)
00637 LOCK_RENDER
00638 INIT_ST
00639
00640 if ((existing && !m_videoSurfaces.contains(existing)) || size.isEmpty())
00641 return 0;
00642
00643 VdpOutputSurface tmp;
00644 vdp_st = vdp_video_surface_create(m_device, type, size.width(),
00645 size.height(), &tmp);
00646 CHECK_ST
00647
00648 if (!ok || !tmp)
00649 {
00650 LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to create video surface.");
00651 return 0;
00652 }
00653
00654 if (existing)
00655 {
00656 m_videoSurfaces[existing].SetID(tmp);
00657 m_videoSurfaceHash[tmp] = existing;
00658 return existing;
00659 }
00660
00661 static uint32_t next_id = 1;
00662 static QMutex id_lock(QMutex::Recursive);
00663
00664 id_lock.lock();
00665 while (m_videoSurfaces.contains(next_id))
00666 if ((++next_id) == 0)
00667 next_id = 1;
00668
00669 uint id = next_id;
00670 m_videoSurfaces.insert(id, VDPAUVideoSurface(tmp, size, type));
00671 m_videoSurfaceHash[tmp] = id;
00672 id_lock.unlock();
00673
00674 return id;
00675 }
00676
00677 uint MythRenderVDPAU::CreateBitmapSurface(const QSize &size, VdpRGBAFormat fmt,
00678 uint existing)
00679 {
00680 CHECK_STATUS(0)
00681 LOCK_RENDER
00682 INIT_ST
00683
00684 if ((existing && !m_bitmapSurfaces.contains(existing)) || size.isEmpty())
00685 return 0;
00686
00687 VdpBitmapSurface tmp;
00688 vdp_st = vdp_bitmap_surface_create(m_device, fmt, size.width(),
00689 size.height(), true, &tmp);
00690 CHECK_ST
00691
00692 if (!ok || !tmp)
00693 {
00694 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create bitmap surface.");
00695 return 0;
00696 }
00697
00698 if (existing)
00699 {
00700 m_bitmapSurfaces[existing].m_id = tmp;
00701 return existing;
00702 }
00703
00704 static uint32_t next_id = 1;
00705 static QMutex id_lock(QMutex::Recursive);
00706
00707 id_lock.lock();
00708 while (m_bitmapSurfaces.contains(next_id))
00709 if ((++next_id) == 0)
00710 next_id = 1;
00711
00712 uint id = next_id;
00713 m_bitmapSurfaces.insert(id, VDPAUBitmapSurface(tmp, size, fmt));
00714 id_lock.unlock();
00715
00716 return id;
00717 }
00718
00719 uint MythRenderVDPAU::CreateDecoder(const QSize &size,
00720 VdpDecoderProfile profile,
00721 uint references, uint existing)
00722 {
00723 CHECK_STATUS(0)
00724 LOCK_DECODE
00725 INIT_ST
00726
00727 if ((existing && !m_decoders.contains(existing)) || size.isEmpty() ||
00728 references < 1)
00729 return 0;
00730
00731 VdpDecoder tmp;
00732 vdp_st = vdp_decoder_create(m_device, profile, size.width(),
00733 size.height(), references, &tmp);
00734 CHECK_ST
00735
00736 if (!ok || !tmp)
00737 {
00738 LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to create decoder.");
00739 return 0;
00740 }
00741
00742 if (existing)
00743 {
00744 m_decoders[existing].m_id = tmp;
00745 return existing;
00746 }
00747
00748 static uint32_t next_id = 1;
00749 static QMutex id_lock(QMutex::Recursive);
00750
00751 id_lock.lock();
00752 while (m_decoders.contains(next_id))
00753 if ((++next_id) == 0)
00754 next_id = 1;
00755
00756 uint id = next_id;
00757 m_decoders.insert(id, VDPAUDecoder(tmp, size, profile, references));
00758 id_lock.unlock();
00759
00760 return id;
00761 }
00762
00763 uint MythRenderVDPAU::CreateVideoMixer(const QSize &size, uint layers,
00764 uint features, VdpChromaType type,
00765 uint existing)
00766 {
00767 CHECK_STATUS(0)
00768 LOCK_RENDER
00769 INIT_ST
00770
00771 if ((existing && !m_videoMixers.contains(existing)) || size.isEmpty())
00772 return 0;
00773
00774 VdpVideoMixer tmp;
00775 uint width = size.width();
00776 uint height = size.height();
00777
00778 VdpVideoMixerParameter parameters[] = {
00779 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
00780 VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
00781 VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE,
00782 VDP_VIDEO_MIXER_PARAMETER_LAYERS,
00783 };
00784
00785 void const * parameter_values[] = { &width, &height, &type, &layers};
00786
00787 int count = 0;
00788 VdpVideoMixerFeature feat[6];
00789 VdpBool enable = true;
00790 const VdpBool enables[6] = { enable, enable, enable, enable, enable, enable };
00791
00792 bool temporal = (features & kVDPFeatTemporal) ||
00793 (features & kVDPFeatSpatial);
00794 if (temporal)
00795 {
00796 feat[count] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL;
00797 count++;
00798 }
00799
00800 if (features & kVDPFeatSpatial)
00801 {
00802 feat[count] = VDP_VIDEO_MIXER_FEATURE_DEINTERLACE_TEMPORAL_SPATIAL;
00803 count++;
00804 }
00805
00806 if ((features & kVDPFeatIVTC) && temporal)
00807 {
00808 feat[count] = VDP_VIDEO_MIXER_FEATURE_INVERSE_TELECINE;
00809 count++;
00810 }
00811
00812 if (features & kVDPFeatDenoise)
00813 {
00814 feat[count] = VDP_VIDEO_MIXER_FEATURE_NOISE_REDUCTION;
00815 count++;
00816 }
00817
00818 if (features & kVDPFeatSharpness)
00819 {
00820 feat[count] = VDP_VIDEO_MIXER_FEATURE_SHARPNESS;
00821 count++;
00822 }
00823
00824 if (features & kVDPFeatHQScaling)
00825 {
00826 if (gVDPAUBestScaling)
00827 {
00828 feat[count] = gVDPAUBestScaling;
00829 count++;
00830 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Enabling high quality scaling.");
00831 }
00832 else
00833 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00834 "High quality scaling not available");
00835 }
00836
00837 vdp_st = vdp_video_mixer_create(m_device, count, count ? feat : NULL,
00838 4, parameters, parameter_values, &tmp);
00839 CHECK_ST
00840
00841 if (!ok || !tmp)
00842 {
00843 LOG(VB_PLAYBACK, LOG_ERR, LOC + "Failed to create video mixer.");
00844 return 0;
00845 }
00846
00847 vdp_st = vdp_video_mixer_set_feature_enables(
00848 tmp, count, count ? feat : NULL, count ? enables : NULL);
00849 CHECK_ST
00850
00851 if (!ok)
00852 LOG(VB_PLAYBACK, LOG_WARNING, LOC +
00853 "WARNING: Failed to enable video mixer features.");
00854
00855 if (existing)
00856 {
00857 m_videoMixers[existing].m_id = tmp;
00858 m_videoMixers[existing].m_features = features;
00859 m_videoMixers[existing].m_type = type;
00860 m_videoMixers[existing].m_size = size;
00861
00862 SetCSCMatrix(existing, &m_videoMixers[existing].m_csc);
00863
00864 if (m_videoMixers[existing].m_noise_reduction)
00865 SetMixerAttribute(existing, kVDPAttribNoiseReduction,
00866 *(m_videoMixers[existing].m_noise_reduction));
00867 if (m_videoMixers[existing].m_sharpness)
00868 SetMixerAttribute(existing, kVDPAttribSharpness,
00869 *(m_videoMixers[existing].m_sharpness));
00870 if (m_videoMixers[existing].m_skip_chroma)
00871 SetMixerAttribute(existing, kVDPAttribSkipChroma,
00872 *(m_videoMixers[existing].m_skip_chroma));
00873 if (m_videoMixers[existing].m_background)
00874 SetMixerAttribute(existing, kVDPAttribBackground,
00875 m_videoMixers[existing].m_background->m_color);
00876 return existing;
00877 }
00878
00879 static uint32_t next_id = 1;
00880 static QMutex id_lock(QMutex::Recursive);
00881
00882 id_lock.lock();
00883 while (m_videoMixers.contains(next_id))
00884 if ((++next_id) == 0)
00885 next_id = 1;
00886
00887 uint id = next_id;
00888 m_videoMixers.insert(id,
00889 VDPAUVideoMixer(tmp, size, layers, features, type));
00890 id_lock.unlock();
00891
00892 return id;
00893 }
00894
00895 uint MythRenderVDPAU::CreateLayer(uint surface, const QRect *src,
00896 const QRect *dst)
00897 {
00898 CHECK_STATUS(0)
00899 LOCK_RENDER
00900
00901 if (!m_outputSurfaces.contains(surface))
00902 return 0;
00903
00904 static uint32_t next_id = 1;
00905 static QMutex id_lock(QMutex::Recursive);
00906
00907 id_lock.lock();
00908 while (m_layers.contains(next_id))
00909 if ((++next_id) == 0)
00910 next_id = 1;
00911
00912 uint id = next_id;
00913 m_layers.insert(id, VDPAULayer(m_outputSurfaces[surface].m_id, src, dst));
00914 id_lock.unlock();
00915
00916 return id;
00917 }
00918
00919 void MythRenderVDPAU::DestroyOutputSurface(uint id)
00920 {
00921 if (!vdp_output_surface_destroy)
00922 return;
00923
00924 CHECK_STATUS()
00925 LOCK_RENDER
00926 INIT_ST
00927
00928 if (!m_outputSurfaces.contains(id))
00929 return;
00930
00931 vdp_st = vdp_output_surface_destroy(m_outputSurfaces[id].m_id);
00932 CHECK_ST
00933 m_outputSurfaces.remove(id);
00934 }
00935
00936 void MythRenderVDPAU::DestroyVideoSurface(uint id)
00937 {
00938 CHECK_STATUS()
00939 LOCK_RENDER
00940 INIT_ST
00941
00942 if (!m_videoSurfaces.contains(id))
00943 return;
00944
00945 vdp_st = vdp_video_surface_destroy(m_videoSurfaces[id].m_id);
00946 CHECK_ST
00947 m_videoSurfaceHash.remove(m_videoSurfaces[id].m_id);
00948 m_videoSurfaces.remove(id);
00949 }
00950
00951 void MythRenderVDPAU::DestroyBitmapSurface(uint id)
00952 {
00953 CHECK_STATUS()
00954 LOCK_RENDER
00955 INIT_ST
00956
00957 if (!m_bitmapSurfaces.contains(id))
00958 return;
00959
00960 vdp_st = vdp_bitmap_surface_destroy(m_bitmapSurfaces[id].m_id);
00961 CHECK_ST
00962 m_bitmapSurfaces.remove(id);
00963 }
00964
00965 void MythRenderVDPAU::DestroyDecoder(uint id)
00966 {
00967 CHECK_STATUS()
00968 LOCK_DECODE
00969 INIT_ST
00970
00971 if (!m_decoders.contains(id))
00972 return;
00973
00974 vdp_st = vdp_decoder_destroy(m_decoders[id].m_id);
00975 CHECK_ST
00976 m_decoders.remove(id);
00977 }
00978
00979 void MythRenderVDPAU::DestroyVideoMixer(uint id)
00980 {
00981 CHECK_STATUS()
00982 LOCK_RENDER
00983 INIT_ST
00984
00985 if (!m_videoMixers.contains(id))
00986 return;
00987
00988 vdp_st = vdp_video_mixer_destroy(m_videoMixers[id].m_id);
00989 CHECK_ST
00990 m_videoMixers.remove(id);
00991 }
00992
00993 void MythRenderVDPAU::DestroyLayer(uint id)
00994 {
00995 CHECK_STATUS()
00996 LOCK_RENDER
00997
00998 if (!m_layers.contains(id))
00999 return;
01000
01001 m_layers.remove(id);
01002 }
01003
01004 bool MythRenderVDPAU::MixAndRend(uint id, VdpVideoMixerPictureStructure field,
01005 uint vid_surface, uint out_surface,
01006 const QVector<uint>* refs, bool top,
01007 QRect src, const QRect &dst,
01008 QRect dst_vid, uint layer1, uint layer2)
01009 {
01010 CHECK_VIDEO_SURFACES(true)
01011 INIT_ST
01012
01013 VdpVideoSurface past_surfaces[2] = { VDP_INVALID_HANDLE,
01014 VDP_INVALID_HANDLE };
01015 VdpVideoSurface future_surfaces[1] = { VDP_INVALID_HANDLE };
01016 VdpVideoSurface vid_surf = 0;
01017 bool deint = false;
01018 uint num_layers = 0;
01019 VdpLayer layers[2];
01020 VdpOutputSurface surf = 0;
01021 VdpVideoMixer mixer = 0;
01022
01023 {
01024 CHECK_STATUS(false)
01025 LOCK_RENDER
01026
01027 if (!out_surface)
01028 out_surface = m_surfaces[m_surface];
01029
01030 if (!m_videoMixers.contains(id) ||
01031 !m_videoSurfaces.contains(vid_surface)||
01032 !m_outputSurfaces.contains(out_surface))
01033 return false;
01034
01035 vid_surf = m_videoSurfaces[vid_surface].m_id;
01036
01037 if (refs && refs->size() == NUM_REFERENCE_FRAMES)
01038 {
01039 deint = true;
01040 VdpVideoSurface act_refs[NUM_REFERENCE_FRAMES];
01041 for (int i = 0; i < NUM_REFERENCE_FRAMES; i++)
01042 {
01043 if (m_videoSurfaces.contains(refs->value(i)))
01044 act_refs[i] = m_videoSurfaces[refs->value(i)].m_id;
01045 else
01046 act_refs[i] = VDP_INVALID_HANDLE;
01047 }
01048
01049 vid_surf = act_refs[1];
01050
01051 if (top)
01052 {
01053 future_surfaces[0] = act_refs[1];
01054 past_surfaces[0] = act_refs[0];
01055 past_surfaces[1] = act_refs[0];
01056 }
01057 else
01058 {
01059 future_surfaces[0] = act_refs[2];
01060 past_surfaces[0] = act_refs[1];
01061 past_surfaces[1] = act_refs[0];
01062 }
01063 }
01064
01065 if (m_layers.contains(layer1))
01066 {
01067 memcpy(&(layers[num_layers]), &(m_layers[layer1].m_layer),
01068 sizeof(VdpLayer));
01069 num_layers++;
01070 }
01071 if (m_layers.contains(layer2))
01072 {
01073 memcpy(&(layers[num_layers]), &(m_layers[layer2].m_layer),
01074 sizeof(VdpLayer));
01075 num_layers++;
01076 }
01077
01078 surf = m_outputSurfaces[out_surface].m_id;
01079 mixer = m_videoMixers[id].m_id;
01080 }
01081
01082 if (dst_vid.top() < 0 && dst_vid.height() > 0)
01083 {
01084 float yscale = (float)src.height() /
01085 (float)dst_vid.height();
01086 int tmp = src.top() -
01087 (int)((float)dst_vid.top() * yscale);
01088 src.setTop(std::max(0, tmp));
01089 dst_vid.setTop(0);
01090 }
01091
01092 if (dst_vid.left() < 0 && dst_vid.width() > 0)
01093 {
01094 float xscale = (float)src.width() /
01095 (float)dst_vid.width();
01096 int tmp = src.left() -
01097 (int)((float)dst_vid.left() * xscale);
01098 src.setLeft(std::max(0, tmp));
01099 dst_vid.setLeft(0);
01100 }
01101
01102 VdpRect outRect, srcRect, outRectVid;
01103
01104 outRect.x0 = dst.left();
01105 outRect.y0 = dst.top();
01106 outRect.x1 = dst.left() + dst.width();
01107 outRect.y1 = dst.top() + dst.height();
01108 srcRect.x0 = src.left();
01109 srcRect.y0 = src.top();
01110 srcRect.x1 = src.left() + src.width();
01111 srcRect.y1 = src.top() + src.height();
01112 outRectVid.x0 = dst_vid.left();
01113 outRectVid.y0 = dst_vid.top();
01114 outRectVid.x1 = dst_vid.left() + dst_vid.width();
01115 outRectVid.y1 = dst_vid.top() + dst_vid.height();
01116
01117
01118 vdp_st = vdp_video_mixer_render(mixer, VDP_INVALID_HANDLE,
01119 NULL, field, deint ? 2 : 0,
01120 deint ? past_surfaces : NULL,
01121 vid_surf, deint ? 1 : 0,
01122 deint ? future_surfaces : NULL,
01123 &srcRect, surf, &outRect, &outRectVid,
01124 num_layers, num_layers ? layers : NULL);
01125 CHECK_ST
01126 return ok;
01127 }
01128
01129 bool MythRenderVDPAU::SetDeinterlacing(uint id, uint deinterlacers)
01130 {
01131 CHECK_STATUS(false)
01132 LOCK_RENDER
01133
01134 if (!m_videoMixers.contains(id))
01135 return false;
01136
01137 static const uint all_deints = kVDPFeatTemporal | kVDPFeatSpatial;
01138 uint current = m_videoMixers[id].m_features;
01139 uint deints = current & all_deints;
01140
01141 if (deints == deinterlacers)
01142 return true;
01143
01144 uint newfeats = (current & ~all_deints) + deinterlacers;
01145 return ChangeVideoMixerFeatures(id, newfeats);
01146 }
01147
01148 bool MythRenderVDPAU::ChangeVideoMixerFeatures(uint id, uint features)
01149 {
01150 CHECK_STATUS(false)
01151 LOCK_RENDER
01152
01153 if (!m_videoMixers.contains(id))
01154 return false;
01155
01156 INIT_ST
01157 vdp_st = vdp_video_mixer_destroy(m_videoMixers[id].m_id);
01158 CHECK_ST
01159 return (id == CreateVideoMixer(m_videoMixers[id].m_size,
01160 m_videoMixers[id].m_layers, features,
01161 m_videoMixers[id].m_type, id));
01162 }
01163
01164 void MythRenderVDPAU::SetCSCMatrix(uint id, void* vals)
01165 {
01166 CHECK_STATUS();
01167 LOCK_RENDER
01168
01169 if (!m_videoMixers.contains(id))
01170 return;
01171
01172 memcpy(&m_videoMixers[id].m_csc, vals, sizeof(VdpCSCMatrix));
01173
01174 VdpVideoMixerAttribute attr = { VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX };
01175 void const * val = { vals };
01176 SetMixerAttribute(id, &attr, &val);
01177 }
01178
01179 int MythRenderVDPAU::SetMixerAttribute(uint id, uint attrib, int value)
01180 {
01181 CHECK_STATUS(false);
01182 LOCK_RENDER
01183
01184 if (!m_videoMixers.contains(id) || attrib > kVDPAttribCSCEnd)
01185 return -1;
01186
01187 if (attrib == kVDPAttribSkipChroma)
01188 {
01189 if (!m_videoMixers[id].m_skip_chroma)
01190 m_videoMixers[id].m_skip_chroma = new uint8_t();
01191 *(m_videoMixers[id].m_skip_chroma) = value;
01192 VdpVideoMixerAttribute attr =
01193 { VDP_VIDEO_MIXER_ATTRIBUTE_SKIP_CHROMA_DEINTERLACE };
01194 void const * val = { &value };
01195 return SetMixerAttribute(id, &attr, &val);
01196 }
01197
01198 if (attrib == kVDPAttribBackground)
01199 {
01200 if (!m_videoMixers[id].m_background)
01201 m_videoMixers[id].m_background = new VDPAUColor();
01202 m_videoMixers[id].m_background->SetColor(value);
01203 VdpVideoMixerAttribute attr =
01204 { VDP_VIDEO_MIXER_ATTRIBUTE_BACKGROUND_COLOR };
01205 void const * val = { &(m_videoMixers[id].m_background->m_vdp_color) };
01206 return SetMixerAttribute(id, &attr, &val);
01207 }
01208
01209 return -1;
01210 }
01211
01212 bool MythRenderVDPAU::SetMixerAttribute(uint id, uint attrib, float value)
01213 {
01214 CHECK_STATUS(false);
01215 LOCK_RENDER
01216
01217 if (!m_videoMixers.contains(id) || attrib < kVDPAttribFiltersStart)
01218 return false;
01219
01220 VdpVideoMixerAttribute attr;
01221 void const * val = { &value };
01222
01223 if (attrib == kVDPAttribNoiseReduction)
01224 {
01225 if (!m_videoMixers[id].m_noise_reduction)
01226 m_videoMixers[id].m_noise_reduction = new float();
01227 *(m_videoMixers[id].m_noise_reduction) = value;
01228 attr = VDP_VIDEO_MIXER_ATTRIBUTE_NOISE_REDUCTION_LEVEL;
01229 }
01230 else if (attrib == kVDPAttribSharpness)
01231 {
01232 if (!m_videoMixers[id].m_sharpness)
01233 m_videoMixers[id].m_sharpness = new float();
01234 *(m_videoMixers[id].m_sharpness) = value;
01235 attr = VDP_VIDEO_MIXER_ATTRIBUTE_SHARPNESS_LEVEL;
01236 }
01237 else
01238 return false;
01239
01240 return SetMixerAttribute(id, &attr, &val);
01241 }
01242
01243 bool MythRenderVDPAU::UploadBitmap(uint id, void* const plane[1], uint32_t pitch[1])
01244 {
01245 VdpBitmapSurface bitmap = 0;
01246 {
01247 CHECK_STATUS(false)
01248 LOCK_RENDER
01249 if (!m_bitmapSurfaces.contains(id))
01250 return false;
01251 bitmap = m_bitmapSurfaces[id].m_id;
01252 }
01253
01254 INIT_ST
01255 vdp_st = vdp_bitmap_surface_put_bits_native(bitmap, plane, pitch, NULL);
01256 CHECK_ST
01257
01258 return ok;
01259 }
01260
01261 bool MythRenderVDPAU::UploadMythImage(uint id, MythImage *image)
01262 {
01263 if (!image)
01264 return false;
01265
01266 void *plane[1] = { image->bits() };
01267 uint32_t pitch[1] = { image->bytesPerLine() };
01268 return UploadBitmap(id, plane, pitch);
01269 }
01270
01271 bool MythRenderVDPAU::UploadYUVFrame(uint id, void* const planes[3],
01272 uint32_t pitches[3])
01273 {
01274 CHECK_VIDEO_SURFACES(false)
01275
01276 VdpVideoSurface surface = 0;
01277 {
01278 CHECK_STATUS(false)
01279 LOCK_RENDER
01280 if (!m_videoSurfaces.contains(id))
01281 return false;
01282 surface = m_videoSurfaces[id].m_id;
01283 }
01284
01285 INIT_ST
01286 vdp_st = vdp_video_surface_put_bits_y_cb_cr(surface,
01287 VDP_YCBCR_FORMAT_YV12,
01288 planes, pitches);
01289 CHECK_ST;
01290 return ok;
01291 }
01292
01293 bool MythRenderVDPAU::DownloadYUVFrame(uint id, void *const planes[3],
01294 uint32_t pitches[3])
01295 {
01296 CHECK_VIDEO_SURFACES(false)
01297
01298 VdpVideoSurface surface = 0;
01299 {
01300 CHECK_STATUS(false)
01301 LOCK_RENDER
01302 if (!m_videoSurfaces.contains(id))
01303 return false;
01304 surface = m_videoSurfaces[id].m_id;
01305 }
01306
01307 INIT_ST
01308 vdp_st = vdp_video_surface_get_bits_y_cb_cr(surface,
01309 VDP_YCBCR_FORMAT_YV12,
01310 planes, pitches);
01311 CHECK_ST
01312 return ok;
01313 }
01314
01315 bool MythRenderVDPAU::DrawBitmap(uint id, uint target,
01316 const QRect *src, const QRect *dst,
01317 VDPBlendType blend,
01318 int alpha, int red, int green, int blue)
01319 {
01320 uint bitmap = VDP_INVALID_HANDLE;
01321 VdpOutputSurface surface = VDP_INVALID_HANDLE;
01322 {
01323 CHECK_STATUS(false)
01324 LOCK_RENDER
01325
01326 if (!target)
01327 target = m_surfaces[m_surface];
01328 if (!m_outputSurfaces.contains(target))
01329 return false;
01330 surface = m_outputSurfaces[target].m_id;
01331 if (id && m_bitmapSurfaces.contains(id))
01332 bitmap = m_bitmapSurfaces[id].m_id;
01333 }
01334
01335 VdpRect vdest, vsrc;
01336 if (dst)
01337 {
01338 int width = dst->width();
01339 int height = dst->height();
01340
01341 if (src)
01342 {
01343 width = std::min(src->width(), width);
01344 height = std::min(src->height(), height);
01345 }
01346
01347 vdest.x0 = dst->x();
01348 vdest.y0 = dst->y();
01349 vdest.x1 = dst->x() + width;
01350 vdest.y1 = dst->y() + height;
01351
01352 if (vdest.x0 < 0)
01353 vdest.x0 = 0;
01354 if (vdest.y0 < 0)
01355 vdest.y0 = 0;
01356 }
01357
01358 if (src)
01359 {
01360 vsrc.x0 = src->x();
01361 vsrc.y0 = src->y();
01362 vsrc.x1 = src->x() + src->width();
01363 vsrc.y1 = src->y() + src->height();
01364 }
01365
01366 VdpColor color;
01367 if (!(red == 0 && green == 0 && blue == 0 && alpha == 0))
01368 {
01369 color.red = red / 255.0f;
01370 color.green = green / 255.0f;
01371 color.blue = blue / 255.0f;
01372 color.alpha = alpha / 255.0f;
01373 }
01374
01375 INIT_ST
01376 vdp_st = vdp_output_surface_render_bitmap_surface(
01377 surface,
01378 dst ? &vdest : NULL, bitmap, src ? &vsrc : NULL,
01379 alpha >= 0 ? &color : NULL, &VDPBlends[blend],
01380 VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
01381 CHECK_ST
01382 return ok;
01383 }
01384
01385 bool MythRenderVDPAU::DrawLayer(uint id, uint target)
01386 {
01387 {
01388 CHECK_STATUS(false)
01389 LOCK_RENDER
01390
01391 if (!m_layers.contains(id))
01392 return false;
01393 if (!target)
01394 target = m_surfaces[m_surface];
01395 if (!m_outputSurfaces.contains(target))
01396 return false;
01397 }
01398
01399 INIT_ST
01400 vdp_st = vdp_output_surface_render_output_surface(
01401 m_outputSurfaces[target].m_id, (m_layers[id].m_layer.destination_rect),
01402 m_layers[id].m_layer.source_surface, (m_layers[id].m_layer.source_rect),
01403 NULL, &VDPBlends[kVDPBlendNormal], VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
01404 CHECK_ST
01405 return ok;
01406 }
01407
01408 int MythRenderVDPAU::GetBitmapSize(uint id)
01409 {
01410 if (!m_bitmapSurfaces.contains(id))
01411 return 0;
01412 QSize sz = m_bitmapSurfaces[id].m_size;
01413 return sz.width() * sz.height() * 4;
01414 }
01415
01416 void* MythRenderVDPAU::GetRender(uint id)
01417 {
01418 CHECK_STATUS(NULL)
01419 LOCK_RENDER
01420
01421 if (!m_videoSurfaces.contains(id))
01422 return NULL;
01423
01424 return &(m_videoSurfaces[id].m_render);
01425 }
01426
01427 uint MythRenderVDPAU::GetSurfaceOwner(VdpVideoSurface surface)
01428 {
01429 CHECK_STATUS(0)
01430 LOCK_RENDER
01431
01432 if (!m_videoSurfaceHash.contains(surface))
01433 return 0;
01434
01435 return m_videoSurfaceHash[surface];
01436 }
01437
01438 QSize MythRenderVDPAU::GetSurfaceSize(uint id)
01439 {
01440 QSize size = QSize(0,0);
01441 CHECK_STATUS(size)
01442 LOCK_RENDER
01443
01444 if (!m_videoSurfaces.contains(id))
01445 return size;
01446
01447 uint width = 0;
01448 uint height = 0;
01449 VdpChromaType dummy;
01450
01451 INIT_ST
01452 vdp_st = vdp_video_surface_get_parameters(m_videoSurfaces[id].m_id,
01453 &dummy, &width, &height);
01454 CHECK_ST
01455 if (!ok)
01456 {
01457 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve surface size.");
01458 return size;
01459 }
01460
01461 size = QSize(width, height);
01462 return size;
01463 }
01464
01465 void MythRenderVDPAU::ClearVideoSurface(uint id)
01466 {
01467 CHECK_VIDEO_SURFACES()
01468 CHECK_STATUS()
01469 LOCK_RENDER
01470 INIT_ST
01471
01472 if (!m_videoSurfaces.contains(id))
01473 return;
01474
01475 uint width = m_videoSurfaces[id].m_size.width();
01476 uint height = m_videoSurfaces[id].m_size.height();
01477 unsigned char *tmp = new unsigned char[(width * height * 3)>>1];
01478
01479 if (!tmp)
01480 return;
01481
01482 memset(tmp, 0, width * height);
01483 memset(tmp + (width * height), 127, (width * height)>>1);
01484 uint32_t pitches[3] = {width, width, width>>1};
01485 void* const planes[3] = {tmp, tmp + (width * height), tmp + (width * height)};
01486 vdp_st = vdp_video_surface_put_bits_y_cb_cr(m_videoSurfaces[id].m_id,
01487 VDP_YCBCR_FORMAT_YV12,
01488 planes, pitches);
01489 CHECK_ST
01490 delete [] tmp;
01491 }
01492
01493 void MythRenderVDPAU::ChangeVideoSurfaceOwner(uint id)
01494 {
01495 LOCK_ALL
01496 if (!m_videoSurfaces.contains(id))
01497 return;
01498
01499 m_videoSurfaces[id].m_owner = QThread::currentThread();
01500 }
01501
01502 void MythRenderVDPAU::Decode(uint id, struct vdpau_render_state *render)
01503 {
01504 CHECK_VIDEO_SURFACES()
01505 VdpDecoder decoder = 0;
01506
01507 {
01508 CHECK_STATUS()
01509 LOCK_DECODE
01510 if (!m_decoders.contains(id))
01511 return;
01512 decoder = m_decoders[id].m_id;
01513 }
01514
01515 INIT_ST
01516 vdp_st = vdp_decoder_render(m_decoders[id].m_id, render->surface,
01517 (VdpPictureInfo const *)&(render->info),
01518 render->bitstream_buffers_used,
01519 render->bitstream_buffers);
01520 CHECK_ST
01521 }
01522
01523 static const char* dummy_get_error_string(VdpStatus status)
01524 {
01525 static const char dummy[] = "Unknown";
01526 return &dummy[0];
01527 }
01528
01529 bool MythRenderVDPAU::CreateDevice(void)
01530 {
01531 if (!m_display)
01532 return false;
01533
01534 INIT_ST
01535 vdp_get_error_string = &dummy_get_error_string;
01536 XLOCK(m_display, vdp_st = vdp_device_create_x11(m_display->GetDisplay(),
01537 m_display->GetScreen(),
01538 &m_device, &vdp_get_proc_address));
01539 CHECK_ST
01540
01541 if (!ok)
01542 {
01543 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create VDPAU device.");
01544 return false;
01545 }
01546
01547 vdp_st = vdp_get_proc_address(m_device, VDP_FUNC_ID_GET_ERROR_STRING,
01548 (void **)&vdp_get_error_string);
01549 ok &= (vdp_st == VDP_STATUS_OK);
01550 if (!ok)
01551 {
01552 vdp_get_error_string = &dummy_get_error_string;
01553 ok = true;
01554 }
01555
01556 return ok;
01557 }
01558
01559 bool MythRenderVDPAU::GetProcs(void)
01560 {
01561 INIT_ST
01562 GET_PROC(VDP_FUNC_ID_DEVICE_DESTROY, vdp_device_destroy);
01563 GET_PROC(VDP_FUNC_ID_VIDEO_SURFACE_CREATE, vdp_video_surface_create);
01564 GET_PROC(VDP_FUNC_ID_VIDEO_SURFACE_DESTROY, vdp_video_surface_destroy);
01565 GET_PROC(VDP_FUNC_ID_VIDEO_SURFACE_PUT_BITS_Y_CB_CR,
01566 vdp_video_surface_put_bits_y_cb_cr);
01567 GET_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_PARAMETERS,
01568 vdp_video_surface_get_parameters);
01569 GET_PROC(VDP_FUNC_ID_VIDEO_SURFACE_GET_BITS_Y_CB_CR,
01570 vdp_video_surface_get_bits_y_cb_cr);
01571 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_CREATE, vdp_output_surface_create);
01572 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_DESTROY, vdp_output_surface_destroy);
01573 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_BITMAP_SURFACE,
01574 vdp_output_surface_render_bitmap_surface);
01575 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_PARAMETERS,
01576 vdp_output_surface_get_parameters);
01577 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_GET_BITS_NATIVE,
01578 vdp_output_surface_get_bits_native);
01579 GET_PROC(VDP_FUNC_ID_OUTPUT_SURFACE_RENDER_OUTPUT_SURFACE,
01580 vdp_output_surface_render_output_surface);
01581 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_CREATE, vdp_video_mixer_create);
01582 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_FEATURE_ENABLES,
01583 vdp_video_mixer_set_feature_enables);
01584 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_DESTROY, vdp_video_mixer_destroy);
01585 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_RENDER, vdp_video_mixer_render);
01586 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_SET_ATTRIBUTE_VALUES,
01587 vdp_video_mixer_set_attribute_values);
01588 GET_PROC(VDP_FUNC_ID_GENERATE_CSC_MATRIX, vdp_generate_csc_matrix);
01589 GET_PROC(VDP_FUNC_ID_VIDEO_MIXER_QUERY_FEATURE_SUPPORT,
01590 vdp_video_mixer_query_feature_support)
01591 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_DESTROY,
01592 vdp_presentation_queue_target_destroy);
01593 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_CREATE,
01594 vdp_presentation_queue_create);
01595 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DESTROY,
01596 vdp_presentation_queue_destroy);
01597 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_DISPLAY,
01598 vdp_presentation_queue_display);
01599 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_BLOCK_UNTIL_SURFACE_IDLE,
01600 vdp_presentation_queue_block_until_surface_idle);
01601 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_TARGET_CREATE_X11,
01602 vdp_presentation_queue_target_create_x11);
01603 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_GET_TIME,
01604 vdp_presentation_queue_get_time);
01605 GET_PROC(VDP_FUNC_ID_PRESENTATION_QUEUE_SET_BACKGROUND_COLOR,
01606 vdp_presentation_queue_set_background_color);
01607 GET_PROC(VDP_FUNC_ID_DECODER_CREATE, vdp_decoder_create);
01608 GET_PROC(VDP_FUNC_ID_DECODER_DESTROY, vdp_decoder_destroy);
01609 GET_PROC(VDP_FUNC_ID_DECODER_RENDER, vdp_decoder_render);
01610 GET_PROC(VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES,
01611 vdp_decoder_query_capabilities);
01612 GET_PROC(VDP_FUNC_ID_BITMAP_SURFACE_CREATE, vdp_bitmap_surface_create);
01613 GET_PROC(VDP_FUNC_ID_BITMAP_SURFACE_DESTROY,vdp_bitmap_surface_destroy);
01614 GET_PROC(VDP_FUNC_ID_BITMAP_SURFACE_PUT_BITS_NATIVE,
01615 vdp_bitmap_surface_put_bits_native);
01616 GET_PROC(VDP_FUNC_ID_PREEMPTION_CALLBACK_REGISTER,
01617 vdp_preemption_callback_register);
01618
01619 vdp_st = vdp_get_proc_address(
01620 m_device, VDP_FUNC_ID_GET_API_VERSION,
01621 (void **)&vdp_get_api_version
01622 );
01623
01624 vdp_st = vdp_get_proc_address(
01625 m_device, VDP_FUNC_ID_GET_INFORMATION_STRING,
01626 (void **)&vdp_get_information_string
01627 );
01628
01629 return ok;
01630 }
01631
01632 bool MythRenderVDPAU::CreatePresentationQueue(void)
01633 {
01634 MythXLocker locker(m_display);
01635 if (!m_device || !m_window)
01636 return false;
01637
01638 m_surface = 0;
01639 INIT_ST
01640 vdp_st = vdp_presentation_queue_target_create_x11(m_device, m_window,
01641 &m_flipTarget);
01642 CHECK_ST
01643 if (!ok)
01644 return false;
01645
01646 vdp_st = vdp_presentation_queue_create(m_device, m_flipTarget,
01647 &m_flipQueue);
01648 CHECK_ST
01649 return ok;
01650 }
01651
01652 bool MythRenderVDPAU::CreatePresentationSurfaces(void)
01653 {
01654 int num = MIN_OUTPUT_SURFACES;
01655 bool ok = true;
01656
01657 for (int i = 0; i < num; i++)
01658 {
01659 uint id = CreateOutputSurface(m_size);
01660 if (id)
01661 {
01662 m_surfaces.push_back(id);
01663 }
01664 else
01665 {
01666 ok = false;
01667 break;
01668 }
01669 }
01670
01671 if (m_surfaces.size() >= MIN_OUTPUT_SURFACES)
01672 {
01673 m_flipReady = m_flipQueue;
01674 LOG(VB_GENERAL, LOG_INFO, LOC +
01675 QString("Created %1 output surfaces.") .arg(m_surfaces.size()));
01676 }
01677 return ok;
01678 }
01679
01680 bool MythRenderVDPAU::RegisterCallback(bool enable)
01681 {
01682 INIT_ST
01683 if (vdp_preemption_callback_register && m_device)
01684 {
01685 vdp_st = vdp_preemption_callback_register(
01686 m_device, enable ? &vdpau_preemption_callback : NULL,
01687 (void*)this);
01688 CHECK_ST
01689 }
01690 else
01691 return false;
01692
01693 return ok;
01694 }
01695
01696 bool MythRenderVDPAU::CheckHardwareSupport(void)
01697 {
01698 if (!m_device || !vdp_decoder_query_capabilities)
01699 return false;
01700
01701 if (!gVDPAUSupportChecked)
01702 {
01703 gVDPAUSupportChecked = true;
01704
01705 if (vdp_get_api_version)
01706 {
01707 uint version;
01708 vdp_get_api_version(&version);
01709 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Version %1").arg(version));
01710 }
01711 if (vdp_get_information_string)
01712 {
01713 const char * info;
01714 vdp_get_information_string(&info);
01715 LOG(VB_GENERAL, LOG_INFO, LOC +
01716 QString("Information %2").arg(info));
01717 }
01718
01719 for (int i = 0; i < NUM_SCALING_LEVELS; i++)
01720 if (IsFeatureAvailable(VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + i))
01721 gVDPAUBestScaling = VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + i;
01722
01723 if (gVDPAUBestScaling)
01724 {
01725 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01726 QString("HQ scaling level %1 of %2 available.")
01727 .arg(gVDPAUBestScaling - VDP_VIDEO_MIXER_FEATURE_HIGH_QUALITY_SCALING_L1 + 1)
01728 .arg(NUM_SCALING_LEVELS));
01729 }
01730 else
01731 LOG(VB_PLAYBACK, LOG_INFO, LOC + "HQ Scaling not supported.");
01732
01733 VdpBool supported = false;
01734
01735 #ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
01736 INIT_ST
01737 uint32_t tmp1, tmp2, tmp3, tmp4;
01738 vdp_st = vdp_decoder_query_capabilities(m_device,
01739 VDP_DECODER_PROFILE_MPEG4_PART2_ASP, &supported,
01740 &tmp1, &tmp2, &tmp3, &tmp4);
01741 CHECK_ST
01742 #endif
01743
01744 gVDPAUMPEG4Accel = (bool)supported;
01745
01746 LOG(VB_PLAYBACK, LOG_INFO, LOC +
01747 QString("MPEG4 hardware acceleration %1supported.")
01748 .arg(gVDPAUMPEG4Accel ? "" : "not "));
01749 }
01750
01751 return true;
01752 }
01753
01754 bool MythRenderVDPAU::IsFeatureAvailable(uint feature)
01755 {
01756 INIT_ST
01757 VdpBool supported = false;
01758 vdp_st = vdp_video_mixer_query_feature_support(m_device,
01759 feature, &supported);
01760 CHECK_ST
01761 return ok && supported;
01762 }
01763
01764 void MythRenderVDPAU::Destroy(void)
01765 {
01766 DestroyPresentationQueue();
01767 DestroyPresentationSurfaces();
01768 DestroyOutputSurfaces();
01769 DestroyVideoSurfaces();
01770 DestroyBitmapSurfaces();
01771 DestroyDecoders();
01772 DestroyVideoMixers();
01773 DestroyLayers();
01774 RegisterCallback(false);
01775 DestroyDevice();
01776 ResetProcs();
01777 m_size = QSize();
01778 m_errored = false;
01779 memset(&m_rect, 0, sizeof(VdpRect));
01780 m_window = 0;
01781
01782 if (m_display)
01783 {
01784 delete m_display;
01785 m_display = NULL;
01786 }
01787 }
01788
01789 void MythRenderVDPAU::DestroyDevice(void)
01790 {
01791 vdp_get_error_string = NULL;
01792 vdp_get_proc_address = NULL;
01793 if (vdp_device_destroy && m_device)
01794 {
01795 vdp_device_destroy(m_device);
01796 m_device = 0;
01797 }
01798 }
01799
01800 void MythRenderVDPAU::ResetProcs(void)
01801 {
01802 vdp_device_destroy = NULL;
01803 vdp_video_surface_create = NULL;
01804 vdp_video_surface_destroy = NULL;
01805 vdp_video_surface_put_bits_y_cb_cr = NULL;
01806 vdp_video_surface_get_parameters = NULL;
01807 vdp_video_surface_get_bits_y_cb_cr = NULL;
01808 vdp_output_surface_put_bits_native = NULL;
01809 vdp_output_surface_create = NULL;
01810 vdp_output_surface_destroy = NULL;
01811 vdp_output_surface_render_bitmap_surface = NULL;
01812 vdp_output_surface_get_parameters = NULL;
01813 vdp_output_surface_get_bits_native = NULL;
01814 vdp_output_surface_render_output_surface = NULL;
01815 vdp_video_mixer_create = NULL;
01816 vdp_video_mixer_set_feature_enables = NULL;
01817 vdp_video_mixer_destroy = NULL;
01818 vdp_video_mixer_render = NULL;
01819 vdp_video_mixer_set_attribute_values = NULL;
01820 vdp_generate_csc_matrix = NULL;
01821 vdp_video_mixer_query_feature_support = NULL;
01822 vdp_presentation_queue_target_destroy = NULL;
01823 vdp_presentation_queue_create = NULL;
01824 vdp_presentation_queue_destroy = NULL;
01825 vdp_presentation_queue_display = NULL;
01826 vdp_presentation_queue_block_until_surface_idle = NULL;
01827 vdp_presentation_queue_target_create_x11 = NULL;
01828 vdp_presentation_queue_get_time = NULL;
01829 vdp_presentation_queue_set_background_color = NULL;
01830 vdp_decoder_create = NULL;
01831 vdp_decoder_destroy = NULL;
01832 vdp_decoder_render = NULL;
01833 vdp_decoder_query_capabilities = NULL;
01834 vdp_bitmap_surface_create = NULL;
01835 vdp_bitmap_surface_destroy = NULL;
01836 vdp_bitmap_surface_put_bits_native = NULL;
01837 vdp_preemption_callback_register = NULL;
01838 }
01839
01840 void MythRenderVDPAU::DestroyPresentationQueue(void)
01841 {
01842 MythXLocker locker(m_display);
01843 INIT_ST
01844 if (vdp_presentation_queue_destroy && m_flipQueue)
01845 {
01846 vdp_st = vdp_presentation_queue_destroy(m_flipQueue);
01847 CHECK_ST
01848 m_flipQueue = 0;
01849 }
01850
01851 if (vdp_presentation_queue_target_destroy && m_flipTarget)
01852 {
01853 vdp_st = vdp_presentation_queue_target_destroy(m_flipTarget);
01854 CHECK_ST
01855 m_flipTarget = 0;
01856 }
01857 m_flipReady = false;
01858 }
01859
01860 void MythRenderVDPAU::DestroyPresentationSurfaces(void)
01861 {
01862 for (int i = 0; i < m_surfaces.size(); i++)
01863 DestroyOutputSurface(m_surfaces[i]);
01864 m_surfaces.clear();
01865 m_surface = 0;
01866 m_flipReady = false;
01867 }
01868
01869 void MythRenderVDPAU::DestroyOutputSurfaces(void)
01870 {
01871 if (!vdp_output_surface_destroy)
01872 return;
01873
01874 if (m_outputSurfaces.size())
01875 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned output surfaces.");
01876
01877 INIT_ST
01878 QHash<uint, VDPAUOutputSurface>::iterator it;
01879 for (it = m_outputSurfaces.begin(); it != m_outputSurfaces.end(); ++it)
01880 {
01881 vdp_st = vdp_output_surface_destroy(it.value().m_id);
01882 CHECK_ST
01883 }
01884 m_outputSurfaces.clear();
01885 }
01886
01887 void MythRenderVDPAU::DestroyVideoSurfaces(void)
01888 {
01889 if (!vdp_video_surface_destroy)
01890 return;
01891
01892 if (m_videoSurfaces.size())
01893 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned video surfaces.");
01894
01895 INIT_ST
01896 QHash<uint, VDPAUVideoSurface>::iterator it;;
01897 for(it = m_videoSurfaces.begin(); it != m_videoSurfaces.end(); ++it)
01898 {
01899 vdp_st = vdp_video_surface_destroy(it.value().m_id);
01900 CHECK_ST
01901 }
01902 m_videoSurfaces.clear();
01903 m_videoSurfaceHash.clear();
01904 }
01905
01906 void MythRenderVDPAU::DestroyLayers(void)
01907 {
01908 if (m_layers.size())
01909 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned layers.");
01910 m_layers.clear();
01911 }
01912
01913 void MythRenderVDPAU::DestroyBitmapSurfaces(void)
01914 {
01915 if (!vdp_bitmap_surface_destroy)
01916 return;
01917
01918 if (m_bitmapSurfaces.size())
01919 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned bitmap surfaces.");
01920
01921 INIT_ST
01922 QHash<uint, VDPAUBitmapSurface>::iterator it;
01923 for (it = m_bitmapSurfaces.begin(); it != m_bitmapSurfaces.end(); ++it)
01924 {
01925 vdp_st = vdp_bitmap_surface_destroy(it.value().m_id);
01926 CHECK_ST
01927 }
01928 m_bitmapSurfaces.clear();
01929 }
01930
01931 void MythRenderVDPAU::DestroyDecoders(void)
01932 {
01933 if (!vdp_decoder_destroy)
01934 return;
01935
01936 if (m_decoders.size())
01937 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned decoders.");
01938
01939 INIT_ST
01940 QHash<uint, VDPAUDecoder>::iterator it;
01941 for (it = m_decoders.begin(); it != m_decoders.end(); ++it)
01942 {
01943 vdp_st = vdp_decoder_destroy(it.value().m_id);
01944 CHECK_ST
01945 }
01946 m_decoders.clear();
01947 }
01948
01949 void MythRenderVDPAU::DestroyVideoMixers(void)
01950 {
01951 if (!vdp_video_mixer_destroy)
01952 return;
01953
01954 if (m_videoMixers.size())
01955 LOG(VB_GENERAL, LOG_WARNING, LOC + "Orphaned video mixers.");
01956
01957 INIT_ST
01958 QHash<uint, VDPAUVideoMixer>::iterator it;
01959 for (it = m_videoMixers.begin(); it != m_videoMixers.end(); ++it)
01960 {
01961 vdp_st = vdp_video_mixer_destroy(it.value().m_id);
01962 CHECK_ST
01963 }
01964 m_videoMixers.clear();
01965 }
01966
01967 bool MythRenderVDPAU::SetMixerAttribute(uint id,
01968 VdpVideoMixerAttribute attribute[1],
01969 void const *value[1])
01970 {
01971 INIT_ST
01972
01973 if (!m_videoMixers.contains(id))
01974 return false;
01975
01976 vdp_st = vdp_video_mixer_set_attribute_values(m_videoMixers[id].m_id, 1,
01977 attribute, value);
01978 CHECK_ST
01979 return ok;
01980 }
01981
01982 void MythRenderVDPAU::Preempted(void)
01983 {
01984 if (!m_preempted || m_recreating)
01985 return;
01986
01987 LOG(VB_GENERAL, LOG_NOTICE, LOC +
01988 "Attempting to re-create VDPAU resources.");
01989 m_recreating = true;
01990 m_flipReady = false;
01991 ResetProcs();
01992 bool ok = CreateDevice();
01993 if (ok) ok = GetProcs();
01994 if (ok && m_window) ok = CreatePresentationQueue();
01995 if (ok && m_window) ok = SetColorKey(m_colorKey);
01996 if (ok) ok = RegisterCallback();
01997
01998 if (ok && m_outputSurfaces.size())
01999 {
02000
02001 QHash<uint ,uint> old_surfaces;
02002 QHash<uint, VDPAUOutputSurface>::iterator it;
02003 for (it = m_outputSurfaces.begin(); it != m_outputSurfaces.end(); ++it)
02004 {
02005 old_surfaces.insert(it.value().m_id, it.key());
02006 uint check = CreateOutputSurface(it.value().m_size,
02007 it.value().m_fmt,it.key());
02008 if (check != it.key())
02009 ok = false;
02010 }
02011 QHash<uint, uint>::iterator old;
02012 for (old = old_surfaces.begin(); old != old_surfaces.end(); ++old)
02013 old.value() = m_outputSurfaces[old.value()].m_id;
02014 QHash<uint, VDPAULayer>::iterator layers;
02015 for (layers = m_layers.begin(); layers != m_layers.end(); ++layers)
02016 {
02017 uint old = layers.value().m_layer.source_surface;
02018 if (old_surfaces.contains(old))
02019 layers.value().m_layer.source_surface = old_surfaces[old];
02020 }
02021 if (ok)
02022 LOG(VB_GENERAL, LOG_INFO, LOC + "Re-created output surfaces.");
02023 }
02024
02025 if (ok && m_bitmapSurfaces.size())
02026 {
02027 QHash<uint, VDPAUBitmapSurface>::iterator it;
02028 for (it = m_bitmapSurfaces.begin(); it != m_bitmapSurfaces.end(); ++it)
02029 {
02030 uint check = CreateBitmapSurface(it.value().m_size,
02031 it.value().m_fmt, it.key());
02032 if (check != it.key())
02033 ok = false;
02034 }
02035 if (ok)
02036 LOG(VB_GENERAL, LOG_INFO, LOC + "Re-created bitmap surfaces.");
02037
02038 }
02039
02040 if (ok && m_decoders.size())
02041 {
02042 QHash<uint, VDPAUDecoder>::iterator it;
02043 for (it = m_decoders.begin(); it != m_decoders.end(); ++it)
02044 {
02045 uint check = CreateDecoder(it.value().m_size, it.value().m_profile,
02046 it.value().m_max_refs, it.key());
02047 if (check != it.key())
02048 ok = false;
02049 }
02050 if (ok)
02051 LOG(VB_GENERAL, LOG_INFO, LOC + "Re-created decoders.");
02052 }
02053
02054 if (ok && m_videoMixers.size())
02055 {
02056 QHash<uint, VDPAUVideoMixer>::iterator it;
02057 for (it = m_videoMixers.begin(); it != m_videoMixers.end(); ++it)
02058 {
02059 uint check = CreateVideoMixer(it.value().m_size,
02060 it.value().m_layers,
02061 it.value().m_features,
02062 it.value().m_type, it.key());
02063 if (check != it.key())
02064 ok = false;
02065 }
02066 if (ok)
02067 LOG(VB_GENERAL, LOG_INFO, LOC + "Re-created video mixers.");
02068 }
02069
02070
02071 m_reset_video_surfaces = true;
02072 QHash<uint, VDPAUVideoSurface>::iterator it;
02073 for (it = m_videoSurfaces.begin(); it != m_videoSurfaces.end(); ++it)
02074 it.value().m_needs_reset = true;
02075
02076 ResetVideoSurfaces();
02077
02078 if (!ok)
02079 {
02080 LOG(VB_GENERAL, LOG_INFO, LOC + "Failed to re-create VDPAU resources.");
02081 m_errored = true;
02082 return;
02083 }
02084
02085 m_preempted = false;
02086 m_recreating = false;
02087 m_flipReady = m_flipQueue;
02088 m_recreated = true;
02089 }
02090
02091 void MythRenderVDPAU::ResetVideoSurfaces(void)
02092 {
02093 LOCK_ALL
02094
02095 bool ok = true;
02096 QThread *this_thread = QThread::currentThread();
02097 QHash<uint, VDPAUVideoSurface>::iterator it;
02098 int surfaces_owned = 0;
02099
02100
02101 QHash<uint, uint> old_surfaces;
02102 for (it = m_videoSurfaces.begin(); it != m_videoSurfaces.end(); ++it)
02103 {
02104 old_surfaces.insert(it.value().m_id, it.key());
02105 if ((it.value().m_owner == this_thread) && it.value().m_needs_reset)
02106 {
02107 uint check = CreateVideoSurface(it.value().m_size,
02108 it.value().m_type, it.key());
02109 if (check != it.key())
02110 ok = false;
02111 surfaces_owned++;
02112 it.value().m_needs_reset = false;
02113 }
02114 }
02115
02116 if (!surfaces_owned)
02117 return;
02118
02119 LOG(VB_GENERAL, LOG_INFO, LOC +
02120 QString("Attempting to reset %1 video surfaces owned by this thread %2")
02121 .arg(surfaces_owned).arg((long long)this_thread));
02122
02123
02124 QHash<uint, uint>::iterator old;
02125 for (old = old_surfaces.begin(); old != old_surfaces.end(); ++old)
02126 old.value() = m_videoSurfaces[old.value()].m_id;
02127
02128
02129 for (it = m_videoSurfaces.begin(); it != m_videoSurfaces.end(); ++it)
02130 {
02131
02132 uint fwd = it.value().m_render.info.mpeg.forward_reference;
02133 uint back = it.value().m_render.info.mpeg.backward_reference;
02134 if (fwd != VDP_INVALID_HANDLE && old_surfaces.contains(fwd))
02135 it.value().m_render.info.mpeg.forward_reference = old_surfaces[fwd];
02136 if (back != VDP_INVALID_HANDLE && old_surfaces.contains(back))
02137 it.value().m_render.info.mpeg.backward_reference = old_surfaces[back];
02138
02139
02140 for (uint i = 0; i < 16; i++)
02141 {
02142 uint ref = it.value().m_render.info.h264.referenceFrames[i].surface;
02143 if (ref != VDP_INVALID_HANDLE && old_surfaces.contains(ref))
02144 it.value().m_render.info.h264.referenceFrames[i].surface =
02145 old_surfaces[ref];
02146 }
02147
02148
02149 fwd = it.value().m_render.info.vc1.forward_reference;
02150 back = it.value().m_render.info.vc1.backward_reference;
02151 if (fwd != VDP_INVALID_HANDLE && old_surfaces.contains(fwd))
02152 it.value().m_render.info.vc1.forward_reference = old_surfaces[fwd];
02153 if (back != VDP_INVALID_HANDLE && old_surfaces.contains(back))
02154 it.value().m_render.info.vc1.backward_reference = old_surfaces[back];
02155
02156
02157 #ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP
02158 fwd = it.value().m_render.info.mpeg4.forward_reference;
02159 back = it.value().m_render.info.mpeg4.backward_reference;
02160 if (fwd != VDP_INVALID_HANDLE && old_surfaces.contains(fwd))
02161 it.value().m_render.info.mpeg4.forward_reference = old_surfaces[fwd];
02162 if (back != VDP_INVALID_HANDLE && old_surfaces.contains(back))
02163 it.value().m_render.info.mpeg4.backward_reference = old_surfaces[back];
02164 #endif
02165 }
02166
02167 if (ok)
02168 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Re-created %1 video surfaces.")
02169 .arg(surfaces_owned));
02170 else
02171 LOG(VB_GENERAL, LOG_ERR, LOC + "Error re-creating video surfaces.");
02172
02173 int remaining = 0;
02174 for (it = m_videoSurfaces.begin(); it != m_videoSurfaces.end(); ++it)
02175 if (it.value().m_needs_reset)
02176 remaining++;
02177
02178 LOG(VB_GENERAL, LOG_INFO, LOC +
02179 QString("%1 of %2 video surfaces still need to be reset")
02180 .arg(remaining).arg(m_videoSurfaces.size()));
02181
02182 m_reset_video_surfaces = remaining;
02183 m_errored = !ok;
02184 }