Skip to content

Commit

Permalink
Add support for Side by Side and Top and Bottom 3D.
Browse files Browse the repository at this point in the history
- add a new OSD 3D menu option (under Video).
- user can then select to discard one stereo field (right or top) for
non-3D capable TV sets or select Side by Side or Top and Bottom to
indicate that this is a 3D stream that will be shown in 3D by the
display.
- when Side by Side or Top and Bottom is selected, all on screen
graphics are rendered into each stereoscopic field at half width or
height. This includes main UI widgets when embedded (e.g. program
guide), visualisers, OSD and PiP/PBP. This is entirely untested on an
actual 3D capable set but should work as expected. There is no disparity
adjustment between the 2 overlays, so the OSD should appear in the plane
of the TV (which may need adjusting).

N.B. Currently only supported when using OpenGL rendering. VDPAU and
Direct3D support will follow.

Additional support for auto-detecting stereoscopic streams is also on
the way.
  • Loading branch information
Mark Kendall committed Dec 29, 2011
1 parent 4159139 commit 18976c4
Show file tree
Hide file tree
Showing 10 changed files with 198 additions and 10 deletions.
14 changes: 13 additions & 1 deletion mythtv/libs/libmythtv/openglvideo.cpp
Expand Up @@ -876,7 +876,8 @@ void OpenGLVideo::SetSoftwareDeinterlacer(const QString &filter)

void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan,
bool softwareDeinterlacing,
long long frame, bool draw_border)
long long frame, StereoscopicMode stereo,
bool draw_border)
{
if (inputTextures.empty() || filters.empty())
return;
Expand Down Expand Up @@ -940,6 +941,17 @@ void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan,
}
}

// discard stereoscopic fields
if (filter->outputBuffer == kDefaultBuffer)
{
if (kStereoscopicModeSideBySideDiscard == stereo)
trect = QRectF(trect.left() / 2.0f, trect.top(),
trect.width() / 2.0f, trect.height());
if (kStereoscopicModeTopAndBottomDiscard == stereo)
trect = QRectF(trect.left(), trect.top() / 2.0f,
trect.width(), trect.height() / 2.0f);
}

// vertex coordinates
QRect display = (filter->outputBuffer == kDefaultBuffer) ?
display_video_rect : frameBufferRect;
Expand Down
3 changes: 2 additions & 1 deletion mythtv/libs/libmythtv/openglvideo.h
Expand Up @@ -65,7 +65,8 @@ class OpenGLVideo

void PrepareFrame(bool topfieldfirst, FrameScanType scan,
bool softwareDeinterlacing,
long long frame, bool draw_border = false);
long long frame, StereoscopicMode stereo,
bool draw_border = false);

void SetMasterViewport(QSize size) { masterViewportSize = size; }
QSize GetViewPort(void) const { return viewportSize; }
Expand Down
7 changes: 7 additions & 0 deletions mythtv/libs/libmythtv/tv_actions.h
Expand Up @@ -112,4 +112,11 @@
/* OSD playback information screen */
#define ACTION_TOGGLEOSDDEBUG "DEBUGOSD"

/* 3D TV */
#define ACTION_3DNONE "3DNONE"
#define ACTION_3DSIDEBYSIDE "3DSIDEBYSIDE"
#define ACTION_3DSIDEBYSIDEDISCARD "3DSIDEBYSIDEDISCARD"
#define ACTION_3DTOPANDBOTTOM "3DTOPANDBOTTOM"
#define ACTION_3DTOPANDBOTTOMDISCARD "3DTOPANDBOTTOMDISCARD"

#endif // TV_ACTIONS_H
53 changes: 53 additions & 0 deletions mythtv/libs/libmythtv/tv_play.cpp
Expand Up @@ -3985,6 +3985,28 @@ bool TV::DiscMenuHandleAction(PlayerContext *ctx, const QStringList &actions)
return ctx->buffer->HandleAction(actions, pts);
}

bool TV::Handle3D(PlayerContext *ctx, const QString &action)
{
ctx->LockDeletePlayer(__FILE__, __LINE__);
if (ctx->player && ctx->player->GetVideoOutput() &&
ctx->player->GetVideoOutput()->StereoscopicModesAllowed())
{
StereoscopicMode mode = kStereoscopicModeNone;
if (ACTION_3DSIDEBYSIDE == action)
mode = kStereoscopicModeSideBySide;
else if (ACTION_3DSIDEBYSIDEDISCARD == action)
mode = kStereoscopicModeSideBySideDiscard;
else if (ACTION_3DTOPANDBOTTOM == action)
mode = kStereoscopicModeTopAndBottom;
else if (ACTION_3DTOPANDBOTTOMDISCARD == action)
mode = kStereoscopicModeTopAndBottomDiscard;
ctx->player->GetVideoOutput()->SetStereoscopicMode(mode);
SetOSDMessage(ctx, StereoscopictoString(mode));
}
ctx->UnlockDeletePlayer(__FILE__, __LINE__);
return true;
}

bool TV::ActiveHandleAction(PlayerContext *ctx,
const QStringList &actions,
bool isDVD, bool isDVDStill)
Expand Down Expand Up @@ -10008,6 +10030,8 @@ void TV::OSDDialogEvent(int result, QString text, QString action)
EditSchedule(actx, kViewSchedule);
else if (action.startsWith("VISUALISER"))
EnableVisualisation(actx, true, false, action);
else if (action.startsWith("3D"))
Handle3D(actx, action);
else if (HandleJumpToProgramAction(actx, QStringList(action)))
{
}
Expand Down Expand Up @@ -10279,6 +10303,8 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
AspectOverrideMode aspectoverride = kAspect_Off;
FrameScanType scan_type = kScan_Ignore;
bool scan_type_locked = false;
bool stereoallowed = false;
StereoscopicMode stereomode = kStereoscopicModeNone;

ctx->LockDeletePlayer(__FILE__, __LINE__);
if (ctx->player)
Expand All @@ -10296,6 +10322,8 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
sup = vo->GetSupportedPictureAttributes();
studio_levels = vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0;
autodetect = !vo->hasHWAcceleration();
stereoallowed = vo->StereoscopicModesAllowed();
stereomode = vo->GetStereoscopicMode();
}
}
ctx->UnlockDeletePlayer(__FILE__, __LINE__);
Expand Down Expand Up @@ -10329,6 +10357,11 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
selected == "ADJUSTPICTURE");
}
}
if (stereoallowed)
{
osd->DialogAddButton(tr("3D"), "DIALOG_MENU_3D_0",
true, selected == "3D");
}
osd->DialogAddButton(tr("Advanced"), "DIALOG_MENU_ADVANCEDVIDEO_0",
true, selected == "ADVANCEDVIDEO");
}
Expand Down Expand Up @@ -10387,6 +10420,26 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
}
}
}
else if (category == "3D")
{
backaction = "VIDEO";
currenttext = tr("3D");
osd->DialogAddButton(tr("None"),
ACTION_3DNONE, false,
stereomode == kStereoscopicModeNone);
osd->DialogAddButton(tr("Side by Side"),
ACTION_3DSIDEBYSIDE, false,
stereomode == kStereoscopicModeSideBySide);
osd->DialogAddButton(tr("Discard Side by Side"),
ACTION_3DSIDEBYSIDEDISCARD, false,
stereomode == kStereoscopicModeSideBySideDiscard);
osd->DialogAddButton(tr("Top and Bottom"),
ACTION_3DTOPANDBOTTOM, false,
stereomode == kStereoscopicModeTopAndBottom);
osd->DialogAddButton(tr("Discard Top and Bottom"),
ACTION_3DTOPANDBOTTOMDISCARD, false,
stereomode == kStereoscopicModeTopAndBottomDiscard);
}
else if (category == "ADVANCEDVIDEO")
{
osd->DialogAddButton(tr("Video Scan"),
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/tv_play.h
Expand Up @@ -228,6 +228,7 @@ class MTV_PUBLIC TV : public QObject
bool TimeStretchHandleAction(PlayerContext*,
const QStringList &actions);
bool DiscMenuHandleAction(PlayerContext*, const QStringList &actions);
bool Handle3D(PlayerContext *ctx, const QString &action);

// Timers and timer events
int StartTimer(int interval, int line);
Expand Down
82 changes: 77 additions & 5 deletions mythtv/libs/libmythtv/videoout_opengl.cpp
Expand Up @@ -528,23 +528,55 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *buffer, FrameScanType t,
gl_context->SetBackground(0, 0, 0, 255);
gl_context->ClearFramebuffer();

// stereoscopic views
QRect main = gl_context->GetViewPort();
QRect first = main;
QRect second = main;
bool twopass = (m_stereo == kStereoscopicModeSideBySide) ||
(m_stereo == kStereoscopicModeTopAndBottom);

if (kStereoscopicModeSideBySide == m_stereo)
{
first = QRect(main.left() / 2, main.top(),
main.width() / 2, main.height());
second = first.translated(main.width() / 2, 0);
}
else if (kStereoscopicModeTopAndBottom == m_stereo)
{
first = QRect(main.left(), main.top() / 2,
main.width(), main.height() / 2);
second = first.translated(0, main.height() / 2);
}

// main UI when embedded
MythMainWindow *mwnd = GetMythMainWindow();
if (gl_context->IsShared() && mwnd && window.IsEmbedding())
if (gl_context->IsShared() && mwnd && mwnd->GetPaintWindow() &&
window.IsEmbedding())
{
if (mwnd->GetPaintWindow())
mwnd->GetPaintWindow()->setMask(QRegion());
if (twopass)
gl_context->SetViewPort(first, true);
mwnd->GetPaintWindow()->setMask(QRegion());
mwnd->draw();
if (twopass)
{
gl_context->SetViewPort(second, true);
mwnd->GetPaintWindow()->setMask(QRegion());
mwnd->draw();
gl_context->SetViewPort(main, true);
}
}

// video
if (gl_videochain && !buffer->dummy)
{
gl_videochain->SetVideoRect(vsz_enabled ? vsz_desired_display_rect :
window.GetDisplayVideoRect(),
window.GetVideoRect());
gl_videochain->PrepareFrame(buffer->top_field_first, t,
m_deinterlacing, framesPlayed);
m_deinterlacing, framesPlayed, m_stereo);
}

// PiPs/PBPs
if (gl_pipchains.size())
{
QMap<MythPlayer*,OpenGLVideo*>::iterator it = gl_pipchains.begin();
Expand All @@ -553,17 +585,50 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *buffer, FrameScanType t,
if (gl_pip_ready[it.key()])
{
bool active = gl_pipchain_active == *it;
if (twopass)
gl_context->SetViewPort(first, true);
(*it)->PrepareFrame(buffer->top_field_first, t,
m_deinterlacing, framesPlayed, active);
m_deinterlacing, framesPlayed,
kStereoscopicModeNone, active);
if (twopass)
{
gl_context->SetViewPort(second, true);
(*it)->PrepareFrame(buffer->top_field_first, t,
m_deinterlacing, framesPlayed,
kStereoscopicModeNone, active);
gl_context->SetViewPort(main);
}
}
}
}

// visualisation
if (m_visual && gl_painter && !window.IsEmbedding())
{
if (twopass)
gl_context->SetViewPort(first, true);
m_visual->Draw(GetTotalOSDBounds(), gl_painter, NULL);
if (twopass)
{
gl_context->SetViewPort(second, true);
m_visual->Draw(GetTotalOSDBounds(), gl_painter, NULL);
gl_context->SetViewPort(main);
}
}

// OSD
if (osd && gl_painter && !window.IsEmbedding())
{
if (twopass)
gl_context->SetViewPort(first, true);
osd->DrawDirect(gl_painter, GetTotalOSDBounds().size(), true);
if (twopass)
{
gl_context->SetViewPort(second, true);
osd->DrawDirect(gl_painter, GetTotalOSDBounds().size(), true);
gl_context->SetViewPort(main);
}
}

gl_context->Flush(false);

Expand Down Expand Up @@ -893,3 +958,10 @@ bool VideoOutputOpenGL::ApproveDeintFilter(const QString& filtername) const

return VideoOutput::ApproveDeintFilter(filtername);
}

QStringList VideoOutputOpenGL::GetVisualiserList(void)
{
if (gl_context)
return VideoVisual::GetVisualiserList(gl_context->Type());
return VideoOutput::GetVisualiserList();
}
8 changes: 6 additions & 2 deletions mythtv/libs/libmythtv/videoout_opengl.h
Expand Up @@ -52,8 +52,12 @@ class VideoOutputOpenGL : public VideoOutput

virtual bool CanVisualise(AudioPlayer *audio, MythRender *render)
{ return VideoOutput::CanVisualise(audio, gl_context); }
virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render)
{ return VideoOutput::SetupVisualisation(audio, gl_context); }
virtual bool SetupVisualisation(AudioPlayer *audio, MythRender *render,
const QString &name)
{ return VideoOutput::SetupVisualisation(audio, gl_context, name); }
virtual QStringList GetVisualiserList(void);

virtual bool StereoscopicModesAllowed(void) { return true; }

protected:
bool CreateCPUResources(void);
Expand Down
5 changes: 4 additions & 1 deletion mythtv/libs/libmythtv/videooutbase.cpp
Expand Up @@ -408,7 +408,10 @@ VideoOutput::VideoOutput() :
osd_painter(NULL), osd_image(NULL),

// Visualisation
m_visual(NULL)
m_visual(NULL),

// 3D TV
m_stereo(kStereoscopicModeNone)
{
memset(&pip_tmp_image, 0, sizeof(pip_tmp_image));
db_display_dim = QSize(gCoreContext->GetNumSetting("DisplaySizeWidth", 0),
Expand Down
8 changes: 8 additions & 0 deletions mythtv/libs/libmythtv/videooutbase.h
Expand Up @@ -262,6 +262,11 @@ class VideoOutput
// Hue adjustment for certain vendors (mostly ATI)
static int CalcHueBase(const QString &adaptor_name);

// 3D TV
virtual bool StereoscopicModesAllowed(void) { return false; }
void SetStereoscopicMode(StereoscopicMode mode) { m_stereo = mode; }
StereoscopicMode GetStereoscopicMode(void) { return m_stereo; }

protected:
void InitBuffers(int numdecode, bool extra_for_pause, int need_free,
int needprebuffer_normal, int needprebuffer_small,
Expand Down Expand Up @@ -343,6 +348,9 @@ class VideoOutput

// Visualisation
VideoVisual *m_visual;

// 3D TV mode
StereoscopicMode m_stereo;
};

#endif
27 changes: 27 additions & 0 deletions mythtv/libs/libmythtv/videoouttypes.h
Expand Up @@ -105,6 +105,33 @@ typedef enum PictureAttributeSupported
kPictureAttributeSupported_Volume = 0x20,
} PictureAttributeSupported;

typedef enum StereoscopicMode
{
kStereoscopicModeNone,
kStereoscopicModeSideBySide,
kStereoscopicModeSideBySideDiscard,
kStereoscopicModeTopAndBottom,
kStereoscopicModeTopAndBottomDiscard,
} StereoscopicMode;

inline QString StereoscopictoString(StereoscopicMode mode)
{
switch (mode)
{
case kStereoscopicModeNone:
return QObject::tr("3D Off");
case kStereoscopicModeSideBySide:
return QObject::tr("3D Side by Side");
case kStereoscopicModeSideBySideDiscard:
return QObject::tr("3D Discard Side by Side");
case kStereoscopicModeTopAndBottom:
return QObject::tr("3D Top and Bottom");
case kStereoscopicModeTopAndBottomDiscard:
return QObject::tr("3D Discard Top and Bottom");
}
return QObject::tr("Unknown");
}

typedef enum VideoErrorState
{
kError_None = 0x00,
Expand Down

0 comments on commit 18976c4

Please sign in to comment.