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
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -876,7 +876,8 @@ void OpenGLVideo::SetSoftwareDeinterlacer(const QString &filter)


void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan, void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan,
bool softwareDeinterlacing, bool softwareDeinterlacing,
long long frame, bool draw_border) long long frame, StereoscopicMode stereo,
bool draw_border)
{ {
if (inputTextures.empty() || filters.empty()) if (inputTextures.empty() || filters.empty())
return; 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 // vertex coordinates
QRect display = (filter->outputBuffer == kDefaultBuffer) ? QRect display = (filter->outputBuffer == kDefaultBuffer) ?
display_video_rect : frameBufferRect; display_video_rect : frameBufferRect;
Expand Down
3 changes: 2 additions & 1 deletion mythtv/libs/libmythtv/openglvideo.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ class OpenGLVideo


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


void SetMasterViewport(QSize size) { masterViewportSize = size; } void SetMasterViewport(QSize size) { masterViewportSize = size; }
QSize GetViewPort(void) const { return viewportSize; } QSize GetViewPort(void) const { return viewportSize; }
Expand Down
7 changes: 7 additions & 0 deletions mythtv/libs/libmythtv/tv_actions.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -112,4 +112,11 @@
/* OSD playback information screen */ /* OSD playback information screen */
#define ACTION_TOGGLEOSDDEBUG "DEBUGOSD" #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 #endif // TV_ACTIONS_H
53 changes: 53 additions & 0 deletions mythtv/libs/libmythtv/tv_play.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3985,6 +3985,28 @@ bool TV::DiscMenuHandleAction(PlayerContext *ctx, const QStringList &actions)
return ctx->buffer->HandleAction(actions, pts); 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, bool TV::ActiveHandleAction(PlayerContext *ctx,
const QStringList &actions, const QStringList &actions,
bool isDVD, bool isDVDStill) bool isDVD, bool isDVDStill)
Expand Down Expand Up @@ -10008,6 +10030,8 @@ void TV::OSDDialogEvent(int result, QString text, QString action)
EditSchedule(actx, kViewSchedule); EditSchedule(actx, kViewSchedule);
else if (action.startsWith("VISUALISER")) else if (action.startsWith("VISUALISER"))
EnableVisualisation(actx, true, false, action); EnableVisualisation(actx, true, false, action);
else if (action.startsWith("3D"))
Handle3D(actx, action);
else if (HandleJumpToProgramAction(actx, QStringList(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; AspectOverrideMode aspectoverride = kAspect_Off;
FrameScanType scan_type = kScan_Ignore; FrameScanType scan_type = kScan_Ignore;
bool scan_type_locked = false; bool scan_type_locked = false;
bool stereoallowed = false;
StereoscopicMode stereomode = kStereoscopicModeNone;


ctx->LockDeletePlayer(__FILE__, __LINE__); ctx->LockDeletePlayer(__FILE__, __LINE__);
if (ctx->player) if (ctx->player)
Expand All @@ -10296,6 +10322,8 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
sup = vo->GetSupportedPictureAttributes(); sup = vo->GetSupportedPictureAttributes();
studio_levels = vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0; studio_levels = vo->GetPictureAttribute(kPictureAttribute_StudioLevels) > 0;
autodetect = !vo->hasHWAcceleration(); autodetect = !vo->hasHWAcceleration();
stereoallowed = vo->StereoscopicModesAllowed();
stereomode = vo->GetStereoscopicMode();
} }
} }
ctx->UnlockDeletePlayer(__FILE__, __LINE__); ctx->UnlockDeletePlayer(__FILE__, __LINE__);
Expand Down Expand Up @@ -10329,6 +10357,11 @@ void TV::FillOSDMenuVideo(const PlayerContext *ctx, OSD *osd,
selected == "ADJUSTPICTURE"); selected == "ADJUSTPICTURE");
} }
} }
if (stereoallowed)
{
osd->DialogAddButton(tr("3D"), "DIALOG_MENU_3D_0",
true, selected == "3D");
}
osd->DialogAddButton(tr("Advanced"), "DIALOG_MENU_ADVANCEDVIDEO_0", osd->DialogAddButton(tr("Advanced"), "DIALOG_MENU_ADVANCEDVIDEO_0",
true, selected == "ADVANCEDVIDEO"); 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") else if (category == "ADVANCEDVIDEO")
{ {
osd->DialogAddButton(tr("Video Scan"), osd->DialogAddButton(tr("Video Scan"),
Expand Down
1 change: 1 addition & 0 deletions mythtv/libs/libmythtv/tv_play.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ class MTV_PUBLIC TV : public QObject
bool TimeStretchHandleAction(PlayerContext*, bool TimeStretchHandleAction(PlayerContext*,
const QStringList &actions); const QStringList &actions);
bool DiscMenuHandleAction(PlayerContext*, const QStringList &actions); bool DiscMenuHandleAction(PlayerContext*, const QStringList &actions);
bool Handle3D(PlayerContext *ctx, const QString &action);


// Timers and timer events // Timers and timer events
int StartTimer(int interval, int line); int StartTimer(int interval, int line);
Expand Down
82 changes: 77 additions & 5 deletions mythtv/libs/libmythtv/videoout_opengl.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -528,23 +528,55 @@ void VideoOutputOpenGL::PrepareFrame(VideoFrame *buffer, FrameScanType t,
gl_context->SetBackground(0, 0, 0, 255); gl_context->SetBackground(0, 0, 0, 255);
gl_context->ClearFramebuffer(); 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(); MythMainWindow *mwnd = GetMythMainWindow();
if (gl_context->IsShared() && mwnd && window.IsEmbedding()) if (gl_context->IsShared() && mwnd && mwnd->GetPaintWindow() &&
window.IsEmbedding())
{ {
if (mwnd->GetPaintWindow()) if (twopass)
mwnd->GetPaintWindow()->setMask(QRegion()); gl_context->SetViewPort(first, true);
mwnd->GetPaintWindow()->setMask(QRegion());
mwnd->draw(); 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) if (gl_videochain && !buffer->dummy)
{ {
gl_videochain->SetVideoRect(vsz_enabled ? vsz_desired_display_rect : gl_videochain->SetVideoRect(vsz_enabled ? vsz_desired_display_rect :
window.GetDisplayVideoRect(), window.GetDisplayVideoRect(),
window.GetVideoRect()); window.GetVideoRect());
gl_videochain->PrepareFrame(buffer->top_field_first, t, gl_videochain->PrepareFrame(buffer->top_field_first, t,
m_deinterlacing, framesPlayed); m_deinterlacing, framesPlayed, m_stereo);
} }


// PiPs/PBPs
if (gl_pipchains.size()) if (gl_pipchains.size())
{ {
QMap<MythPlayer*,OpenGLVideo*>::iterator it = gl_pipchains.begin(); 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()]) if (gl_pip_ready[it.key()])
{ {
bool active = gl_pipchain_active == *it; bool active = gl_pipchain_active == *it;
if (twopass)
gl_context->SetViewPort(first, true);
(*it)->PrepareFrame(buffer->top_field_first, t, (*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 (m_visual && gl_painter && !window.IsEmbedding())
{
if (twopass)
gl_context->SetViewPort(first, true);
m_visual->Draw(GetTotalOSDBounds(), gl_painter, NULL); 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 (osd && gl_painter && !window.IsEmbedding())
{
if (twopass)
gl_context->SetViewPort(first, true);
osd->DrawDirect(gl_painter, GetTotalOSDBounds().size(), 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); gl_context->Flush(false);


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


return VideoOutput::ApproveDeintFilter(filtername); 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
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -52,8 +52,12 @@ class VideoOutputOpenGL : public VideoOutput


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

virtual bool StereoscopicModesAllowed(void) { return true; }


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


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

// 3D TV
m_stereo(kStereoscopicModeNone)
{ {
memset(&pip_tmp_image, 0, sizeof(pip_tmp_image)); memset(&pip_tmp_image, 0, sizeof(pip_tmp_image));
db_display_dim = QSize(gCoreContext->GetNumSetting("DisplaySizeWidth", 0), db_display_dim = QSize(gCoreContext->GetNumSetting("DisplaySizeWidth", 0),
Expand Down
8 changes: 8 additions & 0 deletions mythtv/libs/libmythtv/videooutbase.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -262,6 +262,11 @@ class VideoOutput
// Hue adjustment for certain vendors (mostly ATI) // Hue adjustment for certain vendors (mostly ATI)
static int CalcHueBase(const QString &adaptor_name); 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: protected:
void InitBuffers(int numdecode, bool extra_for_pause, int need_free, void InitBuffers(int numdecode, bool extra_for_pause, int need_free,
int needprebuffer_normal, int needprebuffer_small, int needprebuffer_normal, int needprebuffer_small,
Expand Down Expand Up @@ -343,6 +348,9 @@ class VideoOutput


// Visualisation // Visualisation
VideoVisual *m_visual; VideoVisual *m_visual;

// 3D TV mode
StereoscopicMode m_stereo;
}; };


#endif #endif
27 changes: 27 additions & 0 deletions mythtv/libs/libmythtv/videoouttypes.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ typedef enum PictureAttributeSupported
kPictureAttributeSupported_Volume = 0x20, kPictureAttributeSupported_Volume = 0x20,
} PictureAttributeSupported; } 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 typedef enum VideoErrorState
{ {
kError_None = 0x00, kError_None = 0x00,
Expand Down

0 comments on commit 18976c4

Please sign in to comment.