00001 #include "videoout_nullvdpau.h"
00002 #define LOC QString("NullVDPAU: ")
00003
00004 #define MIN_REFERENCE_FRAMES 2
00005 #define MAX_REFERENCE_FRAMES 16
00006 #define MIN_PROCESS_BUFFER 6
00007
00008 void VideoOutputNullVDPAU::GetRenderOptions(render_opts &opts)
00009 {
00010 opts.renderers->append("nullvdpau");
00011 (*opts.osds)["nullvdpau"].append("dummy");
00012 QStringList dummy(QString("dummy"));
00013 opts.deints->insert("nullvdpau", dummy);
00014 if (opts.decoders->contains("vdpau"))
00015 (*opts.safe_renderers)["vdpau"].append("nullvdpau");
00016 if (opts.decoders->contains("ffmpeg"))
00017 (*opts.safe_renderers)["ffmpeg"].append("nullvdpau");
00018 if (opts.decoders->contains("crystalhd"))
00019 (*opts.safe_renderers)["crystalhd"].append("nullvdpau");
00020 (*opts.safe_renderers)["dummy"].append("nullvdpau");
00021 (*opts.safe_renderers)["nuppel"].append("nullvdpau");
00022
00023 opts.priorities->insert("nullvdpau", 20);
00024 }
00025
00026 VideoOutputNullVDPAU::VideoOutputNullVDPAU()
00027 : m_render(NULL), m_lock(QMutex::Recursive), m_decoder(0), m_pix_fmt(-1),
00028 m_decoder_buffer_size(MAX_REFERENCE_FRAMES),
00029 m_checked_surface_ownership(false), m_shadowBuffers(NULL),
00030 m_surfaceSize(QSize(0,0))
00031 {
00032 }
00033
00034 VideoOutputNullVDPAU::~VideoOutputNullVDPAU()
00035 {
00036 QMutexLocker locker(&m_lock);
00037 TearDown();
00038 }
00039
00040 void VideoOutputNullVDPAU::TearDown(void)
00041 {
00042 DeleteBuffers();
00043 DeleteShadowBuffers();
00044 DeleteRender();
00045 }
00046
00047 bool VideoOutputNullVDPAU::Init(int width, int height, float aspect,
00048 WId winid, const QRect &win_rect,
00049 MythCodecID codec_id)
00050 {
00051 QMutexLocker locker(&m_lock);
00052 bool ok = VideoOutput::Init(width, height, aspect, winid, win_rect, codec_id);
00053 if (!codec_is_vdpau_hw(video_codec_id))
00054 return false;
00055
00056 if (db_vdisp_profile)
00057 db_vdisp_profile->SetVideoRenderer("nullvdpau");
00058 if (ok) ok = InitRender();
00059 if (ok) ok = InitBuffers();
00060 if (ok) ok = InitShadowBuffers();
00061 if (!ok)
00062 return false;
00063
00064 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00065 "Created VDPAU context with GPU decoding)");
00066 return ok;
00067 }
00068
00069 bool VideoOutputNullVDPAU::InitRender(void)
00070 {
00071 QMutexLocker locker(&m_lock);
00072 m_render = new MythRenderVDPAU();
00073 if (m_render && m_render->CreateDecodeOnly())
00074 return true;
00075 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to initialise VDPAU");
00076 return false;
00077 }
00078
00079 void VideoOutputNullVDPAU::DeleteRender(void)
00080 {
00081 QMutexLocker locker(&m_lock);
00082 if (m_render)
00083 {
00084 if (m_decoder)
00085 m_render->DestroyDecoder(m_decoder);
00086 delete m_render;
00087 }
00088
00089 m_decoder = 0;
00090 m_render = NULL;
00091 m_pix_fmt = -1;
00092 }
00093
00094 bool VideoOutputNullVDPAU::InitBuffers(void)
00095 {
00096 QMutexLocker locker(&m_lock);
00097 if (!m_render || !codec_is_vdpau_hw(video_codec_id))
00098 return false;
00099
00100 uint buffer_size = m_decoder_buffer_size + MIN_PROCESS_BUFFER;
00101 const QSize video_dim = window.GetActualVideoDim();
00102 vbuffers.Init(buffer_size, false, 2, 1, 4, 1);
00103 bool ok = CreateVideoSurfaces(buffer_size);
00104 if (ok)
00105 {
00106 for (int i = 0; i < m_video_surfaces.size(); i++)
00107 ok &= vbuffers.CreateBuffer(video_dim.width(),
00108 video_dim.height(), i,
00109 m_render->GetRender(m_video_surfaces[i]),
00110 FMT_VDPAU);
00111 }
00112
00113 if (!ok)
00114 {
00115 DeleteBuffers();
00116 LOG(VB_GENERAL, LOG_ERR, LOC + "Unable to create VDPAU buffers");
00117 return false;
00118 }
00119
00120 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Created VDPAU buffers");
00121 return ok;
00122 }
00123
00124 void VideoOutputNullVDPAU::DeleteBuffers(void)
00125 {
00126 QMutexLocker locker(&m_lock);
00127 DiscardFrames(true);
00128 DeleteVideoSurfaces();
00129 vbuffers.Reset();
00130 vbuffers.DeleteBuffers();
00131 m_checked_surface_ownership = false;
00132 }
00133
00134 bool VideoOutputNullVDPAU::InitShadowBuffers(void)
00135 {
00136 QMutexLocker locker(&m_lock);
00137 if (!codec_is_vdpau_hw(video_codec_id))
00138 return false;
00139
00140 DeleteShadowBuffers();
00141
00142 uint buffer_size = m_decoder_buffer_size + MIN_PROCESS_BUFFER;
00143 if ((vbuffers.Size() != buffer_size) ||
00144 (vbuffers.Size() != (uint)m_video_surfaces.size()))
00145 {
00146 LOG(VB_GENERAL, LOG_ERR, LOC + "Number of GPU buffers is wrong.");
00147 return false;
00148 }
00149
00150 m_surfaceSize = m_render->GetSurfaceSize(m_video_surfaces[0]);
00151 m_shadowBuffers = new VideoBuffers();
00152 if (!m_shadowBuffers)
00153 return false;
00154
00155 m_shadowBuffers->Init(buffer_size, false, 2, 1, 4, 1);
00156 if (!m_shadowBuffers->CreateBuffers(FMT_YV12,
00157 m_surfaceSize.width(),
00158 m_surfaceSize.height()))
00159 {
00160 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create shadow buffers.");
00161 DeleteShadowBuffers();
00162 return false;
00163 }
00164
00165 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 CPU buffers (%2x%3)")
00166 .arg(buffer_size).arg(m_surfaceSize.width())
00167 .arg(m_surfaceSize.height()));
00168 return true;
00169 }
00170
00171 void VideoOutputNullVDPAU::DeleteShadowBuffers(void)
00172 {
00173 QMutexLocker locker(&m_lock);
00174 if (!m_shadowBuffers)
00175 return;
00176
00177 m_shadowBuffers->Reset();
00178 m_shadowBuffers->DeleteBuffers();
00179 delete m_shadowBuffers;
00180 m_shadowBuffers = NULL;
00181 }
00182
00183 bool VideoOutputNullVDPAU::CreateVideoSurfaces(uint num)
00184 {
00185 if (!m_render || num < 1)
00186 return false;
00187
00188 bool ret = true;
00189 const QSize size = window.GetActualVideoDim();
00190 for (uint i = 0; i < num; i++)
00191 {
00192 uint tmp = m_render->CreateVideoSurface(size);
00193 if (tmp)
00194 {
00195 m_video_surfaces.push_back(tmp);
00196 m_render->ClearVideoSurface(tmp);
00197 }
00198 else
00199 {
00200 ret = false;
00201 break;
00202 }
00203 }
00204 return ret;
00205 }
00206
00207 void VideoOutputNullVDPAU::DeleteVideoSurfaces(void)
00208 {
00209 if (!m_render || !m_video_surfaces.size())
00210 return;
00211
00212 for (int i = 0; i < m_video_surfaces.size(); i++)
00213 m_render->DestroyVideoSurface(m_video_surfaces[i]);
00214 m_video_surfaces.clear();
00215 }
00216
00217 void VideoOutputNullVDPAU::ClaimVideoSurfaces(void)
00218 {
00219 if (!m_render)
00220 return;
00221
00222 QMutexLocker locker(&m_lock);
00223 QVector<uint>::iterator it;
00224 for (it = m_video_surfaces.begin(); it != m_video_surfaces.end(); ++it)
00225 m_render->ChangeVideoSurfaceOwner(*it);
00226 m_checked_surface_ownership = true;
00227 }
00228
00229 void VideoOutputNullVDPAU::DrawSlice(VideoFrame *frame, int x, int y, int w, int h)
00230 {
00231 (void)x;
00232 (void)y;
00233 (void)w;
00234 (void)h;
00235
00236 if (m_render && m_render->IsErrored())
00237 errorState = kError_Unknown;
00238
00239 if (IsErrored())
00240 {
00241 LOG(VB_GENERAL, LOG_ERR, LOC + QString("IsErrored() in DrawSlice"));
00242 return;
00243 }
00244
00245 if (!codec_is_vdpau_hw(video_codec_id) || !m_render)
00246 return;
00247
00248 if (!m_checked_surface_ownership)
00249 ClaimVideoSurfaces();
00250
00251 struct vdpau_render_state *render = (struct vdpau_render_state *)frame->buf;
00252 if (!render)
00253 {
00254 LOG(VB_GENERAL, LOG_ERR, LOC + "No video surface to decode to.");
00255 errorState = kError_Unknown;
00256 return;
00257 }
00258
00259 if (frame->pix_fmt != m_pix_fmt)
00260 {
00261 if (m_decoder)
00262 {
00263 LOG(VB_GENERAL, LOG_ERR, LOC + "Picture format has changed.");
00264 errorState = kError_Unknown;
00265 return;
00266 }
00267
00268 uint max_refs = MIN_REFERENCE_FRAMES;
00269 if (frame->pix_fmt == PIX_FMT_VDPAU_H264)
00270 {
00271 max_refs = render->info.h264.num_ref_frames;
00272 if (max_refs < 1 || max_refs > MAX_REFERENCE_FRAMES)
00273 {
00274 uint32_t round_width = (frame->width + 15) & ~15;
00275 uint32_t round_height = (frame->height + 15) & ~15;
00276 uint32_t surf_size = (round_width * round_height * 3) / 2;
00277 max_refs = (12 * 1024 * 1024) / surf_size;
00278 }
00279 if (max_refs > MAX_REFERENCE_FRAMES)
00280 max_refs = MAX_REFERENCE_FRAMES;
00281
00282
00283 int needed = max_refs - m_decoder_buffer_size;
00284 if (needed > 0)
00285 {
00286 QMutexLocker locker(&m_lock);
00287 const QSize size = window.GetActualVideoDim();
00288 uint created = 0;
00289 for (int i = 0; i < needed; i++)
00290 {
00291 uint tmp = m_render->CreateVideoSurface(size);
00292 if (tmp)
00293 {
00294 m_video_surfaces.push_back(tmp);
00295 m_render->ClearVideoSurface(tmp);
00296 if (vbuffers.AddBuffer(size.width(), size.height(),
00297 m_render->GetRender(tmp),
00298 FMT_VDPAU))
00299 {
00300 created++;
00301 int size = (m_surfaceSize.width() *
00302 m_surfaceSize.height() * 3) / 2;
00303 m_shadowBuffers->AddBuffer(m_surfaceSize.width(),
00304 m_surfaceSize.height(),
00305 new unsigned char[size],
00306 FMT_YV12);
00307 }
00308 }
00309 }
00310 m_decoder_buffer_size += created;
00311 LOG(VB_GENERAL, LOG_INFO, LOC +
00312 QString("Added %1 new buffers. New buffer size %2 "
00313 "(%3 decode and %4 process)")
00314 .arg(created).arg(vbuffers.Size())
00315 .arg(m_decoder_buffer_size)
00316 .arg(MIN_PROCESS_BUFFER));
00317 }
00318 }
00319
00320 VdpDecoderProfile vdp_decoder_profile;
00321 switch (frame->pix_fmt)
00322 {
00323 case PIX_FMT_VDPAU_MPEG1:
00324 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG1;
00325 break;
00326 case PIX_FMT_VDPAU_MPEG2:
00327 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG2_MAIN;
00328 break;
00329 case PIX_FMT_VDPAU_MPEG4:
00330 vdp_decoder_profile = VDP_DECODER_PROFILE_MPEG4_PART2_ASP;
00331 break;
00332 case PIX_FMT_VDPAU_H264:
00333 vdp_decoder_profile = VDP_DECODER_PROFILE_H264_HIGH;
00334 break;
00335 case PIX_FMT_VDPAU_WMV3:
00336 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_MAIN;
00337 break;
00338 case PIX_FMT_VDPAU_VC1:
00339 vdp_decoder_profile = VDP_DECODER_PROFILE_VC1_ADVANCED;
00340 break;
00341 default:
00342 LOG(VB_GENERAL, LOG_ERR, LOC +
00343 "Picture format is not supported.");
00344 errorState = kError_Unknown;
00345 return;
00346 }
00347
00348 m_decoder = m_render->CreateDecoder(window.GetActualVideoDim(),
00349 vdp_decoder_profile, max_refs);
00350 if (m_decoder)
00351 {
00352 m_pix_fmt = frame->pix_fmt;
00353 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00354 QString("Created VDPAU decoder (%1 ref frames)")
00355 .arg(max_refs));
00356 }
00357 else
00358 {
00359 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create decoder.");
00360 errorState = kError_Unknown;
00361 return;
00362 }
00363 }
00364 else if (!m_decoder)
00365 {
00366 LOG(VB_GENERAL, LOG_ERR, LOC +
00367 "Pix format already set but no VDPAU decoder.");
00368 errorState = kError_Unknown;
00369 return;
00370 }
00371
00372 m_render->Decode(m_decoder, render);
00373 }
00374
00375 void VideoOutputNullVDPAU::ClearAfterSeek(void)
00376 {
00377 QMutexLocker locker(&m_lock);
00378 LOG(VB_PLAYBACK, LOG_INFO, LOC + "ClearAfterSeek()");
00379 DiscardFrames(false);
00380 }
00381
00382
00383 VideoFrame* VideoOutputNullVDPAU::GetLastDecodedFrame(void)
00384 {
00385 if (!BufferSizeCheck())
00386 return NULL;
00387
00388 VideoFrame* gpu = vbuffers.GetLastDecodedFrame();
00389 for (uint i = 0; i < vbuffers.Size(); i++)
00390 if (vbuffers.at(i) == gpu)
00391 return m_shadowBuffers->at(i);
00392 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find frame.");
00393 return NULL;
00394 }
00395
00396
00397 VideoFrame* VideoOutputNullVDPAU::GetLastShownFrame(void)
00398 {
00399 if (!BufferSizeCheck())
00400 return NULL;
00401
00402 VideoFrame* gpu = vbuffers.GetLastShownFrame();
00403 for (uint i = 0; i < vbuffers.Size(); i++)
00404 if (vbuffers.at(i) == gpu)
00405 return m_shadowBuffers->at(i);
00406 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find frame.");
00407 return NULL;
00408 }
00409
00410
00411 void VideoOutputNullVDPAU::DiscardFrame(VideoFrame *frame)
00412 {
00413 if (!frame || !BufferSizeCheck())
00414 return;
00415
00416
00417 for (uint i = 0; i < m_shadowBuffers->Size(); i++)
00418 {
00419 if (m_shadowBuffers->at(i) == frame)
00420 {
00421 frame = vbuffers.at(i);
00422 break;
00423 }
00424 }
00425
00426
00427 for (uint i = 0; i < vbuffers.Size(); i++)
00428 {
00429 if (vbuffers.at(i) == frame)
00430 {
00431 m_lock.lock();
00432 vbuffers.DoneDisplayingFrame(frame);
00433 m_lock.unlock();
00434 return;
00435 }
00436 }
00437 }
00438
00439
00440 void VideoOutputNullVDPAU::DoneDisplayingFrame(VideoFrame *frame)
00441 {
00442 if (!frame || !BufferSizeCheck())
00443 return;
00444
00445
00446 for (uint i = 0; i < m_shadowBuffers->Size(); i++)
00447 {
00448 if (m_shadowBuffers->at(i) == frame)
00449 {
00450 frame = vbuffers.at(i);
00451 break;
00452 }
00453 }
00454
00455
00456 for (uint i = 0; i < vbuffers.Size(); i++)
00457 {
00458 if (vbuffers.at(i) == frame)
00459 {
00460 m_lock.lock();
00461 if (vbuffers.contains(kVideoBuffer_used, frame))
00462 DiscardFrame(frame);
00463 CheckFrameStates();
00464 m_lock.unlock();
00465 return;
00466 }
00467 }
00468 }
00469
00470 void VideoOutputNullVDPAU::ReleaseFrame(VideoFrame *frame)
00471 {
00472 if (!frame)
00473 return;
00474
00475 if ((frame->codec == FMT_VDPAU) && m_render)
00476 {
00477 if (BufferSizeCheck())
00478 {
00479 uint surface = 0;
00480 struct vdpau_render_state *render =
00481 (struct vdpau_render_state *)frame->buf;
00482 if (render)
00483 surface = m_render->GetSurfaceOwner(render->surface);
00484
00485 for (uint i = 0; i < vbuffers.Size(); i++)
00486 {
00487 if (vbuffers.at(i)->buf == frame->buf)
00488 {
00489 VideoFrame *vf = m_shadowBuffers->at(i);
00490 uint32_t pitches[] = {
00491 vf->pitches[0],
00492 vf->pitches[2],
00493 vf->pitches[1] };
00494 void* const planes[] = {
00495 vf->buf,
00496 vf->buf + vf->offsets[2],
00497 vf->buf + vf->offsets[1] };
00498 if (!m_render->DownloadYUVFrame(surface, planes, pitches))
00499 {
00500 LOG(VB_GENERAL, LOG_ERR, LOC +
00501 "Failed to get frame from GPU.");
00502 }
00503 vf->aspect = frame->aspect;
00504 vf->disp_timecode = frame->disp_timecode;
00505 vf->dummy = frame->dummy;
00506 vf->frameNumber = frame->frameNumber;
00507 vf->interlaced_frame = frame->interlaced_frame;
00508 vf->timecode = frame->timecode;
00509 vf->repeat_pict = frame->repeat_pict;
00510 vf->top_field_first = frame->top_field_first;
00511 }
00512 }
00513 }
00514 }
00515
00516 VideoOutput::ReleaseFrame(frame);
00517 }
00518
00519 bool VideoOutputNullVDPAU::InputChanged(const QSize &input_size,
00520 float aspect,
00521 MythCodecID av_codec_id,
00522 void *codec_private,
00523 bool &aspect_only)
00524 {
00525 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00526 QString("InputChanged(%1,%2,%3) '%4'->'%5'")
00527 .arg(input_size.width()).arg(input_size.height()).arg(aspect)
00528 .arg(toString(video_codec_id)).arg(toString(av_codec_id)));
00529
00530 QMutexLocker locker(&m_lock);
00531
00532 bool cid_changed = (video_codec_id != av_codec_id);
00533 bool res_changed = input_size != window.GetActualVideoDim();
00534
00535 if (!res_changed && !cid_changed)
00536 {
00537 aspect_only = true;
00538 return true;
00539 }
00540
00541 TearDown();
00542 QRect disp = window.GetDisplayVisibleRect();
00543 if (Init(input_size.width(), input_size.height(),
00544 aspect, 0, disp, av_codec_id))
00545 {
00546 return true;
00547 }
00548
00549 LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to re-initialise video output.");
00550 errorState = kError_Unknown;
00551
00552 return false;
00553 }
00554
00555 QStringList VideoOutputNullVDPAU::GetAllowedRenderers(MythCodecID myth_codec_id)
00556 {
00557 QStringList list;
00558 if (codec_is_vdpau_hw(myth_codec_id) && !getenv("NO_VDPAU"))
00559 list += "nullvdpau";
00560 return list;
00561 }
00562
00563 void VideoOutputNullVDPAU::DiscardFrames(bool next_frame_keyframe)
00564 {
00565 QMutexLocker locker(&m_lock);
00566 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DiscardFrames(%1)")
00567 .arg(next_frame_keyframe));
00568 CheckFrameStates();
00569 vbuffers.DiscardFrames(next_frame_keyframe);
00570 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("DiscardFrames() 3: %1 -- done()")
00571 .arg(vbuffers.GetStatus()));
00572 }
00573
00574 void VideoOutputNullVDPAU::CheckFrameStates(void)
00575 {
00576 QMutexLocker locker(&m_lock);
00577 frame_queue_t::iterator it;
00578 it = vbuffers.begin_lock(kVideoBuffer_displayed);
00579 while (it != vbuffers.end(kVideoBuffer_displayed))
00580 {
00581 VideoFrame* frame = *it;
00582 if (vbuffers.contains(kVideoBuffer_decode, frame))
00583 {
00584 LOG(VB_PLAYBACK, LOG_INFO, LOC +
00585 QString("Frame %1 is in use by avlib and so is "
00586 "being held for later discarding.")
00587 .arg(DebugString(frame, true)));
00588 }
00589 else
00590 {
00591 vbuffers.safeEnqueue(kVideoBuffer_avail, frame);
00592 vbuffers.end_lock();
00593 it = vbuffers.begin_lock(kVideoBuffer_displayed);
00594 continue;
00595 }
00596 ++it;
00597 }
00598 vbuffers.end_lock();
00599 }
00600
00601 bool VideoOutputNullVDPAU::BufferSizeCheck(void)
00602 {
00603 if (vbuffers.Size() != m_shadowBuffers->Size())
00604 {
00605 LOG(VB_GENERAL, LOG_ERR, LOC + "Number of GPU buffers not the "
00606 "same as number of CPU buffers.");
00607 return false;
00608 }
00609 return true;
00610 }