diff options
Diffstat (limited to 'dispatcher.c')
-rw-r--r-- | dispatcher.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/dispatcher.c b/dispatcher.c new file mode 100644 index 0000000..3b5ff14 --- /dev/null +++ b/dispatcher.c @@ -0,0 +1,435 @@ +/* + * dispatcher.c + */ + +#include "dispatcher.h" +#include "setup.h" +#include "audiorecorder.h" +#include "postproc.h" +#include "a-tools.h" + +#include <vdr/device.h> +#include <vdr/channels.h> +#include <vdr/plugin.h> + +#include <unistd.h> +#include <sys/statfs.h> +#include <unistd.h> + + +#define COUNTER 30 + +cRecorderChannels RecorderChannels; + +using namespace std; + +/* --- cDispatcher----------------------------------------------------------- */ + +cDispatcher::cDispatcher(void) +:cThread(), cStatus() +{ + tChannelID channel_id; + int c; + + active = false; + + /* Load Channels to record */ + + RecorderChannels.Load(cPluginAudiorecorder::get_cfg().c_str()); + + if (RecorderChannels.Count() < 1) { + dsyslog("[audiorecorder]: you must have defined at least one channel for recording in %s (%s, %s()", cPluginAudiorecorder::get_cfg().c_str(), + __FILE__, __func__); + Skins.QueueMessage(mtInfo, tr("Audiorecorder: No Channels defined !")); + return; + } + + /* initialize one receiver for each channel */ + c = 0; + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (cl->Channel()) { + dsyslog("[audiorecorder]: Channel to be recorded # %d <%s>, <%s> %s (%s, %s())", + c, + cl->ChannelString(), + cl->Comment(), cl->Channel()->Name(), + __FILE__, __func__); + + if (cl->NewAudioReceiver()) + c++; + } + } + + dsyslog("[audiorecorder]: Number of Channels : %d - Number of Receivers : %d (%s, %s))", RecorderChannels.Count(), c, __FILE__, __func__); + + if (SetupValues.start_type == 1) + Activate(true); +} + + +cDispatcher::~cDispatcher() +{ + Activate(false); + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (cl->AudioReceiver()) { + cl->DeleteAudioReceiver(); + } + } +} + + +void cDispatcher::Activate(bool on) +{ + if (on) { + if (! active) { + active = true; + Start(); + } + } else if (active) { + active = false; + Cancel(3); + } +} + + +void cDispatcher::Action(void) +{ + bool interrupted = false; + counter = COUNTER; + + dsyslog("[audiorecorder]: dispatcher thread started (%s, %s())", + __FILE__, __func__); + + while (active) { + sleep(1); + + int num_queued = cPostproc::get_num_queued(); + + if (interrupted && num_queued > (SetupValues.max_postproc / 4)) + continue; + + interrupted = false; + + if (num_queued >= SetupValues.max_postproc) { + dsyslog("[audiorecorder]: max. postprocessings in queue" + " achieved (%d), detaching all receivers (%s, " + "%s())", num_queued, __FILE__, __func__); + + interrupted = true; + detach_receivers(-1, 0); + + continue; + } + + if (! check_free_disc_space()) { + dsyslog("[audiorecorder]: min. free disc space on %s " + "reached (%d mb), recording will be stopped " + "(%s, %s())", + cPluginAudiorecorder::get_recdir().c_str(), + SetupValues.min_free_space, __FILE__, + __func__); + + active = false; + continue; + } + + int receivers = get_attached_receivers(-1); + if (SetupValues.max_receivers > receivers) { + if (counter > 0) + counter--; + else + attach_receivers(); + } + else if (SetupValues.max_receivers < receivers) + detach_receivers(-1, SetupValues.max_receivers); + } + + detach_receivers(-1, 0); + + dsyslog("[audiorecorder]: dispatcher thread stopped (%s, %s())", + __FILE__, __func__); +} + + +void cDispatcher::ChannelSwitch(const cDevice *device, int channel_number) +{ + /* + * workaround to detach active audioreceivers if the attached device + * is switched to another transponder. this should be done inside of + * vdr, but there are situations which lead to emergency exits of vdr + * without this peace of code (for example if a recording starts). + * + */ + + int device_number; + + if (channel_number == 0) { + counter = COUNTER; + return; + } + + device_number = device->DeviceNumber(); + + if (get_attached_receivers(device_number) == 0) + return; + + if (Channels.GetByNumber(channel_number)->Transponder() == + get_transponder_of_first_receiver(device_number)) + return; + + detach_receivers(device_number, 0); +} + + +int cDispatcher::get_attached_receivers(int device_number) +{ + int count = 0; + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (cl->AudioReceiver()) { + if (cl->AudioReceiver()->is_attached(device_number)) { + ++count; + } + } + } + return count; +} + + +int cDispatcher::get_recording_receivers(void) +{ + int count = 0; + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (cl->AudioReceiver()) { + if (cl->AudioReceiver()->is_recording()) { + ++count; + } + } + } + return count; +} + + +int cDispatcher::get_recording_status(const cChannel *channel) +{ + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (cl->AudioReceiver() && cl->AudioReceiver()->get_channel() == channel) { + if (cl->AudioReceiver()->is_recording()) + return 3; + else if (cl->AudioReceiver()->is_attached(-1)) + return 2; + else + return 1; + } + } + + return 0; +} + +int cDispatcher::get_no_of_channels(void) { + return RecorderChannels.Count(); +} + +void cDispatcher::detach_receivers(int detach_device_number, + int devices_remaining) +{ + int device_number; + cDevice *device = NULL; + + Lock(); + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (get_attached_receivers(-1) <= devices_remaining) + break; + + if (!cl->AudioReceiver()) + continue; + + device_number = cl->AudioReceiver()->get_device_number(); + + if (device_number < 0) + continue; + + if (detach_device_number != device_number && detach_device_number > 0) + continue; + + device = cDevice::GetDevice(device_number); + + if (! device) + continue; + + device->Detach(cl->AudioReceiver()); + } + Unlock(); +} + + +void cDispatcher::attach_receivers(void) +{ + cAudioReceiver *receiver; + cDevice *device; + bool needs_detach = false; + + Lock(); + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (get_attached_receivers(-1) >= SetupValues.max_receivers) + break; + + receiver = cl->AudioReceiver(); + + if (! receiver || receiver->is_attached(-1)) + continue; + + device = cDevice::GetDevice(receiver->get_channel(), 0, needs_detach); + + if (! device || (device && needs_detach)) + continue; + +#ifndef AUDIORCORDER_DEVEL + if (device == cDevice::ActualDevice() && + ! device->IsTunedToTransponder(receiver->get_channel())) + continue; +#endif + if (! device->IsTunedToTransponder(receiver->get_channel()) && + ! device->SwitchChannel(receiver->get_channel(), false)) + continue; + + receiver->set_device_number(device->DeviceNumber()); + device->AttachReceiver(receiver); + } + + Unlock(); +} + + +int cDispatcher::get_transponder_of_first_receiver(int device_number) +{ + int transponder = 0; + + for (cChannelList *cl = RecorderChannels.First(); cl; cl = RecorderChannels.Next(cl)) { + if (! cl->AudioReceiver()) + continue; + + if (cl->AudioReceiver()->is_attached(device_number)) { + transponder = cl->AudioReceiver()->get_channel()->Transponder(); + break; + } + } + + return transponder; +} + + +bool cDispatcher::check_free_disc_space(void) +{ + struct statfs stat_fs; + + if (statfs(cPluginAudiorecorder::get_recdir().c_str(), &stat_fs) != 0) + return false; + + if ((uint64_t)stat_fs.f_bavail * (uint64_t)stat_fs.f_bsize < + (uint64_t)SetupValues.min_free_space * (uint64_t)1024 * + (uint64_t)1024) + return false; + + return true; +} + +// -- cRecorderChannels -------------------------------------------------------------- + +bool cRecorderChannels::Load(const char *filename, bool dummy) +{ + dsyslog("[audiorecorder]: <%s> (%s, %s()", filename, __FILE__, __func__); + + if(cConfig < cChannelList >::Load(filename, true)) { + SetSource(First()); + return true; + } + return false; +} + +// -- cChannelList -------------------------------------------------------------- + +cChannelList::cChannelList(void) +{ + audioreceiver = NULL; +} + +cChannelList::cChannelList(const char *_channelstring, const char *_comment) +{ + Set(_channelstring, _comment); +} + +cChannelList::~cChannelList() +{ + if (audioreceiver) { + delete audioreceiver; + } + if (channelstring) { + free(channelstring); + } + if (comment) { + free(comment); + } +} + +void cChannelList::Set(const char *_channelstring, const char *_comment) +{ + channelstring = strdup(_channelstring); + comment = strdup(_comment); + channel_id = tChannelID::FromString(channelstring); + + if (! channel_id.Valid()) { + dsyslog("[audiorecorder]: channel %s is not valid (%s, %s())", channelstring, __FILE__, __func__); + } else { + channel = Channels.GetByChannelID(channel_id, true, true); + if (channel) { + dsyslog("[audiorecorder]: channel %s set (%s, %s())", channel->Name(), __FILE__, __func__); + } else { + dsyslog("[audiorecorder]: channel not set : %s %d, %d, %d, %d, %d (%s, %s())", channelstring, + channel_id.Source(), + channel_id.Nid(), + channel_id.Tid(), + channel_id.Sid(), + channel_id.Rid(), + __FILE__, __func__); + } + } +} + +cAudioReceiver *cChannelList::NewAudioReceiver() +{ + if (channel) { + audioreceiver = new cAudioReceiver(channel); + return audioreceiver; + } + return NULL; +} + +void cChannelList::DeleteAudioReceiver(void) { + if (audioreceiver) + delete audioreceiver; +} + +bool cChannelList::Parse(char *s) +{ + char cs[256], comment[256], *p1; + + int fields = sscanf(s, "%s", cs); + if (fields >= 1) { + p1 = s; + p1 += strlen(cs) + 1; + + strncpy(comment, p1, 255); + comment[255] = 0; + + Set(cs, comment); + return true; + } else { + return false; + } +} + |