summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2016-05-19 06:13:06 (GMT)
committerThomas Reufer <thomas@reufer.ch>2016-05-19 06:13:06 (GMT)
commit00af2c0eb2b86249b5aa680fccd6d3239a05e8f8 (patch)
treeb69af0ec3fe8f29da786324b6d3bb05202a23fab
parentdb7c53d003e8cb10fbc0b0af2f05a829a718780c (diff)
downloadvdr-plugin-rpihddevice-00af2c0eb2b86249b5aa680fccd6d3239a05e8f8.tar.gz
vdr-plugin-rpihddevice-00af2c0eb2b86249b5aa680fccd6d3239a05e8f8.tar.bz2
implement proper handling of display and pixel aspect ratios
-rw-r--r--HISTORY1
-rw-r--r--Makefile4
-rw-r--r--display.c210
-rw-r--r--display.h20
-rw-r--r--omxdevice.c27
-rw-r--r--setup.c14
-rw-r--r--setup.h8
-rw-r--r--tools.c110
-rw-r--r--tools.h22
9 files changed, 350 insertions, 66 deletions
diff --git a/HISTORY b/HISTORY
index a02a1a3..9b41d28 100644
--- a/HISTORY
+++ b/HISTORY
@@ -6,6 +6,7 @@ VDR Plugin 'rpihddevice' Revision History
- make use of advanced deinterlacer configurable
- add debug option to log number of executed OpenVG commands and flushes
- fixed:
+ - implement proper handling of display and pixel aspect ratios
- fixed vertical text position
2016-04-23: Version 1.0.3
diff --git a/Makefile b/Makefile
index 36e9ad5..fddbaeb 100644
--- a/Makefile
+++ b/Makefile
@@ -59,7 +59,7 @@ VCLIBDIR =$(SDKSTAGE)/opt/vc/lib
INCLUDES += -I$(ILCDIR) -I$(VCINCDIR) -I$(VCINCDIR)/interface/vcos/pthreads
INCLUDES += -I$(VCINCDIR)/interface/vmcs_host/linux
-
+
LDLIBS += -lbcm_host -lvcos -lvchiq_arm -lopenmaxil -lGLESv2 -lEGL -lpthread -lrt
LDLIBS += -Wl,--whole-archive $(ILCDIR)/libilclient.a -Wl,--no-whole-archive
LDFLAGS += -L$(VCLIBDIR)
@@ -117,7 +117,7 @@ INCLUDES += $(shell pkg-config --cflags freetype2)
### The object files (add further files here):
ILCLIENT = $(ILCDIR)/libilclient.a
-OBJS = $(PLUGIN).o setup.o omx.o audio.o omxdevice.o ovgosd.o display.o
+OBJS = $(PLUGIN).o tools.o setup.o omx.o audio.o omxdevice.o ovgosd.o display.o
### The main target:
diff --git a/display.c b/display.c
index f4f5273..7add79c 100644
--- a/display.c
+++ b/display.c
@@ -55,6 +55,7 @@ cRpiDisplay* cRpiDisplay::GetInstance(void)
tvstate.display.hdmi.width,
tvstate.display.hdmi.height,
tvstate.display.hdmi.frame_rate,
+ tvstate.display.hdmi.aspect_ratio,
tvstate.display.hdmi.scan_mode != 0,
tvstate.display.hdmi.group,
tvstate.display.hdmi.mode);
@@ -70,7 +71,7 @@ cRpiDisplay* cRpiDisplay::GetInstance(void)
DISPMANX_MODEINFO_T mode;
if (vc_dispmanx_display_get_info(display, &mode) >= 0)
s_instance = new cRpiDefaultDisplay(id,
- mode.width, mode.height);
+ mode.width, mode.height, SDTV_ASPECT_4_3);
vc_dispmanx_display_close(display);
}
@@ -78,7 +79,7 @@ cRpiDisplay* cRpiDisplay::GetInstance(void)
if (!s_instance)
{
ELOG("failed to get display information!");
- s_instance = new cRpiDefaultDisplay(id, 720, 576);
+ s_instance = new cRpiDefaultDisplay(id, 720, 576, SDTV_ASPECT_4_3);
}
}
@@ -93,11 +94,12 @@ void cRpiDisplay::DropInstance(void)
}
cRpiDisplay::cRpiDisplay(int id, int width, int height, int frameRate,
- bool interlaced, bool fixedMode) :
+ int aspectRatio, bool interlaced, bool fixedMode) :
m_id(id),
m_width(width),
m_height(height),
m_frameRate(frameRate),
+ m_aspectRatio(aspectRatio),
m_interlaced(interlaced),
m_fixedMode(fixedMode)
{
@@ -126,7 +128,19 @@ int cRpiDisplay::GetSize(int &width, int &height, double &aspect)
{
width = instance->m_width;
height = instance->m_height;
- aspect = (double)width / height;
+ switch (instance->m_aspectRatio)
+ {
+ case HDMI_ASPECT_4_3:
+ aspect = 4.0 / 3.0;
+ break;
+ case HDMI_ASPECT_16_9:
+ aspect = 16.0 / 9.0;
+ break;
+ default:
+ aspect = (double)width / height;
+ break;
+ }
+ aspect /= (double)width / height;
return 0;
}
return -1;
@@ -205,6 +219,62 @@ int cRpiDisplay::Snapshot(unsigned char* frame, int width, int height)
return -1;
}
+void cRpiDisplay::GetModeFormat(const cVideoFrameFormat *format,
+ int &modeX, int &modeY, int &aspectRatio)
+{
+ /* | Format | PAR | Mode | DAR |
+ * -------------------------------------------------------
+ * NTSC SD MPEG-2 | 720x480 | 8:9 | 720x480 | 4:3 |
+ * NTSC SD MPEG-4 | 720x480 | 10:11 | 720x480 | 4:3 |
+ * NTSC SD MPEG-2 | 720x480 | 32:27 | 720x480 | 16:9 |
+ * NTSC SD MPEG-4 | 720x480 | 40:33 | 720x480 | 16:9 |
+ * PAL SD MPEG-2 | 720x576 | 16:15 | 720x576 | 4:3 |
+ * PAL SD MPEG-4 | 720x576 | 12:11 | 720x576 | 4:3 |
+ * PAL SD MPEG-2 | 720x576 | 64:45 | 720x576 | 16:9 |
+ * PAL SD MPEG-4 | 720x576 | 16:11 | 720x576 | 16:9 |
+ * HD | 1280x720 | 1:1 | 1280x720 | 16:9 |
+ * HD | 1280x1080 | 3:2 | 1920x1080 | 16:9 |
+ * HD | 1920x1080 | 1:1 | 1920x1080 | 16:9 |
+ */
+
+ aspectRatio = HDMI_ASPECT_UNKNOWN;
+ modeY = format->height;
+
+ switch (modeY)
+ {
+ case 480:
+ if ((format->pixelWidth == 8 && format->pixelHeight == 9) ||
+ (format->pixelWidth == 10 && format->pixelHeight == 11))
+ aspectRatio = HDMI_ASPECT_4_3;
+ else if ((format->pixelWidth == 32 && format->pixelHeight == 27) ||
+ (format->pixelWidth == 40 && format->pixelHeight == 33))
+ aspectRatio = HDMI_ASPECT_16_9;
+ modeX = format->width;
+ break;
+
+ case 576:
+ if ((format->pixelWidth == 16 && format->pixelHeight == 15) ||
+ (format->pixelWidth == 12 && format->pixelHeight == 11))
+ aspectRatio = HDMI_ASPECT_4_3;
+ else if ((format->pixelWidth == 64 && format->pixelHeight == 45) ||
+ (format->pixelWidth == 16 && format->pixelHeight == 11))
+ aspectRatio = HDMI_ASPECT_16_9;
+ modeX = format->width;
+ break;
+
+ default:
+ ILOG("unknown video frame format: %dx%d@%d%s PAR(%d:%d)",
+ format->width, format->height, format->frameRate,
+ format->Interlaced() ? "i" : "p",
+ format->pixelWidth, format->pixelHeight);
+ case 720:
+ case 1080:
+ aspectRatio = HDMI_ASPECT_16_9;
+ modeX = format->width * format->pixelWidth / format->pixelHeight;
+ break;
+ }
+}
+
int cRpiDisplay::Update(const cVideoFrameFormat *frameFormat)
{
if (m_fixedMode || (
@@ -215,21 +285,49 @@ int cRpiDisplay::Update(const cVideoFrameFormat *frameFormat)
int newWidth = m_width;
int newHeight = m_height;
int newFrameRate = m_frameRate;
+ int newAspectRatio = m_aspectRatio;
bool newInterlaced = m_interlaced;
switch (cRpiSetup::GetVideoResolution())
{
- case cVideoResolution::e480: newWidth = 720; newHeight = 480; break;
- case cVideoResolution::e576: newWidth = 720; newHeight = 576; break;
- case cVideoResolution::e720: newWidth = 1280; newHeight = 720; break;
- case cVideoResolution::e1080: newWidth = 1920; newHeight = 1080; break;
+ case cVideoResolution::e480:
+ newWidth = 720;
+ newHeight = 480;
+ newAspectRatio = HDMI_ASPECT_4_3;
+ break;
+
+ case cVideoResolution::e480w:
+ newWidth = 720;
+ newHeight = 480;
+ newAspectRatio = HDMI_ASPECT_16_9;
+ break;
+
+ case cVideoResolution::e576:
+ newWidth = 720;
+ newHeight = 576;
+ newAspectRatio = HDMI_ASPECT_4_3;
+ break;
+
+ case cVideoResolution::e576w:
+ newWidth = 720;
+ newHeight = 576;
+ newAspectRatio = HDMI_ASPECT_16_9;
+ break;
+
+ case cVideoResolution::e720:
+ newWidth = 1280;
+ newHeight = 720;
+ newAspectRatio = HDMI_ASPECT_16_9;
+ break;
+
+ case cVideoResolution::e1080:
+ newWidth = 1920;
+ newHeight = 1080;
+ newAspectRatio = HDMI_ASPECT_16_9;
+ break;
case cVideoResolution::eFollowVideo:
- if (frameFormat->width && frameFormat->height)
- {
- newWidth = frameFormat->width;
- newHeight = frameFormat->height;
- }
+ GetModeFormat(frameFormat, newWidth, newHeight, newAspectRatio);
break;
default:
@@ -262,13 +360,26 @@ int cRpiDisplay::Update(const cVideoFrameFormat *frameFormat)
// set new mode only if necessary
if (newWidth != m_width || newHeight != m_height ||
- newFrameRate != m_frameRate || newInterlaced != m_interlaced)
- return SetMode(newWidth, newHeight, newFrameRate,
+ newFrameRate != m_frameRate || newInterlaced != m_interlaced ||
+ newAspectRatio != m_aspectRatio)
+ return SetMode(newWidth, newHeight, newFrameRate, newAspectRatio,
newInterlaced ? frameFormat->scanMode : cScanMode::eProgressive);
return 0;
}
+const char* cRpiDisplay::AspectRatioStr(int aspectRatio)
+{
+ return aspectRatio == HDMI_ASPECT_4_3 ? "4:3" :
+ aspectRatio == HDMI_ASPECT_14_9 ? "14:9" :
+ aspectRatio == HDMI_ASPECT_16_9 ? "16:9" :
+ aspectRatio == HDMI_ASPECT_5_4 ? "5:4" :
+ aspectRatio == HDMI_ASPECT_16_10 ? "16:10" :
+ aspectRatio == HDMI_ASPECT_15_9 ? "15:9" :
+ aspectRatio == HDMI_ASPECT_64_27 ? "64:27" :
+ aspectRatio == HDMI_ASPECT_21_9 ? "21:9" : "unknown";
+}
+
/* ------------------------------------------------------------------------- */
#define HDMI_MAX_MODES 64
@@ -281,8 +392,8 @@ public:
};
cRpiHDMIDisplay::cRpiHDMIDisplay(int id, int width, int height, int frameRate,
- bool interlaced, int group, int mode) :
- cRpiDisplay(id, width, height, frameRate, interlaced, false),
+ int aspectRatio, bool interlaced, int group, int mode) :
+ cRpiDisplay(id, width, height, frameRate, aspectRatio, interlaced, false),
m_modes(new cRpiHDMIDisplay::ModeList()),
m_group(group),
m_mode(mode),
@@ -304,7 +415,7 @@ cRpiHDMIDisplay::cRpiHDMIDisplay(int id, int width, int height, int frameRate,
DLOG("supported HDMI modes:");
for (int i = 0; i < m_modes->nModes; i++)
{
- DLOG("%s[%02d]: %4dx%4d@%2d%s | %s | %3d.%03dMHz%s%s",
+ DLOG("%s[%02d]: %4dx%4d@%2d%s | %*s | %3d.%03dMHz%s%s",
m_modes->modes[i].group == HDMI_RES_GROUP_CEA ? "CEA" :
m_modes->modes[i].group == HDMI_RES_GROUP_DMT ? "DMT" : "---",
m_modes->modes[i].code,
@@ -312,14 +423,7 @@ cRpiHDMIDisplay::cRpiHDMIDisplay(int id, int width, int height, int frameRate,
m_modes->modes[i].height,
m_modes->modes[i].frame_rate,
m_modes->modes[i].scan_mode ? "i" : "p",
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_4_3 ? " 4:3 " :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_14_9 ? "14:9 " :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_16_9 ? "16:9 " :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_5_4 ? " 5:4 " :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_16_10 ? "16:10" :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_15_9 ? "15:9 " :
- m_modes->modes[i].aspect_ratio == HDMI_ASPECT_21_9 ? "21:9 " :
- "unknown aspect ratio",
+ 5, AspectRatioStr(m_modes->modes[i].aspect_ratio),
m_modes->modes[i].pixel_freq / 1000000,
m_modes->modes[i].pixel_freq % 1000000 / 1000,
m_modes->modes[i].native ? " (native)" : "",
@@ -354,31 +458,50 @@ cRpiHDMIDisplay::~cRpiHDMIDisplay()
}
int cRpiHDMIDisplay::SetMode(int width, int height, int frameRate,
- cScanMode::eMode scanMode)
+ int aspectRatio, cScanMode::eMode scanMode)
{
SetHvsSyncUpdate(scanMode);
- bool interlaced = cScanMode::Interlaced(scanMode);
+ int interlaced = cScanMode::Interlaced(scanMode) ? 1 : 0;
+ int mode = -1, altMode = -1;
- for (int i = 0; i < m_modes->nModes; i++)
+ for (int i = 0; i < m_modes->nModes && mode == -1; i++)
{
if (m_modes->modes[i].width == width &&
m_modes->modes[i].height == height &&
m_modes->modes[i].frame_rate == frameRate &&
+ m_modes->modes[i].aspect_ratio == aspectRatio &&
m_modes->modes[i].scan_mode == interlaced)
- {
- DLOG("setting HDMI mode to %dx%d@%2d%s", width, height,
- frameRate, interlaced ? "i" : "p");
-
- m_width = width;
- m_height = height;
- m_frameRate = frameRate;
- m_interlaced = interlaced;
- return SetMode(m_modes->modes[i].group, m_modes->modes[i].code);
- }
+ mode = i;
+
+ else if (m_modes->modes[i].height == height &&
+ m_modes->modes[i].frame_rate == frameRate &&
+ m_modes->modes[i].aspect_ratio == aspectRatio &&
+ m_modes->modes[i].scan_mode == interlaced)
+ altMode = i;
+ }
+
+ if (mode == -1 && altMode != -1)
+ mode = altMode;
+
+ if (mode != -1)
+ {
+ m_width = m_modes->modes[mode].width;
+ m_height = m_modes->modes[mode].height;
+ m_frameRate = m_modes->modes[mode].frame_rate;
+ m_aspectRatio = m_modes->modes[mode].aspect_ratio;
+ m_interlaced = m_modes->modes[mode].scan_mode;
+
+ DLOG("setting HDMI mode to %dx%d@%2d%s (%s)", m_width, m_height,
+ m_frameRate, m_interlaced ? "i" : "p",
+ AspectRatioStr(m_aspectRatio));
+
+ return SetMode(m_modes->modes[mode].group, m_modes->modes[mode].code);
}
- DLOG("failed to set HDMI mode to %dx%d@%2d%s",
- width, height, frameRate, interlaced ? "i" : "p");
+ DLOG("failed to set HDMI mode to %dx%d@%2d%s (%s)",
+ width, height, frameRate, interlaced ? "i" : "p",
+ AspectRatioStr(aspectRatio));
+
return -1;
}
@@ -414,7 +537,8 @@ void cRpiHDMIDisplay::TvServiceCallback(void *data, unsigned int reason,
/* ------------------------------------------------------------------------- */
-cRpiDefaultDisplay::cRpiDefaultDisplay(int id, int width, int height) :
- cRpiDisplay(id, width, height, 50, false, true)
+cRpiDefaultDisplay::cRpiDefaultDisplay(int id, int width, int height,
+ int aspectRatio) :
+ cRpiDisplay(id, width, height, 50, aspectRatio, false, true)
{
}
diff --git a/display.h b/display.h
index 7a203e3..b062aa7 100644
--- a/display.h
+++ b/display.h
@@ -44,23 +44,29 @@ public:
protected:
- cRpiDisplay(int id, int width, int height, int frameRate, bool interlaced,
- bool fixedMode);
+ cRpiDisplay(int id, int width, int height, int frameRate, int aspectRatio,
+ bool interlaced, bool fixedMode);
virtual ~cRpiDisplay();
int Update(const cVideoFrameFormat *videoFormat);
- virtual int SetMode(int width, int height, int frameRate,
+ virtual int SetMode(int width, int height, int frameRate, int aspectRatio,
cScanMode::eMode scanMode) {
return 0;
}
static int SetHvsSyncUpdate(cScanMode::eMode scanMode);
+ static void GetModeFormat(const cVideoFrameFormat *format,
+ int &modeX, int &modeY, int &aspectRatio);
+
+ static const char* AspectRatioStr(int aspectRatio);
+
int m_id;
int m_width;
int m_height;
int m_frameRate;
+ int m_aspectRatio;
bool m_interlaced;
bool m_fixedMode;
@@ -78,13 +84,13 @@ class cRpiHDMIDisplay : public cRpiDisplay
public:
- cRpiHDMIDisplay(int id, int width, int height, int frameRate, bool interlaced,
- int group, int mode);
+ cRpiHDMIDisplay(int id, int width, int height, int frameRate,
+ int aspectRatio, bool interlaced, int group, int mode);
virtual ~cRpiHDMIDisplay();
private:
- virtual int SetMode(int width, int height, int frameRate,
+ virtual int SetMode(int width, int height, int frameRate, int aspectRatio,
cScanMode::eMode scanMode);
int SetMode(int group, int mode);
@@ -107,7 +113,7 @@ class cRpiDefaultDisplay : public cRpiDisplay
public:
- cRpiDefaultDisplay(int id, int width, int height);
+ cRpiDefaultDisplay(int id, int width, int height, int aspectRatio);
};
diff --git a/omxdevice.c b/omxdevice.c
index 684f68d..a1da64f 100644
--- a/omxdevice.c
+++ b/omxdevice.c
@@ -22,6 +22,7 @@
#include "audio.h"
#include "display.h"
#include "setup.h"
+#include "tools.h"
#include <vdr/thread.h>
#include <vdr/remux.h>
@@ -696,18 +697,17 @@ void cOmxDevice::HandleStreamStart()
DBG("HandleStreamStart()");
const cVideoFrameFormat *format = m_omx->GetVideoFrameFormat();
- DLOG("video stream started %dx%d@%d%s PAR(%d:%d)",
+ DLOG("video stream started %dx%d@%d%s, PAR=%d/%d",
format->width, format->height, format->frameRate,
format->Interlaced() ? "i" : "p",
format->pixelWidth, format->pixelHeight);
- cRpiDisplay::SetVideoFormat(format);
+ HandleVideoSetupChanged();
}
void cOmxDevice::HandleVideoSetupChanged()
{
- DBG("HandleVideoSetupChanged()");
-
+ // apply framing parameters
switch (cRpiSetup::GetVideoFraming())
{
default:
@@ -724,7 +724,24 @@ void cOmxDevice::HandleVideoSetupChanged()
break;
}
- cRpiDisplay::SetVideoFormat(m_omx->GetVideoFrameFormat());
+ const cVideoFrameFormat *format = m_omx->GetVideoFrameFormat();
+ double videoPAR = format->pixelHeight ?
+ (double)format->pixelWidth / format->pixelHeight : 1.0f;
+
+ // update display format according current video stream
+ cRpiDisplay::SetVideoFormat(format);
+
+ // get updated display format ...
+ int width, height;
+ double displayPAR;
+ cRpiDisplay::GetSize(width, height, displayPAR);
+
+ // ... and set video render format accordingly
+ cRational renderPAR = cRational(videoPAR / displayPAR);
+ renderPAR.Reduce(100);
+ m_omx->SetPixelAspectRatio(renderPAR.num, renderPAR.den);
+ DLOG("display PAR=%0.3f, setting video render PAR=%d/%d",
+ displayPAR, renderPAR.num, renderPAR.den);
}
void cOmxDevice::FlushStreams(bool flushVideoRender)
diff --git a/setup.c b/setup.c
index 4359e11..2f84219 100644
--- a/setup.c
+++ b/setup.c
@@ -59,10 +59,12 @@ public:
m_videoResolution[0] = tr("default");
m_videoResolution[1] = tr("follow video");
- m_videoResolution[2] = "720x480";
- m_videoResolution[3] = "720x576";
- m_videoResolution[4] = "1280x720";
- m_videoResolution[5] = "1920x1080";
+ m_videoResolution[2] = "720x480 (4:3)";
+ m_videoResolution[3] = "720x480 (16:9)";
+ m_videoResolution[4] = "720x576 (4:3)";
+ m_videoResolution[5] = "720x576 (16:9)";
+ m_videoResolution[6] = "1280x720";
+ m_videoResolution[7] = "1920x1080";
m_videoFrameRate[0] = tr("default");
m_videoFrameRate[1] = tr("follow video");
@@ -122,7 +124,7 @@ private:
if (!cRpiDisplay::IsFixedMode())
{
Add(new cMenuEditStraItem(
- tr("Resolution"), &m_video.resolution, 6, m_videoResolution));
+ tr("Resolution"), &m_video.resolution, 8, m_videoResolution));
Add(new cMenuEditStraItem(
tr("Frame Rate"), &m_video.frameRate, 9, m_videoFrameRate));
@@ -159,7 +161,7 @@ private:
const char *m_audioPort[2];
const char *m_audioFormat[3];
const char *m_videoFraming[3];
- const char *m_videoResolution[6];
+ const char *m_videoResolution[8];
const char *m_videoFrameRate[9];
const char *m_useAdvancedDeinterlacer[3];
};
diff --git a/setup.h b/setup.h
index e99d5c9..f33d5ea 100644
--- a/setup.h
+++ b/setup.h
@@ -111,9 +111,11 @@ public:
static cVideoResolution::eResolution GetVideoResolution(void) {
return GetInstance()->m_video.resolution == 1 ? cVideoResolution::eFollowVideo :
GetInstance()->m_video.resolution == 2 ? cVideoResolution::e480 :
- GetInstance()->m_video.resolution == 3 ? cVideoResolution::e576 :
- GetInstance()->m_video.resolution == 4 ? cVideoResolution::e720 :
- GetInstance()->m_video.resolution == 5 ? cVideoResolution::e1080 :
+ GetInstance()->m_video.resolution == 3 ? cVideoResolution::e480w :
+ GetInstance()->m_video.resolution == 4 ? cVideoResolution::e576 :
+ GetInstance()->m_video.resolution == 5 ? cVideoResolution::e576w :
+ GetInstance()->m_video.resolution == 6 ? cVideoResolution::e720 :
+ GetInstance()->m_video.resolution == 7 ? cVideoResolution::e1080 :
cVideoResolution::eDontChange;
}
diff --git a/tools.c b/tools.c
new file mode 100644
index 0000000..f87fe80
--- /dev/null
+++ b/tools.c
@@ -0,0 +1,110 @@
+/*
+ * rpihddevice - VDR HD output device for Raspberry Pi
+ * Copyright (C) 2014, 2015, 2016 Thomas Reufer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <vdr/tools.h>
+#include "tools.h"
+
+/*
+ * ffmpeg's implementation for rational numbers:
+ * https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/rational.c
+ */
+
+cRational::cRational(double d) :
+ num(0), den(0)
+{
+ int exp;
+ frexp(d, &exp);
+
+ den = 1LL << (29 - max(exp - 1, 0));
+ num = floor(d * den + 0.5);
+
+ Reduce(INT_MAX);
+}
+
+bool cRational::Reduce(int max)
+{
+ cRational a0 = cRational(0, 1), a1 = cRational(1, 0);
+ int sign = (num < 0) ^ (den < 0);
+ if (int div = Gcd(abs(num), abs(den)))
+ {
+ num = abs(num) / div;
+ den = abs(den) / div;
+ }
+ if (num <= max && den <= max)
+ {
+ a1 = cRational(num, den);
+ den = 0;
+ }
+ while (den)
+ {
+ int x = num / den;
+ int nextDen = num - den * x;
+ cRational a2 = cRational(x * a1.num + a0.num, x * a1.den + a0.den);
+ if (a2.num > max || a2.den > max)
+ {
+ if (a1.num)
+ x = (max - a0.num) / a1.num;
+ if (a1.den)
+ x = min(x, (max - a0.den) / a1.den);
+ if (den * (2 * x * a1.den + a0.den) > num * a1.den)
+ a1 = cRational(x * a1.num + a0.num, x * a1.den + a0.den);
+ break;
+ }
+ a0 = a1;
+ a1 = a2;
+ num = den;
+ den = nextDen;
+ }
+ num = sign ? -a1.num : a1.num;
+ den = a1.den;
+ return den == 0;
+}
+
+/*
+ * Stein's binary GCD algorithm:
+ * https://en.wikipedia.org/wiki/Binary_GCD_algorithm
+ */
+
+int cRational::Gcd(int u, int v)
+{
+ if (u == v || v == 0)
+ return u;
+
+ if (u == 0)
+ return v;
+
+ // look for factors of 2
+ if (~u & 1) // u is even
+ {
+ if (v & 1) // v is odd
+ return Gcd(u >> 1, v);
+ else // both u and v are even
+ return Gcd(u >> 1, v >> 1) << 1;
+ }
+
+ if (~v & 1) // u is odd, v is even
+ return Gcd(u, v >> 1);
+
+ // reduce larger argument
+ if (u > v)
+ return Gcd((u - v) >> 1, v);
+
+ return Gcd((v - u) >> 1, u);
+}
diff --git a/tools.h b/tools.h
index c6d198f..4a1a6c5 100644
--- a/tools.h
+++ b/tools.h
@@ -38,7 +38,9 @@ public:
eDontChange = 0,
eFollowVideo,
e480,
+ e480w,
e576,
+ e576w,
e720,
e1080
};
@@ -47,7 +49,9 @@ public:
return (resolution == eDontChange) ? "don't change" :
(resolution == eFollowVideo) ? "follow video" :
(resolution == e480) ? "480" :
+ (resolution == e480w) ? "480w" :
(resolution == e576) ? "576" :
+ (resolution == e576w) ? "576w" :
(resolution == e720) ? "720" :
(resolution == e1080) ? "1080" : "unknown";
}
@@ -215,4 +219,22 @@ public:
}
};
+class cRational
+{
+public:
+
+ cRational(double d);
+ cRational(int _num, int _den) : num(_num), den(_den) { }
+
+ bool Reduce(int max);
+
+ int num;
+ int den;
+
+private:
+
+ cRational();
+ static int Gcd(int u, int v);
+};
+
#endif