Permalink
Browse files

Add support for Side by Side and Top and Bottom 3D.

- 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
Mark Kendall committed Dec 29, 2011
1 parent 4159139 commit 18976c4b137aeeb264e2857ddc4851eef530024e
@@ -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;
@@ -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;
@@ -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; }
@@ -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
@@ -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)
@@ -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)))
{
}
@@ -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)
@@ -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__);
@@ -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");
}
@@ -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"),
@@ -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);
@@ -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();
@@ -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);
@@ -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();
+}
@@ -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);
@@ -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),
@@ -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,
@@ -343,6 +348,9 @@ class VideoOutput
// Visualisation
VideoVisual *m_visual;
+
+ // 3D TV mode
+ StereoscopicMode m_stereo;
};
#endif
@@ -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,

0 comments on commit 18976c4

Please sign in to comment.