mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Version 1.1.26
- Removed signal handling and usleep(5000) from cDvbOsd::Cmd() (apparently this is no longer necessary with DVB driver 1.0.0pre2 or later). - If the primary device (as defined in setup.conf) doesn't have an MPEG decoder (and thus can't be used as a primary device) VDR now scans all devices at startup and uses the first one (if any) that actually has an MPEG decoder. That way this will also work automatically if the primary device is implemented by a plugin. - Fixed a possible deadlock when using the "Blue" button in the "Schedules" menu to switch to an other channel (thanks to Torsten Herz). - Fixed the EPG bugfix code number for the MAX_USEFUL_SUBTITLE_LENGTH fix (thanks to Torsten Herz for reporting this one). - Modified the EPG scanner to avoid CPU load peaks (thanks to Steffen Becker for reporting this one). - Fixed support for Viaccess CAMs (thanks to Axel Gruber for helping to debug this).
This commit is contained in:
parent
b6e4637356
commit
b60eda5a8e
@ -87,6 +87,7 @@ Peter Hofmann <software@pxh.de>
|
|||||||
|
|
||||||
Axel Gruber <axel@agm.de>
|
Axel Gruber <axel@agm.de>
|
||||||
for his support in keeping the Premiere World channels up to date in 'channels.conf'
|
for his support in keeping the Premiere World channels up to date in 'channels.conf'
|
||||||
|
for helping to debug support for Viaccess CAMs
|
||||||
|
|
||||||
Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
||||||
for translating OSD texts to the Dutch language
|
for translating OSD texts to the Dutch language
|
||||||
@ -544,3 +545,11 @@ Jan Ekholm <chakie@infa.abo.fi>
|
|||||||
Marcel Wiesweg <marcel.wiesweg@gmx.de>
|
Marcel Wiesweg <marcel.wiesweg@gmx.de>
|
||||||
for pointing out a problem with high CPU load during replay
|
for pointing out a problem with high CPU load during replay
|
||||||
for reporting broken support for raw OSDs of plugins
|
for reporting broken support for raw OSDs of plugins
|
||||||
|
|
||||||
|
Torsten Herz <torsten.herz@web.de>
|
||||||
|
for fixing a possible deadlock when using the "Blue" button in the "Schedules" menu
|
||||||
|
to switch to an other channel
|
||||||
|
for reporting a wrong EPG bugfix code number for the MAX_USEFUL_SUBTITLE_LENGTH fix
|
||||||
|
|
||||||
|
Steffen Becker <stbecker@rbg.informatik.tu-darmstadt.de>
|
||||||
|
for reporting a problem with CPU load peaks (in the EPG scanner)
|
||||||
|
19
HISTORY
19
HISTORY
@ -1959,7 +1959,7 @@ Video Disk Recorder Revision History
|
|||||||
|
|
||||||
2003-02-16: Version 1.1.25
|
2003-02-16: Version 1.1.25
|
||||||
|
|
||||||
- Fixed high CPU load during replay (thanks Marcel Wiesweg for pointing out this
|
- Fixed high CPU load during replay (thanks to Marcel Wiesweg for pointing out this
|
||||||
one).
|
one).
|
||||||
- Fixed margin handling in cRingBufferLinear.
|
- Fixed margin handling in cRingBufferLinear.
|
||||||
- Now polling the output device in 'Transfer Mode' and retrying to put packets
|
- Now polling the output device in 'Transfer Mode' and retrying to put packets
|
||||||
@ -1970,3 +1970,20 @@ Video Disk Recorder Revision History
|
|||||||
- Fixed broken support for raw OSDs of plugins (thanks to Marcel Wiesweg for
|
- Fixed broken support for raw OSDs of plugins (thanks to Marcel Wiesweg for
|
||||||
reporting this one).
|
reporting this one).
|
||||||
- Broken CAM connections are now restored automatically.
|
- Broken CAM connections are now restored automatically.
|
||||||
|
|
||||||
|
2003-03-19: Version 1.1.26
|
||||||
|
|
||||||
|
- Removed signal handling and usleep(5000) from cDvbOsd::Cmd() (apparently this
|
||||||
|
is no longer necessary with DVB driver 1.0.0pre2 or later).
|
||||||
|
- If the primary device (as defined in setup.conf) doesn't have an MPEG decoder
|
||||||
|
(and thus can't be used as a primary device) VDR now scans all devices at
|
||||||
|
startup and uses the first one (if any) that actually has an MPEG decoder.
|
||||||
|
That way this will also work automatically if the primary device is implemented
|
||||||
|
by a plugin.
|
||||||
|
- Fixed a possible deadlock when using the "Blue" button in the "Schedules" menu
|
||||||
|
to switch to an other channel (thanks to Torsten Herz).
|
||||||
|
- Fixed the EPG bugfix code number for the MAX_USEFUL_SUBTITLE_LENGTH fix (thanks to
|
||||||
|
Torsten Herz for reporting this one).
|
||||||
|
- Modified the EPG scanner to avoid CPU load peaks (thanks to Steffen Becker for
|
||||||
|
reporting this one).
|
||||||
|
- Fixed support for Viaccess CAMs (thanks to Axel Gruber for helping to debug this).
|
||||||
|
4
ci.c
4
ci.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: ci.c 1.7 2003/02/16 11:20:55 kls Exp $
|
* $Id: ci.c 1.8 2003/03/16 22:32:47 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* XXX TODO
|
/* XXX TODO
|
||||||
@ -1232,7 +1232,7 @@ cCiCaPmt::cCiCaPmt(int ProgramNumber)
|
|||||||
capmt[length++] = CPLM_ONLY;
|
capmt[length++] = CPLM_ONLY;
|
||||||
capmt[length++] = (ProgramNumber >> 8) & 0xFF;
|
capmt[length++] = (ProgramNumber >> 8) & 0xFF;
|
||||||
capmt[length++] = ProgramNumber & 0xFF;
|
capmt[length++] = ProgramNumber & 0xFF;
|
||||||
capmt[length++] = 0x00; //XXX version_number, current_next_indicator - apparently may be 0x00
|
capmt[length++] = 0x01; // version_number, current_next_indicator - apparently vn doesn't matter, but cni must be 1
|
||||||
esInfoLengthPos = length;
|
esInfoLengthPos = length;
|
||||||
capmt[length++] = 0x00; // program_info_length H (at program level)
|
capmt[length++] = 0x00; // program_info_length H (at program level)
|
||||||
capmt[length++] = 0x00; // program_info_length L
|
capmt[length++] = 0x00; // program_info_length L
|
||||||
|
4
config.h
4
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.h 1.149 2003/02/15 11:01:04 kls Exp $
|
* $Id: config.h 1.150 2003/03/09 10:01:02 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H
|
#ifndef __CONFIG_H
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
#define VDRVERSION "1.1.25"
|
#define VDRVERSION "1.1.26"
|
||||||
|
|
||||||
#define MAXPRIORITY 99
|
#define MAXPRIORITY 99
|
||||||
#define MAXLIFETIME 99
|
#define MAXLIFETIME 99
|
||||||
|
21
device.c
21
device.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: device.c 1.36 2003/01/03 15:41:14 kls Exp $
|
* $Id: device.c 1.37 2003/03/09 14:05:23 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -106,14 +106,19 @@ bool cDevice::SetPrimaryDevice(int n)
|
|||||||
{
|
{
|
||||||
n--;
|
n--;
|
||||||
if (0 <= n && n < numDevices && device[n]) {
|
if (0 <= n && n < numDevices && device[n]) {
|
||||||
isyslog("setting primary device to %d", n + 1);
|
if (device[n]->HasDecoder()) {
|
||||||
if (primaryDevice)
|
isyslog("setting primary device to %d", n + 1);
|
||||||
primaryDevice->MakePrimaryDevice(false);
|
if (primaryDevice)
|
||||||
primaryDevice = device[n];
|
primaryDevice->MakePrimaryDevice(false);
|
||||||
primaryDevice->MakePrimaryDevice(true);
|
primaryDevice = device[n];
|
||||||
return true;
|
primaryDevice->MakePrimaryDevice(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog("ERROR: device number %d has no MPEG decoder", n + 1);
|
||||||
}
|
}
|
||||||
esyslog("invalid primary device number: %d", n + 1);
|
else
|
||||||
|
esyslog("ERROR: invalid primary device number: %d", n + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
13
dvbosd.c
13
dvbosd.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: dvbosd.c 1.19 2002/08/25 09:53:51 kls Exp $
|
* $Id: dvbosd.c 1.20 2003/03/09 09:59:13 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbosd.h"
|
#include "dvbosd.h"
|
||||||
@ -60,18 +60,7 @@ void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co
|
|||||||
dc.x1 = x1;
|
dc.x1 = x1;
|
||||||
dc.y1 = y1;
|
dc.y1 = y1;
|
||||||
dc.data = (void *)data;
|
dc.data = (void *)data;
|
||||||
// must block all signals, otherwise the command might not be fully executed
|
|
||||||
sigset_t set, oldset;
|
|
||||||
sigfillset(&set);
|
|
||||||
sigdelset(&set, SIGALRM);
|
|
||||||
sigprocmask(SIG_BLOCK, &set, &oldset);
|
|
||||||
ioctl(osdDev, OSD_SEND_CMD, &dc);
|
ioctl(osdDev, OSD_SEND_CMD, &dc);
|
||||||
if (cmd == OSD_SetBlock) // XXX this is the only command that takes longer
|
|
||||||
usleep(5000); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places
|
|
||||||
// XXX and sometimes the OSD was no longer displayed).
|
|
||||||
// XXX Increase the value if the problem still persists on your particular system.
|
|
||||||
// TODO Check if this is still necessary with driver versions after 0.7.
|
|
||||||
sigprocmask(SIG_SETMASK, &oldset, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
eit.c
11
eit.c
@ -16,7 +16,7 @@
|
|||||||
* the Free Software Foundation; either version 2 of the License, or *
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
* (at your option) any later version. *
|
* (at your option) any later version. *
|
||||||
* *
|
* *
|
||||||
* $Id: eit.c 1.65 2003/02/02 15:41:03 kls Exp $
|
* $Id: eit.c 1.67 2003/03/16 11:20:05 kls Exp $
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
|
|
||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
@ -607,7 +607,7 @@ void cEventInfo::FixEpgBugs(void)
|
|||||||
free(pExtendedDescription);
|
free(pExtendedDescription);
|
||||||
pExtendedDescription = pSubtitle;
|
pExtendedDescription = pSubtitle;
|
||||||
pSubtitle = NULL;
|
pSubtitle = NULL;
|
||||||
EpgBugFixStat(5, GetChannelID());
|
EpgBugFixStat(6, GetChannelID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1250,12 +1250,13 @@ void cSIProcessor::Action()
|
|||||||
if (seclen == r)
|
if (seclen == r)
|
||||||
{
|
{
|
||||||
//dsyslog("Received pid 0x%04X with table ID 0x%02X and length of %4d\n", pid, buf[0], seclen);
|
//dsyslog("Received pid 0x%04X with table ID 0x%02X and length of %4d\n", pid, buf[0], seclen);
|
||||||
|
cMutexLock MutexLock(&schedulesMutex); // since the xMem... stuff is not thread safe, we need to use a "global" mutex
|
||||||
|
LOCK_THREAD;
|
||||||
switch (pid)
|
switch (pid)
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00:
|
||||||
if (buf[0] == 0x00)
|
if (buf[0] == 0x00)
|
||||||
{
|
{
|
||||||
LOCK_THREAD;
|
|
||||||
if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
|
if (pmtPid && time(NULL) - lastPmtScan > PMT_SCAN_TIMEOUT) {
|
||||||
DelFilter(pmtPid, 0x02);
|
DelFilter(pmtPid, 0x02);
|
||||||
pmtPid = 0;
|
pmtPid = 0;
|
||||||
@ -1263,7 +1264,6 @@ void cSIProcessor::Action()
|
|||||||
lastPmtScan = time(NULL);
|
lastPmtScan = time(NULL);
|
||||||
}
|
}
|
||||||
if (!pmtPid) {
|
if (!pmtPid) {
|
||||||
cMutexLock MutexLock(&schedulesMutex); // since the xMem... stuff is not thread safe, we need to use a "global" mutex
|
|
||||||
struct LIST *pat = siParsePAT(buf);
|
struct LIST *pat = siParsePAT(buf);
|
||||||
if (pat) {
|
if (pat) {
|
||||||
int Index = 0;
|
int Index = 0;
|
||||||
@ -1301,7 +1301,6 @@ void cSIProcessor::Action()
|
|||||||
case 0x12:
|
case 0x12:
|
||||||
if (buf[0] != 0x72)
|
if (buf[0] != 0x72)
|
||||||
{
|
{
|
||||||
cMutexLock MutexLock(&schedulesMutex);
|
|
||||||
cEIT ceit(buf, seclen, schedules);
|
cEIT ceit(buf, seclen, schedules);
|
||||||
ceit.ProcessEIT(buf, currentSource);
|
ceit.ProcessEIT(buf, currentSource);
|
||||||
}
|
}
|
||||||
@ -1310,9 +1309,7 @@ void cSIProcessor::Action()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
LOCK_THREAD;
|
|
||||||
if (pid == pmtPid && buf[0] == 0x02 && currentSource && currentTransponder) {
|
if (pid == pmtPid && buf[0] == 0x02 && currentSource && currentTransponder) {
|
||||||
cMutexLock MutexLock(&schedulesMutex); // since the xMem... stuff is not thread safe, we need to use a "global" mutex
|
|
||||||
struct Pid *pi = siParsePMT(buf);
|
struct Pid *pi = siParsePMT(buf);
|
||||||
if (pi) {
|
if (pi) {
|
||||||
for (struct LIST *d = (struct LIST *)pi->Descriptors; d; d = (struct LIST *)xSucc(d)) {
|
for (struct LIST *d = (struct LIST *)pi->Descriptors; d; d = (struct LIST *)xSucc(d)) {
|
||||||
|
61
eitscan.c
61
eitscan.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: eitscan.c 1.11 2003/01/26 16:19:24 kls Exp $
|
* $Id: eitscan.c 1.12 2003/03/16 13:29:55 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "eitscan.h"
|
#include "eitscan.h"
|
||||||
@ -16,7 +16,7 @@ cEITScanner::cEITScanner(void)
|
|||||||
{
|
{
|
||||||
lastScan = lastActivity = time(NULL);
|
lastScan = lastActivity = time(NULL);
|
||||||
currentChannel = 0;
|
currentChannel = 0;
|
||||||
lastChannel = 0;
|
memset(lastChannel, 0, sizeof(lastChannel));
|
||||||
numTransponders = 0;
|
numTransponders = 0;
|
||||||
transponders = NULL;
|
transponders = NULL;
|
||||||
}
|
}
|
||||||
@ -51,42 +51,33 @@ void cEITScanner::Process(void)
|
|||||||
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
|
if (Setup.EPGScanTimeout && Channels.MaxNumber() > 1) {
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
|
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
|
||||||
do {
|
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||||
int oldLastChannel = lastChannel;
|
cDevice *Device = cDevice::GetDevice(i);
|
||||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
if (Device && Device->CardIndex() < MAXDVBDEVICES) {
|
||||||
cDevice *Device = cDevice::GetDevice(i);
|
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
|
||||||
if (Device && Device->CardIndex() < MAXDVBDEVICES) {
|
if (!(Device->Receiving(true) || Device->Replaying())) {
|
||||||
if (Device != cDevice::PrimaryDevice() || (cDevice::NumDevices() == 1 && Setup.EPGScanTimeout && now - lastActivity > Setup.EPGScanTimeout * 3600)) {
|
for (;;) {
|
||||||
if (!(Device->Receiving(true) || Device->Replaying())) {
|
cChannel *Channel = Channels.GetByNumber(lastChannel[Device->DeviceNumber()] + 1, 1);
|
||||||
int oldCh = lastChannel;
|
if (Channel) {
|
||||||
int ch = oldCh + 1;
|
lastChannel[Device->DeviceNumber()] = Channel->Number();
|
||||||
while (ch != oldCh) {
|
if (Channel->Sid() && Device->ProvidesChannel(Channel) && !TransponderScanned(Channel)) {
|
||||||
if (ch > Channels.MaxNumber()) {
|
if (Device == cDevice::PrimaryDevice() && !currentChannel)
|
||||||
ch = 1;
|
currentChannel = Device->CurrentChannel();
|
||||||
numTransponders = 0;
|
Device->SwitchChannel(Channel, false);
|
||||||
}
|
break;
|
||||||
cChannel *Channel = Channels.GetByNumber(ch, 1);
|
}
|
||||||
if (Channel) {
|
}
|
||||||
if (!Device->ProvidesChannel(Channel))
|
else {
|
||||||
break;
|
if (lastChannel[Device->DeviceNumber()])
|
||||||
if (Channel->Sid() && !TransponderScanned(Channel)) {
|
numTransponders = 0;
|
||||||
if (Device == cDevice::PrimaryDevice() && !currentChannel)
|
lastChannel[Device->DeviceNumber()] = 0;
|
||||||
currentChannel = Device->CurrentChannel();
|
break;
|
||||||
Device->SwitchChannel(Channel, false);
|
}
|
||||||
lastChannel = ch;
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ch = Channel->Number() + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (lastChannel != oldLastChannel)
|
}
|
||||||
break;
|
|
||||||
lastChannel++;
|
|
||||||
} while (time(NULL) - now < 2);
|
|
||||||
lastScan = time(NULL);
|
lastScan = time(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: eitscan.h 1.1 2002/05/20 11:00:05 kls Exp $
|
* $Id: eitscan.h 1.2 2003/03/16 13:20:40 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __EITSCAN_H
|
#ifndef __EITSCAN_H
|
||||||
@ -19,7 +19,8 @@ private:
|
|||||||
ScanTimeout = 20
|
ScanTimeout = 20
|
||||||
};
|
};
|
||||||
time_t lastScan, lastActivity;
|
time_t lastScan, lastActivity;
|
||||||
int currentChannel, lastChannel;
|
int currentChannel;
|
||||||
|
int lastChannel[MAXDEVICES];
|
||||||
int numTransponders, *transponders;
|
int numTransponders, *transponders;
|
||||||
bool TransponderScanned(cChannel *Channel);
|
bool TransponderScanned(cChannel *Channel);
|
||||||
public:
|
public:
|
||||||
|
12
vdr.c
12
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.145 2003/02/16 10:34:24 kls Exp $
|
* $Id: vdr.c 1.146 2003/03/09 14:07:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -364,6 +364,16 @@ int main(int argc, char *argv[])
|
|||||||
// Primary device:
|
// Primary device:
|
||||||
|
|
||||||
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
|
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
|
||||||
|
if (!cDevice::PrimaryDevice()) {
|
||||||
|
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||||
|
cDevice *d = cDevice::GetDevice(i);
|
||||||
|
if (d && d->HasDecoder()) {
|
||||||
|
isyslog("trying device number %d instead", i + 1);
|
||||||
|
if (cDevice::SetPrimaryDevice(i + 1))
|
||||||
|
Setup.PrimaryDVB = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!cDevice::PrimaryDevice()) {
|
if (!cDevice::PrimaryDevice()) {
|
||||||
const char *msg = "no primary device found - giving up!";
|
const char *msg = "no primary device found - giving up!";
|
||||||
fprintf(stderr, "vdr: %s\n", msg);
|
fprintf(stderr, "vdr: %s\n", msg);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user