diff options
| -rw-r--r-- | HISTORY | 1 | ||||
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | display.c | 210 | ||||
| -rw-r--r-- | display.h | 20 | ||||
| -rw-r--r-- | omxdevice.c | 27 | ||||
| -rw-r--r-- | setup.c | 14 | ||||
| -rw-r--r-- | setup.h | 8 | ||||
| -rw-r--r-- | tools.c | 110 | ||||
| -rw-r--r-- | tools.h | 22 |
9 files changed, 350 insertions, 66 deletions
@@ -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 @@ -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: @@ -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) { } @@ -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) @@ -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]; }; @@ -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; } @@ -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); +} @@ -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 |
