MythTV  0.26-pre
videoout_nullvdpau.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends