|
MythTV
0.26-pre
|
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 // Add extra buffers as necessary 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 // Always returns the CPU version of a frame 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 // Always returns the CPU version of a frame 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 // Should work with either the CPU or GPU version of a frame 00411 void VideoOutputNullVDPAU::DiscardFrame(VideoFrame *frame) 00412 { 00413 if (!frame || !BufferSizeCheck()) 00414 return; 00415 00416 // is this a CPU frame 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 // is this a GPU frame 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 // Should work with either the CPU or GPU version of a frame 00440 void VideoOutputNullVDPAU::DoneDisplayingFrame(VideoFrame *frame) 00441 { 00442 if (!frame || !BufferSizeCheck()) 00443 return; 00444 00445 // is this a CPU frame 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 // is this a GPU frame 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 // assume a direct mapping of GPU to CPU buffers 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 }
1.7.6.1