1
0
mirror of https://github.com/rofafor/vdr-plugin-femon.git synced 2023-10-10 11:36:53 +00:00

Compare commits

..

12 Commits

Author SHA1 Message Date
Rolf Ahrenberg
44fa48c59f Added debug() and error() macros. 2009-10-01 12:13:35 +03:00
Rolf Ahrenberg
554dac9674 Increased video buffer size, added buffer timeouts and fixed deleting AC3 buffer data. 2009-09-29 15:31:32 +03:00
Rolf Ahrenberg
8267abcc3b Revert "Changed cRingBufferLinear to cRingBufferFrame."
This reverts commit edb8b4090a.
2009-09-27 16:59:42 +03:00
Rolf Ahrenberg
ea4561a874 Silenced a compilation warning. 2009-09-26 12:15:26 +03:00
Rolf Ahrenberg
56b9b89204 Cleaned up translations and warnings. 2009-09-26 11:54:31 +03:00
Rolf Ahrenberg
3d994be0b7 Cosmetics. 2009-09-25 22:31:12 +03:00
Rolf Ahrenberg
b7d44e730e Added Chinese translation (Thanks to NanFeng). 2009-09-24 17:28:02 +03:00
Rolf Ahrenberg
edb8b4090a Changed cRingBufferLinear to cRingBufferFrame. 2009-09-22 20:37:15 +03:00
Rolf Ahrenberg
21d9c20beb Removed bitstream parsing from Receive() method.
Refactored the error logging from unimplemented ioctl functions.
2009-09-22 08:44:30 +03:00
Rolf Ahrenberg
080f390a75 Remove compilation warnings. 2009-09-16 12:05:15 +03:00
Rolf Ahrenberg
fefb7f0fde Removed error logging from unimplemented ioctl functions. 2009-09-16 09:37:51 +03:00
Rolf Ahrenberg
ba133f15f8 Changed H.264 parser to show display aspect ratio. 2009-09-15 10:57:53 +03:00
12 changed files with 1149 additions and 195 deletions

View File

@@ -376,3 +376,10 @@ VDR Plugin 'femon' Revision History
- Fixed H.264 bitstream parser.
- Added a mutex to receiver class.
- Added 1080/720/576/480 format symbols into status window.
2009-10-01: Version 1.7.5
- Changed H.264 parser to show display aspect ratio.
- Removed error logging from unimplemented ioctl functions.
- Removed bitstream parsing from Receive() method.
- Added Chinese translation (Thanks to NanFeng).

12
femon.c
View File

@@ -18,7 +18,7 @@
#error "VDR-1.7.0 API version or greater is required!"
#endif
static const char VERSION[] = "1.7.4";
static const char VERSION[] = "1.7.5";
static const char DESCRIPTION[] = trNOOP("DVB Signal Information Monitor (OSD)");
static const char MAINMENUENTRY[] = trNOOP("Signal Information");
@@ -50,13 +50,13 @@ cPluginFemon::cPluginFemon()
// Initialize any member variables here.
// DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
// VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
}
cPluginFemon::~cPluginFemon()
{
// Clean up after yourself!
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
}
const char *cPluginFemon::CommandLineHelp(void)
@@ -96,7 +96,7 @@ void cPluginFemon::Housekeeping(void)
cOsdObject *cPluginFemon::MainMenuAction(void)
{
// Perform the action when selected from the main VDR menu.
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
if (cReplayControl::NowReplaying() || (Channels.Count() <= 0))
Skins.Message(mtInfo, tr("Femon not available"));
else
@@ -274,7 +274,7 @@ public:
cMenuFemonSetup::cMenuFemonSetup(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
dispmodes[eFemonModeBasic] = tr("basic");
dispmodes[eFemonModeTransponder] = tr("transponder");
dispmodes[eFemonModeStream] = tr("stream");
@@ -353,7 +353,7 @@ void cMenuFemonSetup::Setup(void)
void cMenuFemonSetup::Store(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
femonConfig = data;
SetupStore("HideMenu", femonConfig.hidemenu);
SetupStore("DisplayMode", femonConfig.displaymode);

View File

@@ -8,25 +8,47 @@
#include "femontools.h"
#include "femonh264.h"
const eVideoAspectRatio cFemonH264::s_AspectRatios[] =
const cFemonH264::t_DAR cFemonH264::s_DAR[] =
{
VIDEO_ASPECT_RATIO_INVALID,
VIDEO_ASPECT_RATIO_1_1,
VIDEO_ASPECT_RATIO_12_11,
VIDEO_ASPECT_RATIO_10_11,
VIDEO_ASPECT_RATIO_16_11,
VIDEO_ASPECT_RATIO_40_33,
VIDEO_ASPECT_RATIO_24_11,
VIDEO_ASPECT_RATIO_20_11,
VIDEO_ASPECT_RATIO_32_11,
VIDEO_ASPECT_RATIO_80_33,
VIDEO_ASPECT_RATIO_18_11,
VIDEO_ASPECT_RATIO_15_11,
VIDEO_ASPECT_RATIO_64_33,
VIDEO_ASPECT_RATIO_160_99,
VIDEO_ASPECT_RATIO_4_3,
VIDEO_ASPECT_RATIO_3_2,
VIDEO_ASPECT_RATIO_2_1
{ VIDEO_ASPECT_RATIO_1_1, 100 },
{ VIDEO_ASPECT_RATIO_4_3, 133 },
{ VIDEO_ASPECT_RATIO_16_9, 177 },
{ VIDEO_ASPECT_RATIO_2_21_1, 221 },
{ VIDEO_ASPECT_RATIO_12_11, 109 },
{ VIDEO_ASPECT_RATIO_10_11, 90 },
{ VIDEO_ASPECT_RATIO_16_11, 145 },
{ VIDEO_ASPECT_RATIO_40_33, 121 },
{ VIDEO_ASPECT_RATIO_24_11, 218 },
{ VIDEO_ASPECT_RATIO_20_11, 181 },
{ VIDEO_ASPECT_RATIO_32_11, 290 },
{ VIDEO_ASPECT_RATIO_80_33, 242 },
{ VIDEO_ASPECT_RATIO_18_11, 163 },
{ VIDEO_ASPECT_RATIO_15_11, 136 },
{ VIDEO_ASPECT_RATIO_64_33, 193 },
{ VIDEO_ASPECT_RATIO_160_99, 161 },
{ VIDEO_ASPECT_RATIO_3_2, 150 },
{ VIDEO_ASPECT_RATIO_2_1, 200 }
};
const cFemonH264::t_SAR cFemonH264::s_SAR[] =
{
{ 0, 0 }, // VIDEO_ASPECT_RATIO_INVALID
{ 1, 1 }, // VIDEO_ASPECT_RATIO_1_1
{ 12, 11 }, // VIDEO_ASPECT_RATIO_12_11
{ 10, 11 }, // VIDEO_ASPECT_RATIO_10_11
{ 16, 11 }, // VIDEO_ASPECT_RATIO_16_11
{ 40, 33 }, // VIDEO_ASPECT_RATIO_40_33
{ 24, 11 }, // VIDEO_ASPECT_RATIO_24_11
{ 20, 11 }, // VIDEO_ASPECT_RATIO_20_11
{ 32, 11 }, // VIDEO_ASPECT_RATIO_32_11
{ 80, 33 }, // VIDEO_ASPECT_RATIO_80_33
{ 18, 11 }, // VIDEO_ASPECT_RATIO_18_11
{ 15, 11 }, // VIDEO_ASPECT_RATIO_15_11
{ 64, 33 }, // VIDEO_ASPECT_RATIO_64_33
{ 160, 99 }, // VIDEO_ASPECT_RATIO_160_99
{ 4, 3 }, // VIDEO_ASPECT_RATIO_4_3
{ 3, 2 }, // VIDEO_ASPECT_RATIO_3_2
{ 2, 1 } // VIDEO_ASPECT_RATIO_2_1
};
const eVideoFormat cFemonH264::s_VideoFormats[] =
@@ -97,7 +119,7 @@ bool cFemonH264::processVideo(const uint8_t *buf, int len)
if (!aud_found) {
switch (buf[4] >> 5) {
case 0: case 3: case 5: // I_FRAME
//Dprintf("H.264: Found NAL AUD at offset %d/%d", buf - start, len);
//debug("H.264: Found NAL AUD at offset %d/%d\n", int(buf - start), len);
aud_found = true;
break;
case 1: case 4: case 6: // P_FRAME;
@@ -110,7 +132,7 @@ bool cFemonH264::processVideo(const uint8_t *buf, int len)
case NAL_SPS:
if (!sps_found) {
//Dprintf("H.264: Found NAL SPS at offset %d/%d", buf - start, len);
//debug("H.264: Found NAL SPS at offset %d/%d\n", int(buf - start), len);
int nal_len = nalUnescape(nal_data, buf + 4, int(end - buf - 4));
consumed = parseSPS(nal_data, nal_len);
if (consumed > 0)
@@ -120,7 +142,7 @@ bool cFemonH264::processVideo(const uint8_t *buf, int len)
case NAL_SEI:
if (!sei_found) {
//Dprintf("H.264: Found NAL SEI at offset %d/%d", buf - start, len);
//debug("H.264: Found NAL SEI at offset %d/%d\n", iny(buf - start), len);
int nal_len = nalUnescape(nal_data, buf + 4, int(end - buf - 4));
consumed = parseSEI(nal_data, nal_len);
if (consumed > 0)
@@ -141,7 +163,7 @@ bool cFemonH264::processVideo(const uint8_t *buf, int len)
if (aud_found) {
m_VideoHandler->SetVideoCodec(VIDEO_CODEC_H264);
if (sps_found) {
//Dprintf("H.264 SPS: size %dx%d, aspect %d format %d framerate %.2f bitrate %.0f", m_Width, m_Height, m_AspectRatio, m_Format, m_FrameRate, m_BitRate);
//debug("H.264 SPS: size %dx%d, aspect %d format %d framerate %.2f bitrate %.0f\n", m_Width, m_Height, m_AspectRatio, m_Format, m_FrameRate, m_BitRate);
m_VideoHandler->SetVideoFormat(m_Format);
m_VideoHandler->SetVideoSize(m_Width, m_Height);
m_VideoHandler->SetVideoAspectRatio(m_AspectRatio);
@@ -149,7 +171,7 @@ bool cFemonH264::processVideo(const uint8_t *buf, int len)
m_VideoHandler->SetVideoBitrate(m_BitRate);
}
if (sei_found) {
//Dprintf("H.264 SEI: scan %d", m_Scan);
//debug("H.264 SEI: scan %d\n", m_Scan);
m_VideoHandler->SetVideoScan(m_Scan);
}
}
@@ -222,7 +244,7 @@ int cFemonH264::parseSPS(const uint8_t *buf, int len)
bs.skipBits(4); // reserved_zero_4bits
level_idc = bs.getU8(); // level_idc
bs.skipUeGolomb(); // seq_parameter_set_id
//Dprintf("H.264 SPS: profile_idc %d level_idc %d", profile_idc, level_idc);
//debug("H.264 SPS: profile_idc %d level_idc %d\n", profile_idc, level_idc);
switch (profile_idc) {
case 66: // baseline profile
case 77: // main profile
@@ -470,9 +492,9 @@ int cFemonH264::parseSPS(const uint8_t *buf, int len)
width = bs.getUeGolomb() + 1; // pic_width_in_mbs_minus1
height = bs.getUeGolomb() + 1; // pic_height_in_mbs_minus1
frame_mbs_only_flag = bs.getBit(); // frame_mbs_only_flag
//Dprintf("H.264 SPS: pic_width: %u mbs", width);
//Dprintf("H.264 SPS: pic_height: %u mbs", height);
//Dprintf("H.264 SPS: frame only flag: %d", frame_mbs_only_flag);
//debug("H.264 SPS: pic_width: %u mbs\n", width);
//debug("H.264 SPS: pic_height: %u mbs\n", height);
//debug("H.264 SPS: frame only flag: %d\n", frame_mbs_only_flag);
width *= 16;
height *= 16 * (frame_mbs_only_flag ? 1 : 2);
if (!frame_mbs_only_flag)
@@ -484,7 +506,7 @@ int cFemonH264::parseSPS(const uint8_t *buf, int len)
crop_right = bs.getUeGolomb(); // frame_crop_rigth_offset
crop_top = bs.getUeGolomb(); // frame_crop_top_offset
crop_bottom = bs.getUeGolomb(); // frame_crop_bottom_offset
//Dprintf("H.264 SPS: cropping %d %d %d %d", crop_left, crop_top, crop_right, crop_bottom);
//debug("H.264 SPS: cropping %d %d %d %d\n", crop_left, crop_top, crop_right, crop_bottom);
width -= 2 * (crop_left + crop_right);
if (frame_mbs_only_flag)
height -= 2 * (crop_top + crop_bottom);
@@ -494,18 +516,34 @@ int cFemonH264::parseSPS(const uint8_t *buf, int len)
// VUI parameters
if (bs.getBit()) { // vui_parameters_present_flag
if (bs.getBit()) { // aspect_ratio_info_present
uint32_t aspect_ratio_idc;
uint32_t aspect_ratio_idc, sar_width = 0, sar_height = 0;
aspect_ratio_idc = bs.getU8(); // aspect_ratio_idc
//Dprintf("H.264 SPS: aspect_ratio_idc %d", aspect_ratio_idc);
//debug("H.264 SPS: aspect_ratio_idc %d\n", aspect_ratio_idc);
if (aspect_ratio_idc == 255) { // extended sar
bs.skipBits(16); // sar_width
bs.skipBits(16); // sar_height
aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED;
//Dprintf("H.264 SPS: aspect ratio extended");
sar_width = bs.getU16(); // sar_width
sar_height = bs.getU16(); // sar_height
}
else if (aspect_ratio_idc < sizeof(s_AspectRatios) / sizeof(s_AspectRatios[0])) {
aspect_ratio = s_AspectRatios[aspect_ratio_idc];
//Dprintf("H.264 SPS: aspect ratio %d", aspect_ratio);
else if (aspect_ratio_idc < ELEMENTS(s_SAR)) {
sar_width = s_SAR[aspect_ratio_idc].w;
sar_height = s_SAR[aspect_ratio_idc].h;
}
if (sar_width && sar_height) {
int index = -1, ratio = int(100.0L * sar_width * width / sar_height / height);
for (unsigned int i = 0; i < ELEMENTS(s_DAR); ++i) {
if (s_DAR[i].ratio == ratio) {
index = i;
break;
}
}
if (index < 0) {
if (aspect_ratio_idc == 255)
aspect_ratio = VIDEO_ASPECT_RATIO_EXTENDED;
else
aspect_ratio = VIDEO_ASPECT_RATIO_INVALID;
}
else
aspect_ratio = s_DAR[index].dar;
//debug("H.264 SPS: DAR %dx%d (%d)\n", sar_width, sar_height, aspect_ratio);
}
}
if (bs.getBit()) // overscan_info_present_flag
@@ -515,7 +553,7 @@ int cFemonH264::parseSPS(const uint8_t *buf, int len)
video_format = bs.getBits(3); // video_format
if (video_format < sizeof(s_VideoFormats) / sizeof(s_VideoFormats[0])) {
format = s_VideoFormats[video_format];
//Dprintf("H.264 SPS: video format %d", format);
//debug("H.264 SPS: video format %d\n", format);
}
bs.skipBit(); // video_full_range_flag
if (bs.getBit()) { // colour_description_present_flag
@@ -651,7 +689,7 @@ int cFemonH264::parseSEI(const uint8_t *buf, int len)
break;
}
}
//Dprintf("H.264 SEI: pic struct %d scan type %d", pic_struct, scan);
//debug("H.264 SEI: pic struct %d scan type %d\n", pic_struct, scan);
for (int i = 0; i < s_SeiNumClockTsTable[pic_struct]; ++i) {
if (bs.getBit()) { // clock_timestamp_flag[i]
int full_timestamp_flag;
@@ -669,7 +707,7 @@ int cFemonH264::parseSEI(const uint8_t *buf, int len)
scan = VIDEO_SCAN_RESERVED;
break;
}
//Dprintf("H.264 SEI: scan type %d", scan);
//debug("H.264 SEI: scan type %d\n", scan);
bs.skipBit(); // nuit_field_based_flag
bs.skipBits(5); // counting_type
full_timestamp_flag = bs.getBit(); // full_timestamp_flag

View File

@@ -19,6 +19,16 @@ private:
NAL_END_SEQ = 0x0A // End of Sequence
};
typedef struct DAR {
eVideoAspectRatio dar;
int ratio;
} t_DAR;
typedef struct SAR {
int w;
int h;
} t_SAR;
cFemonVideoIf *m_VideoHandler;
uint32_t m_Width;
uint32_t m_Height;
@@ -39,7 +49,8 @@ private:
int parseSPS(const uint8_t *buf, int len);
int parseSEI(const uint8_t *buf, int len);
static const eVideoAspectRatio s_AspectRatios[];
static const t_SAR s_SAR[];
static const t_DAR s_DAR[];
static const eVideoFormat s_VideoFormats[];
static const uint8_t s_SeiNumClockTsTable[9];

View File

@@ -5,6 +5,10 @@
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <ctype.h>
#include <math.h>
#include "femoncfg.h"
@@ -24,6 +28,7 @@
#define OSDSPACING 5
#define OSDROUNDING 10
#define IS_OSDROUNDING (femonConfig.skin == eFemonSkinElchi)
#define IS_OSDRESOLUTION(r1, r2) (abs(r1 - r2) < 20)
#define OSDINFOWIN_Y(offset) (femonConfig.position ? (OSDHEIGHT - OSDINFOHEIGHT + offset) : offset)
#define OSDINFOWIN_X(col) ((col == 4) ? int(round(OSDWIDTH * 0.76)) : \
(col == 3) ? int(round(OSDWIDTH * 0.51)) : \
@@ -62,12 +67,12 @@
#define OSDDRAWSTATUSBAR(value) \
if (value > 0) { \
value = OSDBARWIDTH(value); \
m_Osd->DrawRectangle(0, OSDSTATUSWIN_Y(offset) + 3, min(OSDBARWIDTH(femonConfig.redlimit), value), OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrRed); \
if (value > OSDBARWIDTH(femonConfig.redlimit)) \
m_Osd->DrawRectangle(OSDBARWIDTH(femonConfig.redlimit), OSDSTATUSWIN_Y(offset) + 3, min((OSDWIDTH * femonConfig.greenlimit / 100), value), OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrYellow); \
if (value > OSDBARWIDTH(femonConfig.greenlimit)) \
m_Osd->DrawRectangle(OSDBARWIDTH(femonConfig.greenlimit), OSDSTATUSWIN_Y(offset) + 3, value, OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrGreen); \
int barvalue = OSDBARWIDTH(value); \
m_Osd->DrawRectangle(0, OSDSTATUSWIN_Y(offset) + 3, min(OSDBARWIDTH(femonConfig.redlimit), barvalue), OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrRed); \
if (barvalue > OSDBARWIDTH(femonConfig.redlimit)) \
m_Osd->DrawRectangle(OSDBARWIDTH(femonConfig.redlimit), OSDSTATUSWIN_Y(offset) + 3, min((OSDWIDTH * femonConfig.greenlimit / 100), barvalue), OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrYellow); \
if (barvalue > OSDBARWIDTH(femonConfig.greenlimit)) \
m_Osd->DrawRectangle(OSDBARWIDTH(femonConfig.greenlimit), OSDSTATUSWIN_Y(offset) + 3, barvalue, OSDSTATUSWIN_Y(offset) + OSDROWHEIGHT - 3, femonTheme[femonConfig.theme].clrGreen); \
}
#define OSDDRAWSTATUSTITLEBAR(title) \
@@ -145,7 +150,7 @@ cFemonOsd *cFemonOsd::pInstance = NULL;
cFemonOsd *cFemonOsd::Instance(bool create)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
if ((pInstance == NULL) && create)
{
pInstance = new cFemonOsd();
@@ -165,9 +170,14 @@ cFemonOsd::cFemonOsd()
m_Number(0),
m_OldNumber(0),
m_SNR(0),
m_SNRValid(false),
m_Signal(0),
m_SignalValid(false),
m_BER(0),
m_BERValid(false),
m_UNC(0),
m_UNCValid(false),
m_FrontendStatusValid(false),
m_DisplayMode(femonConfig.displaymode),
m_OsdWidth(cOsd::OsdWidth()),
m_OsdHeight(cOsd::OsdHeight()),
@@ -176,28 +186,30 @@ cFemonOsd::cFemonOsd()
m_Mutex()
{
int tmp;
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
memset(&m_FrontendStatus, 0, sizeof(m_FrontendStatus));
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
m_SvdrpConnection.handle = -1;
m_Font = cFont::CreateFont(Setup.FontSml, min(max(Setup.FontSmlSize, MINFONTSIZE), MAXFONTSIZE));
if (!m_Font || !m_Font->Height()) {
m_Font = new cFemonDummyFont;
esyslog("ERROR: cFemonOsd::cFemonOsd() cannot create required font.");
error("cFemonOsd::cFemonOsd() cannot create required font.");
}
tmp = 5 * bmSymbol[SYMBOL_LOCK].Width() + 6 * OSDSPACING;
if (OSDWIDTH < tmp) {
esyslog("ERROR: cFemonOsd::cFemonOsd() OSD width (%d) smaller than required (%d).", OSDWIDTH, tmp);
error("cFemonOsd::cFemonOsd() OSD width (%d) smaller than required (%d).", OSDWIDTH, tmp);
OSDWIDTH = tmp;
}
tmp = OSDINFOHEIGHT + OSDROWHEIGHT + OSDSTATUSHEIGHT;
if (OSDHEIGHT < tmp) {
esyslog("ERROR: cFemonOsd::cFemonOsd() OSD height (%d) smaller than required (%d).", OSDHEIGHT, tmp);
error("cFemonOsd::cFemonOsd() OSD height (%d) smaller than required (%d).", OSDHEIGHT, tmp);
OSDHEIGHT = tmp;
}
}
cFemonOsd::~cFemonOsd(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
m_Sleep.Signal();
if (Running())
Cancel(3);
@@ -225,8 +237,6 @@ void cFemonOsd::DrawStatusWindow(void)
{
cMutexLock lock(&m_Mutex);
cBitmap *bm = NULL;
int snr = m_SNR / 655;
int signal = m_Signal / 655;
int offset = 0;
int x = OSDWIDTH - OSDROUNDING;
int y = 0;
@@ -280,13 +290,13 @@ void cFemonOsd::DrawStatusWindow(void)
OSDDRAWSTATUSBM(OSDSPACING);
}
if (m_Receiver) {
if (abs(m_Receiver->VideoVerticalSize() - 1080) < 20)
if (IS_OSDRESOLUTION(m_Receiver->VideoVerticalSize(), 1080))
bm = &bmSymbol[SYMBOL_FORMAT_1080];
else if (abs(m_Receiver->VideoVerticalSize() - 720) < 20)
else if (IS_OSDRESOLUTION(m_Receiver->VideoVerticalSize(), 720))
bm = &bmSymbol[SYMBOL_FORMAT_720];
else if (abs(m_Receiver->VideoVerticalSize() - 576) < 20)
else if (IS_OSDRESOLUTION(m_Receiver->VideoVerticalSize(), 576))
bm = &bmSymbol[SYMBOL_FORMAT_576];
else if (abs(m_Receiver->VideoVerticalSize() - 480) < 20)
else if (IS_OSDRESOLUTION(m_Receiver->VideoVerticalSize(), 480))
bm = &bmSymbol[SYMBOL_FORMAT_480];
else
bm = NULL;
@@ -317,24 +327,30 @@ void cFemonOsd::DrawStatusWindow(void)
OSDDRAWSTATUSBM(OSDSPACING);
}
offset += OSDROWHEIGHT;
OSDDRAWSTATUSBAR(signal);
if (m_SignalValid)
OSDDRAWSTATUSBAR(m_Signal / 655);
offset += OSDROWHEIGHT;
OSDDRAWSTATUSBAR(snr);
if (m_SNRValid)
OSDDRAWSTATUSBAR(m_SNR / 655);
offset += OSDROWHEIGHT;
OSDDRAWSTATUSVALUES("STR:", *cString::sprintf("%04x", m_Signal), *cString::sprintf("(%2d%%)", m_Signal / 655), "BER:", *cString::sprintf("%08x", m_BER),
*cString::sprintf("%s:", tr("Video")), *getBitrateMbits(m_Receiver ? m_Receiver->VideoBitrate() : (m_SvdrpFrontend >= 0 ? m_SvdrpVideoBitrate : -1.0)));
OSDDRAWSTATUSVALUES("STR:", m_SignalValid ? *cString::sprintf("%04x", m_Signal) : "", m_SignalValid ? *cString::sprintf("(%2d%%)", m_Signal / 655) : "",
"BER:", m_BERValid ? *cString::sprintf("%08x", m_BER) : "", *cString::sprintf("%s:", tr("Video")),
*getBitrateMbits(m_Receiver ? m_Receiver->VideoBitrate() : (m_SvdrpFrontend >= 0 ? m_SvdrpVideoBitrate : -1.0)));
offset += OSDROWHEIGHT;
OSDDRAWSTATUSVALUES("SNR:", *cString::sprintf("%04x", m_SNR), *cString::sprintf("(%2d%%)", m_SNR / 655), "UNC:", *cString::sprintf("%08x", m_UNC),
OSDDRAWSTATUSVALUES("SNR:", m_SNRValid ? *cString::sprintf("%04x", m_SNR) : "", m_SNRValid ? *cString::sprintf("(%2d%%)", m_SNR / 655) : "",
"UNC:", m_UNCValid ? *cString::sprintf("%08x", m_UNC) : "",
*cString::sprintf("%s:", (m_Receiver && m_Receiver->AC3Valid() && IS_DOLBY_TRACK(track)) ? tr("AC-3") : tr("Audio")),
*getBitrateKbits(m_Receiver ? ((m_Receiver->AC3Valid() && IS_DOLBY_TRACK(track)) ? m_Receiver->AC3Bitrate() : m_Receiver->AudioBitrate()) : (m_SvdrpFrontend >= 0 ? m_SvdrpAudioBitrate : -1.0)));
offset += OSDROWHEIGHT;
x = bmSymbol[SYMBOL_LOCK].Width();
y = (OSDROWHEIGHT - bmSymbol[SYMBOL_LOCK].Height()) / 2;
OSDDRAWSTATUSFRONTEND(1, bmSymbol[SYMBOL_LOCK], FE_HAS_LOCK);
OSDDRAWSTATUSFRONTEND(2, bmSymbol[SYMBOL_SIGNAL], FE_HAS_SIGNAL);
OSDDRAWSTATUSFRONTEND(3, bmSymbol[SYMBOL_CARRIER], FE_HAS_CARRIER);
OSDDRAWSTATUSFRONTEND(4, bmSymbol[SYMBOL_VITERBI], FE_HAS_VITERBI);
OSDDRAWSTATUSFRONTEND(5, bmSymbol[SYMBOL_SYNC], FE_HAS_SYNC);
if (m_FrontendStatusValid) {
OSDDRAWSTATUSFRONTEND(1, bmSymbol[SYMBOL_LOCK], FE_HAS_LOCK);
OSDDRAWSTATUSFRONTEND(2, bmSymbol[SYMBOL_SIGNAL], FE_HAS_SIGNAL);
OSDDRAWSTATUSFRONTEND(3, bmSymbol[SYMBOL_CARRIER], FE_HAS_CARRIER);
OSDDRAWSTATUSFRONTEND(4, bmSymbol[SYMBOL_VITERBI], FE_HAS_VITERBI);
OSDDRAWSTATUSFRONTEND(5, bmSymbol[SYMBOL_SYNC], FE_HAS_SYNC);
}
OSDDRAWSTATUSBOTTOMBAR();
m_Osd->Flush();
}
@@ -487,7 +503,7 @@ void cFemonOsd::DrawInfoWindow(void)
void cFemonOsd::Action(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
cTimeMs t;
SvdrpCommand_v1_0 cmd;
cmd.command = cString::sprintf("PLUG %s INFO\r\n", PLUGIN_NAME_I18N);
@@ -497,11 +513,11 @@ void cFemonOsd::Action(void)
m_SvdrpVideoBitrate = -1.0;
m_SvdrpAudioBitrate = -1.0;
if (m_Frontend != -1) {
CHECK(ioctl(m_Frontend, FE_READ_STATUS, &m_FrontendStatus));
CHECK(ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &m_Signal));
CHECK(ioctl(m_Frontend, FE_READ_SNR, &m_SNR));
CHECK(ioctl(m_Frontend, FE_READ_BER, &m_BER));
CHECK(ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &m_UNC));
m_FrontendStatusValid = (ioctl(m_Frontend, FE_READ_STATUS, &m_FrontendStatus) >= 0);
m_SignalValid = (ioctl(m_Frontend, FE_READ_SIGNAL_STRENGTH, &m_Signal) >= 0);
m_SNRValid = (ioctl(m_Frontend, FE_READ_SNR, &m_SNR) >= 0);
m_BERValid = (ioctl(m_Frontend, FE_READ_BER, &m_BER) >= 0);
m_UNCValid = (ioctl(m_Frontend, FE_READ_UNCORRECTED_BLOCKS, &m_UNC) >= 0);
DrawInfoWindow();
DrawStatusWindow();
}
@@ -509,24 +525,40 @@ void cFemonOsd::Action(void)
cmd.handle = m_SvdrpConnection.handle;
m_SvdrpPlugin->Service("SvdrpCommand-v1.0", &cmd);
if (cmd.responseCode == 900) {
m_FrontendStatusValid = false;
m_SignalValid = false;
m_SNRValid = false;
m_BERValid = false;
m_UNCValid = false;
for (cLine *line = cmd.reply.First(); line; line = cmd.reply.Next(line)) {
const char *s = line->Text();
if (!strncasecmp(s, "CARD:", 5))
m_SvdrpFrontend = (int)strtol(s + 5, NULL, 10);
else if (!strncasecmp(s, "TYPE:", 5))
m_FrontendInfo.type = (fe_type_t)strtol(s + 5, NULL, 10);
else if (!strncasecmp(s, "NAME:", 5))
else if (!strncasecmp(s, "NAME:", 5)) {
strn0cpy(m_FrontendInfo.name, s + 5, sizeof(m_FrontendInfo.name));
else if (!strncasecmp(s, "STAT:", 5))
}
else if (!strncasecmp(s, "STAT:", 5)) {
m_FrontendStatus = (fe_status_t)strtol(s + 5, NULL, 16);
else if (!strncasecmp(s, "SGNL:", 5))
m_FrontendStatusValid = true;
}
else if (!strncasecmp(s, "SGNL:", 5)) {
m_Signal = (uint16_t)strtol(s + 5, NULL, 16);
else if (!strncasecmp(s, "SNRA:", 5))
m_SignalValid = true;
}
else if (!strncasecmp(s, "SNRA:", 5)) {
m_SNR = (uint16_t)strtol(s + 5, NULL, 16);
else if (!strncasecmp(s, "BERA:", 5))
m_SNRValid = true;
}
else if (!strncasecmp(s, "BERA:", 5)) {
m_BER = (uint32_t)strtol(s + 5, NULL, 16);
else if (!strncasecmp(s, "UNCB:", 5))
m_BERValid = true;
}
else if (!strncasecmp(s, "UNCB:", 5)) {
m_UNC = (uint32_t)strtol(s + 5, NULL, 16);
m_UNCValid = true;
}
else if (!strncasecmp(s, "VIBR:", 5))
m_SvdrpVideoBitrate = (double)strtol(s + 5, NULL, 10);
else if (!strncasecmp(s, "AUBR:", 5))
@@ -542,7 +574,7 @@ void cFemonOsd::Action(void)
void cFemonOsd::Show(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
int apid[2] = {0, 0};
int dpid[2] = {0, 0};
eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
@@ -550,9 +582,11 @@ void cFemonOsd::Show(void)
m_Frontend = open(dev, O_RDONLY | O_NONBLOCK);
if (m_Frontend >= 0) {
if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0) {
esyslog("ERROR: cFemonOsd::Show() cannot read frontend info.");
if (!femonConfig.usesvdrp)
error("cFemonOsd::Show() cannot read frontend info.");
close(m_Frontend);
m_Frontend = -1;
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
return;
}
}
@@ -561,7 +595,7 @@ void cFemonOsd::Show(void)
return;
}
else {
esyslog("ERROR: cFemonOsd::Show() cannot open frontend device.");
error("cFemonOsd::Show() cannot open frontend device.");
return;
}
@@ -599,7 +633,7 @@ void cFemonOsd::Show(void)
void cFemonOsd::ChannelSwitch(const cDevice * device, int channelNumber)
{
Dprintf("%s(%d,%d)\n", __PRETTY_FUNCTION__, device->DeviceNumber(), channelNumber);
debug("%s(%d,%d)\n", __PRETTY_FUNCTION__, device->DeviceNumber(), channelNumber);
int apid[2] = {0, 0};
int dpid[2] = {0, 0};
eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
@@ -610,9 +644,11 @@ void cFemonOsd::ChannelSwitch(const cDevice * device, int channelNumber)
m_Frontend = open(dev, O_RDONLY | O_NONBLOCK);
if (m_Frontend >= 0) {
if (ioctl(m_Frontend, FE_GET_INFO, &m_FrontendInfo) < 0) {
esyslog("ERROR: cFemonOsd::ChannelSwitch() cannot read frontend info.");
if (!femonConfig.usesvdrp)
error("cFemonOsd::ChannelSwitch() cannot read frontend info.");
close(m_Frontend);
m_Frontend = -1;
memset(&m_FrontendInfo, 0, sizeof(m_FrontendInfo));
return;
}
}
@@ -621,7 +657,7 @@ void cFemonOsd::ChannelSwitch(const cDevice * device, int channelNumber)
return;
}
else {
esyslog("ERROR: cFemonOsd::ChannelSwitch() cannot open frontend device.");
error("cFemonOsd::ChannelSwitch() cannot open frontend device.");
return;
}
@@ -642,7 +678,7 @@ void cFemonOsd::ChannelSwitch(const cDevice * device, int channelNumber)
void cFemonOsd::SetAudioTrack(int Index, const char * const *Tracks)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
int apid[2] = {0, 0};
int dpid[2] = {0, 0};
eTrackType track = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
@@ -663,7 +699,7 @@ void cFemonOsd::SetAudioTrack(int Index, const char * const *Tracks)
bool cFemonOsd::DeviceSwitch(int direction)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
int device = cDevice::ActualDevice()->DeviceNumber();
direction = sgn(direction);
if (device >= 0) {
@@ -679,7 +715,7 @@ bool cFemonOsd::DeviceSwitch(int direction)
device = cDevice::NumDevices() - 1;
}
if (cDevice::GetDevice(device)->ProvidesChannel(channel, 0)) {
Dprintf("%s(%d) device(%d)\n", __PRETTY_FUNCTION__, direction, device);
debug("%s(%d) device(%d)\n", __PRETTY_FUNCTION__, direction, device);
cStatus::MsgChannelSwitch(cDevice::PrimaryDevice(), 0);
cControl::Shutdown();
cDevice::GetDevice(device)->SwitchChannel(channel, true);
@@ -711,14 +747,14 @@ bool cFemonOsd::SvdrpConnect(void)
m_SvdrpPlugin->Service("SvdrpCommand-v1.0", &cmd);
if (cmd.responseCode != 214) {
m_SvdrpPlugin->Service("SvdrpConnection-v1.0", &m_SvdrpConnection); // close connection
esyslog("ERROR: cFemonOsd::SvdrpConnect() cannot find plugin '%s' on server %s.", PLUGIN_NAME_I18N, *m_SvdrpConnection.serverIp);
error("cFemonOsd::SvdrpConnect() cannot find plugin '%s' on server %s.", PLUGIN_NAME_I18N, *m_SvdrpConnection.serverIp);
}
}
else
esyslog("ERROR: cFemonOsd::SvdrpConnect() cannot connect to SVDRP server.");
error("cFemonOsd::SvdrpConnect() cannot connect to SVDRP server.");
}
else
esyslog("ERROR: cFemonOsd::SvdrpConnect() cannot find plugin '%s'.", SVDRPPLUGIN);
error("cFemonOsd::SvdrpConnect() cannot find plugin '%s'.", SVDRPPLUGIN);
}
return m_SvdrpConnection.handle >= 0;
}
@@ -734,19 +770,19 @@ bool cFemonOsd::SvdrpTune(void)
m_SvdrpPlugin->Service("SvdrpCommand-v1.0", &cmd);
if (cmd.responseCode == 250)
return true;
esyslog("ERROR: cFemonOsd::SvdrpTune() cannot tune server channel.");
error("cFemonOsd::SvdrpTune() cannot tune server channel.");
}
else
esyslog("ERROR: cFemonOsd::SvdrpTune() invalid channel.");
error("cFemonOsd::SvdrpTune() invalid channel.");
}
else
esyslog("ERROR: cFemonOsd::SvdrpTune() unexpected connection state.");
error("cFemonOsd::SvdrpTune() unexpected connection state.");
return false;
}
double cFemonOsd::GetVideoBitrate(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
double value = 0.0;
if (m_Receiver)
@@ -757,7 +793,7 @@ double cFemonOsd::GetVideoBitrate(void)
double cFemonOsd::GetAudioBitrate(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
double value = 0.0;
if (m_Receiver)
@@ -768,7 +804,7 @@ double cFemonOsd::GetAudioBitrate(void)
double cFemonOsd::GetDolbyBitrate(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
double value = 0.0;
if (m_Receiver)

View File

@@ -34,14 +34,19 @@ private:
double m_SvdrpAudioBitrate;
SvdrpConnection_v1_0 m_SvdrpConnection;
cPlugin *m_SvdrpPlugin;
dvb_frontend_info m_FrontendInfo;
int m_Number;
int m_OldNumber;
uint16_t m_SNR;
bool m_SNRValid;
uint16_t m_Signal;
bool m_SignalValid;
uint32_t m_BER;
bool m_BERValid;
uint32_t m_UNC;
bool m_UNCValid;
fe_status_t m_FrontendStatus;
bool m_FrontendStatusValid;
dvb_frontend_info m_FrontendInfo;
int m_DisplayMode;
int m_OsdWidth;
int m_OsdHeight;

View File

@@ -20,21 +20,28 @@ cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vtype, int Vpid
m_DetectMPEG(this, this),
m_DetectAAC(this),
m_DetectAC3(this),
m_VideoBuffer(KILOBYTE(512), TS_SIZE, false, "Femon video"),
m_VideoType(Vtype),
m_VideoPid(Vpid),
m_VideoPacketCount(0),
m_VideoBitrate(0.0),
m_VideoValid(false),
m_AudioBuffer(KILOBYTE(256), TS_SIZE, false, "Femon audio"),
m_AudioPid(Apid[0]),
m_AudioPacketCount(0),
m_AudioBitrate(0.0),
m_AudioValid(false),
m_AC3Buffer(KILOBYTE(256), TS_SIZE, false, "Femon AC3"),
m_AC3Pid(Dpid[0]),
m_AC3PacketCount(0),
m_AC3Bitrate(0),
m_AC3Valid(false)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
m_VideoBuffer.SetTimeouts(0, 100);
m_AudioBuffer.SetTimeouts(0, 100);
m_AC3Buffer.SetTimeouts(0, 100);
m_VideoInfo.codec = VIDEO_CODEC_INVALID;
m_VideoInfo.format = VIDEO_FORMAT_INVALID;
@@ -61,13 +68,13 @@ cFemonReceiver::cFemonReceiver(tChannelID ChannelID, int Ca, int Vtype, int Vpid
cFemonReceiver::~cFemonReceiver(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
Deactivate();
}
void cFemonReceiver::Deactivate(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
debug("%s()\n", __PRETTY_FUNCTION__);
if (m_Active) {
m_Active = false;
m_Sleep.Signal();
@@ -79,7 +86,7 @@ void cFemonReceiver::Deactivate(void)
void cFemonReceiver::Activate(bool On)
{
Dprintf("%s(%d)\n", __PRETTY_FUNCTION__, On);
debug("%s(%d)\n", __PRETTY_FUNCTION__, On);
if (On)
Start();
else
@@ -89,64 +96,149 @@ void cFemonReceiver::Activate(bool On)
void cFemonReceiver::Receive(uchar *Data, int Length)
{
// TS packet length: TS_SIZE
if (Length == TS_SIZE) {
if (Running() && (*Data == TS_SYNC_BYTE) && (Length == TS_SIZE)) {
int len, pid = TsPid(Data);
if (pid == m_VideoPid) {
m_VideoPacketCount++;
if (TsPayloadStart(Data)) {
while (const uint8_t *p = m_VideoAssembler.GetPes(len)) {
if (m_VideoType == 0x1B) { // MPEG4
if (m_DetectH264.processVideo(p, len)) {
m_VideoValid = true;
break;
}
}
else {
if (m_DetectMPEG.processVideo(p, len)) {
m_VideoValid = true;
break;
}
}
}
m_VideoAssembler.Reset();
}
m_VideoAssembler.PutTs(Data, Length);
++m_VideoPacketCount;
len = m_VideoBuffer.Put(Data, Length);
if (len != Length) {
m_VideoBuffer.ReportOverflow(Length - len);
m_VideoBuffer.Clear();
}
}
else if (pid == m_AudioPid) {
m_AudioPacketCount++;
if (const uint8_t *p = m_AudioAssembler.GetPes(len)) {
if (m_DetectAAC.processAudio(p, len) || m_DetectMPEG.processAudio(p, len))
m_AudioValid = true;
m_AudioAssembler.Reset();
++m_AudioPacketCount;
len = m_AudioBuffer.Put(Data, Length);
if (len != Length) {
m_AudioBuffer.ReportOverflow(Length - len);
m_AudioBuffer.Clear();
}
m_AudioAssembler.PutTs(Data, Length);
}
else if (pid == m_AC3Pid) {
m_AC3PacketCount++;
if (const uint8_t *p = m_AC3Assembler.GetPes(len)) {
if (m_DetectAC3.processAudio(p, len))
m_AC3Valid = true;
m_AC3Assembler.Reset();
++m_AC3PacketCount;
len = m_AC3Buffer.Put(Data, Length);
if (len != Length) {
m_AC3Buffer.ReportOverflow(Length - len);
m_AC3Buffer.Clear();
}
m_AC3Assembler.PutTs(Data, Length);
}
}
}
void cFemonReceiver::Action(void)
{
Dprintf("%s()\n", __PRETTY_FUNCTION__);
cTimeMs t;
debug("%s()\n", __PRETTY_FUNCTION__);
cTimeMs calcPeriod(0);
m_Active = true;
while (Running() && m_Active) {
t.Set(0);
// TS packet 188 bytes - 4 byte header; MPEG standard defines 1Mbit = 1000000bit
m_VideoBitrate = (10.0 * 8.0 * 184.0 * m_VideoPacketCount) / femonConfig.calcinterval;
m_VideoPacketCount = 0;
m_AudioBitrate = (10.0 * 8.0 * 184.0 * m_AudioPacketCount) / femonConfig.calcinterval;
m_AudioPacketCount = 0;
m_AC3Bitrate = (10.0 * 8.0 * 184.0 * m_AC3PacketCount) / femonConfig.calcinterval;
m_AC3PacketCount = 0;
m_Sleep.Wait(max((int)(100 * femonConfig.calcinterval - t.Elapsed()), 3));
uint8_t *Data;
double timeout;
int len, Length;
bool processed = false;
// process available video data
while (Data = m_VideoBuffer.Get(Length)) {
if (!m_Active || (Length < TS_SIZE))
break;
Length = TS_SIZE;
if (*Data != TS_SYNC_BYTE) {
for (int i = 1; i < Length; ++i) {
if (Data[i] == TS_SYNC_BYTE) {
Length = i;
break;
}
}
m_VideoBuffer.Del(Length);
continue;
}
processed = true;
if (TsPayloadStart(Data)) {
while (const uint8_t *p = m_VideoAssembler.GetPes(len)) {
if (m_VideoType == 0x1B) { // MPEG4
if (m_DetectH264.processVideo(p, len)) {
m_VideoValid = true;
break;
}
}
else {
if (m_DetectMPEG.processVideo(p, len)) {
m_VideoValid = true;
break;
}
}
}
m_VideoAssembler.Reset();
}
m_VideoAssembler.PutTs(Data, Length);
m_VideoBuffer.Del(Length);
}
// process available audio data
while (Data = m_AudioBuffer.Get(Length)) {
if (!m_Active || (Length < TS_SIZE))
break;
Length = TS_SIZE;
if (*Data != TS_SYNC_BYTE) {
for (int i = 1; i < Length; ++i) {
if (Data[i] == TS_SYNC_BYTE) {
Length = i;
break;
}
}
m_AudioBuffer.Del(Length);
continue;
}
processed = true;
if (const uint8_t *p = m_AudioAssembler.GetPes(len)) {
if (m_DetectAAC.processAudio(p, len) || m_DetectMPEG.processAudio(p, len))
m_AudioValid = true;
m_AudioAssembler.Reset();
}
m_AudioAssembler.PutTs(Data, Length);
m_AudioBuffer.Del(Length);
}
// process available dolby data
while (Data = m_AC3Buffer.Get(Length)) {
if (!m_Active || (Length < TS_SIZE))
break;
Length = TS_SIZE;
if (*Data != TS_SYNC_BYTE) {
for (int i = 1; i < Length; ++i) {
if (Data[i] == TS_SYNC_BYTE) {
Length = i;
break;
}
}
m_AC3Buffer.Del(Length);
continue;
}
processed = true;
if (const uint8_t *p = m_AC3Assembler.GetPes(len)) {
if (m_DetectAC3.processAudio(p, len))
m_AC3Valid = true;
m_AC3Assembler.Reset();
}
m_AC3Assembler.PutTs(Data, Length);
m_AC3Buffer.Del(Length);
}
// calculate bitrates
timeout = double(calcPeriod.Elapsed());
if (m_Active && (timeout >= (100.0 * femonConfig.calcinterval))) {
// TS packet 188 bytes - 4 byte header; MPEG standard defines 1Mbit = 1000000bit
// PES headers should be compensated!
m_VideoBitrate = (1000.0 * 8.0 * 184.0 * m_VideoPacketCount) / timeout;
m_VideoPacketCount = 0;
m_AudioBitrate = (1000.0 * 8.0 * 184.0 * m_AudioPacketCount) / timeout;
m_AudioPacketCount = 0;
m_AC3Bitrate = (1000.0 * 8.0 * 184.0 * m_AC3PacketCount) / timeout;
m_AC3PacketCount = 0;
calcPeriod.Set(0);
}
if (!processed)
m_Sleep.Wait(10); // to avoid busy loop and reduce cpu load
}
}

View File

@@ -21,36 +21,39 @@
class cFemonReceiver : public cReceiver, public cThread, public cFemonVideoIf, public cFemonAudioIf, public cFemonAC3If {
private:
cMutex m_Mutex;
cCondWait m_Sleep;
bool m_Active;
cMutex m_Mutex;
cCondWait m_Sleep;
bool m_Active;
cFemonH264 m_DetectH264;
cFemonMPEG m_DetectMPEG;
cFemonAAC m_DetectAAC;
cFemonAC3 m_DetectAC3;
cFemonH264 m_DetectH264;
cFemonMPEG m_DetectMPEG;
cFemonAAC m_DetectAAC;
cFemonAC3 m_DetectAC3;
cTsToPes m_VideoAssembler;
int m_VideoType;
int m_VideoPid;
int m_VideoPacketCount;
double m_VideoBitrate;
bool m_VideoValid;
video_info_t m_VideoInfo;
cRingBufferLinear m_VideoBuffer;
cTsToPes m_VideoAssembler;
int m_VideoType;
int m_VideoPid;
int m_VideoPacketCount;
double m_VideoBitrate;
bool m_VideoValid;
video_info_t m_VideoInfo;
cTsToPes m_AudioAssembler;
int m_AudioPid;
int m_AudioPacketCount;
double m_AudioBitrate;
bool m_AudioValid;
audio_info_t m_AudioInfo;
cRingBufferLinear m_AudioBuffer;
cTsToPes m_AudioAssembler;
int m_AudioPid;
int m_AudioPacketCount;
double m_AudioBitrate;
bool m_AudioValid;
audio_info_t m_AudioInfo;
cTsToPes m_AC3Assembler;
int m_AC3Pid;
int m_AC3PacketCount;
double m_AC3Bitrate;
bool m_AC3Valid;
ac3_info_t m_AC3Info;
cRingBufferLinear m_AC3Buffer;
cTsToPes m_AC3Assembler;
int m_AC3Pid;
int m_AC3PacketCount;
double m_AC3Bitrate;
bool m_AC3Valid;
ac3_info_t m_AC3Info;
protected:
virtual void Activate(bool On);

View File

@@ -5,6 +5,11 @@
*
*/
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
@@ -86,15 +91,22 @@ cString getFrontendInfo(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return NULL;
CHECK(ioctl(fe, FE_GET_INFO, &value));
CHECK(ioctl(fe, FE_READ_STATUS, &status));
CHECK(ioctl(fe, FE_READ_SIGNAL_STRENGTH, &signal));
CHECK(ioctl(fe, FE_READ_SNR, &snr));
CHECK(ioctl(fe, FE_READ_BER, &ber));
CHECK(ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &unc));
close(fe);
info = cString::sprintf("CARD:%d\nTYPE:%d\nNAME:%s\nSTAT:%02X\nSGNL:%04X\nSNRA:%04X\nBERA:%08X\nUNCB:%08X", cardIndex, value.type, value.name, status, signal, snr, ber, unc);
info = cString::sprintf("CARD:%d", cardIndex);
if (ioctl(fe, FE_GET_INFO, &value) >= 0)
info = cString::sprintf("%s\nTYPE:%d\nNAME:%s", *info, value.type, value.name);
if (ioctl(fe, FE_READ_STATUS, &status) >= 0)
info = cString::sprintf("%s\nSTAT:%02X", *info, status);
if (ioctl(fe, FE_READ_SIGNAL_STRENGTH, &signal) >= 0)
info = cString::sprintf("%s\nSGNL:%04X", *info, signal);
if (ioctl(fe, FE_READ_SNR, &snr) >= 0)
info = cString::sprintf("%s\nSNRA:%04X", *info, snr);
if (ioctl(fe, FE_READ_BER, &ber) >= 0)
info = cString::sprintf("%s\nBERA:%08X", *info, ber);
if (ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &unc) >= 0)
info = cString::sprintf("%s\nUNCB:%08X", *info, unc);
close(fe);
if (cFemonOsd::Instance())
info = cString::sprintf("%s\nVIBR:%.0f\nAUBR:%.0f\nDDBR:%.0f", *info, cFemonOsd::Instance()->GetVideoBitrate(), cFemonOsd::Instance()->GetAudioBitrate(), cFemonOsd::Instance()->GetDolbyBitrate());
@@ -113,7 +125,8 @@ cString getFrontendName(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return NULL;
CHECK(ioctl(fe, FE_GET_INFO, &value));
memset(&value, 0, sizeof(value));
ioctl(fe, FE_GET_INFO, &value);
close(fe);
return (cString::sprintf("%s on device #%d", value.name, cardIndex));
@@ -127,7 +140,8 @@ cString getFrontendStatus(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return NULL;
CHECK(ioctl(fe, FE_READ_STATUS, &value));
memset(&value, 0, sizeof(value));
ioctl(fe, FE_READ_STATUS, &value);
close(fe);
return (cString::sprintf("Status %s:%s:%s:%s:%s on device #%d", (value & FE_HAS_LOCK) ? "LOCKED" : "-", (value & FE_HAS_SIGNAL) ? "SIGNAL" : "-", (value & FE_HAS_CARRIER) ? "CARRIER" : "-", (value & FE_HAS_VITERBI) ? "VITERBI" : "-", (value & FE_HAS_SYNC) ? "SYNC" : "-", cardIndex));
@@ -141,7 +155,7 @@ uint16_t getSignal(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return (value);
CHECK(ioctl(fe, FE_READ_SIGNAL_STRENGTH, &value));
ioctl(fe, FE_READ_SIGNAL_STRENGTH, &value);
close(fe);
return (value);
@@ -155,7 +169,7 @@ uint16_t getSNR(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return (value);
CHECK(ioctl(fe, FE_READ_SNR, &value));
ioctl(fe, FE_READ_SNR, &value);
close(fe);
return (value);
@@ -169,7 +183,7 @@ uint32_t getBER(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return (value);
CHECK(ioctl(fe, FE_READ_BER, &value));
ioctl(fe, FE_READ_BER, &value);
close(fe);
return (value);
@@ -183,7 +197,7 @@ uint32_t getUNC(int cardIndex)
int fe = open(dev, O_RDONLY | O_NONBLOCK);
if (fe < 0)
return (value);
CHECK(ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &value));
ioctl(fe, FE_READ_UNCORRECTED_BLOCKS, &value);
close(fe);
return (value);

View File

@@ -14,9 +14,11 @@
#include <vdr/tools.h>
#ifdef DEBUG
#define Dprintf(x...) printf(x);
#define debug(x...) dsyslog("FEMON: " x);
#define error(x...) esyslog("ERROR: " x);
#else
#define Dprintf(x...) ;
#define debug(x...) ;
#define error(x...) esyslog("ERROR: " x);
#endif
#define ELEMENTS(x) (sizeof(x) / sizeof(x[0]))

373
po/zh_CN.po Normal file
View File

@@ -0,0 +1,373 @@
# VDR plugin language source file.
# Copyright (C) 2007 Rolf Ahrenberg
# This file is distributed under the same license as the femon package.
# Nan Feng VDR <nfgx@21cn.com>, 2009.2
#
msgid ""
msgstr ""
"Project-Id-Version: femon 1.7.3\n"
"Report-Msgid-Bugs-To: Rolf Ahrenberg\n"
"POT-Creation-Date: 2009-08-29 20:57+0300\n"
"PO-Revision-Date: 2009-09-21 21:36+0800\n"
"Last-Translator: NanFeng <nfgx@21cn.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "DVB Signal Information Monitor (OSD)"
msgstr "卫星信号信息显示(OSD)"
msgid "Signal Information"
msgstr "频道信息浏览"
msgid "Femon not available"
msgstr "Femon插件无法使用"
msgid "basic"
msgstr "基本"
msgid "transponder"
msgstr "转发器"
msgid "stream"
msgstr "数据流"
msgid "AC-3"
msgstr "AC-3"
msgid "Classic"
msgstr "经典"
msgid "Elchi"
msgstr "Elchi"
msgid "ST:TNG"
msgstr "ST:TNG"
msgid "DeepBlue"
msgstr "DeepBlue"
msgid "Moronimo"
msgstr "Moronimo"
msgid "Enigma"
msgstr "Enigma"
msgid "EgalsTry"
msgstr "EgalsTry"
msgid "Duotone"
msgstr "双色调"
msgid "SilverGreen"
msgstr "银绿"
msgid "Hide main menu entry"
msgstr "隐藏主菜单条目."
msgid "Define whether the main menu entry is hidden."
msgstr "确定是否进入主菜单是隐藏的."
msgid "Default display mode"
msgstr "默认启动时显示模式."
msgid "Define the default display mode at startup."
msgstr "定义默认启动时显示模式."
msgid "Define the used OSD skin."
msgstr "确定使用的菜单皮肤."
msgid "Define the used OSD theme."
msgstr "确定使用的菜单主题."
msgid "Position"
msgstr "位置"
msgid "Define the position of OSD."
msgstr "确定菜单的位置."
msgid "Red limit [%]"
msgstr "红限制[ ]"
msgid "Define a limit for red bar, which is used to indicate a bad signal."
msgstr "定义一个限制红键,这是用来表示一个坏的信号."
msgid "Green limit [%]"
msgstr "绿限制[ ]"
msgid "Define a limit for green bar, which is used to indicate a good signal."
msgstr "定义一个限制的绿色键,这是用来表示一个良好的信号."
msgid "OSD update interval [0.1s]"
msgstr "菜单更新间隔时间 [0.1s]"
msgid "Define an interval for OSD updates. The smaller interval generates higher CPU load."
msgstr "确定一个OSD的更新时间间隔.较小的间隔产生较高的CPU负载."
msgid "Analyze stream"
msgstr "分析流"
msgid "Define whether the DVB stream is analyzed and bitrates calculated."
msgstr "确定是否DVB流分析和比特率计算."
msgid "Calculation interval [0.1s]"
msgstr "计算时间间隔 [0.1s]"
msgid "Define an interval for calculation. The bigger interval generates more stable values."
msgstr "定义一个时间间隔来计算.更大的时间价格产生将更稳定."
msgid "Use SVDRP service"
msgstr "使用SVDRP服务"
msgid "Define whether the SVDRP service is used in client/server setups."
msgstr "定义是否SVDRP服务是用来在客户机/服务器的设置."
msgid "SVDRP service port"
msgstr "SVDRP服务端口"
msgid "Define the port number of SVDRP service."
msgstr "定义SVDRP服务的端口号."
msgid "SVDRP service IP"
msgstr "SVDRP服务的IP地址"
msgid "Define the IP address of SVDRP service."
msgstr "定义SVDRP服务的IP地址."
msgid "Help"
msgstr "帮助"
msgid "Video"
msgstr "视频"
msgid "Audio"
msgstr "音频"
msgid "Transponder Information"
msgstr "转发器信息"
msgid "Apid"
msgstr "Apid"
msgid "Dpid"
msgstr "Dpid"
msgid "Spid"
msgstr "Spid"
msgid "Nid"
msgstr "Nid"
msgid "Tid"
msgstr "Tid"
msgid "Rid"
msgstr "Rid"
msgid "Coderate"
msgstr "码速率"
msgid "Stream Information"
msgstr "流信息"
msgid "Video Stream"
msgstr "视频流"
msgid "Codec"
msgstr "解码模式"
msgid "Bitrate"
msgstr "比特率"
msgid "Aspect Ratio"
msgstr "纵横比"
msgid "Frame Rate"
msgstr "帧速率"
msgid "Video Format"
msgstr "视频制式"
msgid "Resolution"
msgstr "分辨率"
msgid "Audio Stream"
msgstr "音频流"
msgid "Channel Mode"
msgstr "声道模式"
msgid "Sampling Frequency"
msgstr "采样频率"
msgid "AC-3 Stream"
msgstr "AC-3流"
msgid "Bit Stream Mode"
msgstr "比特流模式"
msgid "Audio Coding Mode"
msgstr "音频编码模式"
msgid "Center Mix Level"
msgstr "中心混合级别"
msgid "Surround Mix Level"
msgstr "环绕混合级别"
msgid "Dolby Surround Mode"
msgstr "杜比环绕声模式"
msgid "Low Frequency Effects"
msgstr "低频效果"
msgid "Dialogue Normalization"
msgstr "对话正常化"
msgid "Fixed"
msgstr "固定"
msgid "Analog"
msgstr "模拟"
msgid "MPEG-2"
msgstr "MPEG-2"
msgid "H.264"
msgstr "H.264"
msgid "MPEG-1 Layer I"
msgstr "MPEG-1 Layer I"
msgid "MPEG-1 Layer II"
msgstr "MPEG-1 Layer II"
msgid "MPEG-1 Layer III"
msgstr "MPEG-1 Layer III"
msgid "MPEG-2 Layer I"
msgstr "MPEG-2 Layer I"
msgid "MPEG-2 Layer II"
msgstr "MPEG-2 Layer II"
msgid "MPEG-2 Layer III"
msgstr "MPEG-2 Layer III"
msgid "HE-AAC"
msgstr "HE-AAC"
msgid "stereo"
msgstr "立体声"
msgid "joint Stereo"
msgstr "联合立体声"
msgid "dual"
msgstr "双重"
msgid "mono"
msgstr "单声道"
msgid "interlaced"
msgstr "隔行扫描"
msgid "progressive"
msgstr "进步"
msgid "reserved"
msgstr "保留"
msgid "extended"
msgstr "扩展"
msgid "unknown"
msgstr "未知"
msgid "component"
msgstr "组件"
msgid "PAL"
msgstr "PAL"
msgid "NTSC"
msgstr "NTSC"
msgid "SECAM"
msgstr "SECAM"
msgid "MAC"
msgstr "MAC"
msgid "Hz"
msgstr "赫兹"
msgid "Complete Main (CM)"
msgstr "完成主要 (CM)"
msgid "Music and Effects (ME)"
msgstr "音乐和效果 (ME)"
msgid "Visually Impaired (VI)"
msgstr "视障 (VI)"
msgid "Hearing Impaired (HI)"
msgstr "听障 (HI)"
msgid "Dialogue (D)"
msgstr "对话 (D)"
msgid "Commentary (C)"
msgstr "评论 (C)"
msgid "Emergency (E)"
msgstr "紧急 (E)"
msgid "Voice Over (VO)"
msgstr "旁白 (VO)"
msgid "Karaoke"
msgstr "卡拉OK"
msgid "Ch1"
msgstr "Ch1"
msgid "Ch2"
msgstr "Ch2"
msgid "C"
msgstr "C"
msgid "L"
msgstr "L"
msgid "R"
msgstr "R"
msgid "S"
msgstr "S"
msgid "SL"
msgstr "SL"
msgid "SR"
msgstr "SR"
msgid "dB"
msgstr "dB"
msgid "not indicated"
msgstr "没有说明"
msgid "MHz"
msgstr "兆赫兹"
msgid "free"
msgstr "免费"
msgid "Mbit/s"
msgstr "兆位/秒"
msgid "kbit/s"
msgstr "千字节/秒"

373
po/zh_TW.po Normal file
View File

@@ -0,0 +1,373 @@
# VDR plugin language source file.
# Copyright (C) 2007 Rolf Ahrenberg
# This file is distributed under the same license as the femon package.
# Nan Feng VDR <nfgx@21cn.com>, 2009.2
#
msgid ""
msgstr ""
"Project-Id-Version: femon 1.7.3\n"
"Report-Msgid-Bugs-To: Rolf Ahrenberg\n"
"POT-Creation-Date: 2009-08-29 20:57+0300\n"
"PO-Revision-Date: 2009-09-21 21:36+0800\n"
"Last-Translator: NanFeng <nfgx@21cn.com>\n"
"Language-Team: <vdr@linuxtv.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "DVB Signal Information Monitor (OSD)"
msgstr "衛星信號信息顯示(OSD)"
msgid "Signal Information"
msgstr "頻道信息瀏覽"
msgid "Femon not available"
msgstr "Femon插件無法使用"
msgid "basic"
msgstr "基本"
msgid "transponder"
msgstr "轉發器"
msgid "stream"
msgstr "數據流"
msgid "AC-3"
msgstr "AC-3"
msgid "Classic"
msgstr "經典"
msgid "Elchi"
msgstr "Elchi"
msgid "ST:TNG"
msgstr "ST:TNG"
msgid "DeepBlue"
msgstr "DeepBlue"
msgid "Moronimo"
msgstr "Moronimo"
msgid "Enigma"
msgstr "Enigma"
msgid "EgalsTry"
msgstr "EgalsTry"
msgid "Duotone"
msgstr "雙色調"
msgid "SilverGreen"
msgstr "銀綠"
msgid "Hide main menu entry"
msgstr "隱藏主菜單條目."
msgid "Define whether the main menu entry is hidden."
msgstr "確定是否進入主菜單是隱藏的."
msgid "Default display mode"
msgstr "默認啟動時顯示模式."
msgid "Define the default display mode at startup."
msgstr "定義默認啟動時顯示模式."
msgid "Define the used OSD skin."
msgstr "確定使用的菜單皮膚."
msgid "Define the used OSD theme."
msgstr "確定使用的菜單主題."
msgid "Position"
msgstr "位置"
msgid "Define the position of OSD."
msgstr "確定菜單的位置."
msgid "Red limit [%]"
msgstr "紅限制[ ]"
msgid "Define a limit for red bar, which is used to indicate a bad signal."
msgstr "定義一個限制紅鍵,這是用來表示一個壞的信號."
msgid "Green limit [%]"
msgstr "綠限制[ ]"
msgid "Define a limit for green bar, which is used to indicate a good signal."
msgstr "定義一個限制的綠色鍵,這是用來表示一個良好的信號."
msgid "OSD update interval [0.1s]"
msgstr "菜單更新間隔時間[0.1s]"
msgid "Define an interval for OSD updates. The smaller interval generates higher CPU load."
msgstr "確定一個OSD的更新時間間隔.較小的間隔產生較高的CPU負載."
msgid "Analyze stream"
msgstr "分析流"
msgid "Define whether the DVB stream is analyzed and bitrates calculated."
msgstr "確定是否DVB流分析和比特率計算."
msgid "Calculation interval [0.1s]"
msgstr "計算時間間隔[0.1s]"
msgid "Define an interval for calculation. The bigger interval generates more stable values."
msgstr "定義一個時間間隔來計算.更大的時間價格產生將更穩定."
msgid "Use SVDRP service"
msgstr "使用SVDRP服務"
msgid "Define whether the SVDRP service is used in client/server setups."
msgstr "定義是否SVDRP服務是用來在客戶機/服務器的設置."
msgid "SVDRP service port"
msgstr "SVDRP服務端口"
msgid "Define the port number of SVDRP service."
msgstr "定義SVDRP服務的端口號."
msgid "SVDRP service IP"
msgstr "SVDRP服務的IP地址"
msgid "Define the IP address of SVDRP service."
msgstr "定義SVDRP服務的IP地址."
msgid "Help"
msgstr "幫助"
msgid "Video"
msgstr "視頻"
msgid "Audio"
msgstr "音頻"
msgid "Transponder Information"
msgstr "轉發器信息"
msgid "Apid"
msgstr "Apid"
msgid "Dpid"
msgstr "Dpid"
msgid "Spid"
msgstr "Spid"
msgid "Nid"
msgstr "Nid"
msgid "Tid"
msgstr "Tid"
msgid "Rid"
msgstr "Rid"
msgid "Coderate"
msgstr "碼速率"
msgid "Stream Information"
msgstr "流信息"
msgid "Video Stream"
msgstr "視頻流"
msgid "Codec"
msgstr "解碼模式"
msgid "Bitrate"
msgstr "比特率"
msgid "Aspect Ratio"
msgstr "縱橫比"
msgid "Frame Rate"
msgstr "幀速率"
msgid "Video Format"
msgstr "視頻制式"
msgid "Resolution"
msgstr "分辨率"
msgid "Audio Stream"
msgstr "音頻流"
msgid "Channel Mode"
msgstr "聲道模式"
msgid "Sampling Frequency"
msgstr "採樣頻率"
msgid "AC-3 Stream"
msgstr "AC-3流"
msgid "Bit Stream Mode"
msgstr "比特流模式"
msgid "Audio Coding Mode"
msgstr "音頻編碼模式"
msgid "Center Mix Level"
msgstr "中心混合級別"
msgid "Surround Mix Level"
msgstr "環繞混合級別"
msgid "Dolby Surround Mode"
msgstr "杜比環繞聲模式"
msgid "Low Frequency Effects"
msgstr "低頻效果"
msgid "Dialogue Normalization"
msgstr "對話正常化"
msgid "Fixed"
msgstr "固定"
msgid "Analog"
msgstr "模擬"
msgid "MPEG-2"
msgstr "MPEG-2"
msgid "H.264"
msgstr "H.264"
msgid "MPEG-1 Layer I"
msgstr "MPEG-1 Layer I"
msgid "MPEG-1 Layer II"
msgstr "MPEG-1 Layer II"
msgid "MPEG-1 Layer III"
msgstr "MPEG-1 Layer III"
msgid "MPEG-2 Layer I"
msgstr "MPEG-2 Layer I"
msgid "MPEG-2 Layer II"
msgstr "MPEG-2 Layer II"
msgid "MPEG-2 Layer III"
msgstr "MPEG-2 Layer III"
msgid "HE-AAC"
msgstr "HE-AAC"
msgid "stereo"
msgstr "立體聲"
msgid "joint Stereo"
msgstr "聯合立體聲"
msgid "dual"
msgstr "雙重"
msgid "mono"
msgstr "單聲道"
msgid "interlaced"
msgstr "隔行掃描"
msgid "progressive"
msgstr "進步"
msgid "reserved"
msgstr "保留"
msgid "extended"
msgstr "擴展"
msgid "unknown"
msgstr "未知"
msgid "component"
msgstr "組件"
msgid "PAL"
msgstr "PAL"
msgid "NTSC"
msgstr "NTSC"
msgid "SECAM"
msgstr "SECAM"
msgid "MAC"
msgstr "MAC"
msgid "Hz"
msgstr "赫茲"
msgid "Complete Main (CM)"
msgstr "完成主要(CM)"
msgid "Music and Effects (ME)"
msgstr "音樂和效果(ME)"
msgid "Visually Impaired (VI)"
msgstr "視障(VI)"
msgid "Hearing Impaired (HI)"
msgstr "聽障(HI)"
msgid "Dialogue (D)"
msgstr "對話(D)"
msgid "Commentary (C)"
msgstr "評論(C)"
msgid "Emergency (E)"
msgstr "緊急(E)"
msgid "Voice Over (VO)"
msgstr "旁白(VO)"
msgid "Karaoke"
msgstr "卡拉OK"
msgid "Ch1"
msgstr "Ch1"
msgid "Ch2"
msgstr "Ch2"
msgid "C"
msgstr "C"
msgid "L"
msgstr "L"
msgid "R"
msgstr "R"
msgid "S"
msgstr "S"
msgid "SL"
msgstr "SL"
msgid "SR"
msgstr "SR"
msgid "dB"
msgstr "dB"
msgid "not indicated"
msgstr "沒有說明"
msgid "MHz"
msgstr "兆赫茲"
msgid "free"
msgstr "免費"
msgid "Mbit/s"
msgstr "兆位/秒"
msgid "kbit/s"
msgstr "千字節/秒"