summaryrefslogtreecommitdiff
path: root/dispatcher.c
diff options
context:
space:
mode:
Diffstat (limited to 'dispatcher.c')
-rw-r--r--dispatcher.c435
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;
+ }
+}
+